PageRenderTime 59ms CodeModel.GetById 26ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/WCFWebApi/src/Microsoft.Server.Common/Microsoft/Server/Common/AsyncResult.cs

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