Code:
/ 4.0 / 4.0 / untmp / 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.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Misc.cs
- WmlTextViewAdapter.cs
- COM2IDispatchConverter.cs
- EntityContainerEntitySet.cs
- objectresult_tresulttype.cs
- ListBoxItemAutomationPeer.cs
- Model3D.cs
- KerberosReceiverSecurityToken.cs
- DbException.cs
- SendSecurityHeaderElement.cs
- ReferencedCollectionType.cs
- MetadataUtilsSmi.cs
- XmlIterators.cs
- SchemaExporter.cs
- AttributeCollection.cs
- MinMaxParagraphWidth.cs
- QilScopedVisitor.cs
- XmlDocumentFragment.cs
- XpsViewerException.cs
- CompressedStack.cs
- VirtualDirectoryMapping.cs
- OSEnvironmentHelper.cs
- BamlLocalizabilityResolver.cs
- Simplifier.cs
- ComPlusTypeValidator.cs
- PageRequestManager.cs
- CopyOfAction.cs
- RedistVersionInfo.cs
- EventWaitHandle.cs
- WaitForChangedResult.cs
- AstTree.cs
- Inflater.cs
- BaseDataList.cs
- TypeSystemProvider.cs
- WebPermission.cs
- DataGridItemEventArgs.cs
- ExpressionVisitor.cs
- WebConfigurationFileMap.cs
- HtmlUtf8RawTextWriter.cs
- AdapterUtil.cs
- WebAdminConfigurationHelper.cs
- HMAC.cs
- ArrayList.cs
- SEHException.cs
- CheckBoxField.cs
- D3DImage.cs
- EdmSchemaAttribute.cs
- Rule.cs
- TextStore.cs
- _RegBlobWebProxyDataBuilder.cs
- XmlEnumAttribute.cs
- Soap11ServerProtocol.cs
- EventMetadata.cs
- DesignerGeometryHelper.cs
- HttpServerVarsCollection.cs
- FamilyMapCollection.cs
- AnimatedTypeHelpers.cs
- CheckableControlBaseAdapter.cs
- FilterElement.cs
- SeparatorAutomationPeer.cs
- DefaultValueTypeConverter.cs
- VisualProxy.cs
- ObjectStateEntryBaseUpdatableDataRecord.cs
- ProfileGroupSettingsCollection.cs
- DispatcherHooks.cs
- UriSection.cs
- ClientCultureInfo.cs
- WindowsFormsHostAutomationPeer.cs
- EnumValidator.cs
- WebServiceHostFactory.cs
- SolidColorBrush.cs
- BoundColumn.cs
- OleDbRowUpdatingEvent.cs
- DataChangedEventManager.cs
- StructuralObject.cs
- XmlConverter.cs
- HostedElements.cs
- WindowsListViewGroupHelper.cs
- NavigationWindowAutomationPeer.cs
- TextBoxAutomationPeer.cs
- XPathArrayIterator.cs
- AsyncPostBackErrorEventArgs.cs
- ConnectionStringsExpressionBuilder.cs
- RuleSettings.cs
- LOSFormatter.cs
- MsmqInputChannelListener.cs
- GenericXmlSecurityToken.cs
- isolationinterop.cs
- SqlClientMetaDataCollectionNames.cs
- XmlTextReader.cs
- SafeArrayRankMismatchException.cs
- TimeSpanMinutesConverter.cs
- CompiledXpathExpr.cs
- SortFieldComparer.cs
- DrawingContextDrawingContextWalker.cs
- RegexStringValidatorAttribute.cs
- CellParaClient.cs
- WSTrustDec2005.cs
- SizeConverter.cs
- TimeZone.cs