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