Inheritance: System.IDisposable
        public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated)
        {
            if (preAllocated == null)
                throw new ArgumentNullException("preAllocated");

            bool addedRefToThis = false;
            bool addedRefToPreAllocated = false;
            try
            {
                addedRefToThis = AddRef();
                addedRefToPreAllocated = preAllocated.AddRef();

                Win32ThreadPoolNativeOverlapped.OverlappedData data = preAllocated._overlapped->Data;
                if (data._boundHandle != null)
                    throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, "preAllocated");

                data._boundHandle = this;

                Interop.mincore.StartThreadpoolIo(_threadPoolHandle);

                return Win32ThreadPoolNativeOverlapped.ToNativeOverlapped(preAllocated._overlapped);
            }
            catch
            {
                if (addedRefToPreAllocated)
                    preAllocated.Release();
                if (addedRefToThis)
                    Release();
                throw;
            }
        }
 internal unsafe static Win32ThreadPoolNativeOverlapped* Allocate(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated)
 {
     Win32ThreadPoolNativeOverlapped* overlapped = AllocateNew();
     try
     {
         overlapped->SetData(callback, state, pinData, preAllocated);
     }
     catch
     {
         Free(overlapped);
         throw;
     }
     return overlapped;
 }
            internal void Reset()
            {
                Debug.Assert(_boundHandle == null); //not in use

                if (_pinnedData != null)
                {
                    for (int i = 0; i < _pinnedData.Length; i++)
                    {
                        if (_pinnedData[i].IsAllocated && _pinnedData[i].Target != null)
                            _pinnedData[i].Target = null;
                    }
                }

                _callback = null;
                _state = null;
                _executionContext = null;
                _completed = false;
                _preAllocated = null;
            }
Beispiel #4
0
        // Win32 file impl
        // TODO: Other platforms
        public unsafe void OpenReadFile(string path)
        {
            var fileHandle = CreateFile(path, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, EFileAttributes.Overlapped, IntPtr.Zero);

            var handle = ThreadPoolBoundHandle.BindHandle(fileHandle);

            var readOperation = new ReadOperation
            {
                Writer = _input,
                FileHandle = fileHandle,
                ThreadPoolBoundHandle = handle,
                IOCallback = IOCallback
            };

            var overlapped = new PreAllocatedOverlapped(IOCallback, readOperation, null);
            readOperation.PreAllocatedOverlapped = overlapped;

            _input.ReadingStarted.ContinueWith((t, state) =>
            {
                ((ReadOperation)state).Read();
            },
            readOperation);
        }
Beispiel #5
0
        partial void OnBufferAllocated()
        {
            Debug.Assert(_buffer != null);
            Debug.Assert(_preallocatedOverlapped == null);

            if (_useAsyncIO)
                _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer);
        }
