Skip to content

Commit 7afceb2

Browse files
authored
Breaking changes for FileStream (#24060)
1 parent 55d44fb commit 7afceb2

File tree

4 files changed

+182
-0
lines changed

4 files changed

+182
-0
lines changed

docs/core/compatibility/6.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ If you're migrating an app to .NET 6, the breaking changes listed here might aff
4242
| [API obsoletions with non-default diagnostic IDs](core-libraries/6.0/obsolete-apis-with-custom-diagnostics.md) | Preview 1 |
4343
| [Changes to nullable reference type annotations](core-libraries/6.0/nullable-ref-type-annotation-changes.md) | Preview 1-2 |
4444
| [Environment.ProcessorCount behavior on Windows](core-libraries/6.0/environment-processorcount-on-windows.md) | Preview 2 |
45+
| [FileStream no longer synchronizes file offset with OS](core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md) | Preview 4 |
46+
| [FileStream.Position updates after ReadAsync or WriteAsync completes](core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md) | Preview 4 |
4547
| [New diagnostic IDs for obsoleted APIs](core-libraries/6.0/diagnostic-id-change-for-obsoletions.md) | Preview 5 |
4648
| [New System.Linq.Queryable method overloads](core-libraries/6.0/additional-linq-queryable-method-overloads.md) | Preview 3-4 |
4749
| [Older framework versions dropped from package](core-libraries/6.0/older-framework-versions-dropped.md) | Preview 5 |
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
title: ".NET 6 breaking change: FileStream doesn't synchronize file offset with OS"
3+
description: Learn about the .NET 6 breaking change in core .NET libraries where FileStream doesn't synchronize the file offset with the operating system.
4+
ms.date: 05/05/2021
5+
---
6+
# FileStream no longer synchronizes file offset with OS
7+
8+
To improve performance, <xref:System.IO.FileStream> no longer synchronizes the file offset with the operating system.
9+
10+
## Change description
11+
12+
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.
13+
14+
The following code shows how the file offset differs between previous .NET versions and .NET 6.
15+
16+
```csharp
17+
[DllImport("kernel32.dll")]
18+
private static extern bool SetFilePointerEx(SafeFileHandle hFile, long liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod);
19+
20+
byte[] bytes = new byte[10_000];
21+
string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
22+
23+
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
24+
{
25+
SafeFileHandle handle = fs.SafeFileHandle;
26+
27+
await fs.WriteAsync(bytes, 0, bytes.Length);
28+
Console.WriteLine(fs.Position); // 10000 in all versions
29+
30+
if (SetFilePointerEx(handle, 0, out long currentOffset, 1 /* get current offset */))
31+
{
32+
Console.WriteLine(currentOffset); // 10000 in .NET 5, 0 in .NET 6
33+
}
34+
}
35+
```
36+
37+
## Version introduced
38+
39+
6.0 Preview 4
40+
41+
## Reason for change
42+
43+
This change was introduced to improve the performance of asynchronous reads and writes and to address the following issues:
44+
45+
- [Win32 FileStream will issue a seek on every ReadAsync call](https://github.com/dotnet/runtime/issue/16354)
46+
- [FileStream.Windows useAsync WriteAsync calls blocking APIs](https://github.com/dotnet/runtime/issue/25905)
47+
48+
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.
49+
50+
## Recommended action
51+
52+
- Modify any code that relied on the offset being synchronized.
53+
54+
- 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.
55+
56+
```xml
57+
{
58+
"configProperties": {
59+
"System.IO.UseNet5CompatFileStream": true
60+
}
61+
}
62+
```
63+
64+
```cmd
65+
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1
66+
```
67+
68+
## Affected APIs
69+
70+
None.
71+
72+
## See also
73+
74+
- [SetFilePointer function](/windows/win32/api/fileapi/nf-fileapi-setfilepointer)
75+
- [SetFilePointerEx function](/windows/win32/api/fileapi/nf-fileapi-setfilepointerex)
76+
77+
<!--
78+
79+
### Category
80+
81+
- Core .NET libraries
82+
83+
### Affected APIS
84+
85+
Not detectible via API analysis.
86+
87+
-->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
title: ".NET 6 breaking change: FileStream.Position updated after ReadAsync or WriteAsync completion"
3+
description: Learn about the .NET 6 breaking change in core .NET libraries where FileStream.Position is updated after ReadAsync or WriteAsync completion.
4+
ms.date: 05/05/2021
5+
---
6+
# FileStream.Position updates after ReadAsync or WriteAsync completes
7+
8+
<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.
9+
10+
## Change description
11+
12+
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.
13+
14+
The following code shows how the value of <xref:System.IO.FileStream.Position?displayProperty=nameWithType> differs between previous .NET versions and .NET 6.
15+
16+
```csharp
17+
byte[] bytes = new byte[10_000];
18+
string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
19+
20+
using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
21+
{
22+
Task[] writes = new Task[3];
23+
24+
writes[0] = fs.WriteAsync(bytes, 0, bytes.Length);
25+
Console.WriteLine(fs.Position); // 10000 in .NET 5, 0 in .NET 6
26+
27+
writes[1] = fs.WriteAsync(bytes, 0, bytes.Length);
28+
Console.WriteLine(fs.Position); // 20000 in .NET 5, 0 in .NET 6
29+
30+
writes[2] = fs.WriteAsync(bytes, 0, bytes.Length);
31+
Console.WriteLine(fs.Position); // 30000 in .NET 5, 0 in .NET 6
32+
33+
await Task.WhenAll(writes);
34+
Console.WriteLine(fs.Position); // 30000 in all versions
35+
}
36+
```
37+
38+
## Version introduced
39+
40+
6.0 Preview 4
41+
42+
## Reason for change
43+
44+
<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.
45+
46+
This change was introduced to allow for 100% asynchronous file I/O with <xref:System.IO.FileStream> and to fix the following issues:
47+
48+
- [FileStream.FlushAsync ends up doing synchronous writes](https://github.com/dotnet/runtime/issue/27643)
49+
- [Win32 FileStream turns async reads into sync reads](https://github.com/dotnet/runtime/issue/16341)
50+
51+
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.
52+
53+
## Recommended action
54+
55+
- Modify any code that relied on the position being set before operations completed.
56+
57+
- 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.
58+
59+
```xml
60+
{
61+
"configProperties": {
62+
"System.IO.UseNet5CompatFileStream": true
63+
}
64+
}
65+
```
66+
67+
```cmd
68+
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1
69+
```
70+
71+
## Affected APIs
72+
73+
- <xref:System.IO.FileStream.Position?displayProperty=fullName>
74+
75+
<!--
76+
77+
### Category
78+
79+
- Core .NET libraries
80+
81+
### Affected APIs
82+
83+
- `P:System.IO.FileStream.Position`
84+
85+
-->

docs/core/compatibility/toc.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ items:
6565
href: core-libraries/6.0/obsolete-apis-with-custom-diagnostics.md
6666
- name: Environment.ProcessorCount behavior on Windows
6767
href: core-libraries/6.0/environment-processorcount-on-windows.md
68+
- name: FileStream no longer synchronizes offset with OS
69+
href: core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md
70+
- name: FileStream.Position updated after completion
71+
href: core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md
6872
- name: New diagnostic IDs for obsoleted APIs
6973
href: core-libraries/6.0/diagnostic-id-change-for-obsoletions.md
7074
- name: New Queryable method overloads
@@ -533,6 +537,10 @@ items:
533537
href: core-libraries/6.0/obsolete-apis-with-custom-diagnostics.md
534538
- name: Environment.ProcessorCount behavior on Windows
535539
href: core-libraries/6.0/environment-processorcount-on-windows.md
540+
- name: FileStream no longer synchronizes offset with OS
541+
href: core-libraries/6.0/filestream-doesnt-sync-offset-with-os.md
542+
- name: FileStream.Position updated after completion
543+
href: core-libraries/6.0/filestream-position-updates-after-readasync-writeasync-completion.md
536544
- name: New diagnostic IDs for obsoleted APIs
537545
href: core-libraries/6.0/diagnostic-id-change-for-obsoletions.md
538546
- name: New Queryable method overloads

0 commit comments

Comments
 (0)