// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; using System.Runtime.InteropServices; namespace System.Threading { public unsafe class Overlapped { private IAsyncResult? _asyncResult; internal object? _callback; // IOCompletionCallback or IOCompletionCallbackHelper private NativeOverlapped* _pNativeOverlapped; private IntPtr _eventHandle; private int _offsetLow; private int _offsetHigh; public Overlapped() { } public Overlapped(int offsetLo, int offsetHi, IntPtr hEvent, IAsyncResult? ar) { _offsetLow = offsetLo; _offsetHigh = offsetHi; _eventHandle = hEvent; _asyncResult = ar; } [Obsolete("This constructor is not 64-bit compatible and has been deprecated. Use the constructor that accepts an IntPtr for the event handle instead.")] public Overlapped(int offsetLo, int offsetHi, int hEvent, IAsyncResult? ar) : this(offsetLo, offsetHi, new IntPtr(hEvent), ar) { } public IAsyncResult AsyncResult { get => _asyncResult!; set => _asyncResult = value; } public int OffsetLow { get => (_pNativeOverlapped != null) ? _pNativeOverlapped->OffsetLow : _offsetLow; set => ((_pNativeOverlapped != null) ? ref _pNativeOverlapped->OffsetLow : ref _offsetLow) = value; } public int OffsetHigh { get => (_pNativeOverlapped != null) ? _pNativeOverlapped->OffsetHigh : _offsetHigh; set => ((_pNativeOverlapped != null) ? ref _pNativeOverlapped->OffsetHigh : ref _offsetHigh) = value; } [Obsolete("Overlapped.EventHandle is not 64-bit compatible and has been deprecated. Use EventHandleIntPtr instead.")] public int EventHandle { get => EventHandleIntPtr.ToInt32(); set => EventHandleIntPtr = new IntPtr(value); } public IntPtr EventHandleIntPtr { get => (_pNativeOverlapped != null) ? _pNativeOverlapped->EventHandle : _eventHandle; set => ((_pNativeOverlapped != null) ? ref _pNativeOverlapped->EventHandle : ref _eventHandle) = value; } [Obsolete("This overload is not safe and has been deprecated. Use Pack(IOCompletionCallback?, object?) instead.")] [CLSCompliant(false)] public NativeOverlapped* Pack(IOCompletionCallback? iocb) => Pack(iocb, null); [CLSCompliant(false)] public NativeOverlapped* Pack(IOCompletionCallback? iocb, object? userData) { if (_pNativeOverlapped != null) { throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } if (iocb != null) { ExecutionContext? ec = ExecutionContext.Capture(); _callback = (ec != null && !ec.IsDefault) ? new IOCompletionCallbackHelper(iocb, ec) : (object)iocb; } else { _callback = null; } return AllocateNativeOverlapped(userData); } [Obsolete("This overload is not safe and has been deprecated. Use UnsafePack(IOCompletionCallback?, object?) instead.")] [CLSCompliant(false)] public NativeOverlapped* UnsafePack(IOCompletionCallback? iocb) => UnsafePack(iocb, null); [CLSCompliant(false)] public NativeOverlapped* UnsafePack(IOCompletionCallback? iocb, object? userData) { if (_pNativeOverlapped != null) { throw new InvalidOperationException(SR.InvalidOperation_Overlapped_Pack); } _callback = iocb; return AllocateNativeOverlapped(userData); } /*==================================================================== * Unpacks an unmanaged native Overlapped struct. * Unpins the native Overlapped struct ====================================================================*/ [CLSCompliant(false)] public static Overlapped Unpack(NativeOverlapped* nativeOverlappedPtr) { ArgumentNullException.ThrowIfNull(nativeOverlappedPtr); return GetOverlappedFromNative(nativeOverlappedPtr); } [CLSCompliant(false)] public static void Free(NativeOverlapped* nativeOverlappedPtr) { ArgumentNullException.ThrowIfNull(nativeOverlappedPtr); GetOverlappedFromNative(nativeOverlappedPtr)._pNativeOverlapped = null; FreeNativeOverlapped(nativeOverlappedPtr); } private NativeOverlapped* AllocateNativeOverlapped(object? userData) { NativeOverlapped* pNativeOverlapped = null; try { nuint handleCount = 1; if (userData != null) { if (userData.GetType() == typeof(object[])) { handleCount += (nuint)((object[])userData).Length; } else { handleCount++; } } pNativeOverlapped = (NativeOverlapped*)NativeMemory.Alloc( (nuint)(sizeof(NativeOverlapped) + sizeof(nuint)) + handleCount * (nuint)sizeof(GCHandle)); GCHandleCountRef(pNativeOverlapped) = 0; pNativeOverlapped->InternalLow = default; pNativeOverlapped->InternalHigh = default; pNativeOverlapped->OffsetLow = _offsetLow; pNativeOverlapped->OffsetHigh = _offsetHigh; pNativeOverlapped->EventHandle = _eventHandle; GCHandleRef(pNativeOverlapped, 0) = GCHandle.Alloc(this); GCHandleCountRef(pNativeOverlapped)++; if (userData != null) { if (userData.GetType() == typeof(object[])) { object[] objArray = (object[])userData; for (int i = 0; i < objArray.Length; i++) { GCHandleRef(pNativeOverlapped, (nuint)(i + 1)) = GCHandle.Alloc(objArray[i], GCHandleType.Pinned); GCHandleCountRef(pNativeOverlapped)++; } } else { GCHandleRef(pNativeOverlapped, 1) = GCHandle.Alloc(userData, GCHandleType.Pinned); GCHandleCountRef(pNativeOverlapped)++; } } Debug.Assert(GCHandleCountRef(pNativeOverlapped) == handleCount); // Tracing needs _pNativeOverlapped to be initialized _pNativeOverlapped = pNativeOverlapped; #if FEATURE_PERFTRACING if (NativeRuntimeEventSource.Log.IsEnabled()) NativeRuntimeEventSource.Log.ThreadPoolIOPack(pNativeOverlapped); #endif NativeOverlapped* pRet = pNativeOverlapped; pNativeOverlapped = null; return pRet; } finally { if (pNativeOverlapped != null) { _pNativeOverlapped = null; FreeNativeOverlapped(pNativeOverlapped); } } } internal static void FreeNativeOverlapped(NativeOverlapped* pNativeOverlapped) { nuint handleCount = GCHandleCountRef(pNativeOverlapped); for (nuint i = 0; i < handleCount; i++) GCHandleRef(pNativeOverlapped, i).Free(); NativeMemory.Free(pNativeOverlapped); } // // The NativeOverlapped structure is followed by GC handle count and inline array of GC handles // private static ref nuint GCHandleCountRef(NativeOverlapped* pNativeOverlapped) => ref *(nuint*)(pNativeOverlapped + 1); private static ref GCHandle GCHandleRef(NativeOverlapped* pNativeOverlapped, nuint index) => ref *((GCHandle*)((nuint*)(pNativeOverlapped + 1) + 1) + index); internal static Overlapped GetOverlappedFromNative(NativeOverlapped* pNativeOverlapped) { object? target = GCHandleRef(pNativeOverlapped, 0).Target; Debug.Assert(target is Overlapped); Overlapped overlapped = (Overlapped)target; Debug.Assert(overlapped._pNativeOverlapped == pNativeOverlapped); return overlapped; } } }