File: System\ServiceModel\Channels\OverlappedContext.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Channels
{
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;
    using System.Threading;
 
    delegate void OverlappedIOCompleteCallback(bool haveResult, int error, int bytesRead);
 
    unsafe class OverlappedContext
    {
        const int HandleOffsetFromOverlapped32 = -4;
        const int HandleOffsetFromOverlapped64 = -3;
 
        static IOCompletionCallback completeCallback;
        static WaitOrTimerCallback eventCallback;
        static WaitOrTimerCallback cleanupCallback;
        static byte[] dummyBuffer = new byte[0];
 
        object[] bufferHolder;
        byte* bufferPtr;
        NativeOverlapped* nativeOverlapped;
        GCHandle pinnedHandle;
        object pinnedTarget;
        Overlapped overlapped;
        RootedHolder rootedHolder;
        OverlappedIOCompleteCallback pendingCallback;  // Null when no async I/O is pending.
        bool deferredFree;
        bool syncOperationPending;
        ManualResetEvent completionEvent;
        IntPtr eventHandle;
 
        // Only used by unbound I/O.
        RegisteredWaitHandle registration;
 
#if DEBUG_EXPENSIVE
        StackTrace freeStack;
#endif
 
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public OverlappedContext()
        {
            if (OverlappedContext.completeCallback == null)
            {
                OverlappedContext.completeCallback = Fx.ThunkCallback(new IOCompletionCallback(CompleteCallback));
            }
            if (OverlappedContext.eventCallback == null)
            {
                OverlappedContext.eventCallback = Fx.ThunkCallback(new WaitOrTimerCallback(EventCallback));
            }
            if (OverlappedContext.cleanupCallback == null)
            {
                OverlappedContext.cleanupCallback = Fx.ThunkCallback(new WaitOrTimerCallback(CleanupCallback));
            }
 
            this.bufferHolder = new object[] { OverlappedContext.dummyBuffer };
            this.overlapped = new Overlapped();
            this.nativeOverlapped = this.overlapped.UnsafePack(OverlappedContext.completeCallback, this.bufferHolder);
 
            // When replacing the buffer, we need to provoke the CLR to fix up the handle of the pin.
            this.pinnedHandle = GCHandle.FromIntPtr(*((IntPtr*)nativeOverlapped +
                (IntPtr.Size == 4 ? HandleOffsetFromOverlapped32 : HandleOffsetFromOverlapped64)));
            this.pinnedTarget = this.pinnedHandle.Target;
 
            // Create the permanently rooted holder and put it in the Overlapped.
            this.rootedHolder = new RootedHolder();
            this.overlapped.AsyncResult = rootedHolder;
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        ~OverlappedContext()
        {
            if (this.nativeOverlapped != null && !AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)
            {
                if (this.syncOperationPending)
                {
                    Fx.Assert(this.rootedHolder != null, "rootedHolder null in Finalize.");
                    Fx.Assert(this.rootedHolder.EventHolder != null, "rootedHolder.EventHolder null in Finalize.");
                    Fx.Assert(OverlappedContext.cleanupCallback != null, "cleanupCallback null in Finalize.");
 
                    // Can't free the overlapped.  Register a callback to deal with this.
                    // This will ressurect the OverlappedContext.
                    // The completionEvent will still be alive (not finalized) since it's rooted by the pending Overlapped in the holder.
                    // We own it now and will close it in the callback.
                    ThreadPool.UnsafeRegisterWaitForSingleObject(this.rootedHolder.EventHolder, OverlappedContext.cleanupCallback, this, Timeout.Infinite, true);
                }
                else
                {
                    Overlapped.Free(this.nativeOverlapped);
                }
            }
        }
 
        // None of the OverlappedContext methods are threadsafe.
        // Free or FreeOrDefer can only be called once.  FreeIfDeferred can be called any number of times, as long as it's only
        // called once after FreeOrDefer.
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public void Free()
        {
            if (this.pendingCallback != null)
            {
                throw Fx.AssertAndThrow("OverlappedContext.Free called while async operation is pending.");
            }
            if (this.syncOperationPending)
            {
                throw Fx.AssertAndThrow("OverlappedContext.Free called while sync operation is pending.");
            }
            if (this.nativeOverlapped == null)
            {
                throw Fx.AssertAndThrow("OverlappedContext.Free called multiple times.");
            }
 
#if DEBUG_EXPENSIVE
            this.freeStack = new StackTrace();
#endif
 
            // The OverlappedData is cached and reused.  It looks weird if there's still a reference to it form here.
            this.pinnedTarget = null;
 
            NativeOverlapped* nativePtr = this.nativeOverlapped;
            this.nativeOverlapped = null;
            Overlapped.Free(nativePtr);
 
            if (this.completionEvent != null)
            {
                this.completionEvent.Close();
            }
 
            GC.SuppressFinalize(this);
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public bool FreeOrDefer()
        {
            if (this.pendingCallback != null || this.syncOperationPending)
            {
                this.deferredFree = true;
                return false;
            }
 
            Free();
            return true;
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public bool FreeIfDeferred()
        {
            if (this.deferredFree)
            {
                return FreeOrDefer();
            }
 
            return false;
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public void StartAsyncOperation(byte[] buffer, OverlappedIOCompleteCallback callback, bool bound)
        {
            if (callback == null)
            {
                throw Fx.AssertAndThrow("StartAsyncOperation called with null callback.");
            }
            if (this.pendingCallback != null)
            {
                throw Fx.AssertAndThrow("StartAsyncOperation called while another is in progress.");
            }
            if (this.syncOperationPending)
            {
                throw Fx.AssertAndThrow("StartAsyncOperation called while a sync operation was already pending.");
            }
            if (this.nativeOverlapped == null)
            {
                throw Fx.AssertAndThrow("StartAsyncOperation called on freed OverlappedContext.");
            }
 
            this.pendingCallback = callback;
 
            if (buffer != null)
            {
                Fx.Assert(object.ReferenceEquals(this.bufferHolder[0], OverlappedContext.dummyBuffer), "StartAsyncOperation: buffer holder corrupted.");
                this.bufferHolder[0] = buffer;
                this.pinnedHandle.Target = this.pinnedTarget;
                this.bufferPtr = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
            }
 
            if (bound)
            {
                this.overlapped.EventHandleIntPtr = IntPtr.Zero;
 
                // For completion ports, the back-reference is this member.
                this.rootedHolder.ThisHolder = this;
            }
            else
            {
                // Need to do this since we register the wait before posting the I/O.
                if (this.completionEvent != null)
                {
                    this.completionEvent.Reset();
                }
 
                this.overlapped.EventHandleIntPtr = EventHandle;
 
                // For unbound, the back-reference is this registration.
                this.registration = ThreadPool.UnsafeRegisterWaitForSingleObject(this.completionEvent, OverlappedContext.eventCallback, this, Timeout.Infinite, true);
            }
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public void CancelAsyncOperation()
        {
            this.rootedHolder.ThisHolder = null;
            if (this.registration != null)
            {
                this.registration.Unregister(null);
                this.registration = null;
            }
            this.bufferPtr = null;
            this.bufferHolder[0] = OverlappedContext.dummyBuffer;
            this.pendingCallback = null;
        }
 
        //  public void StartSyncOperation(byte[] buffer)
        //  {
        //      StartSyncOperation(buffer, ref this.bufferHolder[0], false);
        //  }
 
        // The only holder allowed is Holder[0].  It can be passed in as a ref to prevent repeated expensive array lookups.
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public void StartSyncOperation(byte[] buffer, ref object holder)
        {
            if (this.syncOperationPending)
            {
                throw Fx.AssertAndThrow("StartSyncOperation called while an operation was already pending.");
            }
            if (this.pendingCallback != null)
            {
                throw Fx.AssertAndThrow("StartSyncOperation called while an async operation was already pending.");
            }
            if (this.nativeOverlapped == null)
            {
                throw Fx.AssertAndThrow("StartSyncOperation called on freed OverlappedContext.");
            }
 
            this.overlapped.EventHandleIntPtr = EventHandle;
 
            // Sync operations do NOT root this object.  If it gets finalized, we need to know not to free the buffer.
            // We do root the event.
            this.rootedHolder.EventHolder = this.completionEvent;
            this.syncOperationPending = true;
 
            if (buffer != null)
            {
                Fx.Assert(object.ReferenceEquals(holder, OverlappedContext.dummyBuffer), "StartSyncOperation: buffer holder corrupted.");
                holder = buffer;
                this.pinnedHandle.Target = this.pinnedTarget;
                this.bufferPtr = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
            }
        }
 
        // If this returns false, the OverlappedContext is no longer usable.  It shouldn't be freed or anything.
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public bool WaitForSyncOperation(TimeSpan timeout)
        {
            return WaitForSyncOperation(timeout, ref this.bufferHolder[0]);
        }
 
        // The only holder allowed is Holder[0].  It can be passed in as a ref to prevent repeated expensive array lookups.
        [SecurityCritical]
        public bool WaitForSyncOperation(TimeSpan timeout, ref object holder)
        {
            if (!this.syncOperationPending)
            {
                throw Fx.AssertAndThrow("WaitForSyncOperation called while no operation was pending.");
            }
 
            if (!UnsafeNativeMethods.HasOverlappedIoCompleted(this.nativeOverlapped))
            {
                if (!TimeoutHelper.WaitOne(this.completionEvent, timeout))
                {
                    // We can't free ourselves until the operation is done.  The only way to do that is register a callback.
                    // This will root the object.  No longer any need for the finalizer.  This instance is unusable after this.
                    GC.SuppressFinalize(this);
                    ThreadPool.UnsafeRegisterWaitForSingleObject(this.completionEvent, OverlappedContext.cleanupCallback, this, Timeout.Infinite, true);
                    return false;
                }
            }
 
            Fx.Assert(this.bufferPtr == null || this.bufferPtr == (byte*)Marshal.UnsafeAddrOfPinnedArrayElement((byte[])holder, 0),
                "The buffer moved during a sync call!");
 
            CancelSyncOperation(ref holder);
            return true;
        }
 
        //  public void CancelSyncOperation()
        //  {
        //      CancelSyncOperation(ref this.bufferHolder[0]);
        //  }
 
        // The only holder allowed is Holder[0].  It can be passed in as a ref to prevent repeated expensive array lookups.
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        public void CancelSyncOperation(ref object holder)
        {
            this.bufferPtr = null;
            holder = OverlappedContext.dummyBuffer;
            Fx.Assert(object.ReferenceEquals(this.bufferHolder[0], OverlappedContext.dummyBuffer), "Bad holder passed to CancelSyncOperation.");
 
            this.syncOperationPending = false;
            this.rootedHolder.EventHolder = null;
        }
 
        // This should ONLY be used to make a 'ref object' parameter to the zeroth element, to prevent repeated expensive array lookups.
        public object[] Holder
        {
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            get
            {
                return this.bufferHolder;
            }
        }
 
        public byte* BufferPtr
        {
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            get
            {
                byte* ptr = this.bufferPtr;
                if (ptr == null)
                {
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw Fx.AssertAndThrow("Pointer requested while no operation pending or no buffer provided.");
                }
                return ptr;
            }
        }
 
        public NativeOverlapped* NativeOverlapped
        {
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            get
            {
                NativeOverlapped* ptr = this.nativeOverlapped;
                if (ptr == null)
                {
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw Fx.AssertAndThrow("NativeOverlapped pointer requested after it was freed.");
                }
                return ptr;
            }
        }
 
        IntPtr EventHandle
        {
            get
            {
                if (this.completionEvent == null)
                {
                    this.completionEvent = new ManualResetEvent(false);
                    this.eventHandle = (IntPtr)(1 | (long)this.completionEvent.SafeWaitHandle.DangerousGetHandle());
                }
                return this.eventHandle;
            }
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        static void CompleteCallback(uint error, uint numBytes, NativeOverlapped* nativeOverlapped)
        {
            // Empty out the AsyncResult ASAP to close the leak window.
            Overlapped overlapped = Overlapped.Unpack(nativeOverlapped);
            OverlappedContext pThis = ((RootedHolder)overlapped.AsyncResult).ThisHolder;
            Fx.Assert(pThis != null, "Overlapped.AsyncResult not set. I/O completed multiple times, or cancelled I/O completed.");
            Fx.Assert(object.ReferenceEquals(pThis.overlapped, overlapped), "CompleteCallback completed with corrupt OverlappedContext.overlapped.");
            Fx.Assert(object.ReferenceEquals(pThis.rootedHolder, overlapped.AsyncResult), "CompleteCallback completed with corrupt OverlappedContext.rootedHolder.");
            pThis.rootedHolder.ThisHolder = null;
 
            Fx.Assert(pThis.bufferPtr == null || pThis.bufferPtr == (byte*)Marshal.UnsafeAddrOfPinnedArrayElement((byte[])pThis.bufferHolder[0], 0),
                "Buffer moved during bound async operation!");
 
            // Release the pin.
            pThis.bufferPtr = null;
            pThis.bufferHolder[0] = OverlappedContext.dummyBuffer;
 
            OverlappedIOCompleteCallback callback = pThis.pendingCallback;
            pThis.pendingCallback = null;
            Fx.Assert(callback != null, "PendingCallback not set. I/O completed multiple times, or cancelled I/O completed.");
 
            callback(true, (int)error, checked((int)numBytes));
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        static void EventCallback(object state, bool timedOut)
        {
            OverlappedContext pThis = state as OverlappedContext;
            Fx.Assert(pThis != null, "OverlappedContext.EventCallback registered wait doesn't have an OverlappedContext as state.");
 
            if (timedOut)
            {
                Fx.Assert("OverlappedContext.EventCallback registered wait timed out.");
 
                // Turn this into a leak.  Don't let ourselves get cleaned up - could scratch the heap.
                if (pThis == null || pThis.rootedHolder == null)
                {
                    // We're doomed to do a wild write and corrupt the process.
                    DiagnosticUtility.FailFast("Can't prevent heap corruption.");
                }
                pThis.rootedHolder.ThisHolder = pThis;
                return;
            }
 
            pThis.registration = null;
 
            Fx.Assert(pThis.bufferPtr == null || pThis.bufferPtr == (byte*)Marshal.UnsafeAddrOfPinnedArrayElement((byte[])pThis.bufferHolder[0], 0),
                "Buffer moved during unbound async operation!");
 
            // Release the pin.
            pThis.bufferPtr = null;
            pThis.bufferHolder[0] = OverlappedContext.dummyBuffer;
 
            OverlappedIOCompleteCallback callback = pThis.pendingCallback;
            pThis.pendingCallback = null;
            Fx.Assert(callback != null, "PendingCallback not set. I/O completed multiple times, or cancelled I/O completed.");
 
            callback(false, 0, 0);
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        static void CleanupCallback(object state, bool timedOut)
        {
            OverlappedContext pThis = state as OverlappedContext;
            Fx.Assert(pThis != null, "OverlappedContext.CleanupCallback registered wait doesn't have an OverlappedContext as state.");
 
            if (timedOut)
            {
                Fx.Assert("OverlappedContext.CleanupCallback registered wait timed out.");
 
                // Turn this into a leak.
                return;
            }
 
            Fx.Assert(pThis.bufferPtr == null || pThis.bufferPtr == (byte*)Marshal.UnsafeAddrOfPinnedArrayElement((byte[])pThis.bufferHolder[0], 0),
                "Buffer moved during synchronous deferred cleanup!");
 
            Fx.Assert(pThis.syncOperationPending, "OverlappedContext.CleanupCallback called with no sync operation pending.");
            pThis.pinnedTarget = null;
            pThis.rootedHolder.EventHolder.Close();
            Overlapped.Free(pThis.nativeOverlapped);
        }
 
        // This class is always held onto (rooted) by the packed Overlapped.  The OverlappedContext instance moves itself in and out of
        // this object to root itself.  It's also used to root the ManualResetEvent during sync operations.
        // It needs to be an IAsyncResult since that's what Overlapped takes.
        class RootedHolder : IAsyncResult
        {
            OverlappedContext overlappedBuffer;
            ManualResetEvent eventHolder;
 
 
            public OverlappedContext ThisHolder
            {
                get
                {
                    return this.overlappedBuffer;
                }
 
                set
                {
                    this.overlappedBuffer = value;
                }
            }
 
            public ManualResetEvent EventHolder
            {
                get
                {
                    return this.eventHolder;
                }
 
                set
                {
                    this.eventHolder = value;
                }
            }
 
 
            // Unused IAsyncResult implementation.
            object IAsyncResult.AsyncState
            {
                get
                {
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw Fx.AssertAndThrow("RootedHolder.AsyncState called.");
                }
            }
 
            WaitHandle IAsyncResult.AsyncWaitHandle
            {
                get
                {
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw Fx.AssertAndThrow("RootedHolder.AsyncWaitHandle called.");
                }
            }
 
            bool IAsyncResult.CompletedSynchronously
            {
                get
                {
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw Fx.AssertAndThrow("RootedHolder.CompletedSynchronously called.");
                }
            }
 
            bool IAsyncResult.IsCompleted
            {
                get
                {
#pragma warning suppress 56503 // Microsoft, not a publicly accessible API
                    throw Fx.AssertAndThrow("RootedHolder.IsCompleted called.");
                }
            }
        }
    }
}