diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandle.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandle.cs
index eff292e5840513..b188c8f8a162a9 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandle.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandle.cs
@@ -124,14 +124,65 @@ public static ThreadPoolBoundHandle BindHandle(SafeHandle handle)
/// This method was called after the was disposed.
///
[CLSCompliant(false)]
- public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData)
+ public unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) =>
+ AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: true);
+
+ ///
+ /// Returns an unmanaged pointer to a structure, specifying
+ /// a delegate that is invoked when the asynchronous I/O operation is complete, a user-provided
+ /// object providing context, and managed objects that serve as buffers.
+ ///
+ ///
+ /// An delegate that represents the callback method
+ /// invoked when the asynchronous I/O operation completes.
+ ///
+ ///
+ /// A user-provided object that distinguishes this from other
+ /// instances. Can be .
+ ///
+ ///
+ /// An object or array of objects representing the input or output buffer for the operation. Each
+ /// object represents a buffer, for example an array of bytes. Can be .
+ ///
+ ///
+ /// An unmanaged pointer to a structure.
+ ///
+ ///
+ ///
+ /// The unmanaged pointer returned by this method can be passed to the operating system in
+ /// overlapped I/O operations. The structure is fixed in
+ /// physical memory until is called.
+ ///
+ ///
+ /// The buffer or buffers specified in must be the same as those passed
+ /// to the unmanaged operating system function that performs the asynchronous I/O.
+ ///
+ ///
+ /// is not flowed to the invocation of the callback.
+ ///
+ ///
+ /// The buffers specified in are pinned for the duration of
+ /// the I/O operation.
+ ///
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// This method was called after the was disposed.
+ ///
+ [CLSCompliant(false)]
+ public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) =>
+ AllocateNativeOverlapped(callback, state, pinData, flowExecutionContext: false);
+
+ private unsafe NativeOverlapped* AllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext)
{
if (callback == null)
throw new ArgumentNullException(nameof(callback));
EnsureNotDisposed();
- ThreadPoolBoundHandleOverlapped overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, preAllocated: null);
+ ThreadPoolBoundHandleOverlapped overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, preAllocated: null, flowExecutionContext);
overlapped._boundHandle = this;
return overlapped._nativeOverlapped;
}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandleOverlapped.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandleOverlapped.cs
index bc2d082896170a..3fe953d5d55fa5 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandleOverlapped.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolBoundHandleOverlapped.cs
@@ -6,29 +6,32 @@ namespace System.Threading
///
/// Overlapped subclass adding data needed by ThreadPoolBoundHandle.
///
- internal sealed class ThreadPoolBoundHandleOverlapped : Overlapped
+ internal unsafe sealed class ThreadPoolBoundHandleOverlapped : Overlapped
{
- private static readonly unsafe IOCompletionCallback s_completionCallback = CompletionCallback;
+ private static readonly IOCompletionCallback s_completionCallback = CompletionCallback;
private readonly IOCompletionCallback _userCallback;
internal readonly object? _userState;
- internal PreAllocatedOverlapped? _preAllocated;
- internal unsafe NativeOverlapped* _nativeOverlapped;
+ internal readonly PreAllocatedOverlapped? _preAllocated;
+
+ internal NativeOverlapped* _nativeOverlapped;
internal ThreadPoolBoundHandle? _boundHandle;
internal bool _completed;
- public unsafe ThreadPoolBoundHandleOverlapped(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped? preAllocated)
+ public ThreadPoolBoundHandleOverlapped(IOCompletionCallback callback, object? state, object? pinData, PreAllocatedOverlapped? preAllocated, bool flowExecutionContext)
{
_userCallback = callback;
_userState = state;
_preAllocated = preAllocated;
- _nativeOverlapped = Pack(s_completionCallback, pinData);
- _nativeOverlapped->OffsetLow = 0; // CLR reuses NativeOverlapped instances and does not reset these
+ _nativeOverlapped = flowExecutionContext ?
+ Pack(s_completionCallback, pinData) :
+ UnsafePack(s_completionCallback, pinData);
+ _nativeOverlapped->OffsetLow = 0; // CLR reuses NativeOverlapped instances and does not reset these
_nativeOverlapped->OffsetHigh = 0;
}
- private static unsafe void CompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)
+ private static void CompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)
{
ThreadPoolBoundHandleOverlapped overlapped = (ThreadPoolBoundHandleOverlapped)Overlapped.Unpack(nativeOverlapped);
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs
index d856d1d9231b3f..560f2e449578f1 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs
@@ -47,12 +47,56 @@ public sealed class PreAllocatedOverlapped : IDisposable, IDeferredDisposable
/// This method was called after the was disposed.
///
[CLSCompliant(false)]
- public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData)
+ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) :
+ this(callback, state, pinData, flowExecutionContext: true)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class, specifying
+ /// a delegate that is invoked when each asynchronous I/O operation is complete, a user-provided
+ /// object providing context, and managed objects that serve as buffers.
+ ///
+ ///
+ /// An delegate that represents the callback method
+ /// invoked when each asynchronous I/O operation completes.
+ ///
+ ///
+ /// A user-provided object that distinguishes instance produced from this
+ /// object from other instances. Can be .
+ ///
+ ///
+ /// An object or array of objects representing the input or output buffer for the operations. Each
+ /// object represents a buffer, for example an array of bytes. Can be .
+ ///
+ ///
+ /// The new instance can be passed to
+ /// , to produce
+ /// a instance that can be passed to the operating system in overlapped
+ /// I/O operations. A single instance can only be used for
+ /// a single native I/O operation at a time. However, the state stored in the
+ /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation
+ /// of the callback.
+ ///
+ /// The buffers specified in are pinned until is called.
+ ///
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// This method was called after the was disposed.
+ ///
+ [CLSCompliant(false)]
+ public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) =>
+ new PreAllocatedOverlapped(callback, state, pinData, flowExecutionContext: false);
+
+ private PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData, bool flowExecutionContext)
{
if (callback == null)
throw new ArgumentNullException(nameof(callback));
- _overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this);
+ _overlapped = new ThreadPoolBoundHandleOverlapped(callback, state, pinData, this, flowExecutionContext);
}
internal bool AddRef()
diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
index 7bca8498ebca1a..d79d340c53164a 100644
--- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
+++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
@@ -83,20 +83,9 @@ private enum PinState : byte { None = 0, MultipleBuffer, SendPackets }
[MemberNotNull(nameof(_preAllocatedOverlapped))]
private void InitializeInternals()
{
- // PreAllocatedOverlapped captures ExecutionContext, but SocketAsyncEventArgs ensures
- // that context is properly flowed if necessary, and thus we don't need the overlapped
- // infrastructure capturing and flowing as well.
- bool suppressFlow = !ExecutionContext.IsFlowSuppressed();
- try
- {
- Debug.Assert(OperatingSystem.IsWindows());
- if (suppressFlow) ExecutionContext.SuppressFlow();
- _preAllocatedOverlapped = new PreAllocatedOverlapped(s_completionPortCallback, _strongThisRef, null);
- }
- finally
- {
- if (suppressFlow) ExecutionContext.RestoreFlow();
- }
+ Debug.Assert(OperatingSystem.IsWindows());
+
+ _preAllocatedOverlapped = PreAllocatedOverlapped.UnsafeCreate(s_completionPortCallback, _strongThisRef, null);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"new PreAllocatedOverlapped {_preAllocatedOverlapped}");
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.ValueTaskSource.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.ValueTaskSource.cs
index 1a7fcbe9f913eb..f1153a5cc041ce 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.ValueTaskSource.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.ValueTaskSource.cs
@@ -32,7 +32,7 @@ internal ValueTaskSource(AsyncWindowsFileStreamStrategy strategy)
{
_strategy = strategy;
_source.RunContinuationsAsynchronously = true;
- _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, null);
+ _preallocatedOverlapped = PreAllocatedOverlapped.UnsafeCreate(s_ioCallback, this, null);
}
internal void Dispose()
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs
index 8b51b2597fed0e..ceb9bcc6e40527 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs
@@ -544,7 +544,7 @@ partial void OnBufferAllocated()
Debug.Assert(_preallocatedOverlapped == null);
if (_useAsyncIO)
- _preallocatedOverlapped = new PreAllocatedOverlapped(CompletionSource.s_ioCallback, this, _buffer);
+ _preallocatedOverlapped = PreAllocatedOverlapped.UnsafeCreate(CompletionSource.s_ioCallback, this, _buffer);
}
private CompletionSource? CompareExchangeCurrentOverlappedOwner(CompletionSource? newSource, CompletionSource? existingSource)
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.PlatformNotSupported.cs
index 3e34c04ff6a592..e7da142c9ee2e8 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.PlatformNotSupported.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.PlatformNotSupported.cs
@@ -33,6 +33,10 @@ public static ThreadPoolBoundHandle BindHandle(SafeHandle handle)
throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO);
}
+ [CLSCompliant(false)]
+ public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) =>
+ AllocateNativeOverlapped(callback, state, pinData);
+
[CLSCompliant(false)]
public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated)
{
diff --git a/src/libraries/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs b/src/libraries/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs
index 634e1697d46144..9f9312cc017cc8 100644
--- a/src/libraries/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs
+++ b/src/libraries/System.Threading.Overlapped/ref/System.Threading.Overlapped.cs
@@ -49,6 +49,8 @@ public sealed partial class PreAllocatedOverlapped : System.IDisposable
public PreAllocatedOverlapped(System.Threading.IOCompletionCallback callback, object? state, object? pinData) { }
public void Dispose() { }
~PreAllocatedOverlapped() { }
+ [System.CLSCompliantAttribute(false)]
+ public static System.Threading.PreAllocatedOverlapped UnsafeCreate(System.Threading.IOCompletionCallback callback, object? state, object? pinData) { throw null; }
}
public sealed partial class ThreadPoolBoundHandle : System.IDisposable
{
@@ -64,5 +66,7 @@ public void Dispose() { }
public unsafe void FreeNativeOverlapped(System.Threading.NativeOverlapped* overlapped) { }
[System.CLSCompliantAttribute(false)]
public unsafe static object? GetNativeOverlappedState(System.Threading.NativeOverlapped* overlapped) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public unsafe System.Threading.NativeOverlapped* UnsafeAllocateNativeOverlapped(System.Threading.IOCompletionCallback callback, object? state, object? pinData) { throw null; }
}
}
diff --git a/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_AllocateNativeOverlappedTests.cs b/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_AllocateNativeOverlappedTests.cs
index ca73c0db329e7d..5621e8a4f124e9 100644
--- a/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_AllocateNativeOverlappedTests.cs
+++ b/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_AllocateNativeOverlappedTests.cs
@@ -13,12 +13,10 @@ public partial class ThreadPoolBoundHandleTests
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_NullAsCallback_ThrowsArgumentNullException()
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- AssertExtensions.Throws("callback", () =>
- {
- handle.AllocateNativeOverlapped(null, new object(), new byte[256]);
- });
+ AssertExtensions.Throws("callback", () => handle.AllocateNativeOverlapped(null, new object(), new byte[256]));
+ AssertExtensions.Throws("callback", () => handle.UnsafeAllocateNativeOverlapped(null, new object(), new byte[256]));
}
}
@@ -26,12 +24,9 @@ public unsafe void AllocateNativeOverlapped_NullAsCallback_ThrowsArgumentNullExc
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_PreAllocated_ThrowsArgumentNullException()
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- AssertExtensions.Throws("preAllocated", () =>
- {
- handle.AllocateNativeOverlapped((PreAllocatedOverlapped)null);
- });
+ AssertExtensions.Throws("preAllocated", () => handle.AllocateNativeOverlapped((PreAllocatedOverlapped)null));
}
}
@@ -39,12 +34,14 @@ public unsafe void AllocateNativeOverlapped_PreAllocated_ThrowsArgumentNullExcep
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_NullAsContext_DoesNotThrow()
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
NativeOverlapped* result = handle.AllocateNativeOverlapped((_, __, ___) => { }, (object)null, new byte[256]);
-
Assert.True(result != null);
+ handle.FreeNativeOverlapped(result);
+ result = handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, (object)null, new byte[256]);
+ Assert.True(result != null);
handle.FreeNativeOverlapped(result);
}
}
@@ -53,12 +50,14 @@ public unsafe void AllocateNativeOverlapped_NullAsContext_DoesNotThrow()
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_NullAsPinData_DoesNotThrow()
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
NativeOverlapped* result = handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), (byte[])null);
-
Assert.True(result != null);
+ handle.FreeNativeOverlapped(result);
+ result = handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), (byte[])null);
+ Assert.True(result != null);
handle.FreeNativeOverlapped(result);
}
}
@@ -67,12 +66,14 @@ public unsafe void AllocateNativeOverlapped_NullAsPinData_DoesNotThrow()
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_EmptyArrayAsPinData_DoesNotThrow()
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
NativeOverlapped* result = handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[0]);
-
Assert.True(result != null);
+ handle.FreeNativeOverlapped(result);
+ result = handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[0]);
+ Assert.True(result != null);
handle.FreeNativeOverlapped(result);
}
}
@@ -81,9 +82,10 @@ public unsafe void AllocateNativeOverlapped_EmptyArrayAsPinData_DoesNotThrow()
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_NonBlittableTypeAsPinData_Throws()
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
AssertExtensions.Throws(null, () => handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new NonBlittableType() { s = "foo" }));
+ AssertExtensions.Throws(null, () => handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), new NonBlittableType() { s = "foo" }));
}
}
@@ -91,12 +93,14 @@ public unsafe void AllocateNativeOverlapped_NonBlittableTypeAsPinData_Throws()
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_BlittableTypeAsPinData_DoesNotThrow()
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
NativeOverlapped* result = handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new BlittableType() { i = 42 });
-
Assert.True(result != null);
+ handle.FreeNativeOverlapped(result);
+ result = handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), new BlittableType() { i = 42 });
+ Assert.True(result != null);
handle.FreeNativeOverlapped(result);
}
}
@@ -105,17 +109,20 @@ public unsafe void AllocateNativeOverlapped_BlittableTypeAsPinData_DoesNotThrow(
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_ObjectArrayAsPinData_DoesNotThrow()
{
- object[] array = new object[]
+ var array = new object[]
{
new BlittableType() { i = 1 },
new byte[5],
};
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
NativeOverlapped* result = handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), array);
-
Assert.True(result != null);
+ handle.FreeNativeOverlapped(result);
+ result = handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), array);
+ Assert.True(result != null);
handle.FreeNativeOverlapped(result);
}
}
@@ -124,24 +131,30 @@ public unsafe void AllocateNativeOverlapped_ObjectArrayAsPinData_DoesNotThrow()
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
public unsafe void AllocateNativeOverlapped_ObjectArrayWithNonBlittableTypeAsPinData_Throws()
{
- object[] array = new object[]
+ var array = new object[]
{
new NonBlittableType() { s = "foo" },
new byte[5],
};
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
AssertExtensions.Throws(null, () => handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), array));
+ AssertExtensions.Throws(null, () => handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), array));
}
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void AllocateNativeOverlapped_ReturnedNativeOverlapped_AllFieldsZero()
+ public unsafe void AllocateNativeOverlapped_ReturnedNativeOverlapped_AllFieldsZero(bool useUnsafe)
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- NativeOverlapped* overlapped = handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]);
+ NativeOverlapped* overlapped = useUnsafe ?
+ handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]) :
+ handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]);
Assert.Equal(IntPtr.Zero, overlapped->InternalLow);
Assert.Equal(IntPtr.Zero, overlapped->InternalHigh);
@@ -153,13 +166,17 @@ public unsafe void AllocateNativeOverlapped_ReturnedNativeOverlapped_AllFieldsZe
}
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void AllocateNativeOverlapped_PreAllocated_ReturnedNativeOverlapped_AllFieldsZero()
+ public unsafe void AllocateNativeOverlapped_PreAllocated_ReturnedNativeOverlapped_AllFieldsZero(bool useUnsafe)
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- using(PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256]))
+ using (PreAllocatedOverlapped preAlloc = useUnsafe ?
+ PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), new byte[256]) :
+ new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256]))
{
NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);
@@ -174,19 +191,25 @@ public unsafe void AllocateNativeOverlapped_PreAllocated_ReturnedNativeOverlappe
}
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void AllocateNativeOverlapped_PossibleReusedReturnedNativeOverlapped_OffsetLowAndOffsetHighSetToZero()
- { // The CLR reuses NativeOverlapped underneath, check to make sure that they reset fields back to zero
-
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ public unsafe void AllocateNativeOverlapped_PossibleReusedReturnedNativeOverlapped_OffsetLowAndOffsetHighSetToZero(bool useUnsafe)
+ {
+ // The CLR reuses NativeOverlapped underneath, check to make sure that they reset fields back to zero
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- NativeOverlapped* overlapped = handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]);
+ NativeOverlapped* overlapped = useUnsafe ?
+ handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]) :
+ handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]);
overlapped->OffsetHigh = 1;
overlapped->OffsetLow = 1;
handle.FreeNativeOverlapped(overlapped);
- overlapped = handle.AllocateNativeOverlapped((errorCode, numBytes, overlap) => { }, new object(), new byte[256]);
+ overlapped = useUnsafe ?
+ handle.UnsafeAllocateNativeOverlapped((errorCode, numBytes, overlap) => { }, new object(), new byte[256]) :
+ handle.AllocateNativeOverlapped((errorCode, numBytes, overlap) => { }, new object(), new byte[256]);
Assert.Equal(IntPtr.Zero, overlapped->InternalLow);
Assert.Equal(IntPtr.Zero, overlapped->InternalHigh);
@@ -198,14 +221,19 @@ public unsafe void AllocateNativeOverlapped_PossibleReusedReturnedNativeOverlapp
}
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void AllocateNativeOverlapped_PreAllocated_ReusedReturnedNativeOverlapped_OffsetLowAndOffsetHighSetToZero()
- { // The CLR reuses NativeOverlapped underneath, check to make sure that they reset fields back to zero
-
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ public unsafe void AllocateNativeOverlapped_PreAllocated_ReusedReturnedNativeOverlapped_OffsetLowAndOffsetHighSetToZero(bool useUnsafe)
+ {
+ // The CLR reuses NativeOverlapped underneath, check to make sure that they reset fields back to zero
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256]);
+ PreAllocatedOverlapped preAlloc = useUnsafe ?
+ PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), new byte[256]) :
+ new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256]);
+
NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);
overlapped->OffsetHigh = 1;
overlapped->OffsetLow = 1;
@@ -230,60 +258,59 @@ public unsafe void AllocateNativeOverlapped_WhenDisposed_ThrowsObjectDisposedExc
ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle();
handle.Dispose();
- Assert.Throws(() =>
- {
- handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]);
- });
+ Assert.Throws(() => handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]));
+ Assert.Throws(() => handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]));
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void AllocateNativeOverlapped_PreAllocated_WhenDisposed_ThrowsObjectDisposedException()
+ public unsafe void AllocateNativeOverlapped_PreAllocated_WhenDisposed_ThrowsObjectDisposedException(bool useUnsafe)
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped(delegate { }, null, null);
+ PreAllocatedOverlapped preAlloc = useUnsafe ?
+ PreAllocatedOverlapped.UnsafeCreate(delegate { }, null, null) :
+ new PreAllocatedOverlapped(delegate { }, null, null);
preAlloc.Dispose();
-
- Assert.Throws(() =>
- {
- handle.AllocateNativeOverlapped(preAlloc);
- });
+ Assert.Throws(() => handle.AllocateNativeOverlapped(preAlloc));
}
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void AllocateNativeOverlapped_PreAllocated_WhenHandleDisposed_ThrowsObjectDisposedException()
+ public unsafe void AllocateNativeOverlapped_PreAllocated_WhenHandleDisposed_ThrowsObjectDisposedException(bool useUnsafe)
{
ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle();
handle.Dispose();
- PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped(delegate { }, null, null);
+ PreAllocatedOverlapped preAlloc = useUnsafe ?
+ PreAllocatedOverlapped.UnsafeCreate(delegate { }, null, null) :
+ new PreAllocatedOverlapped(delegate { }, null, null);
- Assert.Throws(() =>
- {
- handle.AllocateNativeOverlapped(preAlloc);
- });
+ Assert.Throws(() => handle.AllocateNativeOverlapped(preAlloc));
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void AllocateNativeOverlapped_PreAllocated_WhenAlreadyAllocated_ThrowsArgumentException()
+ public unsafe void AllocateNativeOverlapped_PreAllocated_WhenAlreadyAllocated_ThrowsArgumentException(bool useUnsafe)
{
- using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
+ using (ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
{
- using(PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped(delegate { }, null, null))
- {
- NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);
+ using PreAllocatedOverlapped preAlloc = useUnsafe ?
+ PreAllocatedOverlapped.UnsafeCreate(delegate { }, null, null) :
+ new PreAllocatedOverlapped(delegate { }, null, null);
- AssertExtensions.Throws("preAllocated", () =>
- {
- handle.AllocateNativeOverlapped(preAlloc);
- });
+ NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);
- handle.FreeNativeOverlapped(overlapped);
- }
+ AssertExtensions.Throws("preAllocated", () => handle.AllocateNativeOverlapped(preAlloc));
+
+ handle.FreeNativeOverlapped(overlapped);
}
}
}
diff --git a/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_IntegrationTests.cs b/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_IntegrationTests.cs
index 008bf7aa2a5712..f7a3fff90cdf52 100644
--- a/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_IntegrationTests.cs
+++ b/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_IntegrationTests.cs
@@ -177,10 +177,13 @@ public unsafe void MultipleOperationsOverMultipleHandles()
handle2.Dispose();
}
- [Fact]
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
[PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
- public unsafe void FlowsAsyncLocalsToCallback()
- { // Makes sure that we flow async locals to callback
+ public unsafe void FlowsAsyncLocalsToCallback(bool shouldFlow)
+ {
+ // Makes sure that we flow async locals to callback
const int DATA_SIZE = 2;
@@ -201,7 +204,9 @@ public unsafe void FlowsAsyncLocalsToCallback()
OnOverlappedOperationCompleted(_, __, ___);
};
- NativeOverlapped* overlapped = boundHandle.AllocateNativeOverlapped(callback, context, data);
+ NativeOverlapped* overlapped = shouldFlow ?
+ boundHandle.AllocateNativeOverlapped(callback, context, data) :
+ boundHandle.UnsafeAllocateNativeOverlapped(callback, context, data);
fixed (byte* p = data)
{
@@ -220,7 +225,66 @@ public unsafe void FlowsAsyncLocalsToCallback()
boundHandle.Dispose();
handle.Dispose();
- Assert.Equal(10, result);
+ Assert.Equal(
+ shouldFlow ? 10 : 0,
+ result);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ [PlatformSpecific(TestPlatforms.Windows)] // ThreadPoolBoundHandle.BindHandle is not supported on Unix
+ public unsafe void FlowsAsyncLocalsToCallback_PreAllocatedOverlapped(bool shouldFlow)
+ {
+ // Makes sure that we flow async locals to callback
+
+ const int DATA_SIZE = 2;
+
+ SafeHandle handle = HandleFactory.CreateAsyncFileHandleForWrite(Path.Combine(TestDirectory, @"AsyncLocal.tmp"));
+ ThreadPoolBoundHandle boundHandle = ThreadPoolBoundHandle.BindHandle(handle);
+
+ OverlappedContext context = new OverlappedContext();
+
+ byte[] data = new byte[DATA_SIZE];
+
+ AsyncLocal asyncLocal = new AsyncLocal();
+ asyncLocal.Value = 10;
+
+ int? result = null;
+ IOCompletionCallback callback = (_, __, ___) => {
+
+ result = asyncLocal.Value;
+ OnOverlappedOperationCompleted(_, __, ___);
+ };
+
+ using (PreAllocatedOverlapped preAlloc = shouldFlow ?
+ new PreAllocatedOverlapped(callback, context, data) :
+ PreAllocatedOverlapped.UnsafeCreate(callback, context, data))
+ {
+ NativeOverlapped* overlapped = boundHandle.AllocateNativeOverlapped(preAlloc);
+
+ fixed (byte* p = data)
+ {
+ int retval = DllImport.WriteFile(boundHandle.Handle, p, DATA_SIZE, IntPtr.Zero, overlapped);
+
+ if (retval == 0)
+ {
+ Assert.Equal(DllImport.ERROR_IO_PENDING, Marshal.GetLastPInvokeError());
+ }
+
+ // Wait for overlapped operation to complete
+ context.Event.WaitOne();
+ }
+
+ boundHandle.FreeNativeOverlapped(overlapped);
+ }
+
+ boundHandle.Dispose();
+ handle.Dispose();
+
+ Assert.Equal(
+ shouldFlow ? 10 : 0,
+ result);
}
private static unsafe void OnOverlappedOperationCompleted(uint errorCode, uint numBytes, NativeOverlapped* overlapped)
diff --git a/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_PreAllocatedOverlappedTests.cs b/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_PreAllocatedOverlappedTests.cs
index ee8ccd3394d0b8..8517a5c0a595a5 100644
--- a/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_PreAllocatedOverlappedTests.cs
+++ b/src/libraries/System.Threading.Overlapped/tests/ThreadPoolBoundHandle_PreAllocatedOverlappedTests.cs
@@ -12,10 +12,8 @@ public partial class ThreadPoolBoundHandleTests
[ActiveIssue("https://github.com/mono/mono/issues/15313", TestRuntimes.Mono)]
public unsafe void PreAllocatedOverlapped_NullAsCallback_ThrowsArgumentNullException()
{
- AssertExtensions.Throws("callback", () =>
- {
- new PreAllocatedOverlapped(null, new object(), new byte[256]);
- });
+ AssertExtensions.Throws("callback", () => new PreAllocatedOverlapped(null, new object(), new byte[256]));
+ AssertExtensions.Throws("callback", () => PreAllocatedOverlapped.UnsafeCreate(null, new object(), new byte[256]));
// Make sure the PreAllocatedOverlapped finalizer does the right thing in the case where the .ctor failed.
GC.Collect();
@@ -25,19 +23,22 @@ public unsafe void PreAllocatedOverlapped_NullAsCallback_ThrowsArgumentNullExcep
[Fact]
public unsafe void PreAllocatedOverlapped_NullAsContext_DoesNotThrow()
{
- using(new PreAllocatedOverlapped((_, __, ___) => { }, (object)null, new byte[256])) {}
+ using (new PreAllocatedOverlapped((_, __, ___) => { }, (object)null, new byte[256])) { }
+ using (PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, (object)null, new byte[256])) { }
}
[Fact]
public unsafe void PreAllocatedOverlapped_NullAsPinData_DoesNotThrow()
{
- using(new PreAllocatedOverlapped((_, __, ___) => { }, new object(), (byte[])null)) {}
+ using (new PreAllocatedOverlapped((_, __, ___) => { }, new object(), (byte[])null)) { }
+ using (PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), (byte[])null)) { }
}
[Fact]
public unsafe void PreAllocatedOverlapped_EmptyArrayAsPinData_DoesNotThrow()
{
- using(new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[0])) {}
+ using (new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[0])) { }
+ using (PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), new byte[0])) { }
}
[Fact]
@@ -45,6 +46,7 @@ public unsafe void PreAllocatedOverlapped_EmptyArrayAsPinData_DoesNotThrow()
public unsafe void PreAllocatedOverlapped_NonBlittableTypeAsPinData_Throws()
{
AssertExtensions.Throws(null, () => new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new NonBlittableType() { s = "foo" }));
+ AssertExtensions.Throws(null, () => PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), new NonBlittableType() { s = "foo" }));
// Make sure the PreAllocatedOverlapped finalizer does the right thing in the case where the .ctor failed.
GC.Collect();
@@ -54,30 +56,35 @@ public unsafe void PreAllocatedOverlapped_NonBlittableTypeAsPinData_Throws()
[Fact]
public unsafe void PreAllocatedOverlapped_BlittableTypeAsPinData_DoesNotThrow()
{
- using(new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new BlittableType() { i = 42 })) {}
+ using (new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new BlittableType() { i = 42 })) { }
+ using (PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), new BlittableType() { i = 42 })) { }
}
[Fact]
public unsafe void PreAllocatedOverlapped_ObjectArrayAsPinData_DoesNotThrow()
{
- object[] array = new object[]
+ var array = new object[]
{
new BlittableType() { i = 1 },
new byte[5],
};
- using(new PreAllocatedOverlapped((_, __, ___) => { }, new object(), array)) {}
+
+ using (new PreAllocatedOverlapped((_, __, ___) => { }, new object(), array)) { }
+ using (PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), array)) { }
}
[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15313", TestRuntimes.Mono)]
public unsafe void PreAllocatedOverlapped_ObjectArrayWithNonBlittableTypeAsPinData_Throws()
{
- object[] array = new object[]
+ var array = new object[]
{
new NonBlittableType() { s = "foo" },
new byte[5],
};
+
AssertExtensions.Throws(null, () => new PreAllocatedOverlapped((_, __, ___) => { }, new object(), array));
+ AssertExtensions.Throws(null, () => PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), array));
// Make sure the PreAllocatedOverlapped finalizer does the right thing in the case where the .ctor failed.
GC.Collect();
@@ -87,12 +94,14 @@ public unsafe void PreAllocatedOverlapped_ObjectArrayWithNonBlittableTypeAsPinDa
[Fact]
public unsafe void PreAllocatedOverlapped_ReturnedNativeOverlapped_InternalLowAndInternalHighSetToZero()
{
- using(new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256])) {}
+ using (new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256])) { }
+ using (PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), new byte[256])) { }
}
[Fact]
public unsafe void PreAllocatedOverlapped_ReturnedNativeOverlapped_OffsetLowAndOffsetHighSetToZero()
{
- using(new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256])) {}
+ using (new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256])) { }
+ using (PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), new byte[256])) { }
}
}
diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs b/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs
index d4a0daeff29c78..a1d410e30bd53c 100644
--- a/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs
+++ b/src/mono/System.Private.CoreLib/src/System/Threading/PreAllocatedOverlapped.cs
@@ -5,8 +5,10 @@ namespace System.Threading
{
public sealed class PreAllocatedOverlapped : System.IDisposable
{
- [System.CLSCompliantAttribute(false)]
- public PreAllocatedOverlapped(System.Threading.IOCompletionCallback callback, object? state, object? pinData) { }
+ [CLSCompliantAttribute(false)]
+ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) { }
+ [CLSCompliantAttribute(false)]
+ public static PreAllocatedOverlapped UnsafeCreate(IOCompletionCallback callback, object? state, object? pinData) => new PreAllocatedOverlapped(callback, state, pinData);
public void Dispose() { }
internal bool IsUserObject(byte[]? buffer) => false;
}