1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Runtime
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Threading;
12 // AsyncResult starts acquired; Complete releases.
13 [Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.ManualResetEvent, SupportsAsync = true, ReleaseMethod = "Complete")]
14 abstract class AsyncResult : IAsyncResult
16 static AsyncCallback asyncCompletionWrapperCallback;
17 AsyncCallback callback;
18 bool completedSynchronously;
22 AsyncCompletion nextAsyncCompletion;
24 Action beforePrepareAsyncCompletionAction;
25 Func<IAsyncResult, bool> checkSyncValidationFunc;
27 [Fx.Tag.SynchronizationObject]
28 ManualResetEvent manualResetEvent;
30 [Fx.Tag.SynchronizationObject(Blocking = false)]
35 StackTrace completeStack;
36 UncompletedAsyncResultMarker marker;
39 protected AsyncResult(AsyncCallback callback, object state)
41 this.callback = callback;
43 this.thisLock = new object();
46 this.marker = new UncompletedAsyncResultMarker(this);
50 public object AsyncState
58 public WaitHandle AsyncWaitHandle
62 if (manualResetEvent != null)
64 return manualResetEvent;
69 if (manualResetEvent == null)
71 manualResetEvent = new ManualResetEvent(isCompleted);
75 return manualResetEvent;
79 public bool CompletedSynchronously
83 return completedSynchronously;
87 public bool HasCallback
91 return this.callback != null;
95 public bool IsCompleted
103 // used in conjunction with PrepareAsyncCompletion to allow for finally blocks
104 protected Action<AsyncResult, Exception> OnCompleting { get; set; }
110 return this.thisLock;
114 // subclasses like TraceAsyncResult can use this to wrap the callback functionality in a scope
115 protected Action<AsyncCallback, IAsyncResult> VirtualCallback
121 protected void Complete(bool completedSynchronously)
123 if (this.isCompleted)
125 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultCompletedTwice(GetType())));
129 this.marker.AsyncResult = null;
131 if (!Fx.FastDebug && completeStack == null)
133 completeStack = new StackTrace();
137 this.completedSynchronously = completedSynchronously;
138 if (OnCompleting != null)
140 // Allow exception replacement, like a catch/throw pattern.
143 OnCompleting(this, this.exception);
145 catch (Exception exception)
147 if (Fx.IsFatal(exception))
151 this.exception = exception;
155 if (completedSynchronously)
157 // If we completedSynchronously, then there's no chance that the manualResetEvent was created so
158 // we don't need to worry about a ----
159 Fx.Assert(this.manualResetEvent == null, "No ManualResetEvent should be created for a synchronous AsyncResult.");
160 this.isCompleted = true;
166 this.isCompleted = true;
167 if (this.manualResetEvent != null)
169 this.manualResetEvent.Set();
174 if (this.callback != null)
178 if (VirtualCallback != null)
180 VirtualCallback(this.callback, this);
187 #pragma warning disable 1634
188 #pragma warning suppress 56500 // transferring exception to another thread
196 throw Fx.Exception.AsError(new CallbackException(InternalSR.AsyncCallbackThrewException, e));
198 #pragma warning restore 1634
202 protected void Complete(bool completedSynchronously, Exception exception)
204 this.exception = exception;
205 Complete(completedSynchronously);
208 static void AsyncCompletionWrapperCallback(IAsyncResult result)
212 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult));
214 if (result.CompletedSynchronously)
219 AsyncResult thisPtr = (AsyncResult)result.AsyncState;
220 if (!thisPtr.OnContinueAsyncCompletion(result))
225 AsyncCompletion callback = thisPtr.GetNextCompletion();
226 if (callback == null)
228 ThrowInvalidAsyncResult(result);
231 bool completeSelf = false;
232 Exception completionException = null;
235 completeSelf = callback(result);
244 completionException = e;
249 thisPtr.Complete(false, completionException);
253 // Note: this should be only derived by the TransactedAsyncResult
254 protected virtual bool OnContinueAsyncCompletion(IAsyncResult result)
259 // Note: this should be used only by the TransactedAsyncResult
260 protected void SetBeforePrepareAsyncCompletionAction(Action beforePrepareAsyncCompletionAction)
262 this.beforePrepareAsyncCompletionAction = beforePrepareAsyncCompletionAction;
265 // Note: this should be used only by the TransactedAsyncResult
266 protected void SetCheckSyncValidationFunc(Func<IAsyncResult, bool> checkSyncValidationFunc)
268 this.checkSyncValidationFunc = checkSyncValidationFunc;
271 protected AsyncCallback PrepareAsyncCompletion(AsyncCompletion callback)
273 if (this.beforePrepareAsyncCompletionAction != null)
275 this.beforePrepareAsyncCompletionAction();
278 this.nextAsyncCompletion = callback;
279 if (AsyncResult.asyncCompletionWrapperCallback == null)
281 AsyncResult.asyncCompletionWrapperCallback = Fx.ThunkCallback(new AsyncCallback(AsyncCompletionWrapperCallback));
283 return AsyncResult.asyncCompletionWrapperCallback;
286 protected bool CheckSyncContinue(IAsyncResult result)
288 AsyncCompletion dummy;
289 return TryContinueHelper(result, out dummy);
292 protected bool SyncContinue(IAsyncResult result)
294 AsyncCompletion callback;
295 if (TryContinueHelper(result, out callback))
297 return callback(result);
305 bool TryContinueHelper(IAsyncResult result, out AsyncCompletion callback)
309 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult));
313 if (this.checkSyncValidationFunc != null)
315 if (!this.checkSyncValidationFunc(result))
320 else if (!result.CompletedSynchronously)
325 callback = GetNextCompletion();
326 if (callback == null)
328 ThrowInvalidAsyncResult("Only call Check/SyncContinue once per async operation (once per PrepareAsyncCompletion).");
333 AsyncCompletion GetNextCompletion()
335 AsyncCompletion result = this.nextAsyncCompletion;
336 this.nextAsyncCompletion = null;
340 protected static void ThrowInvalidAsyncResult(IAsyncResult result)
342 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidAsyncResultImplementation(result.GetType())));
345 protected static void ThrowInvalidAsyncResult(string debugText)
347 string message = InternalSR.InvalidAsyncResultImplementationGeneric;
348 if (debugText != null)
351 message += " " + debugText;
354 throw Fx.Exception.AsError(new InvalidOperationException(message));
357 [Fx.Tag.Blocking(Conditional = "!asyncResult.isCompleted")]
358 protected static TAsyncResult End<TAsyncResult>(IAsyncResult result)
359 where TAsyncResult : AsyncResult
363 throw Fx.Exception.ArgumentNull("result");
366 TAsyncResult asyncResult = result as TAsyncResult;
368 if (asyncResult == null)
370 throw Fx.Exception.Argument("result", InternalSR.InvalidAsyncResult);
373 if (asyncResult.endCalled)
375 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultAlreadyEnded));
379 if (!Fx.FastDebug && asyncResult.endStack == null)
381 asyncResult.endStack = new StackTrace();
385 asyncResult.endCalled = true;
387 if (!asyncResult.isCompleted)
389 asyncResult.AsyncWaitHandle.WaitOne();
392 if (asyncResult.manualResetEvent != null)
394 asyncResult.manualResetEvent.Close();
397 if (asyncResult.exception != null)
399 throw Fx.Exception.AsError(asyncResult.exception);
405 // can be utilized by subclasses to write core completion code for both the [....] and async paths
406 // in one location, signalling chainable synchronous completion with the boolean result,
407 // and leveraging PrepareAsyncCompletion for conversion to an AsyncCallback.
408 // NOTE: requires that "this" is passed in as the state object to the asynchronous sub-call being used with a completion routine.
409 protected delegate bool AsyncCompletion(IAsyncResult result);
412 class UncompletedAsyncResultMarker
414 public UncompletedAsyncResultMarker(AsyncResult result)
416 AsyncResult = result;
419 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode,
420 Justification = "Debug-only facility")]
421 public AsyncResult AsyncResult { get; set; }