Fix bugs in sizing TableLayoutPanel (Xamarin bug 18638)
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / Async / AsyncResultWrapper.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. All rights reserved.\r
4  *\r
5  * This software is subject to the Microsoft Public License (Ms-PL). \r
6  * A copy of the license can be found in the license.htm file included \r
7  * in this distribution.\r
8  *\r
9  * You must not remove this notice, or any other, from this software.\r
10  *\r
11  * ***************************************************************************/\r
12 \r
13 namespace System.Web.Mvc.Async {\r
14     using System;\r
15     using System.Diagnostics.CodeAnalysis;\r
16     using System.Threading;\r
17 \r
18     // This class is used for the following pattern:\r
19 \r
20     // public IAsyncResult BeginInner(..., callback, state);\r
21     // public TInnerResult EndInner(asyncResult);\r
22     // public IAsyncResult BeginOuter(..., callback, state);\r
23     // public TOuterResult EndOuter(asyncResult);\r
24 \r
25     // That is, Begin/EndOuter() wrap Begin/EndInner(), potentially with pre- and post-processing.\r
26 \r
27     internal static class AsyncResultWrapper {\r
28 \r
29         // helper methods\r
30 \r
31         private static Func<AsyncVoid> MakeVoidDelegate(Action action) {\r
32             return () => {\r
33                 action();\r
34                 return default(AsyncVoid);\r
35             };\r
36         }\r
37 \r
38         private static EndInvokeDelegate<AsyncVoid> MakeVoidDelegate(EndInvokeDelegate endDelegate) {\r
39             return ar => {\r
40                 endDelegate(ar);\r
41                 return default(AsyncVoid);\r
42             };\r
43         }\r
44 \r
45         // kicks off an asynchronous operation\r
46 \r
47         public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate) {\r
48             return Begin<TResult>(callback, state, beginDelegate, endDelegate, null /* tag */);\r
49         }\r
50 \r
51         public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {\r
52             return Begin<TResult>(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);\r
53         }\r
54 \r
55         public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag, int timeout) {\r
56             WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);\r
57             asyncResult.Begin(callback, state, timeout);\r
58             return asyncResult;\r
59         }\r
60 \r
61         public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate) {\r
62             return Begin(callback, state, beginDelegate, endDelegate, null /* tag */);\r
63         }\r
64 \r
65         public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag) {\r
66             return Begin(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);\r
67         }\r
68 \r
69         public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag, int timeout) {\r
70             return Begin<AsyncVoid>(callback, state, beginDelegate, MakeVoidDelegate(endDelegate), tag, timeout);\r
71         }\r
72 \r
73         // wraps a synchronous operation in an asynchronous wrapper, but still completes synchronously\r
74 \r
75         public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func) {\r
76             return BeginSynchronous<TResult>(callback, state, func, null /* tag */);\r
77         }\r
78 \r
79         public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func, object tag) {\r
80             // Begin() doesn't perform any work on its own and returns immediately.\r
81             BeginInvokeDelegate beginDelegate = (asyncCallback, asyncState) => {\r
82                 SimpleAsyncResult innerAsyncResult = new SimpleAsyncResult(asyncState);\r
83                 innerAsyncResult.MarkCompleted(true /* completedSynchronously */, asyncCallback);\r
84                 return innerAsyncResult;\r
85             };\r
86 \r
87             // The End() method blocks.\r
88             EndInvokeDelegate<TResult> endDelegate = _ => {\r
89                 return func();\r
90             };\r
91 \r
92             WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);\r
93             asyncResult.Begin(callback, state, Timeout.Infinite);\r
94             return asyncResult;\r
95         }\r
96 \r
97         public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action) {\r
98             return BeginSynchronous(callback, state, action, null /* tag */);\r
99         }\r
100 \r
101         public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action, object tag) {\r
102             return BeginSynchronous<AsyncVoid>(callback, state, MakeVoidDelegate(action), tag);\r
103         }\r
104 \r
105         // completes an asynchronous operation\r
106 \r
107         public static TResult End<TResult>(IAsyncResult asyncResult) {\r
108             return End<TResult>(asyncResult, null /* tag */);\r
109         }\r
110 \r
111         public static TResult End<TResult>(IAsyncResult asyncResult, object tag) {\r
112             return WrappedAsyncResult<TResult>.Cast(asyncResult, tag).End();\r
113         }\r
114 \r
115         public static void End(IAsyncResult asyncResult) {\r
116             End(asyncResult, null /* tag */);\r
117         }\r
118 \r
119         public static void End(IAsyncResult asyncResult, object tag) {\r
120             End<AsyncVoid>(asyncResult, tag);\r
121         }\r
122 \r
123         [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",\r
124             Justification = "The Timer will be disposed of either when it fires or when the operation completes successfully.")]\r
125         private sealed class WrappedAsyncResult<TResult> : IAsyncResult {\r
126 \r
127             private readonly BeginInvokeDelegate _beginDelegate;\r
128             private readonly object _beginDelegateLockObj = new object();\r
129             private readonly EndInvokeDelegate<TResult> _endDelegate;\r
130             private readonly SingleEntryGate _endExecutedGate = new SingleEntryGate(); // prevent End() from being called twice\r
131             private readonly SingleEntryGate _handleCallbackGate = new SingleEntryGate(); // prevent callback from being handled multiple times\r
132             private IAsyncResult _innerAsyncResult;\r
133             private AsyncCallback _originalCallback;\r
134             private readonly object _tag; // prevent an instance of this type from being passed to the wrong End() method\r
135             private volatile bool _timedOut;\r
136             private Timer _timer;\r
137 \r
138             public WrappedAsyncResult(BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {\r
139                 _beginDelegate = beginDelegate;\r
140                 _endDelegate = endDelegate;\r
141                 _tag = tag;\r
142             }\r
143 \r
144             public object AsyncState {\r
145                 get {\r
146                     return _innerAsyncResult.AsyncState;\r
147                 }\r
148             }\r
149 \r
150             public WaitHandle AsyncWaitHandle {\r
151                 get {\r
152                     return _innerAsyncResult.AsyncWaitHandle;\r
153                 }\r
154             }\r
155 \r
156             public bool CompletedSynchronously {\r
157                 get {\r
158                     return _innerAsyncResult.CompletedSynchronously;\r
159                 }\r
160             }\r
161 \r
162             public bool IsCompleted {\r
163                 get {\r
164                     return _innerAsyncResult.IsCompleted;\r
165                 }\r
166             }\r
167 \r
168             // kicks off the process, instantiates a timer if requested\r
169             public void Begin(AsyncCallback callback, object state, int timeout) {\r
170                 _originalCallback = callback;\r
171                 bool completedSynchronously;\r
172 \r
173                 // Force the target Begin() operation to complete before the callback can continue,\r
174                 // since the target operation might perform post-processing of the data.\r
175                 lock (_beginDelegateLockObj) {\r
176                     _innerAsyncResult = _beginDelegate(HandleAsynchronousCompletion, state);\r
177 \r
178                     completedSynchronously = _innerAsyncResult.CompletedSynchronously;\r
179                     if (!completedSynchronously) {\r
180                         if (timeout > Timeout.Infinite) {\r
181                             CreateTimer(timeout);\r
182                         }\r
183                     }\r
184                 }\r
185 \r
186                 if (completedSynchronously) {\r
187                     if (callback != null) {\r
188                         callback(this);\r
189                     }\r
190                 }\r
191             }\r
192 \r
193             public static WrappedAsyncResult<TResult> Cast(IAsyncResult asyncResult, object tag) {\r
194                 if (asyncResult == null) {\r
195                     throw new ArgumentNullException("asyncResult");\r
196                 }\r
197 \r
198                 WrappedAsyncResult<TResult> castResult = asyncResult as WrappedAsyncResult<TResult>;\r
199                 if (castResult != null && Object.Equals(castResult._tag, tag)) {\r
200                     return castResult;\r
201                 }\r
202                 else {\r
203                     throw Error.AsyncCommon_InvalidAsyncResult("asyncResult");\r
204                 }\r
205             }\r
206 \r
207             private void CreateTimer(int timeout) {\r
208                 // this method should be called within a lock(_beginDelegateLockObj)\r
209                 _timer = new Timer(HandleTimeout, null, timeout, Timeout.Infinite /* disable periodic signaling */);\r
210             }\r
211 \r
212             public TResult End() {\r
213                 if (!_endExecutedGate.TryEnter()) {\r
214                     throw Error.AsyncCommon_AsyncResultAlreadyConsumed();\r
215                 }\r
216 \r
217                 if (_timedOut) {\r
218                     throw new TimeoutException();\r
219                 }\r
220                 WaitForBeginToCompleteAndDestroyTimer();\r
221 \r
222                 return _endDelegate(_innerAsyncResult);\r
223             }\r
224 \r
225             private void ExecuteAsynchronousCallback(bool timedOut) {\r
226                 WaitForBeginToCompleteAndDestroyTimer();\r
227 \r
228                 if (_handleCallbackGate.TryEnter()) {\r
229                     _timedOut = timedOut;\r
230                     if (_originalCallback != null) {\r
231                         _originalCallback(this);\r
232                     }\r
233                 }\r
234             }\r
235 \r
236             private void HandleAsynchronousCompletion(IAsyncResult asyncResult) {\r
237                 if (asyncResult.CompletedSynchronously) {\r
238                     // If the operation completed synchronously, the WrappedAsyncResult.Begin() method will handle it.\r
239                     return;\r
240                 }\r
241 \r
242                 ExecuteAsynchronousCallback(false /* timedOut */);\r
243             }\r
244 \r
245             private void HandleTimeout(object state) {\r
246                 ExecuteAsynchronousCallback(true /* timedOut */);\r
247             }\r
248 \r
249             private void WaitForBeginToCompleteAndDestroyTimer() {\r
250                 lock (_beginDelegateLockObj) {\r
251                     // Wait for the target Begin() method to complete, as it might be performing\r
252                     // post-processing. This also forces a memory barrier, so _innerAsyncResult\r
253                     // is guaranteed to be non-null at this point.\r
254 \r
255                     if (_timer != null) {\r
256                         _timer.Dispose();\r
257                     }\r
258                     _timer = null;\r
259                 }\r
260             }\r
261 \r
262         }\r
263 \r
264     }\r
265 }\r