Beispiel #6
0
        private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
        {
            Debug.Assert(_useAsyncIO, "This implementation is for async mode only");
            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
            Debug.Assert(CanRead, "_parent.CanRead");

            // Make sure any pending writes have been flushed before we do a read.
            if (_writePos > 0)
            {
                await FlushWriteAsync(cancellationToken).ConfigureAwait(false);
            }

            // Typically CopyToAsync would be invoked as the only "read" on the stream, but it's possible some reading is
            // done and then the CopyToAsync is issued.  For that case, see if we have any data available in the buffer.
            if (GetBuffer() != null)
            {
                int bufferedBytes = _readLength - _readPos;
                if (bufferedBytes > 0)
                {
                    await destination.WriteAsync(GetBuffer(), _readPos, bufferedBytes, cancellationToken).ConfigureAwait(false);
                    _readPos = _readLength = 0;
                }
            }

            // For efficiency, we avoid creating a new task and associated state for each asynchronous read.
            // Instead, we create a single reusable awaitable object that will be triggered when an await completes
            // and reset before going again.
            var readAwaitable = new AsyncCopyToAwaitable(this);

            // Make sure we are reading from the position that we think we are.
            // Only set the position in the awaitable if we can seek (e.g. not for pipes).
            bool canSeek = CanSeek;
            if (canSeek)
            {
                VerifyOSHandlePosition();
                readAwaitable._position = _filePosition;
            }

            // Get the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use
            // _buffer here, even if it's not null, as concurrent operations are allowed, and another operation may
            // actually be using the buffer already. Plus, it'll be rare for _buffer to be non-null, as typically
            // CopyToAsync is used as the only operation performed on the stream, and the buffer is lazily initialized.
            // Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that
            // we'd likely be unable to use it anyway.  Instead, we rent the buffer from a pool.
            byte[] copyBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
            bufferSize = 0; // repurpose bufferSize to be the high water mark for the buffer, to avoid an extra field in the state machine

            // Allocate an Overlapped we can use repeatedly for all operations
            var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer);
            var cancellationReg = default(CancellationTokenRegistration);
            try
            {
                // Register for cancellation.  We do this once for the whole copy operation, and just try to cancel
                // whatever read operation may currently be in progress, if there is one.  It's possible the cancellation
                // request could come in between operations, in which case we flag that with explicit calls to ThrowIfCancellationRequested
                // in the read/write copy loop.
                if (cancellationToken.CanBeCanceled)
                {
                    cancellationReg = cancellationToken.Register(s =>
                    {
                        var innerAwaitable = (AsyncCopyToAwaitable)s;
                        unsafe
                        {
                            lock (innerAwaitable.CancellationLock) // synchronize with cleanup of the overlapped
                            {
                                if (innerAwaitable._nativeOverlapped != null)
                                {
                                    // Try to cancel the I/O.  We ignore the return value, as cancellation is opportunistic and we
                                    // don't want to fail the operation because we couldn't cancel it.
                                    Interop.Kernel32.CancelIoEx(innerAwaitable._fileStream._fileHandle, innerAwaitable._nativeOverlapped);
                                }
                            }
                        }
                    }, readAwaitable);
                }

                // Repeatedly read from this FileStream and write the results to the destination stream.
                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    readAwaitable.ResetForNextOperation();

                    try
                    {
                        bool synchronousSuccess;
                        int errorCode;
                        unsafe
                        {
                            // Allocate a native overlapped for our reusable overlapped, and set position to read based on the next
                            // desired address stored in the awaitable.  (This position may be 0, if either we're at the beginning or
                            // if the stream isn't seekable.)
                            readAwaitable._nativeOverlapped = _fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(awaitableOverlapped);
                            if (canSeek)
                            {
                                readAwaitable._nativeOverlapped->OffsetLow = unchecked((int)readAwaitable._position);
                                readAwaitable._nativeOverlapped->OffsetHigh = (int)(readAwaitable._position >> 32);
                            }

                            // Kick off the read.
                            synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, 0, copyBuffer.Length, readAwaitable._nativeOverlapped, out errorCode) >= 0;
                        }

                        // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation.
                        if (!synchronousSuccess)
                        {
                            switch (errorCode)
                            {
                                case ERROR_IO_PENDING:
                                    // Async operation in progress.
                                    break;
                                case ERROR_BROKEN_PIPE:
                                case ERROR_HANDLE_EOF:
                                    // We're at or past the end of the file, and the overlapped callback
                                    // won't be raised in these cases. Mark it as completed so that the await
                                    // below will see it as such.
                                    readAwaitable.MarkCompleted();
                                    break;
                                default:
                                    // Everything else is an error (and there won't be a callback).
                                    throw Win32Marshal.GetExceptionForWin32Error(errorCode);
                            }
                        }

                        // Wait for the async operation (which may or may not have already completed), then throw if it failed.
                        await readAwaitable;
                        switch (readAwaitable._errorCode)
                        {
                            case 0: // success
                                Debug.Assert(readAwaitable._numBytes >= 0, $"Expected non-negative numBytes, got {readAwaitable._numBytes}");
                                break;
                            case ERROR_BROKEN_PIPE: // logically success with 0 bytes read (write end of pipe closed)
                            case ERROR_HANDLE_EOF:  // logically success with 0 bytes read (read at end of file)
                                Debug.Assert(readAwaitable._numBytes == 0, $"Expected 0 bytes read, got {readAwaitable._numBytes}");
                                break;
                            case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled
                                throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true));
                            default: // error
                                throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode);
                        }

                        // Successful operation.  If we got zero bytes, we're done: exit the read/write loop.
                        int numBytesRead = (int)readAwaitable._numBytes;
                        if (numBytesRead == 0)
                        {
                            break;
                        }

                        // Otherwise, update the read position for next time accordingly.
                        if (canSeek)
                        {
                            readAwaitable._position += numBytesRead;
                        }

                        // (and keep track of the maximum number of bytes in the buffer we used, to avoid excessive and unnecessary
                        // clearing of the buffer before we return it to the pool)
                        if (numBytesRead > bufferSize)
                        {
                            bufferSize = numBytesRead;
                        }
                    }
                    finally
                    {
                        // Free the resources for this read operation
                        unsafe
                        {
                            NativeOverlapped* overlapped;
                            lock (readAwaitable.CancellationLock) // just an Exchange, but we need this to be synchronized with cancellation, so using the same lock
                            {
                                overlapped = readAwaitable._nativeOverlapped;
                                readAwaitable._nativeOverlapped = null;
                            }
                            if (overlapped != null)
                            {
                                _fileHandle.ThreadPoolBinding.FreeNativeOverlapped(overlapped);
                            }
                        }
                    }

                    // Write out the read data.
                    await destination.WriteAsync(copyBuffer, 0, (int)readAwaitable._numBytes, cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                // Cleanup from the whole copy operation
                cancellationReg.Dispose();
                awaitableOverlapped.Dispose();

                Array.Clear(copyBuffer, 0, bufferSize);
                ArrayPool<byte>.Shared.Return(copyBuffer, clearArray: false);

                // Make sure the stream's current position reflects where we ended up
                if (!_fileHandle.IsClosed && CanSeek)
                {
                    SeekCore(0, SeekOrigin.End);
                }
            }
        }
        private void AllocateBuffer()
        {
            Debug.Assert(_buffer == null);
            Debug.Assert(_preallocatedOverlapped == null);

            _buffer = new byte[_bufferSize];
            if (_isAsync)
            {
                _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer);
            }
        }
    public unsafe void AllocateNativeOverlapped_PreAllocated_ReturnedNativeOverlapped_AllFieldsZero()
    {
        using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
        {
            using(PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256]))
            {
                NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);

                Assert.Equal(IntPtr.Zero, overlapped->InternalLow);
                Assert.Equal(IntPtr.Zero, overlapped->InternalHigh);
                Assert.Equal(0, overlapped->OffsetLow);
                Assert.Equal(0, overlapped->OffsetHigh);
                Assert.Equal(IntPtr.Zero, overlapped->EventHandle);

                handle.FreeNativeOverlapped(overlapped);
            }
        }
    }
    public unsafe void AllocateNativeOverlapped_PreAllocated_WhenHandleDisposed_ThrowsObjectDisposedException()
    {
        ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle();
        handle.Dispose();

        PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped(delegate { }, null, null);

        Assert.Throws<ObjectDisposedException>(() =>
        {
            handle.AllocateNativeOverlapped(preAlloc);
        });
    }
        private void SetData(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated)
        {
            Debug.Assert(callback != null);

            OverlappedData data = Data;

            data._callback = callback;
            data._state = state;
            data._executionContext = ExecutionContext.Capture();
            data._preAllocated = preAllocated;

            //
            // pinData can be any blittable type to be pinned, *or* an instance of object[] each element of which refers to
            // an instance of a blittable type to be pinned.
            //
            if (pinData != null)
            {
                object[] objArray = pinData as object[];
                if (objArray != null && objArray.GetType() == typeof(object[]))
                {
                    if (data._pinnedData == null || data._pinnedData.Length < objArray.Length)
                        Array.Resize(ref data._pinnedData, objArray.Length);

                    for (int i = 0; i < objArray.Length; i++)
                    {
                        if (!data._pinnedData[i].IsAllocated)
                            data._pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned);
                        else
                            data._pinnedData[i].Target = objArray[i];
                    }
                }
                else
                {
                    if (data._pinnedData == null)
                        data._pinnedData = new GCHandle[1];

                    if (!data._pinnedData[0].IsAllocated)
                        data._pinnedData[0] = GCHandle.Alloc(pinData, GCHandleType.Pinned);
                    else
                        data._pinnedData[0].Target = pinData;
                }
            }
        }
        public unsafe NativeOverlapped* AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated)
        {
            if (preAllocated == null)
                throw new ArgumentNullException("preAllocated");

            EnsureNotDisposed();

            preAllocated.AddRef();
            try
            {
                ThreadPoolBoundHandleOverlapped overlapped = preAllocated._overlapped;

                if (overlapped._boundHandle != null)
                    throw new ArgumentException(SR.Argument_PreAllocatedAlreadyAllocated, "preAllocated");

                overlapped._boundHandle = this;

                return overlapped._nativeOverlapped;
            }
            catch
            {
                preAllocated.Release();
                throw;
            }
        }
