Welcome to mirror list, hosted at ThFree Co, Russian Federation.

AsyncResultWrapper.cs « Async « System.Web.Mvc « src - github.com/mono/aspnetwebstack.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 8cc54b7c2cd42306fb6c319eb0d6fba48487fd4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;

namespace System.Web.Mvc.Async
{
    // This class is used for the following pattern:

    // public IAsyncResult BeginInner(..., callback, state);
    // public TInnerResult EndInner(asyncResult);
    // public IAsyncResult BeginOuter(..., callback, state);
    // public TOuterResult EndOuter(asyncResult);

    // That is, Begin/EndOuter() wrap Begin/EndInner(), potentially with pre- and post-processing.

    [DebuggerNonUserCode]
    internal static class AsyncResultWrapper
    {
        // helper methods

        private static Func<AsyncVoid> MakeVoidDelegate(Action action)
        {
            return () =>
            {
                action();
                return default(AsyncVoid);
            };
        }

        private static EndInvokeDelegate<AsyncVoid> MakeVoidDelegate(EndInvokeDelegate endDelegate)
        {
            return ar =>
            {
                endDelegate(ar);
                return default(AsyncVoid);
            };
        }

        // kicks off an asynchronous operation

        public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate)
        {
            return Begin<TResult>(callback, state, beginDelegate, endDelegate, tag: null);
        }

