From da74487da1c238e59ea1056ce1022c1dc029b27a Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 10 Sep 2026 20:39:48 +1000 Subject: [PATCH] Add the net9 Interlocked byte and short overloads Eight members: Exchange and CompareExchange over byte, sbyte, short and ushort. The implementation choice was the whole question, and it was settled by measurement rather than reasoning. Widening to a CompareExchange on the containing 32 bit word is the usual trick, but it writes the neighbouring bytes back, so a concurrent plain write to an adjacent byte can be lost. The BCL demonstrably does not do that: 33 million iterations of a plain write to buffer[1] racing Interlocked.Exchange on buffer[0] produced zero lost writes on net11, so it uses a real byte width atomic. Widening would have been a silent downgrade, not a slowdown. It also needs the address to find the aligned word, so it can touch memory outside the allocation. A lock is used instead. It keeps each operation indivisible against every other one, and since these members do not exist at all below net9 there is nothing outside this file to interoperate with. Writing only the target byte leaves neighbours alone, which is asserted by a test that reproduces the adjacency experiment and passes on the polyfill as well as the BCL. The one real divergence, that a concurrent plain write to the same location can be lost where the BCL operation is indivisible, is noted on all eight. API count 1162 -> 1170. --- apiCount.include.md | 40 ++--- api_list.include.md | 16 ++ assemblySize.include.md | 72 ++++---- readme.md | 132 ++++++++------ src/Consume/Consume.cs | 17 ++ src/Polyfill/Polyfill_Interlocked.cs | 165 ++++++++++++++++++ src/Split/net461/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net462/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net47/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net471/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net472/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net48/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net481/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net5.0/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net6.0/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net7.0/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/net8.0/Polyfill_Interlocked.cs | 120 +++++++++++++ .../netcoreapp2.0/Polyfill_Interlocked.cs | 120 +++++++++++++ .../netcoreapp2.1/Polyfill_Interlocked.cs | 120 +++++++++++++ .../netcoreapp2.2/Polyfill_Interlocked.cs | 120 +++++++++++++ .../netcoreapp3.0/Polyfill_Interlocked.cs | 120 +++++++++++++ .../netcoreapp3.1/Polyfill_Interlocked.cs | 120 +++++++++++++ .../netstandard2.0/Polyfill_Interlocked.cs | 120 +++++++++++++ .../netstandard2.1/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Split/uap10.0/Polyfill_Interlocked.cs | 120 +++++++++++++ src/Tests/InterlockedNarrowTests.cs | 144 +++++++++++++++ 26 files changed, 2752 insertions(+), 114 deletions(-) create mode 100644 src/Polyfill/Polyfill_Interlocked.cs create mode 100644 src/Split/net461/Polyfill_Interlocked.cs create mode 100644 src/Split/net462/Polyfill_Interlocked.cs create mode 100644 src/Split/net47/Polyfill_Interlocked.cs create mode 100644 src/Split/net471/Polyfill_Interlocked.cs create mode 100644 src/Split/net472/Polyfill_Interlocked.cs create mode 100644 src/Split/net48/Polyfill_Interlocked.cs create mode 100644 src/Split/net481/Polyfill_Interlocked.cs create mode 100644 src/Split/net5.0/Polyfill_Interlocked.cs create mode 100644 src/Split/net6.0/Polyfill_Interlocked.cs create mode 100644 src/Split/net7.0/Polyfill_Interlocked.cs create mode 100644 src/Split/net8.0/Polyfill_Interlocked.cs create mode 100644 src/Split/netcoreapp2.0/Polyfill_Interlocked.cs create mode 100644 src/Split/netcoreapp2.1/Polyfill_Interlocked.cs create mode 100644 src/Split/netcoreapp2.2/Polyfill_Interlocked.cs create mode 100644 src/Split/netcoreapp3.0/Polyfill_Interlocked.cs create mode 100644 src/Split/netcoreapp3.1/Polyfill_Interlocked.cs create mode 100644 src/Split/netstandard2.0/Polyfill_Interlocked.cs create mode 100644 src/Split/netstandard2.1/Polyfill_Interlocked.cs create mode 100644 src/Split/uap10.0/Polyfill_Interlocked.cs create mode 100644 src/Tests/InterlockedNarrowTests.cs diff --git a/apiCount.include.md b/apiCount.include.md index e502c84a..42499ca8 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,28 +1,28 @@ -**API count: 1162** +**API count: 1170** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1070 | -| `net462` | 1070 | -| `net47` | 1069 | -| `net471` | 1068 | -| `net472` | 1064 | -| `net48` | 1064 | -| `net481` | 1064 | -| `netstandard2.0` | 1066 | -| `netstandard2.1` | 899 | -| `netcoreapp2.0` | 989 | -| `netcoreapp2.1` | 910 | -| `netcoreapp2.2` | 910 | -| `netcoreapp3.0` | 871 | -| `netcoreapp3.1` | 870 | -| `net5.0` | 748 | -| `net6.0` | 644 | -| `net7.0` | 503 | -| `net8.0` | 382 | +| `net461` | 1078 | +| `net462` | 1078 | +| `net47` | 1077 | +| `net471` | 1076 | +| `net472` | 1072 | +| `net48` | 1072 | +| `net481` | 1072 | +| `netstandard2.0` | 1074 | +| `netstandard2.1` | 907 | +| `netcoreapp2.0` | 997 | +| `netcoreapp2.1` | 918 | +| `netcoreapp2.2` | 918 | +| `netcoreapp3.0` | 879 | +| `netcoreapp3.1` | 878 | +| `net5.0` | 756 | +| `net6.0` | 652 | +| `net7.0` | 511 | +| `net8.0` | 390 | | `net9.0` | 243 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1056 | +| `uap10.0` | 1064 | diff --git a/api_list.include.md b/api_list.include.md index 052722f6..049a4633 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -695,6 +695,22 @@ #### Interlocked + * `byte CompareExchange(byte, byte, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-byte@-system-byte-system-byte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `sbyte CompareExchange(sbyte, sbyte, sbyte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-sbyte@-system-sbyte-system-sbyte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `short CompareExchange(short, short, short)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-int16@-system-int16-system-int16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `ushort CompareExchange(ushort, ushort, ushort)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-uint16@-system-uint16-system-uint16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `byte Exchange(byte, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-byte@-system-byte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `sbyte Exchange(sbyte, sbyte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-sbyte@-system-sbyte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `short Exchange(short, short)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-int16@-system-int16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `ushort Exchange(ushort, ushort)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-uint16@-system-uint16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. * `T And(T, T) where T : struct` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.and?view=net-11.0) * `T Or(T, T) where T : struct` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.or?view=net-11.0) diff --git a/assemblySize.include.md b/assemblySize.include.md index f0111a0b..c1d5524a 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -2,24 +2,24 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 385.5KB | +377.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 341.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 384.5KB | +376.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 388.0KB | +381.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net47 | 7.0KB | 387.5KB | +380.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net471 | 8.5KB | 386.5KB | +378.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net472 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 363.5KB | +354.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| netcoreapp3.1 | 9.5KB | 341.0KB | +331.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net5.0 | 9.5KB | 306.5KB | +297.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net6.0 | 10.0KB | 247.5KB | +237.5KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 216.0KB | +206.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 186.5KB | +177.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 387.0KB | +379.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 343.0KB | +334.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net461 | 8.5KB | 386.0KB | +377.5KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 389.5KB | +382.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net47 | 7.0KB | 389.0KB | +382.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net471 | 8.5KB | 388.0KB | +379.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net472 | 8.5KB | 387.0KB | +378.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 387.0KB | +378.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 387.0KB | +378.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 365.0KB | +356.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 346.0KB | +337.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.2 | 9.0KB | 346.0KB | +337.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp3.0 | 9.5KB | 344.0KB | +334.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| netcoreapp3.1 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net5.0 | 9.5KB | 308.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net6.0 | 10.0KB | 249.0KB | +239.0KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 217.5KB | +207.5KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 188.0KB | +178.5KB | +8.5KB | +512bytes | +512bytes | +3.5KB | | net9.0 | 9.5KB | 117.0KB | +107.5KB | +8.0KB | | +512bytes | +3.0KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -29,24 +29,24 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 561.3KB | +553.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 490.3KB | +481.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 561.3KB | +552.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 564.8KB | +557.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net47 | 7.0KB | 564.1KB | +557.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net471 | 8.5KB | 562.7KB | +554.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net472 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 528.7KB | +519.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 488.3KB | +478.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| netcoreapp3.1 | 9.5KB | 486.8KB | +477.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net5.0 | 9.5KB | 434.9KB | +425.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net6.0 | 10.0KB | 355.7KB | +345.7KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 307.4KB | +297.4KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 264.1KB | +254.6KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 563.3KB | +555.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 492.3KB | +483.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net461 | 8.5KB | 563.3KB | +554.8KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 566.8KB | +559.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net47 | 7.0KB | 566.1KB | +559.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net471 | 8.5KB | 564.7KB | +556.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net472 | 8.5KB | 562.6KB | +554.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 562.6KB | +554.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 562.6KB | +554.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 530.7KB | +521.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 499.0KB | +490.0KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.2 | 9.0KB | 499.0KB | +490.0KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp3.0 | 9.5KB | 490.3KB | +480.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| netcoreapp3.1 | 9.5KB | 488.8KB | +479.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net5.0 | 9.5KB | 436.9KB | +427.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net6.0 | 10.0KB | 357.7KB | +347.7KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 309.3KB | +299.3KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 266.1KB | +256.6KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | | net9.0 | 9.5KB | 169.1KB | +159.6KB | +15.5KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | diff --git a/readme.md b/readme.md index bde9498b..79730c84 100644 --- a/readme.md +++ b/readme.md @@ -13,34 +13,34 @@ The package targets `netstandard2.0` and is designed to support the following ru * `uap10` -**API count: 1162** +**API count: 1170** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1070 | -| `net462` | 1070 | -| `net47` | 1069 | -| `net471` | 1068 | -| `net472` | 1064 | -| `net48` | 1064 | -| `net481` | 1064 | -| `netstandard2.0` | 1066 | -| `netstandard2.1` | 899 | -| `netcoreapp2.0` | 989 | -| `netcoreapp2.1` | 910 | -| `netcoreapp2.2` | 910 | -| `netcoreapp3.0` | 871 | -| `netcoreapp3.1` | 870 | -| `net5.0` | 748 | -| `net6.0` | 644 | -| `net7.0` | 503 | -| `net8.0` | 382 | +| `net461` | 1078 | +| `net462` | 1078 | +| `net47` | 1077 | +| `net471` | 1076 | +| `net472` | 1072 | +| `net48` | 1072 | +| `net481` | 1072 | +| `netstandard2.0` | 1074 | +| `netstandard2.1` | 907 | +| `netcoreapp2.0` | 997 | +| `netcoreapp2.1` | 918 | +| `netcoreapp2.2` | 918 | +| `netcoreapp3.0` | 879 | +| `netcoreapp3.1` | 878 | +| `net5.0` | 756 | +| `net6.0` | 652 | +| `net7.0` | 511 | +| `net8.0` | 390 | | `net9.0` | 243 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1056 | +| `uap10.0` | 1064 | @@ -96,24 +96,24 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 385.5KB | +377.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 341.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 384.5KB | +376.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 388.0KB | +381.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net47 | 7.0KB | 387.5KB | +380.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net471 | 8.5KB | 386.5KB | +378.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net472 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 363.5KB | +354.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| netcoreapp3.1 | 9.5KB | 341.0KB | +331.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net5.0 | 9.5KB | 306.5KB | +297.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net6.0 | 10.0KB | 247.5KB | +237.5KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 216.0KB | +206.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 186.5KB | +177.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 387.0KB | +379.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 343.0KB | +334.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net461 | 8.5KB | 386.0KB | +377.5KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 389.5KB | +382.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net47 | 7.0KB | 389.0KB | +382.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net471 | 8.5KB | 388.0KB | +379.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net472 | 8.5KB | 387.0KB | +378.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 387.0KB | +378.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 387.0KB | +378.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 365.0KB | +356.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 346.0KB | +337.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.2 | 9.0KB | 346.0KB | +337.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp3.0 | 9.5KB | 344.0KB | +334.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| netcoreapp3.1 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net5.0 | 9.5KB | 308.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net6.0 | 10.0KB | 249.0KB | +239.0KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 217.5KB | +207.5KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 188.0KB | +178.5KB | +8.5KB | +512bytes | +512bytes | +3.5KB | | net9.0 | 9.5KB | 117.0KB | +107.5KB | +8.0KB | | +512bytes | +3.0KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -123,24 +123,24 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 561.3KB | +553.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 490.3KB | +481.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 561.3KB | +552.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 564.8KB | +557.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net47 | 7.0KB | 564.1KB | +557.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net471 | 8.5KB | 562.7KB | +554.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net472 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 528.7KB | +519.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 488.3KB | +478.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| netcoreapp3.1 | 9.5KB | 486.8KB | +477.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net5.0 | 9.5KB | 434.9KB | +425.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net6.0 | 10.0KB | 355.7KB | +345.7KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 307.4KB | +297.4KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 264.1KB | +254.6KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 563.3KB | +555.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 492.3KB | +483.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net461 | 8.5KB | 563.3KB | +554.8KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 566.8KB | +559.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net47 | 7.0KB | 566.1KB | +559.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net471 | 8.5KB | 564.7KB | +556.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net472 | 8.5KB | 562.6KB | +554.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 562.6KB | +554.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 562.6KB | +554.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 530.7KB | +521.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 499.0KB | +490.0KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.2 | 9.0KB | 499.0KB | +490.0KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp3.0 | 9.5KB | 490.3KB | +480.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| netcoreapp3.1 | 9.5KB | 488.8KB | +479.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net5.0 | 9.5KB | 436.9KB | +427.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net6.0 | 10.0KB | 357.7KB | +347.7KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 309.3KB | +299.3KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 266.1KB | +256.6KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | | net9.0 | 9.5KB | 169.1KB | +159.6KB | +15.5KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | @@ -1310,6 +1310,22 @@ The class `Polyfill` includes the following extension methods: #### Interlocked + * `byte CompareExchange(byte, byte, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-byte@-system-byte-system-byte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `sbyte CompareExchange(sbyte, sbyte, sbyte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-sbyte@-system-sbyte-system-sbyte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `short CompareExchange(short, short, short)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-int16@-system-int16-system-int16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `ushort CompareExchange(ushort, ushort, ushort)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-uint16@-system-uint16-system-uint16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `byte Exchange(byte, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-byte@-system-byte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `sbyte Exchange(sbyte, sbyte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-sbyte@-system-sbyte)) + * Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `short Exchange(short, short)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-int16@-system-int16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + * `ushort Exchange(ushort, ushort)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-uint16@-system-uint16)) + * Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. * `T And(T, T) where T : struct` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.and?view=net-11.0) * `T Or(T, T) where T : struct` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.or?view=net-11.0) @@ -2591,7 +2607,7 @@ void ObjectDisposedExceptionExample(bool isDisposed) ObjectDisposedException.ThrowIf(isDisposed, typeof(Consume)); } ``` -snippet source | anchor +snippet source | anchor @@ -2610,7 +2626,7 @@ void EnsureExample(Order order, Customer customer, string customerId, string ema this.quantity = Ensure.NotNegativeOrZero(quantity); } ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index 202fdef0..364aab2d 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -216,6 +216,23 @@ void StringBuilder_ParamsSpan_Methods() } #endif + void Interlocked_Narrow_Methods() + { + byte unsignedByte = 0; + sbyte signedByte = 0; + short signedShort = 0; + ushort unsignedShort = 0; + + unsignedByte = Interlocked.Exchange(ref unsignedByte, 1); + unsignedByte = Interlocked.CompareExchange(ref unsignedByte, 1, 0); + signedByte = Interlocked.Exchange(ref signedByte, 1); + signedByte = Interlocked.CompareExchange(ref signedByte, 1, 0); + signedShort = Interlocked.Exchange(ref signedShort, 1); + signedShort = Interlocked.CompareExchange(ref signedShort, 1, 0); + unsignedShort = Interlocked.Exchange(ref unsignedShort, 1); + unsignedShort = Interlocked.CompareExchange(ref unsignedShort, 1, 0); + } + void GuidUsage() { var guid = Guid.CreateVersion7(); diff --git a/src/Polyfill/Polyfill_Interlocked.cs b/src/Polyfill/Polyfill_Interlocked.cs new file mode 100644 index 00000000..83d16ea4 --- /dev/null +++ b/src/Polyfill/Polyfill_Interlocked.cs @@ -0,0 +1,165 @@ +#pragma warning disable + +#if !NET9_0_OR_GREATER + +namespace Polyfills; + +using System.Threading; + +static partial class Polyfill +{ + // There is no 8 or 16 bit interlocked primitive below net9, and the two ways of faking one + // are both worse than a lock: + // + // * Widening to a CompareExchange on the containing 32 bit word writes the neighbouring + // bytes back, so a concurrent plain write to an adjacent byte can be lost. The BCL does + // not have that behaviour, so it would be a silent downgrade rather than a slowdown. + // * It also needs the address to find the aligned word, so it can read and write outside + // the allocation for a location at the end of one. + // + // The lock keeps each operation indivisible with respect to every other one, and since these + // members do not exist at all below net9 there is nothing outside this file to interoperate + // with. Writing only the target byte also leaves neighbours alone, matching the BCL. + static object narrowInterlockedLock = new(); + + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-byte@-system-byte) + //Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-byte@-system-byte-system-byte) + //Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + + return original; + } + } + + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-sbyte@-system-sbyte) + //Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-sbyte@-system-sbyte-system-sbyte) + //Note: Uses a lock rather than a hardware atomic, since no 8 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + + return original; + } + } + + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-int16@-system-int16) + //Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-int16@-system-int16-system-int16) + //Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + + return original; + } + } + + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=net-11.0#system-threading-interlocked-exchange(system-uint16@-system-uint16) + //Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.compareexchange?view=net-11.0#system-threading-interlocked-compareexchange(system-uint16@-system-uint16-system-uint16) + //Note: Uses a lock rather than a hardware atomic, since no 16 bit interlocked primitive exists below net9. Correct against other Interlocked calls, but a concurrent plain write to the same location can be lost, where the BCL operation is indivisible. + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + + return original; + } + } + } +} + +#endif diff --git a/src/Split/net461/Polyfill_Interlocked.cs b/src/Split/net461/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net461/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net462/Polyfill_Interlocked.cs b/src/Split/net462/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net462/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net47/Polyfill_Interlocked.cs b/src/Split/net47/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net47/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net471/Polyfill_Interlocked.cs b/src/Split/net471/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net471/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net472/Polyfill_Interlocked.cs b/src/Split/net472/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net472/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net48/Polyfill_Interlocked.cs b/src/Split/net48/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net48/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net481/Polyfill_Interlocked.cs b/src/Split/net481/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net481/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net5.0/Polyfill_Interlocked.cs b/src/Split/net5.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net5.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net6.0/Polyfill_Interlocked.cs b/src/Split/net6.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net6.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net7.0/Polyfill_Interlocked.cs b/src/Split/net7.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net7.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/net8.0/Polyfill_Interlocked.cs b/src/Split/net8.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/net8.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/netcoreapp2.0/Polyfill_Interlocked.cs b/src/Split/netcoreapp2.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/netcoreapp2.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/netcoreapp2.1/Polyfill_Interlocked.cs b/src/Split/netcoreapp2.1/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/netcoreapp2.1/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/netcoreapp2.2/Polyfill_Interlocked.cs b/src/Split/netcoreapp2.2/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/netcoreapp2.2/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/netcoreapp3.0/Polyfill_Interlocked.cs b/src/Split/netcoreapp3.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/netcoreapp3.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/netcoreapp3.1/Polyfill_Interlocked.cs b/src/Split/netcoreapp3.1/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/netcoreapp3.1/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/netstandard2.0/Polyfill_Interlocked.cs b/src/Split/netstandard2.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/netstandard2.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/netstandard2.1/Polyfill_Interlocked.cs b/src/Split/netstandard2.1/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/netstandard2.1/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Split/uap10.0/Polyfill_Interlocked.cs b/src/Split/uap10.0/Polyfill_Interlocked.cs new file mode 100644 index 00000000..34fbda97 --- /dev/null +++ b/src/Split/uap10.0/Polyfill_Interlocked.cs @@ -0,0 +1,120 @@ +// +#pragma warning disable +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + static object narrowInterlockedLock = new(); + extension(Interlocked) + { + /// + /// Sets an 8-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static byte Exchange(ref byte location1, byte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static byte CompareExchange(ref byte location1, byte value, byte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets an 8-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static sbyte Exchange(ref sbyte location1, sbyte value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 8-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static sbyte CompareExchange(ref sbyte location1, sbyte value, sbyte comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit signed integer to a specified value and returns the original value, as an atomic operation. + /// + public static short Exchange(ref short location1, short value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit signed integers for equality and, if they are equal, replaces the first value. + /// + public static short CompareExchange(ref short location1, short value, short comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + /// + /// Sets a 16-bit unsigned integer to a specified value and returns the original value, as an atomic operation. + /// + public static ushort Exchange(ref ushort location1, ushort value) + { + lock (narrowInterlockedLock) + { + var original = location1; + location1 = value; + return original; + } + } + /// + /// Compares two 16-bit unsigned integers for equality and, if they are equal, replaces the first value. + /// + public static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) + { + lock (narrowInterlockedLock) + { + var original = location1; + if (original == comparand) + { + location1 = value; + } + return original; + } + } + } +} diff --git a/src/Tests/InterlockedNarrowTests.cs b/src/Tests/InterlockedNarrowTests.cs new file mode 100644 index 00000000..9eeecab7 --- /dev/null +++ b/src/Tests/InterlockedNarrowTests.cs @@ -0,0 +1,144 @@ +using System.Threading; + +// Runs on every target, so net9.0 and later validate the BCL and everything below the polyfill. +public class InterlockedNarrowTests +{ + [Test] + public async Task Exchange_ReturnsTheOriginal() + { + byte unsignedByte = 5; + await Assert.That(Interlocked.Exchange(ref unsignedByte, 9)).IsEqualTo((byte) 5); + await Assert.That(unsignedByte).IsEqualTo((byte) 9); + + sbyte signedByte = -128; + await Assert.That(Interlocked.Exchange(ref signedByte, 127)).IsEqualTo((sbyte) -128); + await Assert.That(signedByte).IsEqualTo((sbyte) 127); + + short signedShort = -1; + await Assert.That(Interlocked.Exchange(ref signedShort, 300)).IsEqualTo((short) -1); + await Assert.That(signedShort).IsEqualTo((short) 300); + + ushort unsignedShort = ushort.MaxValue; + await Assert.That(Interlocked.Exchange(ref unsignedShort, 1)).IsEqualTo(ushort.MaxValue); + await Assert.That(unsignedShort).IsEqualTo((ushort) 1); + } + + [Test] + public async Task CompareExchange_ReplacesOnlyOnAMatch() + { + byte unsignedByte = 5; + await Assert.That(Interlocked.CompareExchange(ref unsignedByte, 9, 5)).IsEqualTo((byte) 5); + await Assert.That(unsignedByte).IsEqualTo((byte) 9); + + unsignedByte = 5; + // the original is returned whether or not the comparand matched + await Assert.That(Interlocked.CompareExchange(ref unsignedByte, 9, 4)).IsEqualTo((byte) 5); + await Assert.That(unsignedByte).IsEqualTo((byte) 5); + + sbyte signedByte = -1; + await Assert.That(Interlocked.CompareExchange(ref signedByte, 7, -1)).IsEqualTo((sbyte) -1); + await Assert.That(signedByte).IsEqualTo((sbyte) 7); + + short signedShort = 1000; + await Assert.That(Interlocked.CompareExchange(ref signedShort, 2000, 999)).IsEqualTo((short) 1000); + await Assert.That(signedShort).IsEqualTo((short) 1000); + + ushort unsignedShort = 40000; + await Assert.That(Interlocked.CompareExchange(ref unsignedShort, 50000, 40000)).IsEqualTo((ushort) 40000); + await Assert.That(unsignedShort).IsEqualTo((ushort) 50000); + } + + // The property that rules out implementing this by widening to a CompareExchange on the + // containing 32 bit word: that would write the neighbouring bytes back and could lose a + // concurrent plain write to one of them. The BCL does not do that, and neither does the + // polyfill, which writes only the target byte. + [Test] + public async Task DoesNotDisturbTheAdjacentByte() + { + var buffer = new byte[4]; + var stop = false; + var wrong = 0; + var iterations = 0; + + var writer = new Thread(() => + { + while (!Volatile.Read(ref stop)) + { + buffer[1] = 0xAB; + buffer[3] = 0xCD; + if (Volatile.Read(ref buffer[1]) != 0xAB || + Volatile.Read(ref buffer[3]) != 0xCD) + { + Interlocked.Increment(ref wrong); + } + + Interlocked.Increment(ref iterations); + } + }); + + var exchanger = new Thread(() => + { + byte flip = 0; + while (!Volatile.Read(ref stop)) + { + Interlocked.Exchange(ref buffer[0], flip); + Interlocked.CompareExchange(ref buffer[2], flip, flip); + flip = (byte) (flip ^ 0xFF); + } + }); + + writer.Start(); + exchanger.Start(); + Thread.Sleep(500); + Volatile.Write(ref stop, true); + writer.Join(); + exchanger.Join(); + + var observed = iterations; + await Assert.That(observed).IsGreaterThan(0); + await Assert.That(wrong).IsEqualTo(0); + } + + // no update may be lost when every writer goes through Interlocked, which is the contract + // these members actually have to keep + [Test] + public async Task CompareExchangeLoopLosesNoUpdates() + { + const int threads = 4; + const int perThread = 2000; + + ushort counter = 0; + var workers = new Thread[threads]; + for (var i = 0; i < threads; i++) + { + workers[i] = new Thread(() => + { + for (var n = 0; n < perThread; n++) + { + while (true) + { + var current = Interlocked.CompareExchange(ref counter, 0, 0); + var next = (ushort) (current + 1); + if (Interlocked.CompareExchange(ref counter, next, current) == current) + { + break; + } + } + } + }); + } + + foreach (var worker in workers) + { + worker.Start(); + } + + foreach (var worker in workers) + { + worker.Join(); + } + + var final = counter; + await Assert.That(final).IsEqualTo((ushort) (threads * perThread)); + } +}