From b669408413b8ebaa4a25f21a8a6d2fc5ec0bb843 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:22:42 +0000 Subject: [PATCH 1/5] Initial plan From 4b253ccca8fb552253353e68f020484950488531 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:46:21 +0000 Subject: [PATCH 2/5] Remove isAsync flag validation from FileStream constructors Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- .../src/Resources/Strings.resx | 6 ------ .../src/System/IO/FileStream.cs | 13 ++----------- .../src/System/ThrowHelper.cs | 12 ------------ .../FileStream/ctor_sfh_fa_buffer_async.cs | 10 +++++++--- 4 files changed, 9 insertions(+), 32 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 6082e0ec3f6b86..f511ebeb21810d 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -403,12 +403,6 @@ Byte array for Guid must be exactly {0} bytes long. - - Handle does not support asynchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened synchronously (that is, it was not opened for overlapped I/O). - - - Handle does not support synchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened asynchronously (that is, it was opened explicitly for overlapped I/O). - The number styles AllowHexSpecifier and AllowBinarySpecifier are not supported on floating point data types. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index cee3482ea93579..01766b9949ea8c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -48,7 +48,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS { ValidateHandle(safeHandle, access, bufferSize, isAsync); - _strategy = FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync); + _strategy = FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, safeHandle.IsAsync); } catch { @@ -87,15 +87,6 @@ private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { ValidateHandle(handle, access, bufferSize); - - if (isAsync && !handle.IsAsync) - { - ThrowHelper.ThrowArgumentException_HandleNotAsync(nameof(handle)); - } - else if (!isAsync && handle.IsAsync) - { - ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle)); - } } public FileStream(SafeFileHandle handle, FileAccess access) @@ -114,7 +105,7 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool { ValidateHandle(handle, access, bufferSize, isAsync); - _strategy = FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, isAsync); + _strategy = FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, handle.IsAsync); } public FileStream(string path, FileMode mode) diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index a477e06af0e413..d87f6b114a9cf6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -335,18 +335,6 @@ internal static void ThrowArgumentException(ExceptionResource resource, Exceptio throw GetArgumentException(resource, argument); } - [DoesNotReturn] - internal static void ThrowArgumentException_HandleNotSync(string paramName) - { - throw new ArgumentException(SR.Arg_HandleNotSync, paramName); - } - - [DoesNotReturn] - internal static void ThrowArgumentException_HandleNotAsync(string paramName) - { - throw new ArgumentException(SR.Arg_HandleNotAsync, paramName); - } - [DoesNotReturn] internal static void ThrowArgumentNullException(ExceptionArgument argument) { diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs index 810287764e3293..2b874fd398fc8b 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs @@ -29,16 +29,20 @@ public void MatchedAsync() } [Fact] - public void UnmatchedAsyncThrows() + public void UnmatchedAsyncIsAllowed() { using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, true)) { - Assert.Throws(() => CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, false)); + // isAsync parameter is now ignored, handle.IsAsync is used instead + using (CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, false)) + { } } using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, false)) { - Assert.Throws(() => CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, true)); + // isAsync parameter is now ignored, handle.IsAsync is used instead + using (CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, true)) + { } } } } From 0d106df2bfeb9e649fa9668900b700d9b8a57141 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:55:21 +0000 Subject: [PATCH 3/5] Fix unused parameter issue in ValidateHandle Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- .../System.Private.CoreLib/src/System/IO/FileStream.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 01766b9949ea8c..50f15b4165c117 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -46,7 +46,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); try { - ValidateHandle(safeHandle, access, bufferSize, isAsync); + ValidateHandle(safeHandle, access, bufferSize); _strategy = FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, safeHandle.IsAsync); } @@ -84,11 +84,6 @@ private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int } } - private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - { - ValidateHandle(handle, access, bufferSize); - } - public FileStream(SafeFileHandle handle, FileAccess access) : this(handle, access, DefaultBufferSize) { @@ -103,7 +98,7 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { - ValidateHandle(handle, access, bufferSize, isAsync); + ValidateHandle(handle, access, bufferSize); _strategy = FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, handle.IsAsync); } From 9e8d7869aef98bcef0626664378150ef992ca18f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:58:04 +0000 Subject: [PATCH 4/5] Add assertions to verify FileStream uses handle's IsAsync value Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- .../FileStream/ctor_sfh_fa_buffer_async.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs index 2b874fd398fc8b..52fd5f60582b86 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs @@ -34,15 +34,21 @@ public void UnmatchedAsyncIsAllowed() using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, true)) { // isAsync parameter is now ignored, handle.IsAsync is used instead - using (CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, false)) - { } + using (FileStream newFs = CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, false)) + { + // Verify that the new FileStream uses handle's IsAsync (true), not the parameter (false) + Assert.True(newFs.IsAsync); + } } using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, false)) { // isAsync parameter is now ignored, handle.IsAsync is used instead - using (CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, true)) - { } + using (FileStream newFs = CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, true)) + { + // Verify that the new FileStream uses handle's IsAsync (false), not the parameter (true) + Assert.False(newFs.IsAsync); + } } } } From 7fb921ffb957fc9aa2233a6d74d35db81d6eab69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 18:08:25 +0000 Subject: [PATCH 5/5] Address review feedback: convert test to theory and add async I/O verification Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- .../FileStream/ctor_sfh_fa_buffer_async.cs | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs index 52fd5f60582b86..665bf08dbb3892 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; using Xunit; @@ -28,26 +29,30 @@ public void MatchedAsync() } } - [Fact] - public void UnmatchedAsyncIsAllowed() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task UnmatchedAsyncIsAllowed(bool isAsync) { - using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, true)) + using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, isAsync)) { // isAsync parameter is now ignored, handle.IsAsync is used instead - using (FileStream newFs = CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, false)) + using (FileStream newFs = CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, !isAsync)) { - // Verify that the new FileStream uses handle's IsAsync (true), not the parameter (false) - Assert.True(newFs.IsAsync); - } - } + // Verify that the new FileStream uses handle's IsAsync, not the parameter + Assert.Equal(isAsync, newFs.IsAsync); - using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, false)) - { - // isAsync parameter is now ignored, handle.IsAsync is used instead - using (FileStream newFs = CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, true)) - { - // Verify that the new FileStream uses handle's IsAsync (false), not the parameter (true) - Assert.False(newFs.IsAsync); + // Perform async write, seek to beginning, and async read to verify functionality + byte[] writeBuffer = new byte[] { 1, 2, 3, 4, 5 }; + await newFs.WriteAsync(writeBuffer, 0, writeBuffer.Length); + + newFs.Seek(0, SeekOrigin.Begin); + + byte[] readBuffer = new byte[writeBuffer.Length]; + int bytesRead = await newFs.ReadAsync(readBuffer, 0, readBuffer.Length); + + Assert.Equal(writeBuffer.Length, bytesRead); + Assert.Equal(writeBuffer, readBuffer); } } }