-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Ensure FileStream.Position is correct after a failed|cancelled WriteAsync attempt #56716
Ensure FileStream.Position is correct after a failed|cancelled WriteAsync attempt #56716
Conversation
Tagging subscribers to this area: @dotnet/area-system-io Issue Details@stephentoub I've realised that while writes can't be incomplete, they might fail with an exception and in such cases we should update the position as well.
|
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs
Show resolved
Hide resolved
Just to clarify, this never used to be done in previous .NET releases, either, right? |
....Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.ThreadPoolValueTaskSource.cs
Show resolved
Hide resolved
I think it was since .NET Core 3.1 (as least to some degree): using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace fileCancel
{
class Program
{
static async Task Main()
{
const int writeSize = 1024 * 1024 * 1024;
byte[] buffer = new byte[writeSize];
using (FileStream fs = new FileStream("test.test", FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 1, useAsync: true))
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.Cancel(); // cancelled BEOFRE operation started
Task writeTask = fs.WriteAsync(buffer, 0, buffer.Length, cts.Token);
await AwaitAndSee(writeTask, fs);
cts = new CancellationTokenSource();
writeTask = fs.WriteAsync(buffer, 0, buffer.Length, cts.Token);
cts.Cancel(); // immediately cancelled AFTER operation started
await AwaitAndSee(writeTask, fs);
cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(0.2)); // very short timeout
writeTask = fs.WriteAsync(buffer, 0, buffer.Length, cts.Token);
await AwaitAndSee(writeTask, fs);
}
File.Delete("test.test");
}
private static async Task AwaitAndSee(Task task, FileStream fs)
{
try
{
await task;
Console.WriteLine($"Not cancelled, position: {fs.Position}");
}
catch (OperationCanceledException)
{
Console.WriteLine($"Got cancelled, position: {fs.Position}");
}
}
}
} <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net48</TargetFrameworks>
</PropertyGroup>
</Project>
|
Isn't your example only showing it actually happening when the token is canceled in advance, which means the operation never actually starts, with our cancellation prechecks causing the position to not be updated in the first place? Or, if I'm misunderstanding the example, can you point to what code was updating the position after a failure? |
@stephentoub you are right, so far we were not updating the position after failure FWIW I've checked why in my sample we fail to cancel to Line 124 in b3ab2eb
But it fails with Lines 205 to 206 in 57bfe47
|
@stephentoub could you PTAL? |
@stephentoub I've realised that while writes can't be incomplete, they might fail with an exception and in such cases we should update the position as well.