-
Notifications
You must be signed in to change notification settings - Fork 6k
Breaking changes for FileStream #24060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
d855fba
add position breaking change
gewarren be91bf4
add code snippet
gewarren b1d52b5
add second fs breaking change
gewarren 74e200e
fix bad xref links
gewarren 6ac028c
touch ups
gewarren 250f5bc
add recommended actions
gewarren 97a642d
resolve merge conflicts
gewarren f39063a
resolve conflicts
gewarren File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
.../core/compatibility/core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
--- | ||
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 | ||
|
||
To improve performance, <xref:System.IO.FileStream> no longer synchronizes the file offset with the operating system. | ||
|
||
## Change description | ||
|
||
In previous .NET versions, <xref:System.IO.FileStream> synchronizes the file offset with the Windows operating system (OS) when it reads or writes to a file. It synchronizes the offset by calling [SetFilePointer](/windows/win32/api/fileapi/nf-fileapi-setfilepointer), which is an expensive system call. Starting in .NET 6, <xref:System.IO.FileStream> no longer synchronizes the file offset, and instead just keeps the offset in memory. <xref:System.IO.FileStream.Position?displayProperty=nameWithType> always returns the current offset, but if you obtain the file handle from <xref:System.IO.FileStream.SafeFileHandle?displayProperty=nameWithType> 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) | ||
|
||
With this change, <xref:System.IO.FileStream.ReadAsync%2A> operations are up to two times faster, and <xref:System.IO.FileStream.WriteAsync%2A> operations are up to five times faster. | ||
|
||
## 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 | ||
|
||
None. | ||
|
||
## See also | ||
|
||
- [SetFilePointer function](/windows/win32/api/fileapi/nf-fileapi-setfilepointer) | ||
- [SetFilePointerEx function](/windows/win32/api/fileapi/nf-fileapi-setfilepointerex) | ||
|
||
<!-- | ||
|
||
### Category | ||
|
||
- Core .NET libraries | ||
|
||
### Affected APIS | ||
|
||
Not detectible via API analysis. | ||
|
||
--> |
85 changes: 85 additions & 0 deletions
85
...raries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
--- | ||
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 where FileStream.Position is updated after ReadAsync or WriteAsync completion. | ||
ms.date: 05/05/2021 | ||
--- | ||
# FileStream.Position updates after ReadAsync or WriteAsync completes | ||
|
||
<xref:System.IO.FileStream.Position?displayProperty=nameWithType> is now updated after <xref:System.IO.FileStream.ReadAsync%2A> or <xref:System.IO.FileStream.WriteAsync%2A> completes. | ||
|
||
## Change description | ||
|
||
In previous .NET versions on Windows, <xref:System.IO.FileStream.Position?displayProperty=nameWithType> is updated after the asynchronous read or write operation starts. Starting in .NET 6, <xref:System.IO.FileStream.Position?displayProperty=nameWithType> is updated after those operations complete. | ||
|
||
The following code shows how the value of <xref:System.IO.FileStream.Position?displayProperty=nameWithType> 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 | ||
|
||
## Reason for change | ||
|
||
<xref:System.IO.FileStream> has never been thread-safe, but until .NET 6, .NET has tried to support multiple concurrent calls to its asynchronous methods (<xref:System.IO.FileStream.ReadAsync%2A> and <xref:System.IO.FileStream.WriteAsync%2A>) on Windows. | ||
|
||
This change was introduced to allow for 100% asynchronous file I/O with <xref:System.IO.FileStream> 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 the [FileStream constructor](xref:System.IO.FileStream.%23ctor%2A) is greater than 1), every <xref:System.IO.FileStream.ReadAsync%2A> and <xref:System.IO.FileStream.WriteAsync%2A> operation is serialized. | ||
|
||
## 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 | ||
} | ||
} | ||
``` | ||
|
||
gewarren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
```cmd | ||
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1 | ||
``` | ||
|
||
## Affected APIs | ||
|
||
- <xref:System.IO.FileStream.Position?displayProperty=fullName> | ||
|
||
<!-- | ||
|
||
### Category | ||
|
||
- Core .NET libraries | ||
|
||
### Affected APIs | ||
|
||
- `P:System.IO.FileStream.Position` | ||
|
||
--> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.