/// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed.
/// </exception>
[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);
+
+ /// <summary>
+ /// Returns an unmanaged pointer to a <see cref="NativeOverlapped"/> 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.
+ /// </summary>
+ /// <param name="callback">
+ /// An <see cref="IOCompletionCallback"/> delegate that represents the callback method
+ /// invoked when the asynchronous I/O operation completes.
+ /// </param>
+ /// <param name="state">
+ /// A user-provided object that distinguishes this <see cref="NativeOverlapped"/> from other
+ /// <see cref="NativeOverlapped"/> instances. Can be <see langword="null"/>.
+ /// </param>
+ /// <param name="pinData">
+ /// 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 <see langword="null"/>.
+ /// </param>
+ /// <returns>
+ /// An unmanaged pointer to a <see cref="NativeOverlapped"/> structure.
+ /// </returns>
+ /// <remarks>
+ /// <para>
+ /// The unmanaged pointer returned by this method can be passed to the operating system in
+ /// overlapped I/O operations. The <see cref="NativeOverlapped"/> structure is fixed in
+ /// physical memory until <see cref="FreeNativeOverlapped(NativeOverlapped*)"/> is called.
+ /// </para>
+ /// <para>
+ /// The buffer or buffers specified in <paramref name="pinData"/> must be the same as those passed
+ /// to the unmanaged operating system function that performs the asynchronous I/O.
+ /// </para>
+ /// <para>
+ /// <see cref="ExecutionContext"/> is not flowed to the invocation of the callback.
+ /// </para>
+ /// <note>
+ /// The buffers specified in <paramref name="pinData"/> are pinned for the duration of
+ /// the I/O operation.
+ /// </note>
+ /// </remarks>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="callback"/> is <see langword="null"/>.
+ /// </exception>
+ /// <exception cref="ObjectDisposedException">
+ /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed.
+ /// </exception>
+ [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;
}
/// <summary>
/// Overlapped subclass adding data needed by ThreadPoolBoundHandle.
/// </summary>
- 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);
/// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed.
/// </exception>
[CLSCompliant(false)]
- public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData)
+ public PreAllocatedOverlapped(IOCompletionCallback callback, object? state, object? pinData) :
+ this(callback, state, pinData, flowExecutionContext: true)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PreAllocatedOverlapped"/> 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.
+ /// </summary>
+ /// <param name="callback">
+ /// An <see cref="IOCompletionCallback"/> delegate that represents the callback method
+ /// invoked when each asynchronous I/O operation completes.
+ /// </param>
+ /// <param name="state">
+ /// A user-provided object that distinguishes <see cref="NativeOverlapped"/> instance produced from this
+ /// object from other <see cref="NativeOverlapped"/> instances. Can be <see langword="null"/>.
+ /// </param>
+ /// <param name="pinData">
+ /// 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 <see langword="null"/>.
+ /// </param>
+ /// <remarks>
+ /// The new <see cref="PreAllocatedOverlapped"/> instance can be passed to
+ /// <see cref="ThreadPoolBoundHandle.AllocateNativeOverlapped(PreAllocatedOverlapped)"/>, to produce
+ /// a <see cref="NativeOverlapped"/> instance that can be passed to the operating system in overlapped
+ /// I/O operations. A single <see cref="PreAllocatedOverlapped"/> instance can only be used for
+ /// a single native I/O operation at a time. However, the state stored in the <see cref="PreAllocatedOverlapped"/>
+ /// instance can be reused for subsequent native operations. ExecutionContext is not flowed to the invocation
+ /// of the callback.
+ /// <note>
+ /// The buffers specified in <paramref name="pinData"/> are pinned until <see cref="Dispose"/> is called.
+ /// </note>
+ /// </remarks>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="callback"/> is <see langword="null"/>.
+ /// </exception>
+ /// <exception cref="ObjectDisposedException">
+ /// This method was called after the <see cref="ThreadPoolBoundHandle"/> was disposed.
+ /// </exception>
+ [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()
[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}");
}
{
_strategy = strategy;
_source.RunContinuationsAsynchronously = true;
- _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, null);
+ _preallocatedOverlapped = PreAllocatedOverlapped.UnsafeCreate(s_ioCallback, this, null);
}
internal void Dispose()
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)
}
[CLSCompliant(false)]
+ public unsafe NativeOverlapped* UnsafeAllocateNativeOverlapped(IOCompletionCallback callback, object? state, object? pinData) =>
+ AllocateNativeOverlapped(callback, state, pinData);
+
+ [CLSCompliant(false)]
public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated)
{
if (preAllocated == null)
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
{
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; }
}
}
[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<ArgumentNullException>("callback", () =>
- {
- handle.AllocateNativeOverlapped(null, new object(), new byte[256]);
- });
+ AssertExtensions.Throws<ArgumentNullException>("callback", () => handle.AllocateNativeOverlapped(null, new object(), new byte[256]));
+ AssertExtensions.Throws<ArgumentNullException>("callback", () => handle.UnsafeAllocateNativeOverlapped(null, new object(), new byte[256]));
}
}
[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<ArgumentNullException>("preAllocated", () =>
- {
- handle.AllocateNativeOverlapped((PreAllocatedOverlapped)null);
- });
+ AssertExtensions.Throws<ArgumentNullException>("preAllocated", () => handle.AllocateNativeOverlapped((PreAllocatedOverlapped)null));
}
}
[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);
}
}
[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);
}
}
[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);
}
}
[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<ArgumentException>(null, () => handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new NonBlittableType() { s = "foo" }));
+ AssertExtensions.Throws<ArgumentException>(null, () => handle.UnsafeAllocateNativeOverlapped((_, __, ___) => { }, new object(), new NonBlittableType() { s = "foo" }));
}
}
[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);
}
}
[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);
}
}
[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<ArgumentException>(null, () => handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), array));
+ AssertExtensions.Throws<ArgumentException>(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);
}
}
- [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);
}
}
- [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);
}
}
- [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;
ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle();
handle.Dispose();
- Assert.Throws<ObjectDisposedException>(() =>
- {
- handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]);
- });
+ Assert.Throws<ObjectDisposedException>(() => handle.AllocateNativeOverlapped((_, __, ___) => { }, new object(), new byte[256]));
+ Assert.Throws<ObjectDisposedException>(() => 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<ObjectDisposedException>(() =>
- {
- handle.AllocateNativeOverlapped(preAlloc);
- });
+ Assert.Throws<ObjectDisposedException>(() => 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<ObjectDisposedException>(() =>
- {
- handle.AllocateNativeOverlapped(preAlloc);
- });
+ Assert.Throws<ObjectDisposedException>(() => 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<ArgumentException>("preAllocated", () =>
- {
- handle.AllocateNativeOverlapped(preAlloc);
- });
+ NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);
- handle.FreeNativeOverlapped(overlapped);
- }
+ AssertExtensions.Throws<ArgumentException>("preAllocated", () => handle.AllocateNativeOverlapped(preAlloc));
+
+ handle.FreeNativeOverlapped(overlapped);
}
}
}
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;
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)
{
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<int> asyncLocal = new AsyncLocal<int>();
+ 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)
[ActiveIssue("https://github.com/mono/mono/issues/15313", TestRuntimes.Mono)]
public unsafe void PreAllocatedOverlapped_NullAsCallback_ThrowsArgumentNullException()
{
- AssertExtensions.Throws<ArgumentNullException>("callback", () =>
- {
- new PreAllocatedOverlapped(null, new object(), new byte[256]);
- });
+ AssertExtensions.Throws<ArgumentNullException>("callback", () => new PreAllocatedOverlapped(null, new object(), new byte[256]));
+ AssertExtensions.Throws<ArgumentNullException>("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();
[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]
public unsafe void PreAllocatedOverlapped_NonBlittableTypeAsPinData_Throws()
{
AssertExtensions.Throws<ArgumentException>(null, () => new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new NonBlittableType() { s = "foo" }));
+ AssertExtensions.Throws<ArgumentException>(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();
[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<ArgumentException>(null, () => new PreAllocatedOverlapped((_, __, ___) => { }, new object(), array));
+ AssertExtensions.Throws<ArgumentException>(null, () => PreAllocatedOverlapped.UnsafeCreate((_, __, ___) => { }, new object(), array));
// Make sure the PreAllocatedOverlapped finalizer does the right thing in the case where the .ctor failed.
GC.Collect();
[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])) { }
}
}
{
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;
}