Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
480df01
Initial plan
Copilot Feb 17, 2026
95241dd
Add Interlocked.Or<T> and Interlocked.And<T> polyfills with tests
Copilot Feb 17, 2026
e9341cf
Fix Interlocked polyfills - use generic CompareExchange and fix test …
Copilot Feb 17, 2026
ebd527c
Add comment about NET11 conditional compilation
Copilot Feb 17, 2026
6c9a45b
Address PR feedback: simplify implementation, remove Theory tests, fi…
Copilot Feb 17, 2026
3c26340
Add specific overloads for int/uint/long/ulong, remove namespace qual…
Copilot Feb 17, 2026
9ee2c72
Refactor to use unsafe pointers for non-generic overloads, keep Unsaf…
Copilot Feb 18, 2026
8bee912
Extend Interlocked polyfills to support .NET Standard 2.0+ by impleme…
Copilot Feb 18, 2026
e36d16d
Extend Interlocked polyfills to all TFMs using standard Net100 pattern
Copilot Feb 18, 2026
c61b1d2
Refactor: Move non-generic Interlocked.And/Or to Net50, keep generic …
Copilot Feb 18, 2026
fe503a0
Fix CompareExchange loop logic in Net50/Interlocked.cs
Copilot Feb 18, 2026
7da9286
Remove generic Interlocked.And<T> and Interlocked.Or<T> polyfills - t…
Copilot Feb 18, 2026
9298eae
Fix return values - And/Or methods should return original value, not …
Copilot Feb 18, 2026
bb1f1be
Address PR feedback: simplify test names, reorder methods, add ALLOW_…
Copilot Feb 19, 2026
b6389a2
Remove unsafe modifier from method signatures, use internal unsafe bl…
Copilot Feb 19, 2026
f3c6f0e
Reset Signatures.md to original version before merge
Copilot Feb 19, 2026
f7ee9e5
Merge master into copilot/add-interlocked-polyfills
Copilot Feb 19, 2026
db95a53
Regenerate Signatures.md after merging master
Copilot Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions PolyShim.Tests/Net50/InterlockedTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Threading;
using FluentAssertions;
using Xunit;

namespace PolyShim.Tests.Net50;

public class InterlockedTests
{
[Fact]
public void And_Test()
{
// uint
uint ui = 0xFFFFFFFF;
Interlocked.And(ref ui, 0xAAAAAAAA).Should().Be(0xFFFFFFFF);
ui.Should().Be(0xAAAAAAAA);

// int
int i = unchecked((int)0xFFFFFFFF);
Interlocked.And(ref i, unchecked((int)0xAAAAAAAA)).Should().Be(unchecked((int)0xFFFFFFFF));
i.Should().Be(unchecked((int)0xAAAAAAAA));

// ulong
ulong ul = 0xFFFFFFFFFFFFFFFF;
Interlocked.And(ref ul, 0xAAAAAAAAAAAAAAAA).Should().Be(0xFFFFFFFFFFFFFFFF);
ul.Should().Be(0xAAAAAAAAAAAAAAAA);

// long
long l = unchecked((long)0xFFFFFFFFFFFFFFFF);
Interlocked
.And(ref l, unchecked((long)0xAAAAAAAAAAAAAAAA))
.Should()
.Be(unchecked((long)0xFFFFFFFFFFFFFFFF));
l.Should().Be(unchecked((long)0xAAAAAAAAAAAAAAAA));
}

[Fact]
public void Or_Test()
{
// uint
uint ui = 0x0000FFFF;
Interlocked.Or(ref ui, 0xFFFF0000).Should().Be(0x0000FFFF);
ui.Should().Be(0xFFFFFFFF);

// int
int i = 0x0000FFFF;
Interlocked.Or(ref i, unchecked((int)0xFFFF0000)).Should().Be(0x0000FFFF);
i.Should().Be(unchecked((int)0xFFFFFFFF));

// ulong
ulong ul = 0x00000000FFFFFFFF;
Interlocked.Or(ref ul, 0xFFFFFFFF00000000).Should().Be(0x00000000FFFFFFFF);
ul.Should().Be(0xFFFFFFFFFFFFFFFF);

// long
long l = 0x00000000FFFFFFFF;
Interlocked.Or(ref l, unchecked((long)0xFFFFFFFF00000000)).Should().Be(0x00000000FFFFFFFF);
l.Should().Be(unchecked((long)0xFFFFFFFFFFFFFFFF));
}
}
142 changes: 142 additions & 0 deletions PolyShim/Net50/Interlocked.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#if (NETCOREAPP && !NET5_0_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD)
#nullable enable
// ReSharper disable RedundantUsingDirective
// ReSharper disable CheckNamespace
// ReSharper disable InconsistentNaming
// ReSharper disable PartialTypeWithSinglePart

