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(FuncasyncAction, 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(FuncasyncAction, 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(FuncasyncAction, 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(FuncasyncAction, 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- InputDevice.cs
- TextRenderingModeValidation.cs
- CultureSpecificStringDictionary.cs
- Matrix3D.cs
- PropertyMapper.cs
- BatchParser.cs
- dataprotectionpermissionattribute.cs
- OdbcConnectionPoolProviderInfo.cs
- DecimalMinMaxAggregationOperator.cs
- CompilerState.cs
- DeclarationUpdate.cs
- Button.cs
- ClientRoleProvider.cs
- PropertyIDSet.cs
- DragAssistanceManager.cs
- Message.cs
- DtcInterfaces.cs
- Point3DCollection.cs
- CalendarButtonAutomationPeer.cs
- DataGridCellEditEndingEventArgs.cs
- codemethodreferenceexpression.cs
- NameNode.cs
- TreeViewImageGenerator.cs
- BitmapEffectGroup.cs
- SystemWebCachingSectionGroup.cs
- CustomError.cs
- UpDownBase.cs
- DropShadowBitmapEffect.cs
- SubpageParagraph.cs
- ToolStripSplitButton.cs
- UnsafeNativeMethods.cs
- SqlCachedBuffer.cs
- SecurityTokenAuthenticator.cs
- FrameworkContextData.cs
- StorageMappingItemCollection.cs
- DBSchemaRow.cs
- FontCollection.cs
- UpdatePanelControlTrigger.cs
- EntityAdapter.cs
- SourceLineInfo.cs
- FormatSettings.cs
- NullToBooleanConverter.cs
- Marshal.cs
- ObjectConverter.cs
- ReadOnlyTernaryTree.cs
- ContractTypeNameElement.cs
- NumericUpDownAcceleration.cs
- ZoomingMessageFilter.cs
- HttpHostedTransportConfiguration.cs
- AttributeCollection.cs
- XMLUtil.cs
- DbConnectionFactory.cs
- NamespaceEmitter.cs
- InfoCard.cs
- UnhandledExceptionEventArgs.cs
- AuthenticationSection.cs
- SimpleNameService.cs
- DataGridViewTextBoxEditingControl.cs
- UInt32Storage.cs
- XmlEntity.cs
- securestring.cs
- basenumberconverter.cs
- X509CertificateChain.cs
- XmlSchemaSimpleContent.cs
- NetworkInformationPermission.cs
- Italic.cs
- Classification.cs
- ConnectionConsumerAttribute.cs
- ExpressionVisitor.cs
- ExtenderProvidedPropertyAttribute.cs
- MutexSecurity.cs
- XmlSchemaSimpleType.cs
- parserscommon.cs
- PDBReader.cs
- KeyNotFoundException.cs
- RadialGradientBrush.cs
- CompoundFileReference.cs
- CodeTypeReferenceExpression.cs
- RightsManagementPermission.cs
- CounterSampleCalculator.cs
- FindProgressChangedEventArgs.cs
- ListViewSelectEventArgs.cs
- VectorConverter.cs
- FileEnumerator.cs
- TypeReference.cs
- KerberosTicketHashIdentifierClause.cs
- DataGridViewCellParsingEventArgs.cs
- DoWhileDesigner.xaml.cs
- DataGridViewColumnStateChangedEventArgs.cs
- TreeNodeCollection.cs
- DocumentAutomationPeer.cs
- IgnoreSectionHandler.cs
- HttpFileCollection.cs
- ProviderConnectionPoint.cs
- HttpDictionary.cs
- DllNotFoundException.cs
- parserscommon.cs
- WindowsGraphicsWrapper.cs
- EntityCommandDefinition.cs
- SyndicationDeserializer.cs