Beispiel #12
0
 public unsafe ThreadPoolBoundHandleOverlapped(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated)
 {
     this._userCallback                 = callback;
     this._userState                    = state;
     this._preAllocated                 = preAllocated;
     this._nativeOverlapped             = base.Pack(new IOCompletionCallback(ThreadPoolBoundHandleOverlapped.CompletionCallback), pinData);
     this._nativeOverlapped->OffsetLow  = 0;
     this._nativeOverlapped->OffsetHigh = 0;
 }
        internal static unsafe Win32ThreadPoolNativeOverlapped *Allocate(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated)
        {
            Win32ThreadPoolNativeOverlapped *overlapped = AllocateNew();

            try
            {
                overlapped->SetData(callback, state, pinData, preAllocated);
            }
            catch
            {
                Free(overlapped);
                throw;
            }
            return(overlapped);
        }
        private void SetData(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated)
        {
            Debug.Assert(callback != null);

            OverlappedData data = Data;

            data._callback         = callback;
            data._state            = state;
            data._executionContext = ExecutionContext.Capture();
            data._preAllocated     = preAllocated;

            //
            // pinData can be any blittable type to be pinned, *or* an instance of object[] each element of which refers to
            // an instance of a blittable type to be pinned.
            //
            if (pinData != null)
            {
                object[] objArray = pinData as object[];
                if (objArray != null && objArray.GetType() == typeof(object[]))
                {
                    if (data._pinnedData == null || data._pinnedData.Length < objArray.Length)
                    {
                        Array.Resize(ref data._pinnedData, objArray.Length);
                    }

                    for (int i = 0; i < objArray.Length; i++)
                    {
                        if (!data._pinnedData[i].IsAllocated)
                        {
                            data._pinnedData[i] = GCHandle.Alloc(objArray[i], GCHandleType.Pinned);
                        }
                        else
                        {
                            data._pinnedData[i].Target = objArray[i];
                        }
                    }
                }
                else
                {
                    if (data._pinnedData == null)
                    {
                        data._pinnedData = new GCHandle[1];
                    }

                    if (!data._pinnedData[0].IsAllocated)
                    {
                        data._pinnedData[0] = GCHandle.Alloc(pinData, GCHandleType.Pinned);
                    }
                    else
                    {
                        data._pinnedData[0].Target = pinData;
                    }
                }
            }
        }
