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)); + } +}