diff --git a/apiCount.include.md b/apiCount.include.md index b1c1c59d..331cc84c 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,28 +1,28 @@ -**API count: 1109** +**API count: 1111** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1036 | -| `net462` | 1036 | -| `net47` | 1035 | -| `net471` | 1034 | -| `net472` | 1030 | -| `net48` | 1030 | -| `net481` | 1030 | -| `netstandard2.0` | 1032 | -| `netstandard2.1` | 885 | -| `netcoreapp2.0` | 955 | -| `netcoreapp2.1` | 896 | -| `netcoreapp2.2` | 896 | -| `netcoreapp3.0` | 857 | -| `netcoreapp3.1` | 856 | -| `net5.0` | 726 | -| `net6.0` | 627 | -| `net7.0` | 478 | -| `net8.0` | 357 | -| `net9.0` | 236 | +| `net461` | 1038 | +| `net462` | 1038 | +| `net47` | 1037 | +| `net471` | 1036 | +| `net472` | 1032 | +| `net48` | 1032 | +| `net481` | 1032 | +| `netstandard2.0` | 1034 | +| `netstandard2.1` | 887 | +| `netcoreapp2.0` | 957 | +| `netcoreapp2.1` | 898 | +| `netcoreapp2.2` | 898 | +| `netcoreapp3.0` | 859 | +| `netcoreapp3.1` | 858 | +| `net5.0` | 728 | +| `net6.0` | 629 | +| `net7.0` | 480 | +| `net8.0` | 359 | +| `net9.0` | 238 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1022 | +| `uap10.0` | 1024 | diff --git a/api_list.include.md b/api_list.include.md index 668d8752..5bd0cd3f 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -1494,6 +1494,14 @@ * `ValueTask CompletedTask` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask.completedtask?view=net-11.0) +#### Volatile + + * `void ReadBarrier()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.volatile.readbarrier?view=net-11.0) + * Note: Implemented as a full fence, since no one way barrier exists below net10. Correct but stronger than required, and unlike the BCL it costs a processor instruction on x86 and x64. + * `void WriteBarrier()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.volatile.writebarrier?view=net-11.0) + * Note: Implemented as a full fence, since no one way barrier exists below net10. Correct but stronger than required, and unlike the BCL it costs a processor instruction on x86 and x64. + + #### XDocument * `Task SaveAsync(Stream, SaveOptions, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument.saveasync?view=net-11.0#system-xml-linq-xdocument-saveasync(system-io-stream-system-xml-linq-saveoptions-system-threading-cancellationtoken)) diff --git a/assemblySize.include.md b/assemblySize.include.md index 7fddf141..49b0eb70 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -2,25 +2,25 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 371.0KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 326.5KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 370.0KB | +361.5KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net462 | 7.0KB | 375.0KB | +368.0KB | +7.0KB | +6.5KB | +7.5KB | +11.5KB | -| net47 | 7.0KB | 374.5KB | +367.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 372.5KB | +364.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | -| net472 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 350.0KB | +341.0KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 326.0KB | +316.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net5.0 | 9.5KB | 288.0KB | +278.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net6.0 | 10.0KB | 230.0KB | +220.0KB | +9.5KB | +6.5KB | +512bytes | +3.0KB | -| net7.0 | 10.0KB | 196.0KB | +186.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 166.0KB | +156.5KB | +8.5KB | +512bytes | +512bytes | +3.5KB | -| net9.0 | 9.5KB | 115.5KB | +106.0KB | +8.5KB | | +512bytes | +3.0KB | +| netstandard2.0 | 8.0KB | 371.5KB | +363.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 327.0KB | +318.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | +| net461 | 8.5KB | 370.0KB | +361.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net47 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 372.5KB | +364.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net472 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net48 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net481 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| netcoreapp2.0 | 9.0KB | 350.5KB | +341.5KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 330.5KB | +321.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 330.5KB | +321.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 326.5KB | +317.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 324.5KB | +315.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net5.0 | 9.5KB | 288.5KB | +279.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net6.0 | 10.0KB | 230.0KB | +220.0KB | +10.0KB | +7.0KB | +1.0KB | +3.5KB | +| net7.0 | 10.0KB | 196.5KB | +186.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 166.5KB | +157.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 116.0KB | +106.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 | 544.1KB | +536.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 473.0KB | +464.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 544.1KB | +535.6KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net462 | 7.0KB | 549.1KB | +542.1KB | +14.7KB | +8.2KB | +12.4KB | +16.9KB | -| net47 | 7.0KB | 548.4KB | +541.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 546.0KB | +537.5KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | -| net472 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 512.5KB | +503.5KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 469.5KB | +460.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 467.5KB | +458.0KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net5.0 | 9.5KB | 413.4KB | +403.9KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net6.0 | 10.0KB | 335.4KB | +325.4KB | +17.2KB | +8.2KB | +1.1KB | +3.7KB | -| net7.0 | 10.0KB | 283.6KB | +273.6KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 239.8KB | +230.3KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | -| net9.0 | 9.5KB | 166.8KB | +157.3KB | +16.0KB | | +1.1KB | +3.7KB | +| netstandard2.0 | 8.0KB | 544.9KB | +536.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 473.8KB | +465.3KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | +| net461 | 8.5KB | 544.5KB | +536.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 549.5KB | +542.5KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net47 | 7.0KB | 549.2KB | +542.2KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 546.4KB | +537.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net472 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net48 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net481 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| netcoreapp2.0 | 9.0KB | 513.4KB | +504.4KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 481.0KB | +472.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 481.0KB | +472.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 470.3KB | +460.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 468.3KB | +458.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net5.0 | 9.5KB | 414.2KB | +404.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net6.0 | 10.0KB | 335.8KB | +325.8KB | +17.7KB | +8.7KB | +1.6KB | +4.2KB | +| net7.0 | 10.0KB | 284.4KB | +274.4KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 240.7KB | +231.2KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 167.6KB | +158.1KB | +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 e40fa0f2..97f7e593 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: 1109** +**API count: 1111** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1036 | -| `net462` | 1036 | -| `net47` | 1035 | -| `net471` | 1034 | -| `net472` | 1030 | -| `net48` | 1030 | -| `net481` | 1030 | -| `netstandard2.0` | 1032 | -| `netstandard2.1` | 885 | -| `netcoreapp2.0` | 955 | -| `netcoreapp2.1` | 896 | -| `netcoreapp2.2` | 896 | -| `netcoreapp3.0` | 857 | -| `netcoreapp3.1` | 856 | -| `net5.0` | 726 | -| `net6.0` | 627 | -| `net7.0` | 478 | -| `net8.0` | 357 | -| `net9.0` | 236 | +| `net461` | 1038 | +| `net462` | 1038 | +| `net47` | 1037 | +| `net471` | 1036 | +| `net472` | 1032 | +| `net48` | 1032 | +| `net481` | 1032 | +| `netstandard2.0` | 1034 | +| `netstandard2.1` | 887 | +| `netcoreapp2.0` | 957 | +| `netcoreapp2.1` | 898 | +| `netcoreapp2.2` | 898 | +| `netcoreapp3.0` | 859 | +| `netcoreapp3.1` | 858 | +| `net5.0` | 728 | +| `net6.0` | 629 | +| `net7.0` | 480 | +| `net8.0` | 359 | +| `net9.0` | 238 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1022 | +| `uap10.0` | 1024 | @@ -96,25 +96,25 @@ 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 | 371.0KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 326.5KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 370.0KB | +361.5KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net462 | 7.0KB | 375.0KB | +368.0KB | +7.0KB | +6.5KB | +7.5KB | +11.5KB | -| net47 | 7.0KB | 374.5KB | +367.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 372.5KB | +364.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | -| net472 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 350.0KB | +341.0KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 326.0KB | +316.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net5.0 | 9.5KB | 288.0KB | +278.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net6.0 | 10.0KB | 230.0KB | +220.0KB | +9.5KB | +6.5KB | +512bytes | +3.0KB | -| net7.0 | 10.0KB | 196.0KB | +186.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 166.0KB | +156.5KB | +8.5KB | +512bytes | +512bytes | +3.5KB | -| net9.0 | 9.5KB | 115.5KB | +106.0KB | +8.5KB | | +512bytes | +3.0KB | +| netstandard2.0 | 8.0KB | 371.5KB | +363.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 327.0KB | +318.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | +| net461 | 8.5KB | 370.0KB | +361.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net47 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 372.5KB | +364.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net472 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net48 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net481 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| netcoreapp2.0 | 9.0KB | 350.5KB | +341.5KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 330.5KB | +321.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 330.5KB | +321.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 326.5KB | +317.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 324.5KB | +315.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net5.0 | 9.5KB | 288.5KB | +279.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net6.0 | 10.0KB | 230.0KB | +220.0KB | +10.0KB | +7.0KB | +1.0KB | +3.5KB | +| net7.0 | 10.0KB | 196.5KB | +186.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 166.5KB | +157.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 116.0KB | +106.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,25 +123,25 @@ 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 | 544.1KB | +536.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 473.0KB | +464.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 544.1KB | +535.6KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net462 | 7.0KB | 549.1KB | +542.1KB | +14.7KB | +8.2KB | +12.4KB | +16.9KB | -| net47 | 7.0KB | 548.4KB | +541.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 546.0KB | +537.5KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | -| net472 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 512.5KB | +503.5KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 469.5KB | +460.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 467.5KB | +458.0KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net5.0 | 9.5KB | 413.4KB | +403.9KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net6.0 | 10.0KB | 335.4KB | +325.4KB | +17.2KB | +8.2KB | +1.1KB | +3.7KB | -| net7.0 | 10.0KB | 283.6KB | +273.6KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 239.8KB | +230.3KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | -| net9.0 | 9.5KB | 166.8KB | +157.3KB | +16.0KB | | +1.1KB | +3.7KB | +| netstandard2.0 | 8.0KB | 544.9KB | +536.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 473.8KB | +465.3KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | +| net461 | 8.5KB | 544.5KB | +536.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 549.5KB | +542.5KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net47 | 7.0KB | 549.2KB | +542.2KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 546.4KB | +537.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net472 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net48 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net481 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| netcoreapp2.0 | 9.0KB | 513.4KB | +504.4KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 481.0KB | +472.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 481.0KB | +472.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 470.3KB | +460.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 468.3KB | +458.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net5.0 | 9.5KB | 414.2KB | +404.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net6.0 | 10.0KB | 335.8KB | +325.8KB | +17.7KB | +8.7KB | +1.6KB | +4.2KB | +| net7.0 | 10.0KB | 284.4KB | +274.4KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 240.7KB | +231.2KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 167.6KB | +158.1KB | +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 | @@ -2109,6 +2109,14 @@ The class `Polyfill` includes the following extension methods: * `ValueTask CompletedTask` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask.completedtask?view=net-11.0) +#### Volatile + + * `void ReadBarrier()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.volatile.readbarrier?view=net-11.0) + * Note: Implemented as a full fence, since no one way barrier exists below net10. Correct but stronger than required, and unlike the BCL it costs a processor instruction on x86 and x64. + * `void WriteBarrier()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.threading.volatile.writebarrier?view=net-11.0) + * Note: Implemented as a full fence, since no one way barrier exists below net10. Correct but stronger than required, and unlike the BCL it costs a processor instruction on x86 and x64. + + #### XDocument * `Task SaveAsync(Stream, SaveOptions, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.xml.linq.xdocument.saveasync?view=net-11.0#system-xml-linq-xdocument-saveasync(system-io-stream-system-xml-linq-saveoptions-system-threading-cancellationtoken)) @@ -2497,7 +2505,7 @@ void ObjectDisposedExceptionExample(bool isDisposed) ObjectDisposedException.ThrowIf(isDisposed, typeof(Consume)); } ``` -snippet source | anchor +snippet source | anchor @@ -2516,7 +2524,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 f9df0ab2..c34537ef 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -326,6 +326,12 @@ void Base64Usage() } #endif + void VolatileBarrierUsage() + { + Volatile.ReadBarrier(); + Volatile.WriteBarrier(); + } + void UriSchemeDataUsage() { _ = Uri.UriSchemeData; diff --git a/src/Polyfill/VolatilePolyfill.cs b/src/Polyfill/VolatilePolyfill.cs new file mode 100644 index 00000000..cb7246ca --- /dev/null +++ b/src/Polyfill/VolatilePolyfill.cs @@ -0,0 +1,33 @@ +#if !NET10_0_OR_GREATER + +namespace Polyfills; + +using System.Threading; + +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.volatile.readbarrier?view=net-11.0 + //Note: Implemented as a full fence, since no one way barrier exists below net10. Correct but stronger than required, and unlike the BCL it costs a processor instruction on x86 and x64. + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.volatile.writebarrier?view=net-11.0 + //Note: Implemented as a full fence, since no one way barrier exists below net10. Correct but stronger than required, and unlike the BCL it costs a processor instruction on x86 and x64. + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} + +#endif diff --git a/src/Split/net461/VolatilePolyfill.cs b/src/Split/net461/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net461/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net462/VolatilePolyfill.cs b/src/Split/net462/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net462/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net47/VolatilePolyfill.cs b/src/Split/net47/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net47/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net471/VolatilePolyfill.cs b/src/Split/net471/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net471/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net472/VolatilePolyfill.cs b/src/Split/net472/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net472/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net48/VolatilePolyfill.cs b/src/Split/net48/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net48/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net481/VolatilePolyfill.cs b/src/Split/net481/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net481/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net5.0/VolatilePolyfill.cs b/src/Split/net5.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net5.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net6.0/VolatilePolyfill.cs b/src/Split/net6.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net6.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net7.0/VolatilePolyfill.cs b/src/Split/net7.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net7.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net8.0/VolatilePolyfill.cs b/src/Split/net8.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net8.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/net9.0/VolatilePolyfill.cs b/src/Split/net9.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/net9.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/netcoreapp2.0/VolatilePolyfill.cs b/src/Split/netcoreapp2.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/netcoreapp2.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/netcoreapp2.1/VolatilePolyfill.cs b/src/Split/netcoreapp2.1/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/netcoreapp2.1/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/netcoreapp2.2/VolatilePolyfill.cs b/src/Split/netcoreapp2.2/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/netcoreapp2.2/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/netcoreapp3.0/VolatilePolyfill.cs b/src/Split/netcoreapp3.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/netcoreapp3.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/netcoreapp3.1/VolatilePolyfill.cs b/src/Split/netcoreapp3.1/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/netcoreapp3.1/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/netstandard2.0/VolatilePolyfill.cs b/src/Split/netstandard2.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/netstandard2.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/netstandard2.1/VolatilePolyfill.cs b/src/Split/netstandard2.1/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/netstandard2.1/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Split/uap10.0/VolatilePolyfill.cs b/src/Split/uap10.0/VolatilePolyfill.cs new file mode 100644 index 00000000..ff930b0a --- /dev/null +++ b/src/Split/uap10.0/VolatilePolyfill.cs @@ -0,0 +1,24 @@ +// +#pragma warning disable +namespace Polyfills; +using System.Threading; +static partial class Polyfill +{ + extension(Volatile) + { + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory reads before the call to ReadBarrier execute after memory + /// accesses that follow the call to ReadBarrier. + /// + public static void ReadBarrier() => + Interlocked.MemoryBarrier(); + /// + /// Synchronizes memory access as follows: the processor that executes the current thread cannot reorder + /// instructions in such a way that memory accesses before the call to WriteBarrier execute after + /// memory writes that follow the call to WriteBarrier. + /// + public static void WriteBarrier() => + Interlocked.MemoryBarrier(); + } +} diff --git a/src/Tests/VolatilePolyfillTests.cs b/src/Tests/VolatilePolyfillTests.cs new file mode 100644 index 00000000..9ec0b48b --- /dev/null +++ b/src/Tests/VolatilePolyfillTests.cs @@ -0,0 +1,78 @@ +using System.Diagnostics; +using System.Threading; + +// Memory ordering cannot be asserted deterministically from a unit test: on x86 and x64 the +// message passing case below succeeds even with no barrier at all. These exercise the calls in the +// pattern they exist for, which catches a barrier that throws or fails to compile on a target, and +// leave the ordering guarantee itself to the implementation. +public class VolatilePolyfillTests +{ + [Test] + public async Task Barriers_CanBeCalled() + { + Volatile.ReadBarrier(); + Volatile.WriteBarrier(); + + await Assert.That(true).IsTrue(); + } + + [Test] + public async Task Barriers_MessagePassing() + { + const int iterations = 200; + + var payload = 0; + var published = 0; + var observed = new int[iterations]; + + var writer = Task.Run( + () => + { + for (var i = 1; i <= iterations; i++) + { + payload = i; + Volatile.WriteBarrier(); + Volatile.Write(ref published, i); + } + }); + + var reader = Task.Run( + () => + { + var watch = Stopwatch.StartNew(); + for (var i = 1; i <= iterations; i++) + { + var spin = new SpinWait(); + while (Volatile.Read(ref published) < i) + { + if (watch.Elapsed > TimeSpan.FromSeconds(20)) + { + return; + } + + spin.SpinOnce(); + } + + Volatile.ReadBarrier(); + observed[i - 1] = payload; + } + }); + + var completed = Task.WaitAll(new[] { writer, reader }, TimeSpan.FromSeconds(30)); + + await Assert.That(completed).IsTrue(); + + // the reader never sees a payload older than the one it waited to be published + var stale = -1; + for (var i = 0; i < iterations; i++) + { + if (observed[i] < i + 1) + { + stale = i; + break; + } + } + + await Assert.That(stale).IsEqualTo(-1); + } +}