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): Detailsusing 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 But it fails with |
|
@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.