Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.ServiceModel.Internals / System / Runtime / AsyncResult.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Runtime
6 {
7     using System;
8     using System.Diagnostics;
9     using System.Diagnostics.CodeAnalysis;
10     using System.Threading;
11
12     // AsyncResult starts acquired; Complete releases.
13     [Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.ManualResetEvent, SupportsAsync = true, ReleaseMethod = "Complete")]
14     abstract class AsyncResult : IAsyncResult
15     {
16         static AsyncCallback asyncCompletionWrapperCallback;
17         AsyncCallback callback;
18         bool completedSynchronously;
19         bool endCalled;
20         Exception exception;
21         bool isCompleted;
22         AsyncCompletion nextAsyncCompletion;
23         object state;
24         Action beforePrepareAsyncCompletionAction;
25         Func<IAsyncResult, bool> checkSyncValidationFunc;
26
27         [Fx.Tag.SynchronizationObject]
28         ManualResetEvent manualResetEvent;
29
30         [Fx.Tag.SynchronizationObject(Blocking = false)]
31         object thisLock;
32
33 #if DEBUG
34         StackTrace endStack;
35         StackTrace completeStack;
36         UncompletedAsyncResultMarker marker;
37 #endif
38
39         protected AsyncResult(AsyncCallback callback, object state)
40         {
41             this.callback = callback;
42             this.state = state;
43             this.thisLock = new object();
44
45 #if DEBUG
46             this.marker = new UncompletedAsyncResultMarker(this);
47 #endif
48         }
49
50         public object AsyncState
51         {
52             get
53             {
54                 return state;
55             }
56         }
57
58         public WaitHandle AsyncWaitHandle
59         {
60             get
61             {
62                 if (manualResetEvent != null)
63                 {
64                     return manualResetEvent;
65                 }
66
67                 lock (ThisLock)
68                 {
69                     if (manualResetEvent == null)
70                     {
71                         manualResetEvent = new ManualResetEvent(isCompleted);
72                     }
73                 }
74
75                 return manualResetEvent;
76             }
77         }
78
79         public bool CompletedSynchronously
80         {
81             get
82             {
83                 return completedSynchronously;
84             }
85         }
86
87         public bool HasCallback
88         {
89             get
90             {
91                 return this.callback != null;
92             }
93         }
94
95         public bool IsCompleted
96         {
97             get
98             {
99                 return isCompleted;
100             }
101         }
102
103         // used in conjunction with PrepareAsyncCompletion to allow for finally blocks
104         protected Action<AsyncResult, Exception> OnCompleting { get; set; }
105
106         object ThisLock
107         {
108             get
109             {
110                 return this.thisLock;
111             }
112         }
113
114         // subclasses like TraceAsyncResult can use this to wrap the callback functionality in a scope
115         protected Action<AsyncCallback, IAsyncResult> VirtualCallback
116         {
117             get;
118             set;
119         }
120
121         protected void Complete(bool completedSynchronously)
122         {
123             if (this.isCompleted)
124             {
125                 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultCompletedTwice(GetType())));
126             }
127
128 #if DEBUG
129             this.marker.AsyncResult = null;
130             this.marker = null;
131             if (!Fx.FastDebug && completeStack == null)
132             {
133                 completeStack = new StackTrace();
134             }
135 #endif
136
137             this.completedSynchronously = completedSynchronously;
138             if (OnCompleting != null)
139             {
140                 // Allow exception replacement, like a catch/throw pattern.
141                 try
142                 {
143                     OnCompleting(this, this.exception);
144                 }
145                 catch (Exception exception)
146                 {
147                     if (Fx.IsFatal(exception))
148                     {
149                         throw;
150                     }
151                     this.exception = exception;
152                 }
153             }
154
155             if (completedSynchronously)
156             {
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;
161             }
162             else
163             {
164                 lock (ThisLock)
165                 {
166                     this.isCompleted = true;
167                     if (this.manualResetEvent != null)
168                     {
169                         this.manualResetEvent.Set();
170                     }
171                 }
172             }
173
174             if (this.callback != null)
175             {
176                 try
177                 {
178                     if (VirtualCallback != null)
179                     {
180                         VirtualCallback(this.callback, this);
181                     }
182                     else
183                     {
184                         this.callback(this);
185                     }
186                 }
187 #pragma warning disable 1634
188 #pragma warning suppress 56500 // transferring exception to another thread
189                 catch (Exception e)
190                 {
191                     if (Fx.IsFatal(e))
192                     {
193                         throw;
194                     }
195
196                     throw Fx.Exception.AsError(new CallbackException(InternalSR.AsyncCallbackThrewException, e));
197                 }
198 #pragma warning restore 1634
199             }
200         }
201
202         protected void Complete(bool completedSynchronously, Exception exception)
203         {
204             this.exception = exception;
205             Complete(completedSynchronously);
206         }
207
208         static void AsyncCompletionWrapperCallback(IAsyncResult result)
209         {
210             if (result == null)
211             {
212                 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult));
213             }
214             if (result.CompletedSynchronously)
215             {
216                 return;
217             }
218
219             AsyncResult thisPtr = (AsyncResult)result.AsyncState;
220             if (!thisPtr.OnContinueAsyncCompletion(result))
221             {
222                 return;
223             }
224
225             AsyncCompletion callback = thisPtr.GetNextCompletion();
226             if (callback == null)
227             {
228                 ThrowInvalidAsyncResult(result);
229             }
230
231             bool completeSelf = false;
232             Exception completionException = null;
233             try
234             {
235                 completeSelf = callback(result);
236             }
237             catch (Exception e)
238             {
239                 if (Fx.IsFatal(e))
240                 {
241                     throw;
242                 }
243                 completeSelf = true;
244                 completionException = e;
245             }
246
247             if (completeSelf)
248             {
249                 thisPtr.Complete(false, completionException);
250             }
251         }
252
253         // Note: this should be only derived by the TransactedAsyncResult
254         protected virtual bool OnContinueAsyncCompletion(IAsyncResult result)
255         {
256             return true;
257         }
258
259         // Note: this should be used only by the TransactedAsyncResult
260         protected void SetBeforePrepareAsyncCompletionAction(Action beforePrepareAsyncCompletionAction)
261         {
262             this.beforePrepareAsyncCompletionAction = beforePrepareAsyncCompletionAction;
263         }
264
265         // Note: this should be used only by the TransactedAsyncResult
266         protected void SetCheckSyncValidationFunc(Func<IAsyncResult, bool> checkSyncValidationFunc)
267         {
268             this.checkSyncValidationFunc = checkSyncValidationFunc;
269         }
270
271         protected AsyncCallback PrepareAsyncCompletion(AsyncCompletion callback)
272         {
273             if (this.beforePrepareAsyncCompletionAction != null)
274             {
275                 this.beforePrepareAsyncCompletionAction();
276             }
277
278             this.nextAsyncCompletion = callback;
279             if (AsyncResult.asyncCompletionWrapperCallback == null)
280             {
281                 AsyncResult.asyncCompletionWrapperCallback = Fx.ThunkCallback(new AsyncCallback(AsyncCompletionWrapperCallback));
282             }
283             return AsyncResult.asyncCompletionWrapperCallback;
284         }
285
286         protected bool CheckSyncContinue(IAsyncResult result)
287         {
288             AsyncCompletion dummy;
289             return TryContinueHelper(result, out dummy);
290         }
291
292         protected bool SyncContinue(IAsyncResult result)
293         {
294             AsyncCompletion callback;
295             if (TryContinueHelper(result, out callback))
296             {
297                 return callback(result);
298             }
299             else
300             {
301                 return false;
302             }
303         }
304
305         bool TryContinueHelper(IAsyncResult result, out AsyncCompletion callback)
306         {
307             if (result == null)
308             {
309                 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult));
310             }
311
312             callback = null;
313             if (this.checkSyncValidationFunc != null)
314             {
315                 if (!this.checkSyncValidationFunc(result))
316                 {
317                     return false;
318                 }
319             }
320             else if (!result.CompletedSynchronously)
321             {
322                 return false;
323             }
324
325             callback = GetNextCompletion();
326             if (callback == null)
327             {
328                 ThrowInvalidAsyncResult("Only call Check/SyncContinue once per async operation (once per PrepareAsyncCompletion).");
329             }
330             return true;
331         }
332
333         AsyncCompletion GetNextCompletion()
334         {
335             AsyncCompletion result = this.nextAsyncCompletion;
336             this.nextAsyncCompletion = null;
337             return result;
338         }
339
340         protected static void ThrowInvalidAsyncResult(IAsyncResult result)
341         {
342             throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidAsyncResultImplementation(result.GetType())));
343         }
344
345         protected static void ThrowInvalidAsyncResult(string debugText)
346         {
347             string message = InternalSR.InvalidAsyncResultImplementationGeneric;
348             if (debugText != null)
349             {
350 #if DEBUG
351                 message += " " + debugText;
352 #endif
353             }
354             throw Fx.Exception.AsError(new InvalidOperationException(message));
355         }
356
357         [Fx.Tag.Blocking(Conditional = "!asyncResult.isCompleted")]
358         protected static TAsyncResult End<TAsyncResult>(IAsyncResult result)
359             where TAsyncResult : AsyncResult
360         {
361             if (result == null)
362             {
363                 throw Fx.Exception.ArgumentNull("result");
364             }
365
366             TAsyncResult asyncResult = result as TAsyncResult;
367
368             if (asyncResult == null)
369             {
370                 throw Fx.Exception.Argument("result", InternalSR.InvalidAsyncResult);
371             }
372
373             if (asyncResult.endCalled)
374             {
375                 throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultAlreadyEnded));
376             }
377
378 #if DEBUG
379             if (!Fx.FastDebug && asyncResult.endStack == null)
380             {
381                 asyncResult.endStack = new StackTrace();
382             }
383 #endif
384
385             asyncResult.endCalled = true;
386
387             if (!asyncResult.isCompleted)
388             {
389                 asyncResult.AsyncWaitHandle.WaitOne();
390             }
391
392             if (asyncResult.manualResetEvent != null)
393             {
394                 asyncResult.manualResetEvent.Close();
395             }
396
397             if (asyncResult.exception != null)
398             {
399                 throw Fx.Exception.AsError(asyncResult.exception);
400             }
401
402             return asyncResult;
403         }
404
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);
410
411 #if DEBUG
412         class UncompletedAsyncResultMarker
413         {
414             public UncompletedAsyncResultMarker(AsyncResult result)
415             {
416                 AsyncResult = result;
417             }
418             
419             [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode,
420                 Justification = "Debug-only facility")]
421             public AsyncResult AsyncResult { get; set; }
422         }
423 #endif
424     }
425 }