From 480df01da44305dd1b3ca572d5f875b7e146c019 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:38:17 +0000 Subject: [PATCH 01/17] Initial plan From 95241dda1e1e527592777b5d72bc5c4113c86c89 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:55:29 +0000 Subject: [PATCH 02/17] Add Interlocked.Or and Interlocked.And polyfills with tests Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/Net100/InterlockedTests.cs | 465 ++++++++++++++++++++++ PolyShim/Net100/Interlocked.cs | 199 +++++++++ PolyShim/Signatures.md | 7 +- 3 files changed, 669 insertions(+), 2 deletions(-) create mode 100644 PolyShim.Tests/Net100/InterlockedTests.cs create mode 100644 PolyShim/Net100/Interlocked.cs diff --git a/PolyShim.Tests/Net100/InterlockedTests.cs b/PolyShim.Tests/Net100/InterlockedTests.cs new file mode 100644 index 0000000..5f9ce31 --- /dev/null +++ b/PolyShim.Tests/Net100/InterlockedTests.cs @@ -0,0 +1,465 @@ +using System; +using System.Threading; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.Net100; + +public class InterlockedTests +{ + private enum ByteEnum : byte + { + Value1 = 0b00001111, + Value2 = 0b11110000, + Value3 = 0b10101010, + } + + private enum ShortEnum : short + { + Value1 = 0x0F0F, + Value2 = unchecked((short)0xF0F0), + Value3 = unchecked((short)0xAAAA), + } + + private enum IntEnum : int + { + Value1 = 0x0000FFFF, + Value2 = unchecked((int)0xFFFF0000), + Value3 = unchecked((int)0xAAAAAAAA), + } + + private enum LongEnum : long + { + Value1 = 0x00000000FFFFFFFF, + Value2 = unchecked((long)0xFFFFFFFF00000000), + Value3 = unchecked((long)0xAAAAAAAAAAAAAAAA), + } + + [Theory] + [InlineData((byte)0b00001111, (byte)0b11110000, (byte)0b00000000)] + [InlineData((byte)0b11111111, (byte)0b10101010, (byte)0b10101010)] + [InlineData((byte)0xFF, (byte)0x0F, (byte)0x0F)] + [InlineData((byte)0, (byte)0xFF, (byte)0)] + public void And_Byte_Test(byte initial, byte value, byte expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData((sbyte)0b00001111, (sbyte)0b01110000, (sbyte)0b00000000)] + [InlineData((sbyte)0b01111111, (sbyte)0b00101010, (sbyte)0b00101010)] + [InlineData((sbyte)-1, (sbyte)15, (sbyte)15)] + [InlineData((sbyte)0, (sbyte)-1, (sbyte)0)] + public void And_SByte_Test(sbyte initial, sbyte value, sbyte expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData((ushort)0x00FF, (ushort)0xFF00, (ushort)0x0000)] + [InlineData((ushort)0xFFFF, (ushort)0xAAAA, (ushort)0xAAAA)] + [InlineData((ushort)0x0FFF, (ushort)0xF0FF, (ushort)0x00FF)] + [InlineData((ushort)0, (ushort)0xFFFF, (ushort)0)] + public void And_UInt16_Test(ushort initial, ushort value, ushort expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData((short)0x00FF, (short)0x7F00, (short)0x0000)] + [InlineData((short)0x7FFF, (short)0x2AAA, (short)0x2AAA)] + [InlineData((short)-1, (short)255, (short)255)] + [InlineData((short)0, (short)-1, (short)0)] + public void And_Int16_Test(short initial, short value, short expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData(0x0000FFFF, 0xFFFF0000, 0x00000000)] + [InlineData(0xFFFFFFFF, 0xAAAAAAAA, 0xAAAAAAAA)] + [InlineData(0x0FFFFFFF, 0xF0FFFFFF, 0x00FFFFFF)] + [InlineData(0, -1, 0)] + public void And_Int32_Test(int initial, int value, int expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData(0x0000FFFFU, 0xFFFF0000U, 0x00000000U)] + [InlineData(0xFFFFFFFFU, 0xAAAAAAAAU, 0xAAAAAAAAU)] + [InlineData(0x0FFFFFFFU, 0xF0FFFFFFU, 0x00FFFFFFU)] + [InlineData(0U, 0xFFFFFFFFU, 0U)] + public void And_UInt32_Test(uint initial, uint value, uint expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData(0x00000000FFFFFFFFL, unchecked((long)0xFFFFFFFF00000000L), 0x0000000000000000L)] + [InlineData( + unchecked((long)0xFFFFFFFFFFFFFFFFL), + unchecked((long)0xAAAAAAAAAAAAAAAAL), + unchecked((long)0xAAAAAAAAAAAAAAAAL) + )] + [InlineData(0x0FFFFFFFFFFFFFFFL, unchecked((long)0xF0FFFFFFFFFFFFFFL), 0x00FFFFFFFFFFFFFFL)] + [InlineData(0L, -1L, 0L)] + public void And_Int64_Test(long initial, long value, long expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData(0x00000000FFFFFFFFUL, 0xFFFFFFFF00000000UL, 0x0000000000000000UL)] + [InlineData(0xFFFFFFFFFFFFFFFFUL, 0xAAAAAAAAAAAAAAAAUL, 0xAAAAAAAAAAAAAAAAUL)] + [InlineData(0x0FFFFFFFFFFFFFFFUL, 0xF0FFFFFFFFFFFFFFUL, 0x00FFFFFFFFFFFFFFUL)] + [InlineData(0UL, 0xFFFFFFFFFFFFFFFFUL, 0UL)] + public void And_UInt64_Test(ulong initial, ulong value, ulong expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.And(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Fact] + public void And_ByteEnum_Test() + { + // Arrange + var location1 = ByteEnum.Value1; + var location2 = ByteEnum.Value3; + + // Act + var result1 = Interlocked.And(ref location1, ByteEnum.Value2); + var result2 = Interlocked.And(ref location2, ByteEnum.Value2); + + // Assert + result1.Should().Be(ByteEnum.Value1); + location1.Should().Be((ByteEnum)0b00000000); + + result2.Should().Be(ByteEnum.Value3); + location2.Should().Be((ByteEnum)0b10100000); + } + + [Fact] + public void And_IntEnum_Test() + { + // Arrange + var location1 = IntEnum.Value1; + var location2 = IntEnum.Value3; + + // Act + var result1 = Interlocked.And(ref location1, IntEnum.Value2); + var result2 = Interlocked.And(ref location2, IntEnum.Value2); + + // Assert + result1.Should().Be(IntEnum.Value1); + location1.Should().Be((IntEnum)0x00000000); + + result2.Should().Be(IntEnum.Value3); + location2.Should().Be((IntEnum)unchecked((int)0xAAAA0000)); + } + + [Theory] + [InlineData((byte)0b00001111, (byte)0b11110000, (byte)0b11111111)] + [InlineData((byte)0b00000000, (byte)0b10101010, (byte)0b10101010)] + [InlineData((byte)0xFF, (byte)0x0F, (byte)0xFF)] + [InlineData((byte)0, (byte)0, (byte)0)] + public void Or_Byte_Test(byte initial, byte value, byte expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData((sbyte)0b00001111, (sbyte)0b01110000, (sbyte)0b01111111)] + [InlineData((sbyte)0b00000000, (sbyte)0b00101010, (sbyte)0b00101010)] + [InlineData((sbyte)-1, (sbyte)15, (sbyte)-1)] + [InlineData((sbyte)0, (sbyte)0, (sbyte)0)] + public void Or_SByte_Test(sbyte initial, sbyte value, sbyte expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData((ushort)0x00FF, (ushort)0xFF00, (ushort)0xFFFF)] + [InlineData((ushort)0x0000, (ushort)0xAAAA, (ushort)0xAAAA)] + [InlineData((ushort)0x0FFF, (ushort)0xF000, (ushort)0xFFFF)] + [InlineData((ushort)0, (ushort)0, (ushort)0)] + public void Or_UInt16_Test(ushort initial, ushort value, ushort expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData((short)0x00FF, (short)0x7F00, (short)0x7FFF)] + [InlineData((short)0x0000, (short)0x2AAA, (short)0x2AAA)] + [InlineData((short)0, (short)255, (short)255)] + [InlineData((short)0, (short)0, (short)0)] + public void Or_Int16_Test(short initial, short value, short expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData(0x0000FFFF, 0xFFFF0000, unchecked((int)0xFFFFFFFF))] + [InlineData(0x00000000, 0xAAAAAAAA, unchecked((int)0xAAAAAAAA))] + [InlineData(0x0FFFFFFF, 0xF0000000, unchecked((int)0xFFFFFFFF))] + [InlineData(0, 0, 0)] + public void Or_Int32_Test(int initial, int value, int expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData(0x0000FFFFU, 0xFFFF0000U, 0xFFFFFFFFU)] + [InlineData(0x00000000U, 0xAAAAAAAAU, 0xAAAAAAAAU)] + [InlineData(0x0FFFFFFFU, 0xF0000000U, 0xFFFFFFFFU)] + [InlineData(0U, 0U, 0U)] + public void Or_UInt32_Test(uint initial, uint value, uint expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData( + 0x00000000FFFFFFFFL, + unchecked((long)0xFFFFFFFF00000000L), + unchecked((long)0xFFFFFFFFFFFFFFFFL) + )] + [InlineData( + 0x0000000000000000L, + unchecked((long)0xAAAAAAAAAAAAAAAAL), + unchecked((long)0xAAAAAAAAAAAAAAAAL) + )] + [InlineData( + 0x0FFFFFFFFFFFFFFFL, + unchecked((long)0xF000000000000000L), + unchecked((long)0xFFFFFFFFFFFFFFFFL) + )] + [InlineData(0L, 0L, 0L)] + public void Or_Int64_Test(long initial, long value, long expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Theory] + [InlineData(0x00000000FFFFFFFFUL, 0xFFFFFFFF00000000UL, 0xFFFFFFFFFFFFFFFFUL)] + [InlineData( + 0x0000000000000000UL, + 0xAAAAAAAAAAAAAAAAUL, + 0xAAAAAAAAAAAAAAAAUL + )] + [InlineData(0x0FFFFFFFFFFFFFFFUL, 0xF000000000000000UL, 0xFFFFFFFFFFFFFFFFUL)] + [InlineData(0UL, 0UL, 0UL)] + public void Or_UInt64_Test(ulong initial, ulong value, ulong expected) + { + // Arrange + var location = initial; + + // Act + var result = Interlocked.Or(ref location, value); + + // Assert + result.Should().Be(initial); + location.Should().Be(expected); + } + + [Fact] + public void Or_ByteEnum_Test() + { + // Arrange + var location1 = ByteEnum.Value1; + var location2 = ByteEnum.Value3; + + // Act + var result1 = Interlocked.Or(ref location1, ByteEnum.Value2); + var result2 = Interlocked.Or(ref location2, ByteEnum.Value2); + + // Assert + result1.Should().Be(ByteEnum.Value1); + location1.Should().Be((ByteEnum)0b11111111); + + result2.Should().Be(ByteEnum.Value3); + location2.Should().Be((ByteEnum)0b11111010); + } + + [Fact] + public void Or_IntEnum_Test() + { + // Arrange + var location1 = IntEnum.Value1; + var location2 = IntEnum.Value3; + + // Act + var result1 = Interlocked.Or(ref location1, IntEnum.Value2); + var result2 = Interlocked.Or(ref location2, IntEnum.Value2); + + // Assert + result1.Should().Be(IntEnum.Value1); + location1.Should().Be((IntEnum)unchecked((int)0xFFFFFFFF)); + + result2.Should().Be(IntEnum.Value3); + location2.Should().Be((IntEnum)unchecked((int)0xFFFFAAAA)); + } + + [Fact] + public void And_Float_ThrowsNotSupportedException() + { + // Arrange + var location = 1.5f; + + // Act & Assert + var ex = Assert.Throws(() => Interlocked.And(ref location, 2.5f)); + ex.Message.Should().Contain("integer"); + } + + [Fact] + public void Or_Float_ThrowsNotSupportedException() + { + // Arrange + var location = 1.5f; + + // Act & Assert + var ex = Assert.Throws(() => Interlocked.Or(ref location, 2.5f)); + ex.Message.Should().Contain("integer"); + } + + [Fact] + public void And_Double_ThrowsNotSupportedException() + { + // Arrange + var location = 1.5; + + // Act & Assert + var ex = Assert.Throws(() => Interlocked.And(ref location, 2.5)); + ex.Message.Should().Contain("integer"); + } + + [Fact] + public void Or_Double_ThrowsNotSupportedException() + { + // Arrange + var location = 1.5; + + // Act & Assert + var ex = Assert.Throws(() => Interlocked.Or(ref location, 2.5)); + ex.Message.Should().Contain("integer"); + } +} diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs new file mode 100644 index 0000000..b6f982d --- /dev/null +++ b/PolyShim/Net100/Interlocked.cs @@ -0,0 +1,199 @@ +#if (NETCOREAPP) || (NETFRAMEWORK) || (NETSTANDARD) +#if NET9_0_OR_GREATER +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[ExcludeFromCodeCoverage] +internal static class MemberPolyfills_Net100_Interlocked +{ + extension(global::System.Threading.Interlocked) + { + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0) + public static T And(ref T location1, T value) + where T : struct + { + // Only integer primitive types and enum types backed by integer types are supported. + // Floating-point types and floating-point backed enums are not supported. + if ( + (!typeof(T).IsPrimitive && !typeof(T).IsEnum) + || typeof(T) == typeof(float) + || typeof(T) == typeof(double) + || ( + typeof(T).IsEnum + && ( + typeof(T).GetEnumUnderlyingType() == typeof(float) + || typeof(T).GetEnumUnderlyingType() == typeof(double) + ) + ) + ) + { + throw new NotSupportedException( + "Only integer primitive types and enums backed by integer types are supported." + ); + } + + // For all types, use CompareExchange-based implementation since we're polyfilling + // the generic overload that doesn't exist yet + if (Unsafe.SizeOf() == 1) + { + ref byte loc = ref Unsafe.As(ref location1); + byte val = Unsafe.As(ref value); + byte current = loc; + while (true) + { + byte newValue = (byte)(current & val); + byte oldValue = (byte) + global::System.Threading.Interlocked.CompareExchange( + ref Unsafe.As(ref loc), + newValue, + current + ); + if (oldValue == current) + { + byte result = oldValue; + return Unsafe.As(ref result); + } + current = oldValue; + } + } + + if (Unsafe.SizeOf() == 2) + { + ref ushort loc = ref Unsafe.As(ref location1); + ushort val = Unsafe.As(ref value); + ushort current = loc; + while (true) + { + ushort newValue = (ushort)(current & val); + ushort oldValue = (ushort) + global::System.Threading.Interlocked.CompareExchange( + ref Unsafe.As(ref loc), + newValue, + current + ); + if (oldValue == current) + { + ushort result = oldValue; + return Unsafe.As(ref result); + } + current = oldValue; + } + } + + if (Unsafe.SizeOf() == 4) + { + ref int loc = ref Unsafe.As(ref location1); + int val = Unsafe.As(ref value); + int result = global::System.Threading.Interlocked.And(ref loc, val); + return Unsafe.As(ref result); + } + + // Unsafe.SizeOf() == 8 + { + ref long loc = ref Unsafe.As(ref location1); + long val = Unsafe.As(ref value); + long result = global::System.Threading.Interlocked.And(ref loc, val); + return Unsafe.As(ref result); + } + } + + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0) + public static T Or(ref T location1, T value) + where T : struct + { + // Only integer primitive types and enum types backed by integer types are supported. + // Floating-point types and floating-point backed enums are not supported. + if ( + (!typeof(T).IsPrimitive && !typeof(T).IsEnum) + || typeof(T) == typeof(float) + || typeof(T) == typeof(double) + || ( + typeof(T).IsEnum + && ( + typeof(T).GetEnumUnderlyingType() == typeof(float) + || typeof(T).GetEnumUnderlyingType() == typeof(double) + ) + ) + ) + { + throw new NotSupportedException( + "Only integer primitive types and enums backed by integer types are supported." + ); + } + + // For all types, use CompareExchange-based implementation since we're polyfilling + // the generic overload that doesn't exist yet + if (Unsafe.SizeOf() == 1) + { + ref byte loc = ref Unsafe.As(ref location1); + byte val = Unsafe.As(ref value); + byte current = loc; + while (true) + { + byte newValue = (byte)(current | val); + byte oldValue = (byte) + global::System.Threading.Interlocked.CompareExchange( + ref Unsafe.As(ref loc), + newValue, + current + ); + if (oldValue == current) + { + byte result = oldValue; + return Unsafe.As(ref result); + } + current = oldValue; + } + } + + if (Unsafe.SizeOf() == 2) + { + ref ushort loc = ref Unsafe.As(ref location1); + ushort val = Unsafe.As(ref value); + ushort current = loc; + while (true) + { + ushort newValue = (ushort)(current | val); + ushort oldValue = (ushort) + global::System.Threading.Interlocked.CompareExchange( + ref Unsafe.As(ref loc), + newValue, + current + ); + if (oldValue == current) + { + ushort result = oldValue; + return Unsafe.As(ref result); + } + current = oldValue; + } + } + + if (Unsafe.SizeOf() == 4) + { + ref int loc = ref Unsafe.As(ref location1); + int val = Unsafe.As(ref value); + int result = global::System.Threading.Interlocked.Or(ref loc, val); + return Unsafe.As(ref result); + } + + // Unsafe.SizeOf() == 8 + { + ref long loc = ref Unsafe.As(ref location1); + long val = Unsafe.As(ref value); + long result = global::System.Threading.Interlocked.Or(ref loc, val); + return Unsafe.As(ref result); + } + } + } +} +#endif +#endif diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index d5c8212..012500b 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 355 +- **Total:** 357 - **Types:** 76 -- **Members:** 279 +- **Members:** 281 ___ @@ -110,6 +110,9 @@ ___ - [`void Move(string, string, bool)`](https://learn.microsoft.com/dotnet/api/system.io.file.move#system-io-file-move(system-string-system-string-system-boolean)) .NET Core 3.0 - `float` - [`bool TryParse(string, IFormatProvider?, out float)`](https://learn.microsoft.com/dotnet/api/system.single.tryparse#system-single-tryparse(system-string-system-iformatprovider-system-single@)) .NET 7.0 +- `global::System.Threading.Interlocked` + - [`unsafe T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 + - [`unsafe T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 - `HashCode` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.hashcode) .NET Core 2.1 - `HashSet` From e9341cf2c6a69a6c5b64564f59babe6dc69f1e05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:58:05 +0000 Subject: [PATCH 03/17] Fix Interlocked polyfills - use generic CompareExchange and fix test data types Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/Net100/InterlockedTests.cs | 18 ++++------ PolyShim/Net100/Interlocked.cs | 44 +++++++++++------------ 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/PolyShim.Tests/Net100/InterlockedTests.cs b/PolyShim.Tests/Net100/InterlockedTests.cs index 5f9ce31..4a41045 100644 --- a/PolyShim.Tests/Net100/InterlockedTests.cs +++ b/PolyShim.Tests/Net100/InterlockedTests.cs @@ -108,9 +108,9 @@ public void And_Int16_Test(short initial, short value, short expected) } [Theory] - [InlineData(0x0000FFFF, 0xFFFF0000, 0x00000000)] - [InlineData(0xFFFFFFFF, 0xAAAAAAAA, 0xAAAAAAAA)] - [InlineData(0x0FFFFFFF, 0xF0FFFFFF, 0x00FFFFFF)] + [InlineData(0x0000FFFF, unchecked((int)0xFFFF0000), 0x00000000)] + [InlineData(unchecked((int)0xFFFFFFFF), unchecked((int)0xAAAAAAAA), unchecked((int)0xAAAAAAAA))] + [InlineData(0x0FFFFFFF, unchecked((int)0xF0FFFFFF), 0x00FFFFFF)] [InlineData(0, -1, 0)] public void And_Int32_Test(int initial, int value, int expected) { @@ -294,9 +294,9 @@ public void Or_Int16_Test(short initial, short value, short expected) } [Theory] - [InlineData(0x0000FFFF, 0xFFFF0000, unchecked((int)0xFFFFFFFF))] - [InlineData(0x00000000, 0xAAAAAAAA, unchecked((int)0xAAAAAAAA))] - [InlineData(0x0FFFFFFF, 0xF0000000, unchecked((int)0xFFFFFFFF))] + [InlineData(0x0000FFFF, unchecked((int)0xFFFF0000), unchecked((int)0xFFFFFFFF))] + [InlineData(0x00000000, unchecked((int)0xAAAAAAAA), unchecked((int)0xAAAAAAAA))] + [InlineData(0x0FFFFFFF, unchecked((int)0xF0000000), unchecked((int)0xFFFFFFFF))] [InlineData(0, 0, 0)] public void Or_Int32_Test(int initial, int value, int expected) { @@ -361,11 +361,7 @@ public void Or_Int64_Test(long initial, long value, long expected) [Theory] [InlineData(0x00000000FFFFFFFFUL, 0xFFFFFFFF00000000UL, 0xFFFFFFFFFFFFFFFFUL)] - [InlineData( - 0x0000000000000000UL, - 0xAAAAAAAAAAAAAAAAUL, - 0xAAAAAAAAAAAAAAAAUL - )] + [InlineData(0x0000000000000000UL, 0xAAAAAAAAAAAAAAAAUL, 0xAAAAAAAAAAAAAAAAUL)] [InlineData(0x0FFFFFFFFFFFFFFFUL, 0xF000000000000000UL, 0xFFFFFFFFFFFFFFFFUL)] [InlineData(0UL, 0UL, 0UL)] public void Or_UInt64_Test(ulong initial, ulong value, ulong expected) diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index b6f982d..f08e71f 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -50,12 +50,11 @@ public static T And(ref T location1, T value) while (true) { byte newValue = (byte)(current & val); - byte oldValue = (byte) - global::System.Threading.Interlocked.CompareExchange( - ref Unsafe.As(ref loc), - newValue, - current - ); + byte oldValue = global::System.Threading.Interlocked.CompareExchange( + ref loc, + newValue, + current + ); if (oldValue == current) { byte result = oldValue; @@ -73,12 +72,11 @@ ref Unsafe.As(ref loc), while (true) { ushort newValue = (ushort)(current & val); - ushort oldValue = (ushort) - global::System.Threading.Interlocked.CompareExchange( - ref Unsafe.As(ref loc), - newValue, - current - ); + ushort oldValue = global::System.Threading.Interlocked.CompareExchange( + ref loc, + newValue, + current + ); if (oldValue == current) { ushort result = oldValue; @@ -139,12 +137,11 @@ public static T Or(ref T location1, T value) while (true) { byte newValue = (byte)(current | val); - byte oldValue = (byte) - global::System.Threading.Interlocked.CompareExchange( - ref Unsafe.As(ref loc), - newValue, - current - ); + byte oldValue = global::System.Threading.Interlocked.CompareExchange( + ref loc, + newValue, + current + ); if (oldValue == current) { byte result = oldValue; @@ -162,12 +159,11 @@ ref Unsafe.As(ref loc), while (true) { ushort newValue = (ushort)(current | val); - ushort oldValue = (ushort) - global::System.Threading.Interlocked.CompareExchange( - ref Unsafe.As(ref loc), - newValue, - current - ); + ushort oldValue = global::System.Threading.Interlocked.CompareExchange( + ref loc, + newValue, + current + ); if (oldValue == current) { ushort result = oldValue; From ebd527cefdf4c8d1ac637008b5c77f563410ea33 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:00:13 +0000 Subject: [PATCH 04/17] Add comment about NET11 conditional compilation Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net100/Interlocked.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index f08e71f..6e20690 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -1,4 +1,6 @@ #if (NETCOREAPP) || (NETFRAMEWORK) || (NETSTANDARD) +// NOTE: These polyfills will be automatically disabled when NET11_0_OR_GREATER is defined. +// Until then, they provide the generic And/Or methods for .NET 9 and 10. #if NET9_0_OR_GREATER #nullable enable // ReSharper disable RedundantUsingDirective From 6c9a45b1838cae0f77919645b9f58ca2e1270465 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:18:20 +0000 Subject: [PATCH 05/17] Address PR feedback: simplify implementation, remove Theory tests, fix conditionals Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/Net100/InterlockedTests.cs | 474 +++------------------- PolyShim/Net100/Interlocked.cs | 153 +------ PolyShim/Signatures.md | 6 +- 3 files changed, 81 insertions(+), 552 deletions(-) diff --git a/PolyShim.Tests/Net100/InterlockedTests.cs b/PolyShim.Tests/Net100/InterlockedTests.cs index 4a41045..e1a1f3f 100644 --- a/PolyShim.Tests/Net100/InterlockedTests.cs +++ b/PolyShim.Tests/Net100/InterlockedTests.cs @@ -7,455 +7,97 @@ namespace PolyShim.Tests.Net100; public class InterlockedTests { - private enum ByteEnum : byte - { - Value1 = 0b00001111, - Value2 = 0b11110000, - Value3 = 0b10101010, - } - - private enum ShortEnum : short - { - Value1 = 0x0F0F, - Value2 = unchecked((short)0xF0F0), - Value3 = unchecked((short)0xAAAA), - } - private enum IntEnum : int { Value1 = 0x0000FFFF, Value2 = unchecked((int)0xFFFF0000), - Value3 = unchecked((int)0xAAAAAAAA), - } - - private enum LongEnum : long - { - Value1 = 0x00000000FFFFFFFF, - Value2 = unchecked((long)0xFFFFFFFF00000000), - Value3 = unchecked((long)0xAAAAAAAAAAAAAAAA), - } - - [Theory] - [InlineData((byte)0b00001111, (byte)0b11110000, (byte)0b00000000)] - [InlineData((byte)0b11111111, (byte)0b10101010, (byte)0b10101010)] - [InlineData((byte)0xFF, (byte)0x0F, (byte)0x0F)] - [InlineData((byte)0, (byte)0xFF, (byte)0)] - public void And_Byte_Test(byte initial, byte value, byte expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData((sbyte)0b00001111, (sbyte)0b01110000, (sbyte)0b00000000)] - [InlineData((sbyte)0b01111111, (sbyte)0b00101010, (sbyte)0b00101010)] - [InlineData((sbyte)-1, (sbyte)15, (sbyte)15)] - [InlineData((sbyte)0, (sbyte)-1, (sbyte)0)] - public void And_SByte_Test(sbyte initial, sbyte value, sbyte expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData((ushort)0x00FF, (ushort)0xFF00, (ushort)0x0000)] - [InlineData((ushort)0xFFFF, (ushort)0xAAAA, (ushort)0xAAAA)] - [InlineData((ushort)0x0FFF, (ushort)0xF0FF, (ushort)0x00FF)] - [InlineData((ushort)0, (ushort)0xFFFF, (ushort)0)] - public void And_UInt16_Test(ushort initial, ushort value, ushort expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData((short)0x00FF, (short)0x7F00, (short)0x0000)] - [InlineData((short)0x7FFF, (short)0x2AAA, (short)0x2AAA)] - [InlineData((short)-1, (short)255, (short)255)] - [InlineData((short)0, (short)-1, (short)0)] - public void And_Int16_Test(short initial, short value, short expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData(0x0000FFFF, unchecked((int)0xFFFF0000), 0x00000000)] - [InlineData(unchecked((int)0xFFFFFFFF), unchecked((int)0xAAAAAAAA), unchecked((int)0xAAAAAAAA))] - [InlineData(0x0FFFFFFF, unchecked((int)0xF0FFFFFF), 0x00FFFFFF)] - [InlineData(0, -1, 0)] - public void And_Int32_Test(int initial, int value, int expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData(0x0000FFFFU, 0xFFFF0000U, 0x00000000U)] - [InlineData(0xFFFFFFFFU, 0xAAAAAAAAU, 0xAAAAAAAAU)] - [InlineData(0x0FFFFFFFU, 0xF0FFFFFFU, 0x00FFFFFFU)] - [InlineData(0U, 0xFFFFFFFFU, 0U)] - public void And_UInt32_Test(uint initial, uint value, uint expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData(0x00000000FFFFFFFFL, unchecked((long)0xFFFFFFFF00000000L), 0x0000000000000000L)] - [InlineData( - unchecked((long)0xFFFFFFFFFFFFFFFFL), - unchecked((long)0xAAAAAAAAAAAAAAAAL), - unchecked((long)0xAAAAAAAAAAAAAAAAL) - )] - [InlineData(0x0FFFFFFFFFFFFFFFL, unchecked((long)0xF0FFFFFFFFFFFFFFL), 0x00FFFFFFFFFFFFFFL)] - [InlineData(0L, -1L, 0L)] - public void And_Int64_Test(long initial, long value, long expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData(0x00000000FFFFFFFFUL, 0xFFFFFFFF00000000UL, 0x0000000000000000UL)] - [InlineData(0xFFFFFFFFFFFFFFFFUL, 0xAAAAAAAAAAAAAAAAUL, 0xAAAAAAAAAAAAAAAAUL)] - [InlineData(0x0FFFFFFFFFFFFFFFUL, 0xF0FFFFFFFFFFFFFFUL, 0x00FFFFFFFFFFFFFFUL)] - [InlineData(0UL, 0xFFFFFFFFFFFFFFFFUL, 0UL)] - public void And_UInt64_Test(ulong initial, ulong value, ulong expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.And(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); } [Fact] - public void And_ByteEnum_Test() + public void And_SupportsVariousIntegerTypes() { - // Arrange - var location1 = ByteEnum.Value1; - var location2 = ByteEnum.Value3; + // uint + uint ui = 0xFFFFFFFF; + Interlocked.And(ref ui, 0xAAAAAAAA).Should().Be(0xFFFFFFFF); + ui.Should().Be(0xAAAAAAAA); - // Act - var result1 = Interlocked.And(ref location1, ByteEnum.Value2); - var result2 = Interlocked.And(ref location2, ByteEnum.Value2); + // int + int i = unchecked((int)0xFFFFFFFF); + Interlocked.And(ref i, unchecked((int)0xAAAAAAAA)).Should().Be(unchecked((int)0xFFFFFFFF)); + i.Should().Be(unchecked((int)0xAAAAAAAA)); - // Assert - result1.Should().Be(ByteEnum.Value1); - location1.Should().Be((ByteEnum)0b00000000); + // ulong + ulong ul = 0xFFFFFFFFFFFFFFFF; + Interlocked.And(ref ul, 0xAAAAAAAAAAAAAAAA).Should().Be(0xFFFFFFFFFFFFFFFF); + ul.Should().Be(0xAAAAAAAAAAAAAAAA); - result2.Should().Be(ByteEnum.Value3); - location2.Should().Be((ByteEnum)0b10100000); - } - - [Fact] - public void And_IntEnum_Test() - { - // Arrange - var location1 = IntEnum.Value1; - var location2 = IntEnum.Value3; + // long + long l = unchecked((long)0xFFFFFFFFFFFFFFFF); + Interlocked + .And(ref l, unchecked((long)0xAAAAAAAAAAAAAAAA)) + .Should() + .Be(unchecked((long)0xFFFFFFFFFFFFFFFF)); + l.Should().Be(unchecked((long)0xAAAAAAAAAAAAAAAA)); - // Act - var result1 = Interlocked.And(ref location1, IntEnum.Value2); - var result2 = Interlocked.And(ref location2, IntEnum.Value2); - - // Assert - result1.Should().Be(IntEnum.Value1); - location1.Should().Be((IntEnum)0x00000000); - - result2.Should().Be(IntEnum.Value3); - location2.Should().Be((IntEnum)unchecked((int)0xAAAA0000)); - } - - [Theory] - [InlineData((byte)0b00001111, (byte)0b11110000, (byte)0b11111111)] - [InlineData((byte)0b00000000, (byte)0b10101010, (byte)0b10101010)] - [InlineData((byte)0xFF, (byte)0x0F, (byte)0xFF)] - [InlineData((byte)0, (byte)0, (byte)0)] - public void Or_Byte_Test(byte initial, byte value, byte expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData((sbyte)0b00001111, (sbyte)0b01110000, (sbyte)0b01111111)] - [InlineData((sbyte)0b00000000, (sbyte)0b00101010, (sbyte)0b00101010)] - [InlineData((sbyte)-1, (sbyte)15, (sbyte)-1)] - [InlineData((sbyte)0, (sbyte)0, (sbyte)0)] - public void Or_SByte_Test(sbyte initial, sbyte value, sbyte expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData((ushort)0x00FF, (ushort)0xFF00, (ushort)0xFFFF)] - [InlineData((ushort)0x0000, (ushort)0xAAAA, (ushort)0xAAAA)] - [InlineData((ushort)0x0FFF, (ushort)0xF000, (ushort)0xFFFF)] - [InlineData((ushort)0, (ushort)0, (ushort)0)] - public void Or_UInt16_Test(ushort initial, ushort value, ushort expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData((short)0x00FF, (short)0x7F00, (short)0x7FFF)] - [InlineData((short)0x0000, (short)0x2AAA, (short)0x2AAA)] - [InlineData((short)0, (short)255, (short)255)] - [InlineData((short)0, (short)0, (short)0)] - public void Or_Int16_Test(short initial, short value, short expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData(0x0000FFFF, unchecked((int)0xFFFF0000), unchecked((int)0xFFFFFFFF))] - [InlineData(0x00000000, unchecked((int)0xAAAAAAAA), unchecked((int)0xAAAAAAAA))] - [InlineData(0x0FFFFFFF, unchecked((int)0xF0000000), unchecked((int)0xFFFFFFFF))] - [InlineData(0, 0, 0)] - public void Or_Int32_Test(int initial, int value, int expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData(0x0000FFFFU, 0xFFFF0000U, 0xFFFFFFFFU)] - [InlineData(0x00000000U, 0xAAAAAAAAU, 0xAAAAAAAAU)] - [InlineData(0x0FFFFFFFU, 0xF0000000U, 0xFFFFFFFFU)] - [InlineData(0U, 0U, 0U)] - public void Or_UInt32_Test(uint initial, uint value, uint expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData( - 0x00000000FFFFFFFFL, - unchecked((long)0xFFFFFFFF00000000L), - unchecked((long)0xFFFFFFFFFFFFFFFFL) - )] - [InlineData( - 0x0000000000000000L, - unchecked((long)0xAAAAAAAAAAAAAAAAL), - unchecked((long)0xAAAAAAAAAAAAAAAAL) - )] - [InlineData( - 0x0FFFFFFFFFFFFFFFL, - unchecked((long)0xF000000000000000L), - unchecked((long)0xFFFFFFFFFFFFFFFFL) - )] - [InlineData(0L, 0L, 0L)] - public void Or_Int64_Test(long initial, long value, long expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); - } - - [Theory] - [InlineData(0x00000000FFFFFFFFUL, 0xFFFFFFFF00000000UL, 0xFFFFFFFFFFFFFFFFUL)] - [InlineData(0x0000000000000000UL, 0xAAAAAAAAAAAAAAAAUL, 0xAAAAAAAAAAAAAAAAUL)] - [InlineData(0x0FFFFFFFFFFFFFFFUL, 0xF000000000000000UL, 0xFFFFFFFFFFFFFFFFUL)] - [InlineData(0UL, 0UL, 0UL)] - public void Or_UInt64_Test(ulong initial, ulong value, ulong expected) - { - // Arrange - var location = initial; - - // Act - var result = Interlocked.Or(ref location, value); - - // Assert - result.Should().Be(initial); - location.Should().Be(expected); + // enum + var e = IntEnum.Value1; + Interlocked.And(ref e, IntEnum.Value2).Should().Be(IntEnum.Value1); + e.Should().Be((IntEnum)0); } [Fact] - public void Or_ByteEnum_Test() + public void Or_SupportsVariousIntegerTypes() { - // Arrange - var location1 = ByteEnum.Value1; - var location2 = ByteEnum.Value3; + // uint + uint ui = 0x0000FFFF; + Interlocked.Or(ref ui, 0xFFFF0000).Should().Be(0x0000FFFF); + ui.Should().Be(0xFFFFFFFF); - // Act - var result1 = Interlocked.Or(ref location1, ByteEnum.Value2); - var result2 = Interlocked.Or(ref location2, ByteEnum.Value2); + // int + int i = 0x0000FFFF; + Interlocked.Or(ref i, unchecked((int)0xFFFF0000)).Should().Be(0x0000FFFF); + i.Should().Be(unchecked((int)0xFFFFFFFF)); - // Assert - result1.Should().Be(ByteEnum.Value1); - location1.Should().Be((ByteEnum)0b11111111); + // ulong + ulong ul = 0x00000000FFFFFFFF; + Interlocked.Or(ref ul, 0xFFFFFFFF00000000).Should().Be(0x00000000FFFFFFFF); + ul.Should().Be(0xFFFFFFFFFFFFFFFF); - result2.Should().Be(ByteEnum.Value3); - location2.Should().Be((ByteEnum)0b11111010); - } + // long + long l = 0x00000000FFFFFFFF; + Interlocked + .Or(ref l, unchecked((long)0xFFFFFFFF00000000)) + .Should() + .Be(0x00000000FFFFFFFF); + l.Should().Be(unchecked((long)0xFFFFFFFFFFFFFFFF)); - [Fact] - public void Or_IntEnum_Test() - { - // Arrange - var location1 = IntEnum.Value1; - var location2 = IntEnum.Value3; - - // Act - var result1 = Interlocked.Or(ref location1, IntEnum.Value2); - var result2 = Interlocked.Or(ref location2, IntEnum.Value2); - - // Assert - result1.Should().Be(IntEnum.Value1); - location1.Should().Be((IntEnum)unchecked((int)0xFFFFFFFF)); - - result2.Should().Be(IntEnum.Value3); - location2.Should().Be((IntEnum)unchecked((int)0xFFFFAAAA)); + // enum + var e = IntEnum.Value1; + Interlocked.Or(ref e, IntEnum.Value2).Should().Be(IntEnum.Value1); + e.Should().Be((IntEnum)unchecked((int)0xFFFFFFFF)); } [Fact] - public void And_Float_ThrowsNotSupportedException() + public void And_ThrowsForFloatingPointTypes() { - // Arrange - var location = 1.5f; - - // Act & Assert - var ex = Assert.Throws(() => Interlocked.And(ref location, 2.5f)); + float f = 1.5f; + var ex = Assert.Throws(() => Interlocked.And(ref f, 2.5f)); ex.Message.Should().Contain("integer"); - } - [Fact] - public void Or_Float_ThrowsNotSupportedException() - { - // Arrange - var location = 1.5f; - - // Act & Assert - var ex = Assert.Throws(() => Interlocked.Or(ref location, 2.5f)); + double d = 1.5; + ex = Assert.Throws(() => Interlocked.And(ref d, 2.5)); ex.Message.Should().Contain("integer"); } [Fact] - public void And_Double_ThrowsNotSupportedException() + public void Or_ThrowsForFloatingPointTypes() { - // Arrange - var location = 1.5; - - // Act & Assert - var ex = Assert.Throws(() => Interlocked.And(ref location, 2.5)); + float f = 1.5f; + var ex = Assert.Throws(() => Interlocked.Or(ref f, 2.5f)); ex.Message.Should().Contain("integer"); - } - - [Fact] - public void Or_Double_ThrowsNotSupportedException() - { - // Arrange - var location = 1.5; - // Act & Assert - var ex = Assert.Throws(() => Interlocked.Or(ref location, 2.5)); + double d = 1.5; + ex = Assert.Throws(() => Interlocked.Or(ref d, 2.5)); ex.Message.Should().Contain("integer"); } } diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index 6e20690..bdbf2a0 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -1,7 +1,4 @@ -#if (NETCOREAPP) || (NETFRAMEWORK) || (NETSTANDARD) -// NOTE: These polyfills will be automatically disabled when NET11_0_OR_GREATER is defined. -// Until then, they provide the generic And/Or methods for .NET 9 and 10. -#if NET9_0_OR_GREATER +#if NET5_0_OR_GREATER #nullable enable // ReSharper disable RedundantUsingDirective // ReSharper disable CheckNamespace @@ -11,187 +8,77 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; [ExcludeFromCodeCoverage] internal static class MemberPolyfills_Net100_Interlocked { - extension(global::System.Threading.Interlocked) + extension(System.Threading.Interlocked) { // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0) public static T And(ref T location1, T value) where T : struct { - // Only integer primitive types and enum types backed by integer types are supported. - // Floating-point types and floating-point backed enums are not supported. - if ( - (!typeof(T).IsPrimitive && !typeof(T).IsEnum) - || typeof(T) == typeof(float) - || typeof(T) == typeof(double) - || ( - typeof(T).IsEnum - && ( - typeof(T).GetEnumUnderlyingType() == typeof(float) - || typeof(T).GetEnumUnderlyingType() == typeof(double) - ) - ) - ) + // Reject floating-point types + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { throw new NotSupportedException( "Only integer primitive types and enums backed by integer types are supported." ); } - // For all types, use CompareExchange-based implementation since we're polyfilling - // the generic overload that doesn't exist yet - if (Unsafe.SizeOf() == 1) - { - ref byte loc = ref Unsafe.As(ref location1); - byte val = Unsafe.As(ref value); - byte current = loc; - while (true) - { - byte newValue = (byte)(current & val); - byte oldValue = global::System.Threading.Interlocked.CompareExchange( - ref loc, - newValue, - current - ); - if (oldValue == current) - { - byte result = oldValue; - return Unsafe.As(ref result); - } - current = oldValue; - } - } - - if (Unsafe.SizeOf() == 2) - { - ref ushort loc = ref Unsafe.As(ref location1); - ushort val = Unsafe.As(ref value); - ushort current = loc; - while (true) - { - ushort newValue = (ushort)(current & val); - ushort oldValue = global::System.Threading.Interlocked.CompareExchange( - ref loc, - newValue, - current - ); - if (oldValue == current) - { - ushort result = oldValue; - return Unsafe.As(ref result); - } - current = oldValue; - } - } - if (Unsafe.SizeOf() == 4) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = global::System.Threading.Interlocked.And(ref loc, val); + int result = System.Threading.Interlocked.And(ref loc, val); return Unsafe.As(ref result); } - // Unsafe.SizeOf() == 8 + if (Unsafe.SizeOf() == 8) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = global::System.Threading.Interlocked.And(ref loc, val); + long result = System.Threading.Interlocked.And(ref loc, val); return Unsafe.As(ref result); } + + throw new NotSupportedException( + $"Type {typeof(T).Name} is not supported. Only 4-byte and 8-byte integer types and enums are supported." + ); } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0) public static T Or(ref T location1, T value) where T : struct { - // Only integer primitive types and enum types backed by integer types are supported. - // Floating-point types and floating-point backed enums are not supported. - if ( - (!typeof(T).IsPrimitive && !typeof(T).IsEnum) - || typeof(T) == typeof(float) - || typeof(T) == typeof(double) - || ( - typeof(T).IsEnum - && ( - typeof(T).GetEnumUnderlyingType() == typeof(float) - || typeof(T).GetEnumUnderlyingType() == typeof(double) - ) - ) - ) + // Reject floating-point types + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { throw new NotSupportedException( "Only integer primitive types and enums backed by integer types are supported." ); } - // For all types, use CompareExchange-based implementation since we're polyfilling - // the generic overload that doesn't exist yet - if (Unsafe.SizeOf() == 1) - { - ref byte loc = ref Unsafe.As(ref location1); - byte val = Unsafe.As(ref value); - byte current = loc; - while (true) - { - byte newValue = (byte)(current | val); - byte oldValue = global::System.Threading.Interlocked.CompareExchange( - ref loc, - newValue, - current - ); - if (oldValue == current) - { - byte result = oldValue; - return Unsafe.As(ref result); - } - current = oldValue; - } - } - - if (Unsafe.SizeOf() == 2) - { - ref ushort loc = ref Unsafe.As(ref location1); - ushort val = Unsafe.As(ref value); - ushort current = loc; - while (true) - { - ushort newValue = (ushort)(current | val); - ushort oldValue = global::System.Threading.Interlocked.CompareExchange( - ref loc, - newValue, - current - ); - if (oldValue == current) - { - ushort result = oldValue; - return Unsafe.As(ref result); - } - current = oldValue; - } - } - if (Unsafe.SizeOf() == 4) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = global::System.Threading.Interlocked.Or(ref loc, val); + int result = System.Threading.Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } - // Unsafe.SizeOf() == 8 + if (Unsafe.SizeOf() == 8) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = global::System.Threading.Interlocked.Or(ref loc, val); + long result = System.Threading.Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } + + throw new NotSupportedException( + $"Type {typeof(T).Name} is not supported. Only 4-byte and 8-byte integer types and enums are supported." + ); } } } #endif -#endif diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 012500b..3b193c1 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -110,9 +110,6 @@ ___ - [`void Move(string, string, bool)`](https://learn.microsoft.com/dotnet/api/system.io.file.move#system-io-file-move(system-string-system-string-system-boolean)) .NET Core 3.0 - `float` - [`bool TryParse(string, IFormatProvider?, out float)`](https://learn.microsoft.com/dotnet/api/system.single.tryparse#system-single-tryparse(system-string-system-iformatprovider-system-single@)) .NET 7.0 -- `global::System.Threading.Interlocked` - - [`unsafe T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 - - [`unsafe T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 - `HashCode` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.hashcode) .NET Core 2.1 - `HashSet` @@ -382,6 +379,9 @@ ___ - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.versioning.supportedosplatformattribute) .NET 5.0 - `SupportedOSPlatformGuardAttribute` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.versioning.supportedosplatformguardattribute) .NET 6.0 +- `System.Threading.Interlocked` + - [`T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 + - [`T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 - `SystemException` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.systemexception) .NET Core 2.0 - `T[]` From 3c26340adce5c22e7080d8ba22366432a5b2cf36 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:36:32 +0000 Subject: [PATCH 06/17] Add specific overloads for int/uint/long/ulong, remove namespace qualification Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net100/Interlocked.cs | 116 +++++++++++++++++++++++++++++++-- PolyShim/Signatures.md | 18 +++-- 2 files changed, 124 insertions(+), 10 deletions(-) diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index bdbf2a0..90575d0 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -8,12 +8,33 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Threading; [ExcludeFromCodeCoverage] internal static class MemberPolyfills_Net100_Interlocked { - extension(System.Threading.Interlocked) + extension(Interlocked) { + public static int And(ref int location1, int value) => + Interlocked.And(ref location1, value); + + public static uint And(ref uint location1, uint value) + { + ref int loc = ref Unsafe.As(ref location1); + int result = Interlocked.And(ref loc, (int)value); + return (uint)result; + } + + public static long And(ref long location1, long value) => + Interlocked.And(ref location1, value); + + public static ulong And(ref ulong location1, ulong value) + { + ref long loc = ref Unsafe.As(ref location1); + long result = Interlocked.And(ref loc, (long)value); + return (ulong)result; + } + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0) public static T And(ref T location1, T value) where T : struct @@ -26,11 +47,44 @@ public static T And(ref T location1, T value) ); } + if (typeof(T) == typeof(int)) + { + ref int loc = ref Unsafe.As(ref location1); + int val = Unsafe.As(ref value); + int result = And(ref loc, val); + return Unsafe.As(ref result); + } + + if (typeof(T) == typeof(uint)) + { + ref uint loc = ref Unsafe.As(ref location1); + uint val = Unsafe.As(ref value); + uint result = And(ref loc, val); + return Unsafe.As(ref result); + } + + if (typeof(T) == typeof(long)) + { + ref long loc = ref Unsafe.As(ref location1); + long val = Unsafe.As(ref value); + long result = And(ref loc, val); + return Unsafe.As(ref result); + } + + if (typeof(T) == typeof(ulong)) + { + ref ulong loc = ref Unsafe.As(ref location1); + ulong val = Unsafe.As(ref value); + ulong result = And(ref loc, val); + return Unsafe.As(ref result); + } + + // For other types (enums), use size-based dispatch if (Unsafe.SizeOf() == 4) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = System.Threading.Interlocked.And(ref loc, val); + int result = And(ref loc, val); return Unsafe.As(ref result); } @@ -38,7 +92,7 @@ public static T And(ref T location1, T value) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = System.Threading.Interlocked.And(ref loc, val); + long result = And(ref loc, val); return Unsafe.As(ref result); } @@ -47,6 +101,25 @@ public static T And(ref T location1, T value) ); } + public static int Or(ref int location1, int value) => Interlocked.Or(ref location1, value); + + public static uint Or(ref uint location1, uint value) + { + ref int loc = ref Unsafe.As(ref location1); + int result = Interlocked.Or(ref loc, (int)value); + return (uint)result; + } + + public static long Or(ref long location1, long value) => + Interlocked.Or(ref location1, value); + + public static ulong Or(ref ulong location1, ulong value) + { + ref long loc = ref Unsafe.As(ref location1); + long result = Interlocked.Or(ref loc, (long)value); + return (ulong)result; + } + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0) public static T Or(ref T location1, T value) where T : struct @@ -59,11 +132,44 @@ public static T Or(ref T location1, T value) ); } + if (typeof(T) == typeof(int)) + { + ref int loc = ref Unsafe.As(ref location1); + int val = Unsafe.As(ref value); + int result = Or(ref loc, val); + return Unsafe.As(ref result); + } + + if (typeof(T) == typeof(uint)) + { + ref uint loc = ref Unsafe.As(ref location1); + uint val = Unsafe.As(ref value); + uint result = Or(ref loc, val); + return Unsafe.As(ref result); + } + + if (typeof(T) == typeof(long)) + { + ref long loc = ref Unsafe.As(ref location1); + long val = Unsafe.As(ref value); + long result = Or(ref loc, val); + return Unsafe.As(ref result); + } + + if (typeof(T) == typeof(ulong)) + { + ref ulong loc = ref Unsafe.As(ref location1); + ulong val = Unsafe.As(ref value); + ulong result = Or(ref loc, val); + return Unsafe.As(ref result); + } + + // For other types (enums), use size-based dispatch if (Unsafe.SizeOf() == 4) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = System.Threading.Interlocked.Or(ref loc, val); + int result = Or(ref loc, val); return Unsafe.As(ref result); } @@ -71,7 +177,7 @@ public static T Or(ref T location1, T value) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = System.Threading.Interlocked.Or(ref loc, val); + long result = Or(ref loc, val); return Unsafe.As(ref result); } diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 3b193c1..32bdc3a 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 357 +- **Total:** 365 - **Types:** 76 -- **Members:** 281 +- **Members:** 289 ___ @@ -175,6 +175,17 @@ ___ - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.index) .NET Core 3.0 - `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@)) .NET 7.0 +- `Interlocked` + - `int And(ref int, int)` .NET 10.0 + - `int Or(ref int, int)` .NET 10.0 + - `long And(ref long, long)` .NET 10.0 + - `long Or(ref long, long)` .NET 10.0 + - [`T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 + - [`T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 + - `uint And(ref uint, uint)` .NET 10.0 + - `uint Or(ref uint, uint)` .NET 10.0 + - `ulong And(ref ulong, ulong)` .NET 10.0 + - `ulong Or(ref ulong, ulong)` .NET 10.0 - `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@)) .NET 7.0 - [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) .NET 5.0 @@ -379,9 +390,6 @@ ___ - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.versioning.supportedosplatformattribute) .NET 5.0 - `SupportedOSPlatformGuardAttribute` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.versioning.supportedosplatformguardattribute) .NET 6.0 -- `System.Threading.Interlocked` - - [`T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 - - [`T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 - `SystemException` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.systemexception) .NET Core 2.0 - `T[]` From 9ee2c72d9c47102531bc17fce46920488ef4f0f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 07:32:04 +0000 Subject: [PATCH 07/17] Refactor to use unsafe pointers for non-generic overloads, keep Unsafe for generic methods Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/Net100/InterlockedTests.cs | 5 +- PolyShim/Net100/Interlocked.cs | 66 ++++++++++++++--------- PolyShim/Signatures.md | 8 +-- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/PolyShim.Tests/Net100/InterlockedTests.cs b/PolyShim.Tests/Net100/InterlockedTests.cs index e1a1f3f..d0dedb4 100644 --- a/PolyShim.Tests/Net100/InterlockedTests.cs +++ b/PolyShim.Tests/Net100/InterlockedTests.cs @@ -65,10 +65,7 @@ public void Or_SupportsVariousIntegerTypes() // long long l = 0x00000000FFFFFFFF; - Interlocked - .Or(ref l, unchecked((long)0xFFFFFFFF00000000)) - .Should() - .Be(0x00000000FFFFFFFF); + Interlocked.Or(ref l, unchecked((long)0xFFFFFFFF00000000)).Should().Be(0x00000000FFFFFFFF); l.Should().Be(unchecked((long)0xFFFFFFFFFFFFFFFF)); // enum diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index 90575d0..eaeeeb4 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -1,4 +1,4 @@ -#if NET5_0_OR_GREATER +#if NET9_0_OR_GREATER #nullable enable // ReSharper disable RedundantUsingDirective // ReSharper disable CheckNamespace @@ -7,8 +7,10 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Threading; +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER +using Unsafe = System.Runtime.CompilerServices.Unsafe; +#endif [ExcludeFromCodeCoverage] internal static class MemberPolyfills_Net100_Interlocked @@ -18,24 +20,29 @@ internal static class MemberPolyfills_Net100_Interlocked public static int And(ref int location1, int value) => Interlocked.And(ref location1, value); - public static uint And(ref uint location1, uint value) + public static unsafe uint And(ref uint location1, uint value) { - ref int loc = ref Unsafe.As(ref location1); - int result = Interlocked.And(ref loc, (int)value); - return (uint)result; + fixed (uint* ptr = &location1) + { + int result = Interlocked.And(ref *(int*)ptr, *(int*)&value); + return *(uint*)&result; + } } public static long And(ref long location1, long value) => Interlocked.And(ref location1, value); - public static ulong And(ref ulong location1, ulong value) + public static unsafe ulong And(ref ulong location1, ulong value) { - ref long loc = ref Unsafe.As(ref location1); - long result = Interlocked.And(ref loc, (long)value); - return (ulong)result; + fixed (ulong* ptr = &location1) + { + long result = Interlocked.And(ref *(long*)ptr, *(long*)&value); + return *(ulong*)&result; + } } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0) +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER public static T And(ref T location1, T value) where T : struct { @@ -47,6 +54,7 @@ public static T And(ref T location1, T value) ); } + // Only handle known types - requires Unsafe for generic type handling if (typeof(T) == typeof(int)) { ref int loc = ref Unsafe.As(ref location1); @@ -79,8 +87,9 @@ public static T And(ref T location1, T value) return Unsafe.As(ref result); } - // For other types (enums), use size-based dispatch - if (Unsafe.SizeOf() == 4) + // For other types (enums), dispatch based on size + var size = Unsafe.SizeOf(); + if (size == 4) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); @@ -88,7 +97,7 @@ public static T And(ref T location1, T value) return Unsafe.As(ref result); } - if (Unsafe.SizeOf() == 8) + if (size == 8) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); @@ -100,27 +109,33 @@ public static T And(ref T location1, T value) $"Type {typeof(T).Name} is not supported. Only 4-byte and 8-byte integer types and enums are supported." ); } +#endif public static int Or(ref int location1, int value) => Interlocked.Or(ref location1, value); - public static uint Or(ref uint location1, uint value) + public static unsafe uint Or(ref uint location1, uint value) { - ref int loc = ref Unsafe.As(ref location1); - int result = Interlocked.Or(ref loc, (int)value); - return (uint)result; + fixed (uint* ptr = &location1) + { + int result = Interlocked.Or(ref *(int*)ptr, *(int*)&value); + return *(uint*)&result; + } } public static long Or(ref long location1, long value) => Interlocked.Or(ref location1, value); - public static ulong Or(ref ulong location1, ulong value) + public static unsafe ulong Or(ref ulong location1, ulong value) { - ref long loc = ref Unsafe.As(ref location1); - long result = Interlocked.Or(ref loc, (long)value); - return (ulong)result; + fixed (ulong* ptr = &location1) + { + long result = Interlocked.Or(ref *(long*)ptr, *(long*)&value); + return *(ulong*)&result; + } } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0) +#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER public static T Or(ref T location1, T value) where T : struct { @@ -132,6 +147,7 @@ public static T Or(ref T location1, T value) ); } + // Only handle known types - requires Unsafe for generic type handling if (typeof(T) == typeof(int)) { ref int loc = ref Unsafe.As(ref location1); @@ -164,8 +180,9 @@ public static T Or(ref T location1, T value) return Unsafe.As(ref result); } - // For other types (enums), use size-based dispatch - if (Unsafe.SizeOf() == 4) + // For other types (enums), dispatch based on size + var size = Unsafe.SizeOf(); + if (size == 4) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); @@ -173,7 +190,7 @@ public static T Or(ref T location1, T value) return Unsafe.As(ref result); } - if (Unsafe.SizeOf() == 8) + if (size == 8) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); @@ -185,6 +202,7 @@ public static T Or(ref T location1, T value) $"Type {typeof(T).Name} is not supported. Only 4-byte and 8-byte integer types and enums are supported." ); } +#endif } } #endif diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 32bdc3a..743dbf7 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -182,10 +182,10 @@ ___ - `long Or(ref long, long)` .NET 10.0 - [`T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 - [`T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 - - `uint And(ref uint, uint)` .NET 10.0 - - `uint Or(ref uint, uint)` .NET 10.0 - - `ulong And(ref ulong, ulong)` .NET 10.0 - - `ulong Or(ref ulong, ulong)` .NET 10.0 + - `unsafe uint And(ref uint, uint)` .NET 10.0 + - `unsafe uint Or(ref uint, uint)` .NET 10.0 + - `unsafe ulong And(ref ulong, ulong)` .NET 10.0 + - `unsafe ulong Or(ref ulong, ulong)` .NET 10.0 - `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@)) .NET 7.0 - [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) .NET 5.0 From 8bee912f6b9914a819ce315373b3988252968503 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 07:46:17 +0000 Subject: [PATCH 08/17] Extend Interlocked polyfills to support .NET Standard 2.0+ by implementing And/Or with CompareExchange Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net100/Interlocked.cs | 122 +++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 35 deletions(-) diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index eaeeeb4..abe40e6 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -1,4 +1,4 @@ -#if NET9_0_OR_GREATER +#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP2_0_OR_GREATER || NET461_OR_GREATER #nullable enable // ReSharper disable RedundantUsingDirective // ReSharper disable CheckNamespace @@ -8,7 +8,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading; -#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER +#if NET5_0_OR_GREATER using Unsafe = System.Runtime.CompilerServices.Unsafe; #endif @@ -17,32 +17,109 @@ internal static class MemberPolyfills_Net100_Interlocked { extension(Interlocked) { - public static int And(ref int location1, int value) => - Interlocked.And(ref location1, value); +#if !NET9_0_OR_GREATER + // Polyfill non-generic And/Or methods for pre-.NET 9 + public static int And(ref int location1, int value) + { + int current = location1; + int newValue; + do + { + newValue = current & value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + + return newValue; + } + + public static long And(ref long location1, long value) + { + long current = location1; + long newValue; + do + { + newValue = current & value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + + return newValue; + } + + public static int Or(ref int location1, int value) + { + int current = location1; + int newValue; + do + { + newValue = current | value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + return newValue; + } + + public static long Or(ref long location1, long value) + { + long current = location1; + long newValue; + do + { + newValue = current | value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + + return newValue; + } +#else + // Use native methods on .NET 9+ + public static int And(ref int location1, int value) => Interlocked.And(ref location1, value); + + public static long And(ref long location1, long value) => Interlocked.And(ref location1, value); + + public static int Or(ref int location1, int value) => Interlocked.Or(ref location1, value); + + public static long Or(ref long location1, long value) => Interlocked.Or(ref location1, value); +#endif + + // uint and ulong overloads use unsafe pointers to delegate to int/long public static unsafe uint And(ref uint location1, uint value) { fixed (uint* ptr = &location1) { - int result = Interlocked.And(ref *(int*)ptr, *(int*)&value); + int result = And(ref *(int*)ptr, *(int*)&value); return *(uint*)&result; } } - public static long And(ref long location1, long value) => - Interlocked.And(ref location1, value); - public static unsafe ulong And(ref ulong location1, ulong value) { fixed (ulong* ptr = &location1) { - long result = Interlocked.And(ref *(long*)ptr, *(long*)&value); + long result = And(ref *(long*)ptr, *(long*)&value); + return *(ulong*)&result; + } + } + + public static unsafe uint Or(ref uint location1, uint value) + { + fixed (uint* ptr = &location1) + { + int result = Or(ref *(int*)ptr, *(int*)&value); + return *(uint*)&result; + } + } + + public static unsafe ulong Or(ref ulong location1, ulong value) + { + fixed (ulong* ptr = &location1) + { + long result = Or(ref *(long*)ptr, *(long*)&value); return *(ulong*)&result; } } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0) -#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER +#if NET5_0_OR_GREATER public static T And(ref T location1, T value) where T : struct { @@ -109,33 +186,8 @@ public static T And(ref T location1, T value) $"Type {typeof(T).Name} is not supported. Only 4-byte and 8-byte integer types and enums are supported." ); } -#endif - - public static int Or(ref int location1, int value) => Interlocked.Or(ref location1, value); - - public static unsafe uint Or(ref uint location1, uint value) - { - fixed (uint* ptr = &location1) - { - int result = Interlocked.Or(ref *(int*)ptr, *(int*)&value); - return *(uint*)&result; - } - } - - public static long Or(ref long location1, long value) => - Interlocked.Or(ref location1, value); - - public static unsafe ulong Or(ref ulong location1, ulong value) - { - fixed (ulong* ptr = &location1) - { - long result = Interlocked.Or(ref *(long*)ptr, *(long*)&value); - return *(ulong*)&result; - } - } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0) -#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER public static T Or(ref T location1, T value) where T : struct { From e36d16dcf43d22cb051e043946f5774e4d133766 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 08:05:41 +0000 Subject: [PATCH 09/17] Extend Interlocked polyfills to all TFMs using standard Net100 pattern Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net100/Interlocked.cs | 14 +++++++++----- PolyShim/Signatures.md | 8 ++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index abe40e6..b194138 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -1,4 +1,4 @@ -#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP2_0_OR_GREATER || NET461_OR_GREATER +#if (NETCOREAPP && !NET11_0_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD) #nullable enable // ReSharper disable RedundantUsingDirective // ReSharper disable CheckNamespace @@ -72,13 +72,17 @@ public static long Or(ref long location1, long value) } #else // Use native methods on .NET 9+ - public static int And(ref int location1, int value) => Interlocked.And(ref location1, value); + public static int And(ref int location1, int value) => + Interlocked.And(ref location1, value); - public static long And(ref long location1, long value) => Interlocked.And(ref location1, value); + public static long And(ref long location1, long value) => + Interlocked.And(ref location1, value); - public static int Or(ref int location1, int value) => Interlocked.Or(ref location1, value); + public static int Or(ref int location1, int value) => + Interlocked.Or(ref location1, value); - public static long Or(ref long location1, long value) => Interlocked.Or(ref location1, value); + public static long Or(ref long location1, long value) => + Interlocked.Or(ref location1, value); #endif // uint and ulong overloads use unsafe pointers to delegate to int/long diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 743dbf7..4b55105 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 365 +- **Total:** 369 - **Types:** 76 -- **Members:** 289 +- **Members:** 293 ___ @@ -177,8 +177,12 @@ ___ - [`bool TryParse(string, IFormatProvider?, out int)`](https://learn.microsoft.com/dotnet/api/system.int32.tryparse#system-int32-tryparse(system-string-system-iformatprovider-system-int32@)) .NET 7.0 - `Interlocked` - `int And(ref int, int)` .NET 10.0 + - `int And(ref int, int)` .NET 10.0 + - `int Or(ref int, int)` .NET 10.0 - `int Or(ref int, int)` .NET 10.0 - `long And(ref long, long)` .NET 10.0 + - `long And(ref long, long)` .NET 10.0 + - `long Or(ref long, long)` .NET 10.0 - `long Or(ref long, long)` .NET 10.0 - [`T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 - [`T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 From c61b1d29c2a33df536a8c9839124b3f2cf59d0c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 08:30:04 +0000 Subject: [PATCH 10/17] Refactor: Move non-generic Interlocked.And/Or to Net50, keep generic methods in Net100 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net100/Interlocked.cs | 129 +++------------------------------ PolyShim/Net50/Interlocked.cs | 108 +++++++++++++++++++++++++++ PolyShim/Signatures.md | 24 +++--- 3 files changed, 130 insertions(+), 131 deletions(-) create mode 100644 PolyShim/Net50/Interlocked.cs diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs index b194138..f75b75d 100644 --- a/PolyShim/Net100/Interlocked.cs +++ b/PolyShim/Net100/Interlocked.cs @@ -17,111 +17,6 @@ internal static class MemberPolyfills_Net100_Interlocked { extension(Interlocked) { -#if !NET9_0_OR_GREATER - // Polyfill non-generic And/Or methods for pre-.NET 9 - public static int And(ref int location1, int value) - { - int current = location1; - int newValue; - do - { - newValue = current & value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); - - return newValue; - } - - public static long And(ref long location1, long value) - { - long current = location1; - long newValue; - do - { - newValue = current & value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); - - return newValue; - } - - public static int Or(ref int location1, int value) - { - int current = location1; - int newValue; - do - { - newValue = current | value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); - - return newValue; - } - - public static long Or(ref long location1, long value) - { - long current = location1; - long newValue; - do - { - newValue = current | value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); - - return newValue; - } -#else - // Use native methods on .NET 9+ - public static int And(ref int location1, int value) => - Interlocked.And(ref location1, value); - - public static long And(ref long location1, long value) => - Interlocked.And(ref location1, value); - - public static int Or(ref int location1, int value) => - Interlocked.Or(ref location1, value); - - public static long Or(ref long location1, long value) => - Interlocked.Or(ref location1, value); -#endif - - // uint and ulong overloads use unsafe pointers to delegate to int/long - public static unsafe uint And(ref uint location1, uint value) - { - fixed (uint* ptr = &location1) - { - int result = And(ref *(int*)ptr, *(int*)&value); - return *(uint*)&result; - } - } - - public static unsafe ulong And(ref ulong location1, ulong value) - { - fixed (ulong* ptr = &location1) - { - long result = And(ref *(long*)ptr, *(long*)&value); - return *(ulong*)&result; - } - } - - public static unsafe uint Or(ref uint location1, uint value) - { - fixed (uint* ptr = &location1) - { - int result = Or(ref *(int*)ptr, *(int*)&value); - return *(uint*)&result; - } - } - - public static unsafe ulong Or(ref ulong location1, ulong value) - { - fixed (ulong* ptr = &location1) - { - long result = Or(ref *(long*)ptr, *(long*)&value); - return *(ulong*)&result; - } - } - // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0) #if NET5_0_OR_GREATER public static T And(ref T location1, T value) @@ -140,7 +35,7 @@ public static T And(ref T location1, T value) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = And(ref loc, val); + int result = Interlocked.And(ref loc, val); return Unsafe.As(ref result); } @@ -148,7 +43,7 @@ public static T And(ref T location1, T value) { ref uint loc = ref Unsafe.As(ref location1); uint val = Unsafe.As(ref value); - uint result = And(ref loc, val); + uint result = Interlocked.And(ref loc, val); return Unsafe.As(ref result); } @@ -156,7 +51,7 @@ public static T And(ref T location1, T value) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = And(ref loc, val); + long result = Interlocked.And(ref loc, val); return Unsafe.As(ref result); } @@ -164,7 +59,7 @@ public static T And(ref T location1, T value) { ref ulong loc = ref Unsafe.As(ref location1); ulong val = Unsafe.As(ref value); - ulong result = And(ref loc, val); + ulong result = Interlocked.And(ref loc, val); return Unsafe.As(ref result); } @@ -174,7 +69,7 @@ public static T And(ref T location1, T value) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = And(ref loc, val); + int result = Interlocked.And(ref loc, val); return Unsafe.As(ref result); } @@ -182,7 +77,7 @@ public static T And(ref T location1, T value) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = And(ref loc, val); + long result = Interlocked.And(ref loc, val); return Unsafe.As(ref result); } @@ -208,7 +103,7 @@ public static T Or(ref T location1, T value) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = Or(ref loc, val); + int result = Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } @@ -216,7 +111,7 @@ public static T Or(ref T location1, T value) { ref uint loc = ref Unsafe.As(ref location1); uint val = Unsafe.As(ref value); - uint result = Or(ref loc, val); + uint result = Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } @@ -224,7 +119,7 @@ public static T Or(ref T location1, T value) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = Or(ref loc, val); + long result = Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } @@ -232,7 +127,7 @@ public static T Or(ref T location1, T value) { ref ulong loc = ref Unsafe.As(ref location1); ulong val = Unsafe.As(ref value); - ulong result = Or(ref loc, val); + ulong result = Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } @@ -242,7 +137,7 @@ public static T Or(ref T location1, T value) { ref int loc = ref Unsafe.As(ref location1); int val = Unsafe.As(ref value); - int result = Or(ref loc, val); + int result = Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } @@ -250,7 +145,7 @@ public static T Or(ref T location1, T value) { ref long loc = ref Unsafe.As(ref location1); long val = Unsafe.As(ref value); - long result = Or(ref loc, val); + long result = Interlocked.Or(ref loc, val); return Unsafe.As(ref result); } diff --git a/PolyShim/Net50/Interlocked.cs b/PolyShim/Net50/Interlocked.cs new file mode 100644 index 0000000..e1af583 --- /dev/null +++ b/PolyShim/Net50/Interlocked.cs @@ -0,0 +1,108 @@ +#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) + { + // Non-generic And/Or methods for int and long + public static int And(ref int location1, int value) + { + int current = location1; + int newValue; + do + { + newValue = current & value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + + return newValue; + } + + public static long And(ref long location1, long value) + { + long current = location1; + long newValue; + do + { + newValue = current & value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + + return newValue; + } + + public static int Or(ref int location1, int value) + { + int current = location1; + int newValue; + do + { + newValue = current | value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + + return newValue; + } + + public static long Or(ref long location1, long value) + { + long current = location1; + long newValue; + do + { + newValue = current | value; + current = Interlocked.CompareExchange(ref location1, newValue, current); + } while (current != newValue); + + return newValue; + } + + // uint/ulong overloads use unsafe pointers + public static unsafe uint And(ref uint location1, uint value) + { + fixed (uint* ptr = &location1) + { + int result = Interlocked.And(ref *(int*)ptr, *(int*)&value); + return *(uint*)&result; + } + } + + public static unsafe ulong And(ref ulong location1, ulong value) + { + fixed (ulong* ptr = &location1) + { + long result = Interlocked.And(ref *(long*)ptr, *(long*)&value); + return *(ulong*)&result; + } + } + + public static unsafe uint Or(ref uint location1, uint value) + { + fixed (uint* ptr = &location1) + { + int result = Interlocked.Or(ref *(int*)ptr, *(int*)&value); + return *(uint*)&result; + } + } + + public static unsafe ulong Or(ref ulong location1, ulong value) + { + fixed (ulong* ptr = &location1) + { + long result = Interlocked.Or(ref *(long*)ptr, *(long*)&value); + return *(ulong*)&result; + } + } + } +} + +#endif diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 4b55105..f9ae264 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 369 +- **Total:** 365 - **Types:** 76 -- **Members:** 293 +- **Members:** 289 ___ @@ -176,20 +176,16 @@ ___ - `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@)) .NET 7.0 - `Interlocked` - - `int And(ref int, int)` .NET 10.0 - - `int And(ref int, int)` .NET 10.0 - - `int Or(ref int, int)` .NET 10.0 - - `int Or(ref int, int)` .NET 10.0 - - `long And(ref long, long)` .NET 10.0 - - `long And(ref long, long)` .NET 10.0 - - `long Or(ref long, long)` .NET 10.0 - - `long Or(ref long, long)` .NET 10.0 + - `int And(ref int, int)` .NET 5.0 + - `int Or(ref int, int)` .NET 5.0 + - `long And(ref long, long)` .NET 5.0 + - `long Or(ref long, long)` .NET 5.0 - [`T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 - [`T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 - - `unsafe uint And(ref uint, uint)` .NET 10.0 - - `unsafe uint Or(ref uint, uint)` .NET 10.0 - - `unsafe ulong And(ref ulong, ulong)` .NET 10.0 - - `unsafe ulong Or(ref ulong, ulong)` .NET 10.0 + - `unsafe uint And(ref uint, uint)` .NET 5.0 + - `unsafe uint Or(ref uint, uint)` .NET 5.0 + - `unsafe ulong And(ref ulong, ulong)` .NET 5.0 + - `unsafe ulong Or(ref ulong, ulong)` .NET 5.0 - `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@)) .NET 7.0 - [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) .NET 5.0 From fe503a061f1bb595ecef67e90ef547485244d21b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 08:32:45 +0000 Subject: [PATCH 11/17] Fix CompareExchange loop logic in Net50/Interlocked.cs Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net50/Interlocked.cs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/PolyShim/Net50/Interlocked.cs b/PolyShim/Net50/Interlocked.cs index e1af583..c59ef4b 100644 --- a/PolyShim/Net50/Interlocked.cs +++ b/PolyShim/Net50/Interlocked.cs @@ -16,52 +16,52 @@ internal static class MemberPolyfills_Net50_Interlocked // Non-generic And/Or methods for int and long public static int And(ref int location1, int value) { - int current = location1; - int newValue; + int current, newValue, original; do { + current = location1; newValue = current & value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); + original = Interlocked.CompareExchange(ref location1, newValue, current); + } while (original != current); return newValue; } public static long And(ref long location1, long value) { - long current = location1; - long newValue; + long current, newValue, original; do { + current = location1; newValue = current & value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); + original = Interlocked.CompareExchange(ref location1, newValue, current); + } while (original != current); return newValue; } public static int Or(ref int location1, int value) { - int current = location1; - int newValue; + int current, newValue, original; do { + current = location1; newValue = current | value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); + original = Interlocked.CompareExchange(ref location1, newValue, current); + } while (original != current); return newValue; } public static long Or(ref long location1, long value) { - long current = location1; - long newValue; + long current, newValue, original; do { + current = location1; newValue = current | value; - current = Interlocked.CompareExchange(ref location1, newValue, current); - } while (current != newValue); + original = Interlocked.CompareExchange(ref location1, newValue, current); + } while (original != current); return newValue; } From 7da92866cc370ea24e0ee9b7bda691eee62495dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 20:42:58 +0000 Subject: [PATCH 12/17] Remove generic Interlocked.And and Interlocked.Or polyfills - these methods don't exist in .NET Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- .../{Net100 => Net50}/InterlockedTests.cs | 43 +---- PolyShim/Net100/Interlocked.cs | 159 ------------------ PolyShim/Net50/Interlocked.cs | 26 ++- PolyShim/Signatures.md | 22 ++- 4 files changed, 31 insertions(+), 219 deletions(-) rename PolyShim.Tests/{Net100 => Net50}/InterlockedTests.cs (58%) delete mode 100644 PolyShim/Net100/Interlocked.cs diff --git a/PolyShim.Tests/Net100/InterlockedTests.cs b/PolyShim.Tests/Net50/InterlockedTests.cs similarity index 58% rename from PolyShim.Tests/Net100/InterlockedTests.cs rename to PolyShim.Tests/Net50/InterlockedTests.cs index d0dedb4..4707133 100644 --- a/PolyShim.Tests/Net100/InterlockedTests.cs +++ b/PolyShim.Tests/Net50/InterlockedTests.cs @@ -1,18 +1,11 @@ -using System; using System.Threading; using FluentAssertions; using Xunit; -namespace PolyShim.Tests.Net100; +namespace PolyShim.Tests.Net50; public class InterlockedTests { - private enum IntEnum : int - { - Value1 = 0x0000FFFF, - Value2 = unchecked((int)0xFFFF0000), - } - [Fact] public void And_SupportsVariousIntegerTypes() { @@ -38,11 +31,6 @@ public void And_SupportsVariousIntegerTypes() .Should() .Be(unchecked((long)0xFFFFFFFFFFFFFFFF)); l.Should().Be(unchecked((long)0xAAAAAAAAAAAAAAAA)); - - // enum - var e = IntEnum.Value1; - Interlocked.And(ref e, IntEnum.Value2).Should().Be(IntEnum.Value1); - e.Should().Be((IntEnum)0); } [Fact] @@ -67,34 +55,5 @@ public void Or_SupportsVariousIntegerTypes() long l = 0x00000000FFFFFFFF; Interlocked.Or(ref l, unchecked((long)0xFFFFFFFF00000000)).Should().Be(0x00000000FFFFFFFF); l.Should().Be(unchecked((long)0xFFFFFFFFFFFFFFFF)); - - // enum - var e = IntEnum.Value1; - Interlocked.Or(ref e, IntEnum.Value2).Should().Be(IntEnum.Value1); - e.Should().Be((IntEnum)unchecked((int)0xFFFFFFFF)); - } - - [Fact] - public void And_ThrowsForFloatingPointTypes() - { - float f = 1.5f; - var ex = Assert.Throws(() => Interlocked.And(ref f, 2.5f)); - ex.Message.Should().Contain("integer"); - - double d = 1.5; - ex = Assert.Throws(() => Interlocked.And(ref d, 2.5)); - ex.Message.Should().Contain("integer"); - } - - [Fact] - public void Or_ThrowsForFloatingPointTypes() - { - float f = 1.5f; - var ex = Assert.Throws(() => Interlocked.Or(ref f, 2.5f)); - ex.Message.Should().Contain("integer"); - - double d = 1.5; - ex = Assert.Throws(() => Interlocked.Or(ref d, 2.5)); - ex.Message.Should().Contain("integer"); } } diff --git a/PolyShim/Net100/Interlocked.cs b/PolyShim/Net100/Interlocked.cs deleted file mode 100644 index f75b75d..0000000 --- a/PolyShim/Net100/Interlocked.cs +++ /dev/null @@ -1,159 +0,0 @@ -#if (NETCOREAPP && !NET11_0_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD) -#nullable enable -// ReSharper disable RedundantUsingDirective -// ReSharper disable CheckNamespace -// ReSharper disable InconsistentNaming -// ReSharper disable PartialTypeWithSinglePart - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -#if NET5_0_OR_GREATER -using Unsafe = System.Runtime.CompilerServices.Unsafe; -#endif - -[ExcludeFromCodeCoverage] -internal static class MemberPolyfills_Net100_Interlocked -{ - extension(Interlocked) - { - // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0) -#if NET5_0_OR_GREATER - public static T And(ref T location1, T value) - where T : struct - { - // Reject floating-point types - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - throw new NotSupportedException( - "Only integer primitive types and enums backed by integer types are supported." - ); - } - - // Only handle known types - requires Unsafe for generic type handling - if (typeof(T) == typeof(int)) - { - ref int loc = ref Unsafe.As(ref location1); - int val = Unsafe.As(ref value); - int result = Interlocked.And(ref loc, val); - return Unsafe.As(ref result); - } - - if (typeof(T) == typeof(uint)) - { - ref uint loc = ref Unsafe.As(ref location1); - uint val = Unsafe.As(ref value); - uint result = Interlocked.And(ref loc, val); - return Unsafe.As(ref result); - } - - if (typeof(T) == typeof(long)) - { - ref long loc = ref Unsafe.As(ref location1); - long val = Unsafe.As(ref value); - long result = Interlocked.And(ref loc, val); - return Unsafe.As(ref result); - } - - if (typeof(T) == typeof(ulong)) - { - ref ulong loc = ref Unsafe.As(ref location1); - ulong val = Unsafe.As(ref value); - ulong result = Interlocked.And(ref loc, val); - return Unsafe.As(ref result); - } - - // For other types (enums), dispatch based on size - var size = Unsafe.SizeOf(); - if (size == 4) - { - ref int loc = ref Unsafe.As(ref location1); - int val = Unsafe.As(ref value); - int result = Interlocked.And(ref loc, val); - return Unsafe.As(ref result); - } - - if (size == 8) - { - ref long loc = ref Unsafe.As(ref location1); - long val = Unsafe.As(ref value); - long result = Interlocked.And(ref loc, val); - return Unsafe.As(ref result); - } - - throw new NotSupportedException( - $"Type {typeof(T).Name} is not supported. Only 4-byte and 8-byte integer types and enums are supported." - ); - } - - // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0) - public static T Or(ref T location1, T value) - where T : struct - { - // Reject floating-point types - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - throw new NotSupportedException( - "Only integer primitive types and enums backed by integer types are supported." - ); - } - - // Only handle known types - requires Unsafe for generic type handling - if (typeof(T) == typeof(int)) - { - ref int loc = ref Unsafe.As(ref location1); - int val = Unsafe.As(ref value); - int result = Interlocked.Or(ref loc, val); - return Unsafe.As(ref result); - } - - if (typeof(T) == typeof(uint)) - { - ref uint loc = ref Unsafe.As(ref location1); - uint val = Unsafe.As(ref value); - uint result = Interlocked.Or(ref loc, val); - return Unsafe.As(ref result); - } - - if (typeof(T) == typeof(long)) - { - ref long loc = ref Unsafe.As(ref location1); - long val = Unsafe.As(ref value); - long result = Interlocked.Or(ref loc, val); - return Unsafe.As(ref result); - } - - if (typeof(T) == typeof(ulong)) - { - ref ulong loc = ref Unsafe.As(ref location1); - ulong val = Unsafe.As(ref value); - ulong result = Interlocked.Or(ref loc, val); - return Unsafe.As(ref result); - } - - // For other types (enums), dispatch based on size - var size = Unsafe.SizeOf(); - if (size == 4) - { - ref int loc = ref Unsafe.As(ref location1); - int val = Unsafe.As(ref value); - int result = Interlocked.Or(ref loc, val); - return Unsafe.As(ref result); - } - - if (size == 8) - { - ref long loc = ref Unsafe.As(ref location1); - long val = Unsafe.As(ref value); - long result = Interlocked.Or(ref loc, val); - return Unsafe.As(ref result); - } - - throw new NotSupportedException( - $"Type {typeof(T).Name} is not supported. Only 4-byte and 8-byte integer types and enums are supported." - ); - } -#endif - } -} -#endif diff --git a/PolyShim/Net50/Interlocked.cs b/PolyShim/Net50/Interlocked.cs index c59ef4b..f3a38de 100644 --- a/PolyShim/Net50/Interlocked.cs +++ b/PolyShim/Net50/Interlocked.cs @@ -13,10 +13,12 @@ internal static class MemberPolyfills_Net50_Interlocked { extension(Interlocked) { - // Non-generic And/Or methods for int and long + // 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; + int current, + newValue, + original; do { current = location1; @@ -27,9 +29,12 @@ public static int And(ref int location1, int value) return newValue; } + // 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; + long current, + newValue, + original; do { current = location1; @@ -40,9 +45,12 @@ public static long And(ref long location1, long value) return newValue; } + // 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; + int current, + newValue, + original; do { current = location1; @@ -53,9 +61,12 @@ public static int Or(ref int location1, int value) return newValue; } + // 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; + long current, + newValue, + original; do { current = location1; @@ -66,7 +77,7 @@ public static long Or(ref long location1, long value) return newValue; } - // uint/ulong overloads use unsafe pointers + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32) public static unsafe uint And(ref uint location1, uint value) { fixed (uint* ptr = &location1) @@ -76,6 +87,7 @@ public static unsafe uint And(ref uint location1, uint value) } } + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64) public static unsafe ulong And(ref ulong location1, ulong value) { fixed (ulong* ptr = &location1) @@ -85,6 +97,7 @@ public static unsafe ulong And(ref ulong location1, ulong value) } } + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32) public static unsafe uint Or(ref uint location1, uint value) { fixed (uint* ptr = &location1) @@ -94,6 +107,7 @@ public static unsafe uint Or(ref uint location1, uint value) } } + // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64) public static unsafe ulong Or(ref ulong location1, ulong value) { fixed (ulong* ptr = &location1) diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index f9ae264..095ae7d 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 365 +- **Total:** 363 - **Types:** 76 -- **Members:** 289 +- **Members:** 287 ___ @@ -176,16 +176,14 @@ ___ - `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@)) .NET 7.0 - `Interlocked` - - `int And(ref int, int)` .NET 5.0 - - `int Or(ref int, int)` .NET 5.0 - - `long And(ref long, long)` .NET 5.0 - - `long Or(ref long, long)` .NET 5.0 - - [`T And(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and-1(-0@-0)) .NET 10.0 - - [`T Or(ref T, T) where T : struct`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or-1(-0@-0)) .NET 10.0 - - `unsafe uint And(ref uint, uint)` .NET 5.0 - - `unsafe uint Or(ref uint, uint)` .NET 5.0 - - `unsafe ulong And(ref ulong, ulong)` .NET 5.0 - - `unsafe ulong Or(ref ulong, ulong)` .NET 5.0 + - [`int And(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int32@-system-int32)) .NET 5.0 + - [`int Or(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int32@-system-int32)) .NET 5.0 + - [`long And(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int64@-system-int64)) .NET 5.0 + - [`long Or(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int64@-system-int64)) .NET 5.0 + - [`unsafe uint And(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32)) .NET 5.0 + - [`unsafe uint Or(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32)) .NET 5.0 + - [`unsafe ulong And(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64)) .NET 5.0 + - [`unsafe ulong Or(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64)) .NET 5.0 - `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@)) .NET 7.0 - [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) .NET 5.0 From 9298eae2f9e524f7d4825f75d673a6a7728e5d16 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 20:46:14 +0000 Subject: [PATCH 13/17] Fix return values - And/Or methods should return original value, not new value Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net50/Interlocked.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PolyShim/Net50/Interlocked.cs b/PolyShim/Net50/Interlocked.cs index f3a38de..6f17074 100644 --- a/PolyShim/Net50/Interlocked.cs +++ b/PolyShim/Net50/Interlocked.cs @@ -26,7 +26,7 @@ public static int And(ref int location1, int value) original = Interlocked.CompareExchange(ref location1, newValue, current); } while (original != current); - return newValue; + return original; } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int64@-system-int64) @@ -42,7 +42,7 @@ public static long And(ref long location1, long value) original = Interlocked.CompareExchange(ref location1, newValue, current); } while (original != current); - return newValue; + return original; } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int32@-system-int32) @@ -58,7 +58,7 @@ public static int Or(ref int location1, int value) original = Interlocked.CompareExchange(ref location1, newValue, current); } while (original != current); - return newValue; + return original; } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int64@-system-int64) @@ -74,7 +74,7 @@ public static long Or(ref long location1, long value) original = Interlocked.CompareExchange(ref location1, newValue, current); } while (original != current); - return newValue; + return original; } // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32) From bb1f1bed4ed8844f477ac43ab82ff53dadc8eeb4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:24:10 +0000 Subject: [PATCH 14/17] Address PR feedback: simplify test names, reorder methods, add ALLOW_UNSAFE_BLOCKS check Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/Net50/InterlockedTests.cs | 4 +- PolyShim/Net50/Interlocked.cs | 68 +++++++++++++----------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/PolyShim.Tests/Net50/InterlockedTests.cs b/PolyShim.Tests/Net50/InterlockedTests.cs index 4707133..8743c23 100644 --- a/PolyShim.Tests/Net50/InterlockedTests.cs +++ b/PolyShim.Tests/Net50/InterlockedTests.cs @@ -7,7 +7,7 @@ namespace PolyShim.Tests.Net50; public class InterlockedTests { [Fact] - public void And_SupportsVariousIntegerTypes() + public void And_Test() { // uint uint ui = 0xFFFFFFFF; @@ -34,7 +34,7 @@ public void And_SupportsVariousIntegerTypes() } [Fact] - public void Or_SupportsVariousIntegerTypes() + public void Or_Test() { // uint uint ui = 0x0000FFFF; diff --git a/PolyShim/Net50/Interlocked.cs b/PolyShim/Net50/Interlocked.cs index 6f17074..68ba7c1 100644 --- a/PolyShim/Net50/Interlocked.cs +++ b/PolyShim/Net50/Interlocked.cs @@ -29,6 +29,18 @@ public static int And(ref int location1, int value) 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 unsafe uint And(ref uint location1, uint value) + { + 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) { @@ -45,6 +57,18 @@ public static long And(ref long location1, long value) 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 unsafe ulong And(ref ulong location1, ulong value) + { + 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) { @@ -61,6 +85,18 @@ public static int Or(ref int location1, int value) 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 unsafe uint Or(ref uint location1, uint value) + { + 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) { @@ -77,36 +113,7 @@ public static long Or(ref long location1, long value) return original; } - // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32) - public static unsafe uint And(ref uint location1, uint value) - { - fixed (uint* ptr = &location1) - { - int result = Interlocked.And(ref *(int*)ptr, *(int*)&value); - return *(uint*)&result; - } - } - - // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64) - public static unsafe ulong And(ref ulong location1, ulong value) - { - fixed (ulong* ptr = &location1) - { - long result = Interlocked.And(ref *(long*)ptr, *(long*)&value); - return *(ulong*)&result; - } - } - - // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32) - public static unsafe uint Or(ref uint location1, uint value) - { - fixed (uint* ptr = &location1) - { - int result = Interlocked.Or(ref *(int*)ptr, *(int*)&value); - return *(uint*)&result; - } - } - +#if ALLOW_UNSAFE_BLOCKS // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64) public static unsafe ulong Or(ref ulong location1, ulong value) { @@ -116,6 +123,7 @@ public static unsafe ulong Or(ref ulong location1, ulong value) return *(ulong*)&result; } } +#endif } } From b6389a29111d4d497922f483a05ec7f98df0e8cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:18:02 +0000 Subject: [PATCH 15/17] Remove unsafe modifier from method signatures, use internal unsafe blocks instead Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Net50/Interlocked.cs | 44 ++++++++++++++++++++++------------- PolyShim/Signatures.md | 8 +++---- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/PolyShim/Net50/Interlocked.cs b/PolyShim/Net50/Interlocked.cs index 68ba7c1..576834e 100644 --- a/PolyShim/Net50/Interlocked.cs +++ b/PolyShim/Net50/Interlocked.cs @@ -31,12 +31,15 @@ public static int And(ref int location1, int value) #if ALLOW_UNSAFE_BLOCKS // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32) - public static unsafe uint And(ref uint location1, uint value) + public static uint And(ref uint location1, uint value) { - fixed (uint* ptr = &location1) + unsafe { - int result = Interlocked.And(ref *(int*)ptr, *(int*)&value); - return *(uint*)&result; + fixed (uint* ptr = &location1) + { + int result = Interlocked.And(ref *(int*)ptr, *(int*)&value); + return *(uint*)&result; + } } } #endif @@ -59,12 +62,15 @@ public static long And(ref long location1, long value) #if ALLOW_UNSAFE_BLOCKS // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64) - public static unsafe ulong And(ref ulong location1, ulong value) + public static ulong And(ref ulong location1, ulong value) { - fixed (ulong* ptr = &location1) + unsafe { - long result = Interlocked.And(ref *(long*)ptr, *(long*)&value); - return *(ulong*)&result; + fixed (ulong* ptr = &location1) + { + long result = Interlocked.And(ref *(long*)ptr, *(long*)&value); + return *(ulong*)&result; + } } } #endif @@ -87,12 +93,15 @@ public static int Or(ref int location1, int value) #if ALLOW_UNSAFE_BLOCKS // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32) - public static unsafe uint Or(ref uint location1, uint value) + public static uint Or(ref uint location1, uint value) { - fixed (uint* ptr = &location1) + unsafe { - int result = Interlocked.Or(ref *(int*)ptr, *(int*)&value); - return *(uint*)&result; + fixed (uint* ptr = &location1) + { + int result = Interlocked.Or(ref *(int*)ptr, *(int*)&value); + return *(uint*)&result; + } } } #endif @@ -115,12 +124,15 @@ public static long Or(ref long location1, long value) #if ALLOW_UNSAFE_BLOCKS // https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64) - public static unsafe ulong Or(ref ulong location1, ulong value) + public static ulong Or(ref ulong location1, ulong value) { - fixed (ulong* ptr = &location1) + unsafe { - long result = Interlocked.Or(ref *(long*)ptr, *(long*)&value); - return *(ulong*)&result; + fixed (ulong* ptr = &location1) + { + long result = Interlocked.Or(ref *(long*)ptr, *(long*)&value); + return *(ulong*)&result; + } } } #endif diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 095ae7d..a4e7afa 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -180,10 +180,10 @@ ___ - [`int Or(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int32@-system-int32)) .NET 5.0 - [`long And(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int64@-system-int64)) .NET 5.0 - [`long Or(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int64@-system-int64)) .NET 5.0 - - [`unsafe uint And(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32)) .NET 5.0 - - [`unsafe uint Or(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32)) .NET 5.0 - - [`unsafe ulong And(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64)) .NET 5.0 - - [`unsafe ulong Or(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64)) .NET 5.0 + - [`uint And(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32)) .NET 5.0 + - [`uint Or(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32)) .NET 5.0 + - [`ulong And(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64)) .NET 5.0 + - [`ulong Or(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64)) .NET 5.0 - `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@)) .NET 7.0 - [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) .NET 5.0 From f3c6f0e51479c22e234d52d4f3c7c7211f9ef04a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:27:16 +0000 Subject: [PATCH 16/17] Reset Signatures.md to original version before merge --- PolyShim/Signatures.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index a4e7afa..d5c8212 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 363 +- **Total:** 355 - **Types:** 76 -- **Members:** 287 +- **Members:** 279 ___ @@ -175,15 +175,6 @@ ___ - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.index) .NET Core 3.0 - `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@)) .NET 7.0 -- `Interlocked` - - [`int And(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int32@-system-int32)) .NET 5.0 - - [`int Or(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int32@-system-int32)) .NET 5.0 - - [`long And(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int64@-system-int64)) .NET 5.0 - - [`long Or(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int64@-system-int64)) .NET 5.0 - - [`uint And(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32)) .NET 5.0 - - [`uint Or(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32)) .NET 5.0 - - [`ulong And(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64)) .NET 5.0 - - [`ulong Or(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64)) .NET 5.0 - `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@)) .NET 7.0 - [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) .NET 5.0 From db95a53ff8defc353b35edb0972c41c272a615a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:28:20 +0000 Subject: [PATCH 17/17] Regenerate Signatures.md after merging master Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/Signatures.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 5456834..8a19aca 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 364 +- **Total:** 372 - **Types:** 78 -- **Members:** 286 +- **Members:** 294 ___ @@ -175,6 +175,15 @@ ___ - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.index) .NET Core 3.0 - `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@)) .NET 7.0 +- `Interlocked` + - [`int And(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int32@-system-int32)) .NET 5.0 + - [`int Or(ref int, int)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int32@-system-int32)) .NET 5.0 + - [`long And(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-int64@-system-int64)) .NET 5.0 + - [`long Or(ref long, long)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-int64@-system-int64)) .NET 5.0 + - [`uint And(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint32@-system-uint32)) .NET 5.0 + - [`uint Or(ref uint, uint)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint32@-system-uint32)) .NET 5.0 + - [`ulong And(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.and#system-threading-interlocked-and(system-uint64@-system-uint64)) .NET 5.0 + - [`ulong Or(ref ulong, ulong)`](https://learn.microsoft.com/dotnet/api/system.threading.interlocked.or#system-threading-interlocked-or(system-uint64@-system-uint64)) .NET 5.0 - `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@)) .NET 7.0 - [`bool TryParse(string?, out IntPtr)`](https://learn.microsoft.com/dotnet/api/system.intptr.tryparse#system-intptr-tryparse(system-string-system-intptr@)) .NET 5.0