Beispiel #15
0
        public unsafe ThreadPoolBoundHandleOverlapped(IOCompletionCallback callback, object state, object pinData, PreAllocatedOverlapped preAllocated)
        {
            _userCallback = callback;
            _userState    = state;
            _preAllocated = preAllocated;

            _nativeOverlapped             = Pack(s_completionCallback, pinData);
            _nativeOverlapped->OffsetLow  = 0;       // CLR reuses NativeOverlapped instances and does not reset these
            _nativeOverlapped->OffsetHigh = 0;
        }
    public unsafe void AllocateNativeOverlapped_PreAllocated_WhenAlreadyAllocated_ThrowsArgumentException()
    {
        using(ThreadPoolBoundHandle handle = CreateThreadPoolBoundHandle())
        {
            using(PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped(delegate { }, null, null))
            {
                NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);

                Assert.Throws<ArgumentException>(() =>
                {
                    handle.AllocateNativeOverlapped(preAlloc);
                });

                handle.FreeNativeOverlapped(overlapped);
            }
        }
    }
Beispiel #17
0
        private void AllocateBuffer()
        {
            Debug.Assert(_buffer == null);
            Debug.Assert(_preallocatedOverlapped == null);

            _buffer = new byte[_bufferSize]; // TODO: Issue #5598: Use ArrayPool.
            if (_isAsync)
            {
                _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer);
            }
        }
        // Cleans up any existing Overlapped object and related state variables.
        private void FreeOverlapped(bool checkForShutdown)
        {
            if (!checkForShutdown || !Environment.HasShutdownStarted)
            {
                // Free the overlapped object.
                if (_ptrNativeOverlapped != null && !_ptrNativeOverlapped.IsInvalid)
                {
                    _ptrNativeOverlapped.Dispose();
                    _ptrNativeOverlapped = null;
                }

                // Free the preallocated overlapped object. This in turn will unpin
                // any pinned buffers.
                if (_preAllocatedOverlapped != null)
                {
                    _preAllocatedOverlapped.Dispose();
                    _preAllocatedOverlapped = null;

                    _pinState = PinState.None;
                    _pinnedAcceptBuffer = null;
                    _pinnedSingleBuffer = null;
                    _pinnedSingleBufferOffset = 0;
                    _pinnedSingleBufferCount = 0;
                }

                // Free any allocated GCHandles.
                if (_socketAddressGCHandle.IsAllocated)
                {
                    _socketAddressGCHandle.Free();
                    _pinnedSocketAddress = null;
                }

                if (_wsaMessageBufferGCHandle.IsAllocated)
                {
                    _wsaMessageBufferGCHandle.Free();
                    _ptrWSAMessageBuffer = IntPtr.Zero;
                }

                if (_wsaRecvMsgWSABufferArrayGCHandle.IsAllocated)
                {
                    _wsaRecvMsgWSABufferArrayGCHandle.Free();
                    _ptrWSARecvMsgWSABufferArray = IntPtr.Zero;
                }

                if (_controlBufferGCHandle.IsAllocated)
                {
                    _controlBufferGCHandle.Free();
                    _ptrControlBuffer = IntPtr.Zero;
                }
            }
        }
 public unsafe System.Threading.NativeOverlapped *AllocateNativeOverlapped(System.Threading.PreAllocatedOverlapped preAllocated)
 {
     throw null;
 }
        // Sets up an Overlapped object with with multiple buffers pinned.
        unsafe private void SetupOverlappedMultiple()
        {
            ArraySegment<byte>[] tempList = new ArraySegment<byte>[_bufferList.Count];
            _bufferList.CopyTo(tempList, 0);

            // Number of things to pin is number of buffers.
            // Ensure we have properly sized object array.
            if (_objectsToPin == null || (_objectsToPin.Length != tempList.Length))
            {
                _objectsToPin = new object[tempList.Length];
            }

            // Fill in object array.
            for (int i = 0; i < (tempList.Length); i++)
            {
                _objectsToPin[i] = tempList[i].Array;
            }

            if (_wsaBufferArray == null || _wsaBufferArray.Length != tempList.Length)
            {
                _wsaBufferArray = new WSABuffer[tempList.Length];
            }

            // Pin buffers and fill in WSABuffer descriptor pointers and lengths.
            _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, _objectsToPin);
            if (GlobalLog.IsEnabled)
            {
                GlobalLog.Print(
                    "SocketAsyncEventArgs#" + LoggingHash.HashString(this) + "::SetupOverlappedMultiple: new PreAllocatedOverlapped." +
                    LoggingHash.HashString(_preAllocatedOverlapped));
            }

            for (int i = 0; i < tempList.Length; i++)
            {
                ArraySegment<byte> localCopy = tempList[i];
                RangeValidationHelpers.ValidateSegment(localCopy);
                _wsaBufferArray[i].Pointer = Marshal.UnsafeAddrOfPinnedArrayElement(localCopy.Array, localCopy.Offset);
                _wsaBufferArray[i].Length = localCopy.Count;
            }
            _pinState = PinState.MultipleBuffer;
        }
        // Sets up an Overlapped object with either _buffer or _acceptBuffer pinned.
        unsafe private void SetupOverlappedSingle(bool pinSingleBuffer)
        {
            // Pin buffer, get native pointers, and fill in WSABuffer descriptor.
            if (pinSingleBuffer)
            {
                if (_buffer != null)
                {
                    _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, _buffer);
                    if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"new PreAllocatedOverlapped pinSingleBuffer=true, non-null buffer:{_preAllocatedOverlapped}");

                    _pinnedSingleBuffer = _buffer;
                    _pinnedSingleBufferOffset = _offset;
                    _pinnedSingleBufferCount = _count;
                    _ptrSingleBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(_buffer, _offset);
                    _ptrAcceptBuffer = IntPtr.Zero;
                    _wsaBuffer.Pointer = _ptrSingleBuffer;
                    _wsaBuffer.Length = _count;
                    _pinState = PinState.SingleBuffer;
                }
                else
                {
                    _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, null);
                    if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"new PreAllocatedOverlapped pinSingleBuffer=true, null buffer: {_preAllocatedOverlapped}");

                    _pinnedSingleBuffer = null;
                    _pinnedSingleBufferOffset = 0;
                    _pinnedSingleBufferCount = 0;
                    _ptrSingleBuffer = IntPtr.Zero;
                    _ptrAcceptBuffer = IntPtr.Zero;
                    _wsaBuffer.Pointer = _ptrSingleBuffer;
                    _wsaBuffer.Length = _count;
                    _pinState = PinState.NoBuffer;
                }
            }
            else
            {
                _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, _acceptBuffer);
                if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"new PreAllocatedOverlapped pinSingleBuffer=false:{_preAllocatedOverlapped}");

                _pinnedAcceptBuffer = _acceptBuffer;
                _ptrAcceptBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(_acceptBuffer, 0);
                _ptrSingleBuffer = IntPtr.Zero;
                _pinState = PinState.SingleAcceptBuffer;
            }
        }
    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())
        {
            PreAllocatedOverlapped preAlloc = new PreAllocatedOverlapped((_, __, ___) => { }, new object(), new byte[256]);
            NativeOverlapped* overlapped = handle.AllocateNativeOverlapped(preAlloc);
            overlapped->OffsetHigh = 1;
            overlapped->OffsetLow = 1;
            handle.FreeNativeOverlapped(overlapped);

            overlapped = handle.AllocateNativeOverlapped(preAlloc);

            Assert.Equal(IntPtr.Zero, overlapped->InternalLow);
            Assert.Equal(IntPtr.Zero, overlapped->InternalHigh);
            Assert.Equal(0, overlapped->OffsetLow);
            Assert.Equal(0, overlapped->OffsetHigh);
            Assert.Equal(IntPtr.Zero, overlapped->EventHandle);

            handle.FreeNativeOverlapped(overlapped);
        }
    }
        // Sets up an Overlapped object with either _buffer or _acceptBuffer pinned.
        unsafe private void SetupOverlappedSingle(bool pinSingleBuffer)
        {
            // Pin buffer, get native pointers, and fill in WSABuffer descriptor.
            if (pinSingleBuffer)
            {
                if (_buffer != null)
                {
                    _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, _buffer);
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.Print(
                            "SocketAsyncEventArgs#" + LoggingHash.HashString(this) +
                            "::SetupOverlappedSingle: new PreAllocatedOverlapped pinSingleBuffer=true, non-null buffer: " +
                            LoggingHash.HashString(_preAllocatedOverlapped));
                    }

                    _pinnedSingleBuffer = _buffer;
                    _pinnedSingleBufferOffset = _offset;
                    _pinnedSingleBufferCount = _count;
                    _ptrSingleBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(_buffer, _offset);
                    _ptrAcceptBuffer = IntPtr.Zero;
                    _wsaBuffer.Pointer = _ptrSingleBuffer;
                    _wsaBuffer.Length = _count;
                    _pinState = PinState.SingleBuffer;
                }
                else
                {
                    _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, null);
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.Print(
                            "SocketAsyncEventArgs#" + LoggingHash.HashString(this) +
                            "::SetupOverlappedSingle: new PreAllocatedOverlapped pinSingleBuffer=true, null buffer: " +
                            LoggingHash.HashString(_preAllocatedOverlapped));
                    }

                    _pinnedSingleBuffer = null;
                    _pinnedSingleBufferOffset = 0;
                    _pinnedSingleBufferCount = 0;
                    _ptrSingleBuffer = IntPtr.Zero;
                    _ptrAcceptBuffer = IntPtr.Zero;
                    _wsaBuffer.Pointer = _ptrSingleBuffer;
                    _wsaBuffer.Length = _count;
                    _pinState = PinState.NoBuffer;
                }
            }
            else
            {
                _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, _acceptBuffer);
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Print(
                        "SocketAsyncEventArgs#" + LoggingHash.HashString(this) +
                        "::SetupOverlappedSingle: new PreAllocatedOverlapped pinSingleBuffer=false: " +
                        LoggingHash.HashString(_preAllocatedOverlapped));
                }

                _pinnedAcceptBuffer = _acceptBuffer;
                _ptrAcceptBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(_acceptBuffer, 0);
                _ptrSingleBuffer = IntPtr.Zero;
                _pinState = PinState.SingleAcceptBuffer;
            }
        }
    public unsafe void MultipleOperationsOverMultipleHandles()
    {
        const int DATA_SIZE = 2;

        SafeHandle handle1 = HandleFactory.CreateAsyncFileHandleForWrite(@"MultipleOperationsOverMultipleHandle1.tmp");
        SafeHandle handle2 = HandleFactory.CreateAsyncFileHandleForWrite(@"MultipleOperationsOverMultipleHandle2.tmp");
        ThreadPoolBoundHandle boundHandle1 = ThreadPoolBoundHandle.BindHandle(handle1);
        ThreadPoolBoundHandle boundHandle2 = ThreadPoolBoundHandle.BindHandle(handle2);

        OverlappedContext result1 = new OverlappedContext();
        OverlappedContext result2 = new OverlappedContext();

        byte[] data1 = new byte[DATA_SIZE];
        data1[0] = (byte)'A';
        data1[1] = (byte)'B';


        byte[] data2 = new byte[DATA_SIZE];
        data2[0] = (byte)'C';
        data2[1] = (byte)'D';

        PreAllocatedOverlapped preAlloc1 = new PreAllocatedOverlapped(OnOverlappedOperationCompleted, result1, data1);
        PreAllocatedOverlapped preAlloc2 = new PreAllocatedOverlapped(OnOverlappedOperationCompleted, result2, data2);

        for (int i = 0; i < 10; i++)
        {
            NativeOverlapped* overlapped1 = boundHandle1.AllocateNativeOverlapped(preAlloc1);
            NativeOverlapped* overlapped2 = boundHandle2.AllocateNativeOverlapped(preAlloc2);

            fixed (byte* p1 = data1, p2 = data2)
            {
                int retval = DllImport.WriteFile(boundHandle1.Handle, p1, DATA_SIZE, IntPtr.Zero, overlapped1);

                if (retval == 0)
                {
                    Assert.Equal(DllImport.ERROR_IO_PENDING, Marshal.GetLastWin32Error());
                }


                retval = DllImport.WriteFile(boundHandle2.Handle, p2, DATA_SIZE, IntPtr.Zero, overlapped2);

                if (retval == 0)
                {
                    Assert.Equal(DllImport.ERROR_IO_PENDING, Marshal.GetLastWin32Error());
                }

                // Wait for overlapped operations to complete
                WaitHandle.WaitAll(new WaitHandle[] { result1.Event, result2.Event });
            }

            boundHandle1.FreeNativeOverlapped(overlapped1);
            boundHandle2.FreeNativeOverlapped(overlapped2);

            result1.Event.Reset();
            result2.Event.Reset();

            Assert.Equal(0, result1.ErrorCode);
            Assert.Equal(0, result2.ErrorCode);
            Assert.Equal(DATA_SIZE, result1.BytesWritten);
            Assert.Equal(DATA_SIZE, result2.BytesWritten);
        }

        boundHandle1.Dispose();
        boundHandle2.Dispose();
        preAlloc1.Dispose();
        preAlloc2.Dispose();
        handle1.Dispose();
        handle2.Dispose();
    }
        // Sets up an Overlapped object for SendPacketsAsync.
        unsafe private void SetupOverlappedSendPackets()
        {
            int index;

            // Alloc native descriptor.
            _sendPacketsDescriptor =
                new Interop.Winsock.TransmitPacketsElement[_sendPacketsElementsFileCount + _sendPacketsElementsBufferCount];

            // Number of things to pin is number of buffers + 1 (native descriptor).
            // Ensure we have properly sized object array.
            if (_objectsToPin == null || (_objectsToPin.Length != _sendPacketsElementsBufferCount + 1))
            {
                _objectsToPin = new object[_sendPacketsElementsBufferCount + 1];
            }

            // Fill in objects to pin array. Native descriptor buffer first and then user specified buffers.
            _objectsToPin[0] = _sendPacketsDescriptor;
            index = 1;
            foreach (SendPacketsElement spe in _sendPacketsElementsInternal)
            {
                if (spe != null && spe._buffer != null && spe._count > 0)
                {
                    _objectsToPin[index] = spe._buffer;
                    index++;
                }
            }

            // Pin buffers.
            _preAllocatedOverlapped = new PreAllocatedOverlapped(CompletionPortCallback, this, _objectsToPin);
            if (GlobalLog.IsEnabled)
            {
                GlobalLog.Print(
                    "SocketAsyncEventArgs#" + LoggingHash.HashString(this) + "::SetupOverlappedSendPackets: new PreAllocatedOverlapped: " +
                    LoggingHash.HashString(_preAllocatedOverlapped));
            }

            // Get pointer to native descriptor.
            _ptrSendPacketsDescriptor = Marshal.UnsafeAddrOfPinnedArrayElement(_sendPacketsDescriptor, 0);

            // Fill in native descriptor.
            int descriptorIndex = 0;
            int fileIndex = 0;
            foreach (SendPacketsElement spe in _sendPacketsElementsInternal)
            {
                if (spe != null)
                {
                    if (spe._buffer != null && spe._count > 0)
                    {
                        // This element is a buffer.
                        _sendPacketsDescriptor[descriptorIndex].buffer = Marshal.UnsafeAddrOfPinnedArrayElement(spe._buffer, spe._offset);
                        _sendPacketsDescriptor[descriptorIndex].length = (uint)spe._count;
                        _sendPacketsDescriptor[descriptorIndex].flags = (Interop.Winsock.TransmitPacketsElementFlags)spe._flags;
                        descriptorIndex++;
                    }
                    else if (spe._filePath != null)
                    {
                        // This element is a file.
                        _sendPacketsDescriptor[descriptorIndex].fileHandle = _sendPacketsFileHandles[fileIndex].DangerousGetHandle();
                        _sendPacketsDescriptor[descriptorIndex].fileOffset = spe._offset;
                        _sendPacketsDescriptor[descriptorIndex].length = (uint)spe._count;
                        _sendPacketsDescriptor[descriptorIndex].flags = (Interop.Winsock.TransmitPacketsElementFlags)spe._flags;
                        fileIndex++;
                        descriptorIndex++;
                    }
                }
            }

            _pinState = PinState.SendPackets;
        }
        public unsafe NativeOverlapped *AllocateNativeOverlapped(PreAllocatedOverlapped preAllocated)
        {
            ArgumentNullException.ThrowIfNull(preAllocated);

            throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO);
        }