        public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag)
        {
            return Begin<TResult>(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
        }

        public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag, int timeout)
        {
            WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
            asyncResult.Begin(callback, state, timeout);
            return asyncResult;
        }

        public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate)
        {
            return Begin(callback, state, beginDelegate, endDelegate, tag: null);
        }

        public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag)
        {
            return Begin(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
        }

        public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag, int timeout)
        {
            return Begin<AsyncVoid>(callback, state, beginDelegate, MakeVoidDelegate(endDelegate), tag, timeout);
        }

        // wraps a synchronous operation in an asynchronous wrapper, but still completes synchronously

        public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func)
        {
            return BeginSynchronous<TResult>(callback, state, func, tag: null);
        }

        public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func, object tag)
        {
            // Begin() doesn't perform any work on its own and returns immediately.
            BeginInvokeDelegate beginDelegate = (asyncCallback, asyncState) =>
            {
                SimpleAsyncResult innerAsyncResult = new SimpleAsyncResult(asyncState);
                innerAsyncResult.MarkCompleted(completedSynchronously: true, callback: asyncCallback);
                return innerAsyncResult;
            };

            // The End() method blocks.
            EndInvokeDelegate<TResult> endDelegate = _ =>
            {
                return func();
            };

            WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
            asyncResult.Begin(callback, state, Timeout.Infinite);
            return asyncResult;
        }

        public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action)
        {
            return BeginSynchronous(callback, state, action, tag: null);
        }

        public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action, object tag)
        {
            return BeginSynchronous<AsyncVoid>(callback, state, MakeVoidDelegate(action), tag);
        }

        // completes an asynchronous operation

        public static TResult End<TResult>(IAsyncResult asyncResult)
        {
            return End<TResult>(asyncResult, tag: null);
        }

        public static TResult End<TResult>(IAsyncResult asyncResult, object tag)
        {
            return WrappedAsyncResult<TResult>.Cast(asyncResult, tag).End();
        }

        public static void End(IAsyncResult asyncResult)
        {
            End(asyncResult, tag: null);
        }

        public static void End(IAsyncResult asyncResult, object tag)
        {
            End<AsyncVoid>(asyncResult, tag);
        }

        [DebuggerNonUserCode]
        [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "The Timer will be disposed of either when it fires or when the operation completes successfully.")]
        private sealed class WrappedAsyncResult<TResult> : IAsyncResult
        {
            private const int AsyncStateNone = 0;
            private const int AsyncStateBeginUnwound = 1;
            private const int AsyncStateCallbackFired = 2;

            private int _asyncState;
            private readonly BeginInvokeDelegate _beginDelegate;
            private readonly object _beginDelegateLockObj = new object();
            private readonly EndInvokeDelegate<TResult> _endDelegate;
            private readonly SingleEntryGate _endExecutedGate = new SingleEntryGate(); // prevent End() from being called twice
            private readonly SingleEntryGate _handleCallbackGate = new SingleEntryGate(); // prevent callback from being handled multiple times
            private readonly object _tag; // prevent an instance of this type from being passed to the wrong End() method
            private IAsyncResult _innerAsyncResult;
            private AsyncCallback _originalCallback;
            private volatile bool _timedOut;
            private Timer _timer;

            public WrappedAsyncResult(BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag)
            {
                _beginDelegate = beginDelegate;
                _endDelegate = endDelegate;
                _tag = tag;
            }

            public object AsyncState
            {
                get { return _innerAsyncResult.AsyncState; }
            }

            public WaitHandle AsyncWaitHandle
            {
                get { return _innerAsyncResult.AsyncWaitHandle; }
            }

            public bool CompletedSynchronously { get; private set; }

            public bool IsCompleted
            {
                get { return _innerAsyncResult.IsCompleted; }
            }

            // kicks off the process, instantiates a timer if requested
            public void Begin(AsyncCallback callback, object state, int timeout)
            {
                _originalCallback = callback;

                // Force the target Begin() operation to complete before the callback can continue,
                // since the target operation might perform post-processing of the data.
                lock (_beginDelegateLockObj)
                {
                    _innerAsyncResult = _beginDelegate(HandleAsynchronousCompletion, state);

                    // If the callback has already fired, then the completion routine has no-oped and we
                    // can just treat this as if it were a normal synchronous completion.
                    int originalState = Interlocked.Exchange(ref _asyncState, AsyncStateBeginUnwound);
                    bool callbackAlreadyFired = (originalState == AsyncStateCallbackFired);

                    CompletedSynchronously = callbackAlreadyFired || _innerAsyncResult.CompletedSynchronously;

                    if (!CompletedSynchronously)
                    {
                        if (timeout > Timeout.Infinite)
                        {
                            CreateTimer(timeout);
                        }
                    }
                }

                if (CompletedSynchronously)
                {
                    if (callback != null)
                    {
                        callback(this);
                    }
                }
            }

            public static WrappedAsyncResult<TResult> Cast(IAsyncResult asyncResult, object tag)
            {
                if (asyncResult == null)
                {
                    throw new ArgumentNullException("asyncResult");
                }

                WrappedAsyncResult<TResult> castResult = asyncResult as WrappedAsyncResult<TResult>;
                if (castResult != null && Equals(castResult._tag, tag))
                {
                    return castResult;
                }
                else
                {
                    throw Error.AsyncCommon_InvalidAsyncResult("asyncResult");
                }
            }

            private void CreateTimer(int timeout)
            {
                // this method should be called within a lock(_beginDelegateLockObj)
                _timer = new Timer(HandleTimeout, null, timeout, Timeout.Infinite /* disable periodic signaling */);
            }

            public TResult End()
            {
                if (!_endExecutedGate.TryEnter())
                {
                    throw Error.AsyncCommon_AsyncResultAlreadyConsumed();
                }

                if (_timedOut)
                {
                    throw new TimeoutException();
                }
                WaitForBeginToCompleteAndDestroyTimer();

                return _endDelegate(_innerAsyncResult);
            }

            private void ExecuteAsynchronousCallback(bool timedOut)
            {
                WaitForBeginToCompleteAndDestroyTimer();

                if (_handleCallbackGate.TryEnter())
                {
                    _timedOut = timedOut;
                    if (_originalCallback != null)
                    {
                        _originalCallback(this);
                    }
                }
            }

            private void HandleAsynchronousCompletion(IAsyncResult asyncResult)
            {
                // Transition the async state to CALLBACK_FIRED. If the Begin* method hasn't yet unwound,
                // then we can no-op here since the Begin method will query the _asyncState field and
                // treat this as a regular synchronous completion.
                int originalState = Interlocked.Exchange(ref _asyncState, AsyncStateCallbackFired);
                if (originalState != AsyncStateBeginUnwound)
                {
                    return;
                }

                ExecuteAsynchronousCallback(timedOut: false);
            }

            private void HandleTimeout(object state)
            {
                ExecuteAsynchronousCallback(timedOut: true);
            }

            private void WaitForBeginToCompleteAndDestroyTimer()
            {
                lock (_beginDelegateLockObj)
                {
                    // Wait for the target Begin() method to complete, as it might be performing
                    // post-processing. This also forces a memory barrier, so _innerAsyncResult
                    // is guaranteed to be non-null at this point.

                    if (_timer != null)
                    {
                        _timer.Dispose();
                    }
                    _timer = null;
                }
            }
        }
    }
}