Skip to content

Commit

Permalink
Update FileStream.Position breaking change (#40776)
Browse files Browse the repository at this point in the history
  • Loading branch information
gewarren committed May 9, 2024
1 parent ce44a46 commit 2e0cedb
Showing 1 changed file with 5 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,10 @@ ms.date: 10/04/2022

## 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.
In previous .NET versions on Windows, <xref:System.IO.FileStream.Position?displayProperty=nameWithType> was updated after the asynchronous read or write operation started. Starting in .NET 6, <xref:System.IO.FileStream.Position?displayProperty=nameWithType> is updated optimistically:

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
}
```
- After <xref:System.IO.FileStream.WriteAsync%2A> starts, but if the operation fails or is canceled, the position is corrected.
- When <xref:System.IO.FileStream.ReadAsync%2A> starts, but if the entire buffer isn't read, the position is corrected after the operation completes.

## Version introduced

Expand All @@ -48,11 +27,9 @@ This change was introduced to allow for 100% asynchronous file I/O with <xref:Sy
- [FileStream.FlushAsync ends up doing synchronous writes](https://github.com/dotnet/runtime/issues/27643)
- [Win32 FileStream turns async reads into sync reads](https://github.com/dotnet/runtime/issues/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.
- If you rely on <xref:System.IO.FileStream.Position?displayProperty=nameWithType> being set before the read or write starts because your code performs *parallel* reads or writes, you should switch to use the <xref:System.IO.RandomAccess?displayProperty=fullName> API instead. The <xref:System.IO.RandomAccess> API is designed for parallel file operations.

- 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.

Expand All @@ -68,7 +45,7 @@ Now, when buffering is enabled (that is, the `bufferSize` argument that's passed
set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1
```

> [!NOTE]
> [!IMPORTANT]
> This switch is only available in .NET 6. It was [removed in .NET 7](../7.0/filestream-compat-switch.md).
## Affected APIs
Expand Down

0 comments on commit 2e0cedb

Please sign in to comment.