using System.Diagnostics.CodeAnalysis;
using System.Threading;

[ExcludeFromCodeCoverage]
internal static class MemberPolyfills_Net50_Interlocked
{
extension(Interlocked)
{
// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int32@-system-int32)
public static int And(ref int location1, int value)
{
int current,
newValue,
original;
do
{
current = location1;
newValue = current & value;
original = Interlocked.CompareExchange(ref location1, newValue, current);
} while (original != current);

return original;
}

#if ALLOW_UNSAFE_BLOCKS
// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32)
public static uint And(ref uint location1, uint value)
{
unsafe
{
fixed (uint* ptr = &location1)
{
int result = Interlocked.And(ref *(int*)ptr, *(int*)&value);
return *(uint*)&result;
}
}
}
#endif

// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int64@-system-int64)
public static long And(ref long location1, long value)
{
long current,
newValue,
original;
do
{
current = location1;
newValue = current & value;
original = Interlocked.CompareExchange(ref location1, newValue, current);
} while (original != current);

return original;
}

#if ALLOW_UNSAFE_BLOCKS
// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64)
public static ulong And(ref ulong location1, ulong value)
{
unsafe
{
fixed (ulong* ptr = &location1)
{
long result = Interlocked.And(ref *(long*)ptr, *(long*)&value);
return *(ulong*)&result;
}
}
}
#endif

// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int32@-system-int32)
public static int Or(ref int location1, int value)
{
int current,
newValue,
original;
do
{
current = location1;
newValue = current | value;
original = Interlocked.CompareExchange(ref location1, newValue, current);
} while (original != current);

return original;
}

#if ALLOW_UNSAFE_BLOCKS
// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32)
public static uint Or(ref uint location1, uint value)
{
unsafe
{
fixed (uint* ptr = &location1)
{
int result = Interlocked.Or(ref *(int*)ptr, *(int*)&value);
return *(uint*)&result;
}
}
}
#endif

// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int64@-system-int64)
public static long Or(ref long location1, long value)
{
long current,
newValue,
original;
do
{
current = location1;
newValue = current | value;
original = Interlocked.CompareExchange(ref location1, newValue, current);
} while (original != current);

return original;
}

#if ALLOW_UNSAFE_BLOCKS
// https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64)
public static ulong Or(ref ulong location1, ulong value)
{
unsafe
{
fixed (ulong* ptr = &location1)
{
long result = Interlocked.Or(ref *(long*)ptr, *(long*)&value);
return *(ulong*)&result;
}
}
}
#endif
}
}

#endif
13 changes: 11 additions & 2 deletions PolyShim/Signatures.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Signatures

- **Total:** 364
- **Total:** 372
- **Types:** 78
- **Members:** 286
- **Members:** 294

___

Expand Down Expand Up @@ -175,6 +175,15 @@ ___
- [**[struct]**](https://learn.microsoft.com/dotnet/api/system.index) <sup><sub>.NET Core 3.0</sub></sup>
- `int`
- [`bool TryParse(string, IFormatProvider?, out int)`](https://learn.microsoft.com/dotnet/api/system.int32.tryparse#system-int32-tryparse(system-string-system-iformatprovider-system-int32@)) <sup><sub>.NET 7.0</sub></sup>
- `Interlocked`
- [`int And(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int32@-system-int32)) <sup><sub>.NET 5.0</sub></sup>
- [`int Or(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int32@-system-int32)) <sup><sub>.NET 5.0</sub></sup>
- [`long And(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int64@-system-int64)) <sup><sub>.NET 5.0</sub></sup>
- [`long Or(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int64@-system-int64)) <sup><sub>.NET 5.0</sub></sup>
- [`uint And(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32)) <sup><sub>.NET 5.0</sub></sup>
- [`uint Or(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32)) <sup><sub>.NET 5.0</sub></sup>
- [`ulong And(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64)) <sup><sub>.NET 5.0</sub></sup>
- [`ulong Or(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64)) <sup><sub>.NET 5.0</sub></sup>
- `IntPtr`
- [`bool TryParse(string, IFormatProvider?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-iformatprovider-system-intptr@)) <sup><sub>.NET 7.0</sub></sup>
- [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) <sup><sub>.NET 5.0</sub></sup>
Expand Down
Loading