BaseAsyncResult.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / BaseAsyncResult.cs / 1305376 / BaseAsyncResult.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
// query object
//  
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client 
{
    using System;
    using System.Diagnostics;
 
#if !ASTORIA_LIGHT // Data.Services http stack
    using System.Net; 
#else 
    using System.Data.Services.Http;
#endif 

    /// 
    /// Implementation of IAsyncResult
    ///  
    internal abstract class BaseAsyncResult : IAsyncResult
    { 
        /// Originating object, used to validate End* 
        internal readonly object Source;
 
        /// Originating method on source, to differentiate between different methods from same source
        internal readonly string Method;

        /// User callback passed to Begin* 
        private readonly AsyncCallback userCallback;
 
        /// User state passed to Begin* 
        private readonly object userState;
 
        /// wait handle for user to wait until done, we only use this within lock of asyncWaitDisposeLock.
        private System.Threading.ManualResetEvent asyncWait;

        /// Holding exception to throw as a nested exception during to End* 
        private Exception failure;
 
        /// Abortable request 
        private WebRequest abortable;
 
        /// true unless something completes asynchronously
        private bool completedSynchronously = true;

        /// true when really completed for the user 
        private bool userCompleted;
 
        /// true when no more changes are pending, 0 false, 1 completed, 2 aborted 
        private int completed;
 
        /// verify we only invoke the user callback once, 0 false, 1 true
        private int userNotified;

        /// non-zero after End*, 0 false, 1, true 
        private int done;
 
        /// true if the AsyncWaitHandle has already been disposed. 
        private bool asyncWaitDisposed;
 
        /// delay created object to lock to prevent using disposed asyncWait handle.
        private object asyncWaitDisposeLock;

        ///  
        /// ctor
        ///  
        /// source object of async request 
        /// async method name on source object
        /// user callback to invoke when complete 
        /// user state
        internal BaseAsyncResult(object source, string method, AsyncCallback callback, object state)
        {
            Debug.Assert(null != source, "null source"); 
            this.Source = source;
            this.Method = method; 
            this.userCallback = callback; 
            this.userState = state;
        } 

#if ASTORIA_LIGHT
        /// 
        /// Generic function delegate that is declared in .Net Framework 4.0, but not .Net Framework 3.5 or Silverlight 
        /// 
        /// type of parameter 1 
        /// type of parameter 2 
        /// type of parameter 3
        /// type of parameter 4 
        /// type of parameter 5
        /// type of result
        /// generic parameter 1
        /// generic parameter 2 
        /// generic parameter 3
        /// generic parameter 4 
        /// generic parameter 5 
        /// generic result
        internal delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); 
#endif

        #region IAsyncResult implmentation - AsyncState, AsyncWaitHandle, CompletedSynchronously, IsCompleted
 
        /// user state object parameter
        public object AsyncState 
        { 
            get { return this.userState; }
        } 

        /// wait handle for when waiting is required
        /// if displayed by debugger, it undesirable to create the WaitHandle
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
        public System.Threading.WaitHandle AsyncWaitHandle
        { 
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "WaitHandle is disposed during EndXXX(IAsyncResult) method")] 
            get
            { 
                if (null == this.asyncWait)
                {   // delay create the wait handle since the user may never use it
                    // like asyncWait which will be GC'd, the losers in creating the asyncWait will also be GC'd
                    System.Threading.Interlocked.CompareExchange(ref this.asyncWait, new System.Threading.ManualResetEvent(this.IsCompleted), null); 

                    // multi-thread condition 
                    // 1) thread 1 returned IAsyncResult and !IsCompleted so AsyncWaitHandle.WaitOne() 
                    // 2) thread 2 signals complete, however thread 1 has retrieved this.completed but not assigned asyncWait
                    if (this.IsCompleted) 
                    {   // yes, Set may be called multiple times - but user would have to assume ManualResetEvent and call Reset
                        //
                        // There is a very small window for race condition between setting the wait handle here and disposing
                        // the wait handle inside EndExecute(). Say thread1 calls EndExecute() and IsCompleted is already true we won't 
                        // create asyncWait from inside thread1, and thread2 wakes up right before thread1 tries to dispose the handle and
                        // thread2 calls AsyncWaitHandle which creates a new asyncWait handle, if thread1 wakes up right before the Set event 
                        // here and disposes the asyncWait handle we just created here, Set() will throw ObjectDisposedException. 
                        // SetAsyncWaitHandle() will protect this scenario with a critical section.
                        this.SetAsyncWaitHandle(); 
                    }
                }

                // Note that if the first time AsyncWaitHandle gets called is after EndExecute() is completed, we don't dispose the 
                // newly created handle, it'll just get GC'd.
                return this.asyncWait; 
            } 
        }
 
        /// did the result complete synchronously?
        public bool CompletedSynchronously
        {
            get { return this.completedSynchronously; } 
            internal set { this.completedSynchronously = value; }
        } 
 
        /// is the result complete?
        public bool IsCompleted 
        {
            get { return this.userCompleted; }
        }
 
        /// is the result complete?
        internal bool IsCompletedInternally 
        { 
            get { return (0 != this.completed); }
        } 

        /// abort the result
        internal bool IsAborted
        { 
            get { return (2 == this.completed); }
        } 
 
        #endregion
 
        /// 
        /// WebRequest available for DataServiceContext.CancelRequest
        /// 
        internal WebRequest Abortable 
        {
            get 
            { 
                return this.abortable;
            } 

            set
            {
                this.abortable = value; 
                if ((null != value) && this.IsAborted)
                {   // if the value hadn't been set yet, but aborting then propogate the abort 
                    value.Abort(); 
                }
            } 
        }

        /// first exception that happened
        internal Exception Failure 
        {
            get { return this.failure; } 
        } 

        ///  
        /// common handler for EndExecuteBatch & EndSaveChanges
        /// 
        /// derived type of the AsyncResult
        /// source object of async request 
        /// async method name on source object
        /// the asyncResult being ended 
        /// data service response for batch 
        internal static T EndExecute(object source, string method, IAsyncResult asyncResult) where T : BaseAsyncResult
        { 
            Util.CheckArgumentNull(asyncResult, "asyncResult");

            T result = (asyncResult as T);
            if ((null == result) || (source != result.Source) || (result.Method != method)) 
            {
                throw Error.Argument(Strings.Context_DidNotOriginateAsync, "asyncResult"); 
            } 

            Debug.Assert((result.CompletedSynchronously && result.IsCompleted) || !result.CompletedSynchronously, "CompletedSynchronously && !IsCompleted"); 

            if (!result.IsCompleted)
            {   // if the user doesn't want to wait forever, they should explictly wait on the handle with a timeout
                result.AsyncWaitHandle.WaitOne(); 

                Debug.Assert(result.IsCompleted, "not completed after waiting"); 
            } 

            // Prevent EndExecute from being called more than once. 
            if (System.Threading.Interlocked.Exchange(ref result.done, 1) != 0)
            {
                throw Error.Argument(Strings.Context_AsyncAlreadyDone, "asyncResult");
            } 

            // Dispose the wait handle. 
            if (null != result.asyncWait) 
            {
                System.Threading.Interlocked.CompareExchange(ref result.asyncWaitDisposeLock, new object(), null); 
                lock (result.asyncWaitDisposeLock)
                {
                    result.asyncWaitDisposed = true;
                    Util.Dispose(result.asyncWait); 
                }
            } 
 
            if (result.IsAborted)
            { 
                throw Error.InvalidOperation(Strings.Context_OperationCanceled);
            }

            if (null != result.Failure) 
            {
                if (Util.IsKnownClientExcption(result.Failure)) 
                { 
                    throw result.Failure;
                } 

                throw Error.InvalidOperation(Strings.DataServiceException_GeneralError, result.Failure);
            }
 
            return result;
        } 
 
        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make 
        /// async calls to their APIs using the specific pattern they've prescribed. This method runs in the caller thread and invokes
        /// the BeginXXX methods.  It then checks IAsyncResult.CompletedSynchronously and if it is true, we invoke the callback in the
        /// caller thread.
        ///  
        /// 
        /// This is the action that invokes the BeginXXX method. Note we MUST use our special callback from GetDataServiceAsyncCallback() 
        /// when invoking the async call. 
        /// 
        /// async callback to be called when the operation is complete 
        /// A user-provided object that distinguishes this particular asynchronous request from other requests.
        /// Returns the async result from the BeginXXX method.
        /// 
        /// CompletedSynchronously (for System.Net networking stack) means "was the operation completed before the first time 
        /// that somebody asked if it was completed synchronously"? They do this because some of their asynchronous operations
        /// (particularly those in the Socket class) will avoid the cost of capturing and transferring the ExecutionContext 
        /// to the callback thread by checking CompletedSynchronously, and calling the callback from within BeginXxx instead of 
        /// on the completion port thread if the native winsock call completes quickly.
        /// 
        /// For other operations however (notably those in HttpWebRequest), they use the same underlying IAsyncResult implementation,
        /// but do NOT check CompletedSynchronously before returning from BeginXxx.  That means that CompletedSynchronously will
        /// be false if and only if you checked it from the thread which called BeginXxx BEFORE the operation completed.  It will
        /// then continue to be false even after IsCompleted becomes true. 
        ///
        /// Note that CompletedSynchronously == true does not guarantee anything about how much of your callback has executed. 
        /// 
        /// The usual pattern for handling synchronous completion is that both the caller and callback should check CompletedSynchronously.
        /// If its true, the callback should do nothing and the caller should call EndRead and process the result. 
        /// This guarantees that the caller and callback are not accessing the stream or buffer concurrently without the need
        /// for explicit synchronization between the two.
        /// 
        internal static IAsyncResult InvokeAsync(Func asyncAction, AsyncCallback callback, object state) 
        {
            IAsyncResult asyncResult = asyncAction(BaseAsyncResult.GetDataServiceAsyncCallback(callback), state); 
            return PostInvokeAsync(asyncResult, callback); 
        }
 
        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make
        /// async calls to their APIs using the specific pattern they've prescribed. This method runs in the caller thread and invokes
        /// the BeginXXX methods.  It then checks IAsyncResult.CompletedSynchronously and if it is true, we invoke the callback in the 
        /// caller thread.
        ///  
        ///  
        /// This is the action that invokes the BeginXXX method. Note we MUST use our special callback from GetDataServiceAsyncCallback()
        /// when invoking the async call. 
        /// 
        /// buffer to transfer the data
        /// byte offset in buffer
        /// max number of bytes in the buffer 
        /// async callback to be called when the operation is complete
        /// A user-provided object that distinguishes this particular asynchronous request from other requests. 
        /// An IAsyncResult that represents the asynchronous operation, which could still be pending 
        /// Please see remarks on the other InvokeAsync() overload.
        internal static IAsyncResult InvokeAsync(Func asyncAction, byte[] buffer, int offset, int length, AsyncCallback callback, object state) 
        {
            IAsyncResult asyncResult = asyncAction(buffer, offset, length, BaseAsyncResult.GetDataServiceAsyncCallback(callback), state);
            return PostInvokeAsync(asyncResult, callback);
        } 

        /// Set the AsyncWait and invoke the user callback. 
        ///  
        /// If the background thread gets a ThreadAbort, the userCallback will never be invoked.
        /// This is why it's generally important to never wait forever, but to have more specific 
        /// time limit.  Also then cancel the operation, to make sure its stopped, to avoid
        /// multi-threading if your wait time limit was just too short.
        /// 
        internal void HandleCompleted() 
        {
            // Dev10 Bug #524145: even if background thread of async operation encounters 
            // an "uncatchable" exception, do the minimum to unblock the async result. 
            if (this.IsCompletedInternally && (System.Threading.Interlocked.Exchange(ref this.userNotified, 1) == 0))
            { 
                this.abortable = null; // reset abort via CancelRequest
                try
                {
                    // avoid additional work when aborting for exceptional reasons 
                    if (!Util.DoNotHandleException(this.Failure))
                    { 
                        // the CompleteRequest may do additional work which is why 
                        // it is important not to signal the user via either the
                        // IAsyncResult.IsCompleted, IAsyncResult.WaitHandle or the callback 
                        this.CompletedRequest();
                    }
                }
                catch (Exception ex) 
                {
                    if (this.HandleFailure(ex)) 
                    { 
                        throw;
                    } 
                }
                finally
                {
                    // 1. set IAsyncResult.IsCompleted, otherwise user was 
                    // signalled on another thread, but the property may not be true.
                    this.userCompleted = true; 
 
                    // 2. signal the wait handle because it can't be first nor can it be last.
                    // 
                    // There is a very small window for race condition between setting the wait handle here and disposing
                    // the wait handle inside EndExecute(). Say thread1 is the async thread that executes up till this point, i.e. right
                    // after userCompleted is set to true and before the asyncWait is signaled; thread2 wakes up and calls EndExecute() till
                    // right before we try to dispose the wait handle; thread3 wakes up and calls AsyncWaitHandle which creates a new instance 
                    // for this.asyncWait; thread2 then resumes to dispose this.asyncWait and if at this point thread1 sets this.asyncWait,
                    // we'll get an ObjectDisposedException on thread1.  SetAsyncWaitHandle() will protect this scenario with a critical section. 
                    this.SetAsyncWaitHandle(); 

                    // 3. invoke the callback because user may throw an exception and stop any further processing 
                    if ((null != this.userCallback) && !(this.Failure is System.Threading.ThreadAbortException) && !(this.Failure is System.StackOverflowException))
                    {   // any exception thrown by user should be "unhandled"
                        // it's possible callback will be invoked while another creates and sets the asyncWait
                        this.userCallback(this); 
                    }
                } 
            } 
        }
 
        /// Cache the exception that happened on the background thread for the caller of EndSaveChanges.
        /// exception object from background thread
        /// true if the exception (like StackOverflow or ThreadAbort) should be rethrown
        internal bool HandleFailure(Exception e) 
        {
            System.Threading.Interlocked.CompareExchange(ref this.failure, e, null); 
            this.SetCompleted(); 
            return Util.DoNotHandleException(e);
        } 

        /// Set the async result as completed and aborted.
        internal void SetAborted()
        { 
            System.Threading.Interlocked.Exchange(ref this.completed, 2);
        } 
 
        /// Set the async result as completed.
        internal void SetCompleted() 
        {
            System.Threading.Interlocked.CompareExchange(ref this.completed, 1, 0);
        }
 
        /// invoked for derived classes to cleanup before callback is invoked
        protected abstract void CompletedRequest(); 
 
        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make 
        /// async calls to their APIs using the specific pattern they've prescribed. This method runs in the caller thread after the
        /// BeginXXX method returns.  It checks IAsyncResult.CompletedSynchronously and if it is true, we invoke the callback in the
        /// caller thread.
        ///  
        /// The IAsyncResult that represents the asynchronous operation we just called, which could still be pending
        /// Callback to be invoked when IAsyncResult.CompletedSynchronously is true. 
        /// Returns an IAsyncResult that represents the asynchronous operation we just called, which could still be pending 
        /// Please see remarks on BaseAsyncResult.InvokeAsync().
        private static IAsyncResult PostInvokeAsync(IAsyncResult asyncResult, AsyncCallback callback) 
        {
            Debug.Assert(asyncResult != null, "asyncResult != null");
            if (asyncResult.CompletedSynchronously)
            { 
                Debug.Assert(asyncResult.IsCompleted, "asyncResult.IsCompleted");
                callback(asyncResult); 
            } 

            return asyncResult; 
        }

        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make 
        /// async calls to their APIs using the specific pattern they've prescribed. This method returns an AsyncCallback which we can pass
        /// to the BeginXXX methods in the caller thread.  The returned callback will only run the wrapped callback if 
        /// IAsyncResult.CompletedSynchronously is false, otherwise it returns immediately. 
        /// 
        /// callback to be wrapped 
        /// Returnes a callback which will only run the wrapped callback if IAsyncResult.CompletedSynchronously is false, otherwise it returns immediately.
        /// Please see remarks on BaseAsyncResult.InvokeAsync().
        private static AsyncCallback GetDataServiceAsyncCallback(AsyncCallback callback)
        { 
            return (asyncResult) =>
            { 
                Debug.Assert(asyncResult != null && asyncResult.IsCompleted, "asyncResult != null && asyncResult.IsCompleted"); 
                if (asyncResult.CompletedSynchronously)
                { 
                    return;
                }

                callback(asyncResult); 
            };
        } 
 
        /// 
        /// Sets the async wait handle 
        /// 
        private void SetAsyncWaitHandle()
        {
            if (null != this.asyncWait) 
            {
                System.Threading.Interlocked.CompareExchange(ref this.asyncWaitDisposeLock, new object(), null); 
                lock (this.asyncWaitDisposeLock) 
                {
                    if (!this.asyncWaitDisposed) 
                    {
                        this.asyncWait.Set();
                    }
                } 
            }
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
// query object
//  
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client 
{
    using System;
    using System.Diagnostics;
 
#if !ASTORIA_LIGHT // Data.Services http stack
    using System.Net; 
#else 
    using System.Data.Services.Http;
#endif 

    /// 
    /// Implementation of IAsyncResult
    ///  
    internal abstract class BaseAsyncResult : IAsyncResult
    { 
        /// Originating object, used to validate End* 
        internal readonly object Source;
 
        /// Originating method on source, to differentiate between different methods from same source
        internal readonly string Method;

        /// User callback passed to Begin* 
        private readonly AsyncCallback userCallback;
 
        /// User state passed to Begin* 
        private readonly object userState;
 
        /// wait handle for user to wait until done, we only use this within lock of asyncWaitDisposeLock.
        private System.Threading.ManualResetEvent asyncWait;

        /// Holding exception to throw as a nested exception during to End* 
        private Exception failure;
 
        /// Abortable request 
        private WebRequest abortable;
 
        /// true unless something completes asynchronously
        private bool completedSynchronously = true;

        /// true when really completed for the user 
        private bool userCompleted;
 
        /// true when no more changes are pending, 0 false, 1 completed, 2 aborted 
        private int completed;
 
        /// verify we only invoke the user callback once, 0 false, 1 true
        private int userNotified;

        /// non-zero after End*, 0 false, 1, true 
        private int done;
 
        /// true if the AsyncWaitHandle has already been disposed. 
        private bool asyncWaitDisposed;
 
        /// delay created object to lock to prevent using disposed asyncWait handle.
        private object asyncWaitDisposeLock;

        ///  
        /// ctor
        ///  
        /// source object of async request 
        /// async method name on source object
        /// user callback to invoke when complete 
        /// user state
        internal BaseAsyncResult(object source, string method, AsyncCallback callback, object state)
        {
            Debug.Assert(null != source, "null source"); 
            this.Source = source;
            this.Method = method; 
            this.userCallback = callback; 
            this.userState = state;
        } 

#if ASTORIA_LIGHT
        /// 
        /// Generic function delegate that is declared in .Net Framework 4.0, but not .Net Framework 3.5 or Silverlight 
        /// 
        /// type of parameter 1 
        /// type of parameter 2 
        /// type of parameter 3
        /// type of parameter 4 
        /// type of parameter 5
        /// type of result
        /// generic parameter 1
        /// generic parameter 2 
        /// generic parameter 3
        /// generic parameter 4 
        /// generic parameter 5 
        /// generic result
        internal delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); 
#endif

        #region IAsyncResult implmentation - AsyncState, AsyncWaitHandle, CompletedSynchronously, IsCompleted
 
        /// user state object parameter
        public object AsyncState 
        { 
            get { return this.userState; }
        } 

        /// wait handle for when waiting is required
        /// if displayed by debugger, it undesirable to create the WaitHandle
        [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
        public System.Threading.WaitHandle AsyncWaitHandle
        { 
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "WaitHandle is disposed during EndXXX(IAsyncResult) method")] 
            get
            { 
                if (null == this.asyncWait)
                {   // delay create the wait handle since the user may never use it
                    // like asyncWait which will be GC'd, the losers in creating the asyncWait will also be GC'd
                    System.Threading.Interlocked.CompareExchange(ref this.asyncWait, new System.Threading.ManualResetEvent(this.IsCompleted), null); 

                    // multi-thread condition 
                    // 1) thread 1 returned IAsyncResult and !IsCompleted so AsyncWaitHandle.WaitOne() 
                    // 2) thread 2 signals complete, however thread 1 has retrieved this.completed but not assigned asyncWait
                    if (this.IsCompleted) 
                    {   // yes, Set may be called multiple times - but user would have to assume ManualResetEvent and call Reset
                        //
                        // There is a very small window for race condition between setting the wait handle here and disposing
                        // the wait handle inside EndExecute(). Say thread1 calls EndExecute() and IsCompleted is already true we won't 
                        // create asyncWait from inside thread1, and thread2 wakes up right before thread1 tries to dispose the handle and
                        // thread2 calls AsyncWaitHandle which creates a new asyncWait handle, if thread1 wakes up right before the Set event 
                        // here and disposes the asyncWait handle we just created here, Set() will throw ObjectDisposedException. 
                        // SetAsyncWaitHandle() will protect this scenario with a critical section.
                        this.SetAsyncWaitHandle(); 
                    }
                }

                // Note that if the first time AsyncWaitHandle gets called is after EndExecute() is completed, we don't dispose the 
                // newly created handle, it'll just get GC'd.
                return this.asyncWait; 
            } 
        }
 
        /// did the result complete synchronously?
        public bool CompletedSynchronously
        {
            get { return this.completedSynchronously; } 
            internal set { this.completedSynchronously = value; }
        } 
 
        /// is the result complete?
        public bool IsCompleted 
        {
            get { return this.userCompleted; }
        }
 
        /// is the result complete?
        internal bool IsCompletedInternally 
        { 
            get { return (0 != this.completed); }
        } 

        /// abort the result
        internal bool IsAborted
        { 
            get { return (2 == this.completed); }
        } 
 
        #endregion
 
        /// 
        /// WebRequest available for DataServiceContext.CancelRequest
        /// 
        internal WebRequest Abortable 
        {
            get 
            { 
                return this.abortable;
            } 

            set
            {
                this.abortable = value; 
                if ((null != value) && this.IsAborted)
                {   // if the value hadn't been set yet, but aborting then propogate the abort 
                    value.Abort(); 
                }
            } 
        }

        /// first exception that happened
        internal Exception Failure 
        {
            get { return this.failure; } 
        } 

        ///  
        /// common handler for EndExecuteBatch & EndSaveChanges
        /// 
        /// derived type of the AsyncResult
        /// source object of async request 
        /// async method name on source object
        /// the asyncResult being ended 
        /// data service response for batch 
        internal static T EndExecute(object source, string method, IAsyncResult asyncResult) where T : BaseAsyncResult
        { 
            Util.CheckArgumentNull(asyncResult, "asyncResult");

            T result = (asyncResult as T);
            if ((null == result) || (source != result.Source) || (result.Method != method)) 
            {
                throw Error.Argument(Strings.Context_DidNotOriginateAsync, "asyncResult"); 
            } 

            Debug.Assert((result.CompletedSynchronously && result.IsCompleted) || !result.CompletedSynchronously, "CompletedSynchronously && !IsCompleted"); 

            if (!result.IsCompleted)
            {   // if the user doesn't want to wait forever, they should explictly wait on the handle with a timeout
                result.AsyncWaitHandle.WaitOne(); 

                Debug.Assert(result.IsCompleted, "not completed after waiting"); 
            } 

            // Prevent EndExecute from being called more than once. 
            if (System.Threading.Interlocked.Exchange(ref result.done, 1) != 0)
            {
                throw Error.Argument(Strings.Context_AsyncAlreadyDone, "asyncResult");
            } 

            // Dispose the wait handle. 
            if (null != result.asyncWait) 
            {
                System.Threading.Interlocked.CompareExchange(ref result.asyncWaitDisposeLock, new object(), null); 
                lock (result.asyncWaitDisposeLock)
                {
                    result.asyncWaitDisposed = true;
                    Util.Dispose(result.asyncWait); 
                }
            } 
 
            if (result.IsAborted)
            { 
                throw Error.InvalidOperation(Strings.Context_OperationCanceled);
            }

            if (null != result.Failure) 
            {
                if (Util.IsKnownClientExcption(result.Failure)) 
                { 
                    throw result.Failure;
                } 

                throw Error.InvalidOperation(Strings.DataServiceException_GeneralError, result.Failure);
            }
 
            return result;
        } 
 
        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make 
        /// async calls to their APIs using the specific pattern they've prescribed. This method runs in the caller thread and invokes
        /// the BeginXXX methods.  It then checks IAsyncResult.CompletedSynchronously and if it is true, we invoke the callback in the
        /// caller thread.
        ///  
        /// 
        /// This is the action that invokes the BeginXXX method. Note we MUST use our special callback from GetDataServiceAsyncCallback() 
        /// when invoking the async call. 
        /// 
        /// async callback to be called when the operation is complete 
        /// A user-provided object that distinguishes this particular asynchronous request from other requests.
        /// Returns the async result from the BeginXXX method.
        /// 
        /// CompletedSynchronously (for System.Net networking stack) means "was the operation completed before the first time 
        /// that somebody asked if it was completed synchronously"? They do this because some of their asynchronous operations
        /// (particularly those in the Socket class) will avoid the cost of capturing and transferring the ExecutionContext 
        /// to the callback thread by checking CompletedSynchronously, and calling the callback from within BeginXxx instead of 
        /// on the completion port thread if the native winsock call completes quickly.
        /// 
        /// For other operations however (notably those in HttpWebRequest), they use the same underlying IAsyncResult implementation,
        /// but do NOT check CompletedSynchronously before returning from BeginXxx.  That means that CompletedSynchronously will
        /// be false if and only if you checked it from the thread which called BeginXxx BEFORE the operation completed.  It will
        /// then continue to be false even after IsCompleted becomes true. 
        ///
        /// Note that CompletedSynchronously == true does not guarantee anything about how much of your callback has executed. 
        /// 
        /// The usual pattern for handling synchronous completion is that both the caller and callback should check CompletedSynchronously.
        /// If its true, the callback should do nothing and the caller should call EndRead and process the result. 
        /// This guarantees that the caller and callback are not accessing the stream or buffer concurrently without the need
        /// for explicit synchronization between the two.
        /// 
        internal static IAsyncResult InvokeAsync(Func asyncAction, AsyncCallback callback, object state) 
        {
            IAsyncResult asyncResult = asyncAction(BaseAsyncResult.GetDataServiceAsyncCallback(callback), state); 
            return PostInvokeAsync(asyncResult, callback); 
        }
 
        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make
        /// async calls to their APIs using the specific pattern they've prescribed. This method runs in the caller thread and invokes
        /// the BeginXXX methods.  It then checks IAsyncResult.CompletedSynchronously and if it is true, we invoke the callback in the 
        /// caller thread.
        ///  
        ///  
        /// This is the action that invokes the BeginXXX method. Note we MUST use our special callback from GetDataServiceAsyncCallback()
        /// when invoking the async call. 
        /// 
        /// buffer to transfer the data
        /// byte offset in buffer
        /// max number of bytes in the buffer 
        /// async callback to be called when the operation is complete
        /// A user-provided object that distinguishes this particular asynchronous request from other requests. 
        /// An IAsyncResult that represents the asynchronous operation, which could still be pending 
        /// Please see remarks on the other InvokeAsync() overload.
        internal static IAsyncResult InvokeAsync(Func asyncAction, byte[] buffer, int offset, int length, AsyncCallback callback, object state) 
        {
            IAsyncResult asyncResult = asyncAction(buffer, offset, length, BaseAsyncResult.GetDataServiceAsyncCallback(callback), state);
            return PostInvokeAsync(asyncResult, callback);
        } 

        /// Set the AsyncWait and invoke the user callback. 
        ///  
        /// If the background thread gets a ThreadAbort, the userCallback will never be invoked.
        /// This is why it's generally important to never wait forever, but to have more specific 
        /// time limit.  Also then cancel the operation, to make sure its stopped, to avoid
        /// multi-threading if your wait time limit was just too short.
        /// 
        internal void HandleCompleted() 
        {
            // Dev10 Bug #524145: even if background thread of async operation encounters 
            // an "uncatchable" exception, do the minimum to unblock the async result. 
            if (this.IsCompletedInternally && (System.Threading.Interlocked.Exchange(ref this.userNotified, 1) == 0))
            { 
                this.abortable = null; // reset abort via CancelRequest
                try
                {
                    // avoid additional work when aborting for exceptional reasons 
                    if (!Util.DoNotHandleException(this.Failure))
                    { 
                        // the CompleteRequest may do additional work which is why 
                        // it is important not to signal the user via either the
                        // IAsyncResult.IsCompleted, IAsyncResult.WaitHandle or the callback 
                        this.CompletedRequest();
                    }
                }
                catch (Exception ex) 
                {
                    if (this.HandleFailure(ex)) 
                    { 
                        throw;
                    } 
                }
                finally
                {
                    // 1. set IAsyncResult.IsCompleted, otherwise user was 
                    // signalled on another thread, but the property may not be true.
                    this.userCompleted = true; 
 
                    // 2. signal the wait handle because it can't be first nor can it be last.
                    // 
                    // There is a very small window for race condition between setting the wait handle here and disposing
                    // the wait handle inside EndExecute(). Say thread1 is the async thread that executes up till this point, i.e. right
                    // after userCompleted is set to true and before the asyncWait is signaled; thread2 wakes up and calls EndExecute() till
                    // right before we try to dispose the wait handle; thread3 wakes up and calls AsyncWaitHandle which creates a new instance 
                    // for this.asyncWait; thread2 then resumes to dispose this.asyncWait and if at this point thread1 sets this.asyncWait,
                    // we'll get an ObjectDisposedException on thread1.  SetAsyncWaitHandle() will protect this scenario with a critical section. 
                    this.SetAsyncWaitHandle(); 

                    // 3. invoke the callback because user may throw an exception and stop any further processing 
                    if ((null != this.userCallback) && !(this.Failure is System.Threading.ThreadAbortException) && !(this.Failure is System.StackOverflowException))
                    {   // any exception thrown by user should be "unhandled"
                        // it's possible callback will be invoked while another creates and sets the asyncWait
                        this.userCallback(this); 
                    }
                } 
            } 
        }
 
        /// Cache the exception that happened on the background thread for the caller of EndSaveChanges.
        /// exception object from background thread
        /// true if the exception (like StackOverflow or ThreadAbort) should be rethrown
        internal bool HandleFailure(Exception e) 
        {
            System.Threading.Interlocked.CompareExchange(ref this.failure, e, null); 
            this.SetCompleted(); 
            return Util.DoNotHandleException(e);
        } 

        /// Set the async result as completed and aborted.
        internal void SetAborted()
        { 
            System.Threading.Interlocked.Exchange(ref this.completed, 2);
        } 
 
        /// Set the async result as completed.
        internal void SetCompleted() 
        {
            System.Threading.Interlocked.CompareExchange(ref this.completed, 1, 0);
        }
 
        /// invoked for derived classes to cleanup before callback is invoked
        protected abstract void CompletedRequest(); 
 
        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make 
        /// async calls to their APIs using the specific pattern they've prescribed. This method runs in the caller thread after the
        /// BeginXXX method returns.  It checks IAsyncResult.CompletedSynchronously and if it is true, we invoke the callback in the
        /// caller thread.
        ///  
        /// The IAsyncResult that represents the asynchronous operation we just called, which could still be pending
        /// Callback to be invoked when IAsyncResult.CompletedSynchronously is true. 
        /// Returns an IAsyncResult that represents the asynchronous operation we just called, which could still be pending 
        /// Please see remarks on BaseAsyncResult.InvokeAsync().
        private static IAsyncResult PostInvokeAsync(IAsyncResult asyncResult, AsyncCallback callback) 
        {
            Debug.Assert(asyncResult != null, "asyncResult != null");
            if (asyncResult.CompletedSynchronously)
            { 
                Debug.Assert(asyncResult.IsCompleted, "asyncResult.IsCompleted");
                callback(asyncResult); 
            } 

            return asyncResult; 
        }

        /// 
        /// Due to the unexpected behaviors of IAsyncResult.CompletedSynchronously in the System.Net networking stack, we have to make 
        /// async calls to their APIs using the specific pattern they've prescribed. This method returns an AsyncCallback which we can pass
        /// to the BeginXXX methods in the caller thread.  The returned callback will only run the wrapped callback if 
        /// IAsyncResult.CompletedSynchronously is false, otherwise it returns immediately. 
        /// 
        /// callback to be wrapped 
        /// Returnes a callback which will only run the wrapped callback if IAsyncResult.CompletedSynchronously is false, otherwise it returns immediately.
        /// Please see remarks on BaseAsyncResult.InvokeAsync().
        private static AsyncCallback GetDataServiceAsyncCallback(AsyncCallback callback)
        { 
            return (asyncResult) =>
            { 
                Debug.Assert(asyncResult != null && asyncResult.IsCompleted, "asyncResult != null && asyncResult.IsCompleted"); 
                if (asyncResult.CompletedSynchronously)
                { 
                    return;
                }

                callback(asyncResult); 
            };
        } 
 
        /// 
        /// Sets the async wait handle 
        /// 
        private void SetAsyncWaitHandle()
        {
            if (null != this.asyncWait) 
            {
                System.Threading.Interlocked.CompareExchange(ref this.asyncWaitDisposeLock, new object(), null); 
                lock (this.asyncWaitDisposeLock) 
                {
                    if (!this.asyncWaitDisposed) 
                    {
                        this.asyncWait.Set();
                    }
                } 
            }
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK