From d855fba1a1ace17ee411251d26564c2322aee09d Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Wed, 5 May 2021 13:58:06 -0700 Subject: [PATCH 1/6] add position breaking change --- docs/core/compatibility/6.0.md | 1 + ...s-after-readasync-writeasync-completion.md | 47 +++++++++++++++++++ docs/core/compatibility/toc.yml | 4 ++ 3 files changed, 52 insertions(+) create mode 100644 docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md diff --git a/docs/core/compatibility/6.0.md b/docs/core/compatibility/6.0.md index 5b22af10caaef..1d5bf0cb46a97 100644 --- a/docs/core/compatibility/6.0.md +++ b/docs/core/compatibility/6.0.md @@ -30,6 +30,7 @@ If you're migrating an app to .NET 6, the breaking changes listed here might aff ## Core .NET libraries - [Changes to nullable reference type annotations](core-libraries/6.0/nullable-ref-type-annotation-changes.md) +- [FileStream.Position updates after ReadAsync or WriteAsync completes](core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md) - [New System.Linq.Queryable method overloads](core-libraries/6.0/additional-linq-queryable-method-overloads.md) - [Some parameters in Stream-derived types are renamed](core-libraries/6.0/parameters-renamed-on-stream-derived-types.md) - [Standard numeric format parsing precision](core-libraries/6.0/numeric-format-parsing-handles-higher-precision.md) diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md new file mode 100644 index 0000000000000..5f20e5d8492ed --- /dev/null +++ b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md @@ -0,0 +1,47 @@ +--- +title: ".NET 6 breaking change: FileStream.Position updated after ReadAsync or WriteAsync completion" +description: Learn about the .NET 6 breaking change in core .NET libraries FileStream.Position is updated after ReadAsync or WriteAsync completion. +ms.date: 05/05/2021 +--- +# FileStream.Position updates after ReadAsync or WriteAsync completes + + is now updated after or complete. + +## Change description + +In previous .NET versions on Windows, is updated after the asynchronous read or write operation starts. Starting in .NET 6, is updated after those operations complete. + +## Version introduced + +6.0 Preview 4 + +## Reason for change + + has never been thread-safe, but until .NET 6, .NET has tried to support multiple concurrent calls to its async methods ( and ) on Windows. + +This change was introduced to allow for 100% asynchronous file I/O with and to fix the following issues: + +- [FileStream.FlushAsync ends up doing synchronous writes](https://github.com/dotnet/runtime/issue/27643) +- [Win32 FileStream turns async reads into sync reads](https://github.com/dotnet/runtime/issue/16341) + +Now when buffering is enabled (that is, the `bufferSize` argument that's passed to is greater than 1), every and operation is serialized. + +## Recommended action + + + +## Affected APIs + +- + + diff --git a/docs/core/compatibility/toc.yml b/docs/core/compatibility/toc.yml index 488fb66e4df28..16d6f3855c59d 100644 --- a/docs/core/compatibility/toc.yml +++ b/docs/core/compatibility/toc.yml @@ -51,6 +51,8 @@ items: href: aspnet-core/6.0/signalr-java-client-updated.md - name: Core .NET libraries items: + - name: FileStream.Position updated after completion + href: core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md - name: New Queryable method overloads href: core-libraries/6.0/additional-linq-queryable-method-overloads.md - name: Nullability annotation changes @@ -475,6 +477,8 @@ items: items: - name: .NET 6 items: + - name: FileStream.Position updated after completion + href: core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md - name: New Queryable method overloads href: core-libraries/6.0/additional-linq-queryable-method-overloads.md - name: Nullability annotation changes From be91bf4d5471a9a7731d693570552a5854a4b943 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Wed, 5 May 2021 14:06:27 -0700 Subject: [PATCH 2/6] add code snippet --- ...s-after-readasync-writeasync-completion.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md index 5f20e5d8492ed..36c1a858e94bb 100644 --- a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md +++ b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md @@ -11,6 +11,30 @@ ms.date: 05/05/2021 In previous .NET versions on Windows, is updated after the asynchronous read or write operation starts. Starting in .NET 6, is updated after those operations complete. +The following code shows how the value of differs between previous .NET versions and .NET 6. + +```csharp +byte[] bytes = new byte[10_000]; +string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + +using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true)) +{ + Task[] writes = new Task[3]; + + writes[0] = fs.WriteAsync(bytes, 0, bytes.Length); + Console.WriteLine(fs.Position); // 10000 in .NET 5, 0 in .NET 6 + + writes[1] = fs.WriteAsync(bytes, 0, bytes.Length); + Console.WriteLine(fs.Position); // 20000 in .NET 5, 0 in .NET 6 + + writes[2] = fs.WriteAsync(bytes, 0, bytes.Length); + Console.WriteLine(fs.Position); // 30000 in .NET 5, 0 in .NET 6 + + await Task.WhenAll(writes); + Console.WriteLine(fs.Position); // 30000 in all versions +} +``` + ## Version introduced 6.0 Preview 4 From b1d52b50aa33bcb5df600279ddca3712da90273a Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Wed, 5 May 2021 14:51:53 -0700 Subject: [PATCH 3/6] add second fs breaking change --- docs/core/compatibility/6.0.md | 1 + .../filestream-doesnt-sync-offset-with-os.md | 64 +++++++++++++++++++ ...s-after-readasync-writeasync-completion.md | 2 +- docs/core/compatibility/toc.yml | 4 ++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md diff --git a/docs/core/compatibility/6.0.md b/docs/core/compatibility/6.0.md index 1d5bf0cb46a97..83db4c2d16897 100644 --- a/docs/core/compatibility/6.0.md +++ b/docs/core/compatibility/6.0.md @@ -30,6 +30,7 @@ If you're migrating an app to .NET 6, the breaking changes listed here might aff ## Core .NET libraries - [Changes to nullable reference type annotations](core-libraries/6.0/nullable-ref-type-annotation-changes.md) +- [FileStream no longer synchronizes file offset with OS](core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md) - [FileStream.Position updates after ReadAsync or WriteAsync completes](core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md) - [New System.Linq.Queryable method overloads](core-libraries/6.0/additional-linq-queryable-method-overloads.md) - [Some parameters in Stream-derived types are renamed](core-libraries/6.0/parameters-renamed-on-stream-derived-types.md) diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md b/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md new file mode 100644 index 0000000000000..6d187f0c25ad3 --- /dev/null +++ b/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md @@ -0,0 +1,64 @@ +--- +title: ".NET 6 breaking change: FileStream doesn't synchronize file offset with OS" +description: Learn about the .NET 6 breaking change in core .NET libraries where FileStream doesn't synchronize the file offset with the operating system. +ms.date: 05/05/2021 +--- +# FileStream no longer synchronizes file offset with OS + + no longer synchronizes the file offset with the operating system, and just keeps the offset in memory. + +## Change description + +In previous .NET versions, synchronizes the file offset with the Windows operating system (OS) when it writes to a file. It synchronizes the offset by calling , which is an expensive system call. Starting in .NET 6, no longer synchronizes the file offset, and instead just keeps the offset in memory. always returns the current offset, but if you obtain the file handle from and query the OS for the current file offset using a system call, the offset value will be 0. + +The following code shows how the file offset differs between previous .NET versions and .NET 6. + +```csharp +[DllImport("kernel32.dll")] +private static extern bool SetFilePointerEx(SafeFileHandle hFile, long liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod); + +byte[] bytes = new byte[10_000]; +string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + +using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true)) +{ + SafeFileHandle handle = fs.SafeFileHandle; + + await fs.WriteAsync(bytes, 0, bytes.Length); + Console.WriteLine(fs.Position); // 10000 in all versions + + if (SetFilePointerEx(handle, 0, out long currentOffset, 1 /* get current offset */)) + { + Console.WriteLine(currentOffset); // 10000 in .NET 5, 0 in .NET 6 + } +} +``` + +## Version introduced + +6.0 Preview 4 + +## Reason for change + +This change was introduced to improve the performance of asynchronous reads and writes and to address the following issues: + +- [Win32 FileStream will issue a seek on every ReadAsync call](https://github.com/dotnet/runtime/issue/16354) +- [FileStream.Windows useAsync WriteAsync calls blocking APIs](https://github.com/dotnet/runtime/issue/25905) + +This change has allowed for up to two times faster operations and up to five times faster operations. + +## Recommended action + + + +## Affected APIs + +- + + diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md index 36c1a858e94bb..06cd1ec8b6053 100644 --- a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md +++ b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md @@ -1,6 +1,6 @@ --- title: ".NET 6 breaking change: FileStream.Position updated after ReadAsync or WriteAsync completion" -description: Learn about the .NET 6 breaking change in core .NET libraries FileStream.Position is updated after ReadAsync or WriteAsync completion. +description: Learn about the .NET 6 breaking change in core .NET libraries where FileStream.Position is updated after ReadAsync or WriteAsync completion. ms.date: 05/05/2021 --- # FileStream.Position updates after ReadAsync or WriteAsync completes diff --git a/docs/core/compatibility/toc.yml b/docs/core/compatibility/toc.yml index 16d6f3855c59d..6d11635c29976 100644 --- a/docs/core/compatibility/toc.yml +++ b/docs/core/compatibility/toc.yml @@ -51,6 +51,8 @@ items: href: aspnet-core/6.0/signalr-java-client-updated.md - name: Core .NET libraries items: + - name: FileStream no longer synchronizes offset with OS + href: core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md - name: FileStream.Position updated after completion href: core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md - name: New Queryable method overloads @@ -477,6 +479,8 @@ items: items: - name: .NET 6 items: + - name: FileStream no longer synchronizes offset with OS + href: core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md - name: FileStream.Position updated after completion href: core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md - name: New Queryable method overloads From 74e200ebef2c3776f8a56b928b8cdb8ba382db34 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Wed, 5 May 2021 15:37:00 -0700 Subject: [PATCH 4/6] fix bad xref links --- .../6.0/filestream-doesnt-sync-offset-with-os.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md b/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md index 6d187f0c25ad3..3fe56c74ae3f4 100644 --- a/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md +++ b/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md @@ -9,7 +9,7 @@ ms.date: 05/05/2021 ## Change description -In previous .NET versions, synchronizes the file offset with the Windows operating system (OS) when it writes to a file. It synchronizes the offset by calling , which is an expensive system call. Starting in .NET 6, no longer synchronizes the file offset, and instead just keeps the offset in memory. always returns the current offset, but if you obtain the file handle from and query the OS for the current file offset using a system call, the offset value will be 0. +In previous .NET versions, synchronizes the file offset with the Windows operating system (OS) when it writes to a file. It synchronizes the offset by calling `SetFilePointer`, which is an expensive system call. Starting in .NET 6, no longer synchronizes the file offset, and instead just keeps the offset in memory. always returns the current offset, but if you obtain the file handle from and query the OS for the current file offset using a system call, the offset value will be 0. The following code shows how the file offset differs between previous .NET versions and .NET 6. @@ -53,7 +53,7 @@ This change has allowed for up to two times faster +None. diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md index 06cd1ec8b6053..fd71d10220de6 100644 --- a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md +++ b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md @@ -5,7 +5,7 @@ ms.date: 05/05/2021 --- # FileStream.Position updates after ReadAsync or WriteAsync completes - is now updated after or complete. + is now updated after or completes. ## Change description @@ -41,14 +41,14 @@ using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrit ## Reason for change - has never been thread-safe, but until .NET 6, .NET has tried to support multiple concurrent calls to its async methods ( and ) on Windows. + has never been thread-safe, but until .NET 6, .NET has tried to support multiple concurrent calls to its asynchronous methods ( and ) on Windows. This change was introduced to allow for 100% asynchronous file I/O with and to fix the following issues: - [FileStream.FlushAsync ends up doing synchronous writes](https://github.com/dotnet/runtime/issue/27643) - [Win32 FileStream turns async reads into sync reads](https://github.com/dotnet/runtime/issue/16341) -Now when buffering is enabled (that is, the `bufferSize` argument that's passed to is greater than 1), every and operation is serialized. +Now, when buffering is enabled (that is, the `bufferSize` argument that's passed to the [FileStream constructor](xref:System.IO.FileStream.%23ctor%2A) is greater than 1), every and operation is serialized. ## Recommended action From 250f5bc37b4ca20d43a02ff2457e9360fad41bb8 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 24 Jun 2021 16:31:07 -0700 Subject: [PATCH 6/6] add recommended actions --- .../6.0/filestream-doesnt-sync-offset-with-os.md | 14 ++++++++++++++ ...ates-after-readasync-writeasync-completion.md | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md b/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md index 48f9dfeee7c05..46e3c426d9f45 100644 --- a/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md +++ b/docs/core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md @@ -49,7 +49,21 @@ With this change, operations are up to ## Recommended action +- Modify any code that relied on the offset being synchronized. +- To enable the .NET 5 behavior in .NET 6, specify an `AppContext` switch or an environment variable. By setting the switch to `true`, you opt out of all performance improvements made to `FileStream` in .NET 6. + + ```xml + { + "configProperties": { + "System.IO.UseNet5CompatFileStream": true + } + } + ``` + + ```cmd + set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1 + ``` ## Affected APIs diff --git a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md index fd71d10220de6..04c5ba580aaee 100644 --- a/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md +++ b/docs/core/compatibility/core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md @@ -20,7 +20,7 @@ string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true)) { Task[] writes = new Task[3]; - + writes[0] = fs.WriteAsync(bytes, 0, bytes.Length); Console.WriteLine(fs.Position); // 10000 in .NET 5, 0 in .NET 6 @@ -52,7 +52,21 @@ Now, when buffering is enabled (that is, the `bufferSize` argument that's passed ## Recommended action +- Modify any code that relied on the position being set before operations completed. + +- To enable the .NET 5 behavior in .NET 6, specify an `AppContext` switch or an environment variable. By setting the switch to `true`, you opt out of all performance improvements made to `FileStream` in .NET 6. + + ```xml + { + "configProperties": { + "System.IO.UseNet5CompatFileStream": true + } + } + ``` + ```cmd + set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1 + ``` ## Affected APIs