File: AsyncResultWrapper.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (256 lines) | stat: -rw-r--r-- 10,960 bytes parent folder | download | duplicates (9)
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
namespace System.Web.Mvc.Async {
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Threading;

    // 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.

    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, null /* tag */);
        }

        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, null /* tag */);
        }

        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, null /* tag */);
        }

        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(true /* completedSynchronously */, 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, null /* tag */);
        }

        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, null /* tag */);
        }

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

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

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

        [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 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 IAsyncResult _innerAsyncResult;
            private AsyncCallback _originalCallback;
            private readonly object _tag; // prevent an instance of this type from being passed to the wrong End() method
            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 {
                    return _innerAsyncResult.CompletedSynchronously;
                }
            }

            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;
                bool completedSynchronously;

                // 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);

                    completedSynchronously = _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 && Object.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) {
                if (asyncResult.CompletedSynchronously) {
                    // If the operation completed synchronously, the WrappedAsyncResult.Begin() method will handle it.
                    return;
                }

                ExecuteAsynchronousCallback(false /* timedOut */);
            }

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

            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;
                }
            }

        }

    }
}