Merge pull request #3373 from marek-safar/net-4.6.2
[mono.git] / mcs / class / referencesource / System.Web / AspNetSynchronizationContextBase.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="AspNetSynchronizationContextBase.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 namespace System.Web {
8     using System;
9     using System.Runtime.ExceptionServices;
10     using System.Threading;
11     using System.Threading.Tasks;
12     using System.Web.Util;
13
14     // Provides an abstraction around the different SynchronizationContext-derived types that the
15     // ASP.NET runtime might use. Consumers can target this abstraction rather than coding against
16     // the various concrete types directly.
17
18     internal abstract class AspNetSynchronizationContextBase : SynchronizationContext {
19
20         private AllowAsyncOperationsBlockDisposable _allowAsyncOperationsBlockDisposable;
21
22         internal abstract bool AllowAsyncDuringSyncStages { get; set; }
23         internal abstract bool Enabled { get; }
24         internal Exception Error {
25             get {
26                 ExceptionDispatchInfo dispatchInfo = ExceptionDispatchInfo;
27                 return (dispatchInfo != null) ? dispatchInfo.SourceException : null;
28             }
29         }
30         internal abstract ExceptionDispatchInfo ExceptionDispatchInfo { get; }
31         internal abstract int PendingOperationsCount { get; }
32
33         internal abstract void ClearError();
34         internal abstract void Disable();
35         internal abstract void Enable();
36
37         internal abstract bool PendingCompletion(WaitCallback callback);
38
39         // A helper method which provides a Task-based wrapper around the PendingCompletion method.
40         // NOTE: The caller should verify that there are never outstanding calls to PendingCompletion
41         // or to WaitForPendingOperationsAsync, since each call replaces the continuation that will
42         // be invoked.
43         internal Task WaitForPendingOperationsAsync() {
44             TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
45
46             WaitCallback callback = _ => {
47                 Exception ex = Error;
48                 if (ex != null) {
49                     // We're going to observe the exception in the returned Task. We shouldn't keep
50                     // it around in the SynchronizationContext or it will fault future Tasks.
51                     ClearError();
52                     tcs.TrySetException(ex);
53                 }
54                 else {
55                     tcs.TrySetResult(null);
56                 }
57             };
58
59             if (!PendingCompletion(callback)) {
60                 // If PendingCompletion returns false, there are no pending operations and the
61                 // callback will not be invoked, so we should just signal the TCS immediately.
62                 callback(null);
63             }
64
65             return tcs.Task;
66         }
67
68         // These methods are used in the synchronous handler execution step so that a synchronous IHttpHandler
69         // can call asynchronous methods without locking on the HttpApplication instance (possibly causing
70         // deadlocks).
71
72         internal abstract void SetSyncCaller();
73         internal abstract void ResetSyncCaller();
74
75         // These methods are used for synchronization, e.g. to create a lock that is tied to the current
76         // thread. The legacy implementation locks on the HttpApplication instance, for example.
77
78         internal abstract void AssociateWithCurrentThread();
79         internal abstract void DisassociateFromCurrentThread();
80
81         // These methods are used for telling the synchronization context when it is legal for an application
82         // to kick off async void methods. They are used by the "AllowAsyncDuringSyncStages" setting to
83         // determine whether kicking off an operation should throw.
84
85         internal virtual void AllowVoidAsyncOperations() { /* no-op by default */ }
86         internal virtual void ProhibitVoidAsyncOperations() { /* no-op by default */ }
87
88         // helper method for wrapping AllowVoidAsyncOperations / ProhibitVoidAsyncOperations in a using block
89         internal IDisposable AllowVoidAsyncOperationsBlock() {
90             if (_allowAsyncOperationsBlockDisposable == null) {
91                 _allowAsyncOperationsBlockDisposable = new AllowAsyncOperationsBlockDisposable(this);
92             }
93
94             AllowVoidAsyncOperations();
95             return _allowAsyncOperationsBlockDisposable;
96         }
97
98         // Helper method to wrap Associate / Disassociate calls in a using() statement
99         internal IDisposable AcquireThreadLock() {
100             AssociateWithCurrentThread();
101             return new DisposableAction(DisassociateFromCurrentThread);
102         }
103
104         private sealed class AllowAsyncOperationsBlockDisposable : IDisposable {
105             private readonly AspNetSynchronizationContextBase _syncContext;
106
107             public AllowAsyncOperationsBlockDisposable(AspNetSynchronizationContextBase syncContext) {
108                 _syncContext = syncContext;
109             }
110
111             public void Dispose() {
112                 _syncContext.ProhibitVoidAsyncOperations();
113             }
114         }
115         
116     }
117 }