Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System / net / System / Net / _LazyAsyncResult.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="_LazyAsyncResult.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net
8 {
9     using System.Threading;
10     using System.Diagnostics;
11     using System.Collections;
12
13     // LazyAsyncResult - Base class for all IAsyncResult classes
14     // that want to take advantage of lazy allocated event handles
15     internal class LazyAsyncResult : IAsyncResult
16     {
17         private const int c_HighBit = unchecked((int) 0x80000000);
18         private const int c_ForceAsyncCount = 50;
19
20 #if !NET_PERF
21         // This is to avoid user mistakes when they queue another async op from a callback the completes sync.
22         [ThreadStatic]
23         private static ThreadContext t_ThreadContext;
24
25         private static ThreadContext CurrentThreadContext
26         {
27             get
28             {
29                 ThreadContext threadContext = t_ThreadContext;
30                 if (threadContext == null)
31                 {
32                     threadContext = new ThreadContext();
33                     t_ThreadContext = threadContext;
34                 }
35                 return threadContext;
36             }
37         }
38
39         private class ThreadContext
40         {
41             internal int m_NestedIOCount;
42         }
43 #endif
44
45 #if DEBUG
46         internal object _DebugAsyncChain;           // Optionally used to track chains of async calls.
47         private bool _ProtectState;                 // Used by ContextAwareResult to prevent some calls.
48 #endif
49
50 #if TRACK_LAR
51         internal static Hashtable _PendingResults = Hashtable.Synchronized(new Hashtable());
52         internal static int _PendingIndex = 0;
53         internal int _MyIndex;
54 #endif
55
56         //
57         // class members
58         //
59         private object m_AsyncObject;               // Caller's async object.
60         private object m_AsyncState;                // Caller's state object.
61         private AsyncCallback m_AsyncCallback;      // Caller's callback method.
62         private object m_Result;                    // Final IO result to be returned byt the End*() method.
63         private int m_ErrorCode;                    // Win32 error code for Win32 IO async calls (that want to throw).
64         private int m_IntCompleted;                 // Sign bit indicates synchronous completion if set.
65                                                     // Remaining bits count the number of InvokeCallbak() calls.
66
67         private bool m_EndCalled;                   // true if the user called the End*() method.
68         private bool m_UserEvent;                   // true if the event has been (or is about to be) handed to the user
69
70         private object m_Event;                     // lazy allocated event to be returned in the IAsyncResult for the client to wait on
71
72
73         internal LazyAsyncResult(object myObject, object myState, AsyncCallback myCallBack) {
74             m_AsyncObject = myObject;
75             m_AsyncState = myState;
76             m_AsyncCallback = myCallBack;
77             m_Result = DBNull.Value;
78             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor()");
79
80 #if TRACK_LAR
81             _MyIndex = Interlocked.Increment(ref _PendingIndex);
82             _PendingResults.Add(_MyIndex, this);
83 #endif
84         }
85
86         // Allows creating a pre-completed result with less interlockeds.  Beware!  Constructor calls the callback.
87         // if a derived class ever uses this and overloads Cleanup, this may need to change
88         internal LazyAsyncResult(object myObject, object myState, AsyncCallback myCallBack, object result)
89         {
90             GlobalLog.Assert(result != DBNull.Value, "LazyAsyncResult#{0}::.ctor()|Result can't be set to DBNull - it's a special internal value.", ValidationHelper.HashString(this));
91             m_AsyncObject = myObject;
92             m_AsyncState = myState;
93             m_AsyncCallback = myCallBack;
94             m_Result =  result;
95             m_IntCompleted = 1;
96
97             if (m_AsyncCallback != null) {
98                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() invoking callback");
99                 m_AsyncCallback(this);
100             }
101             else  {
102                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() no callback to invoke");
103             }
104
105             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor() (pre-completed)");
106         }
107
108         // Interface method to return the original async object:
109         internal object AsyncObject {
110             get {
111                 return m_AsyncObject;
112             }
113         }
114
115         // Interface method to return the caller's state object.
116         public object AsyncState {
117             get {
118                 return m_AsyncState;
119             }
120         }
121
122         protected AsyncCallback AsyncCallback
123         {
124             get
125             {
126                 return m_AsyncCallback;
127             }
128
129             set
130             {
131                 m_AsyncCallback = value;
132             }
133         }
134
135         // Interface property to return a WaitHandle that can be waited on for I/O completion.
136         // This property implements lazy event creation.
137         // the event object is only created when this property is accessed,
138         // since we're internally only using callbacks, as long as the user is using
139         // callbacks as well we will not create an event at all.
140         // If this is used, the event cannot be disposed because it is under the control of the
141         // application.  Internal should use InternalWaitForCompletion instead - never AsyncWaitHandle.
142         public WaitHandle AsyncWaitHandle
143         {
144             get
145             {
146                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_AsyncWaitHandle()");
147
148 #if DEBUG
149                 // Can't be called when state is protected.
150                 if (_ProtectState)
151                 {
152                     throw new InvalidOperationException("get_AsyncWaitHandle called in protected state");
153                 }
154 #endif
155
156                 ManualResetEvent asyncEvent;
157
158                 // Indicates that the user has seen the event; it can't be disposed.
159                 m_UserEvent = true;
160
161                 // The user has access to this object.  Lock-in CompletedSynchronously.
162                 if (m_IntCompleted == 0)
163                 {
164                     Interlocked.CompareExchange(ref m_IntCompleted, c_HighBit, 0);
165                 }
166
167                 // Because InternalWaitForCompletion() tries to dispose this event, it's
168                 // possible for m_Event to become null immediately after being set, but only if
169                 // IsCompleted has become true.  Therefore it's possible for this property
170                 // to give different (set) events to different callers when IsCompleted is true.
171                 asyncEvent = (ManualResetEvent) m_Event;
172                 while (asyncEvent == null)
173                 {
174                     LazilyCreateEvent(out asyncEvent);
175                 }
176
177                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_AsyncWaitHandle() m_Event:" + ValidationHelper.HashString(m_Event));
178                 return asyncEvent;
179             }
180         }
181
182         // Returns true if this call created the event.
183         // May return with a null handle.  That means it thought it got one, but it was disposed in the mean time.
184         private bool LazilyCreateEvent(out ManualResetEvent waitHandle)
185         {
186             // lazy allocation of the event:
187             // if this property is never accessed this object is never created
188             waitHandle = new ManualResetEvent(false);
189             try
190             {
191                 if (Interlocked.CompareExchange(ref m_Event, waitHandle, null) == null)
192                 {
193                     if (InternalPeekCompleted)
194                     {
195                         waitHandle.Set();
196                     }
197                     return true;
198                 }
199                 else
200                 {
201                     waitHandle.Close();
202                     waitHandle = (ManualResetEvent) m_Event;
203                     // There's a chance here that m_Event became null.  But the only way is if another thread completed
204                     // in InternalWaitForCompletion and disposed it.  If we're in InternalWaitForCompletion, we now know
205                     // IsCompleted is set, so we can avoid the wait when waitHandle comes back null.  AsyncWaitHandle
206                     // will try again in this case.
207                     return false;
208                 }
209             }
210             catch
211             {
212                 // This should be very rare, but doing this will reduce the chance of deadlock.
213                 m_Event = null;
214                 if (waitHandle != null)
215                     waitHandle.Close();
216                 throw;
217             }
218         }
219
220         // This allows ContextAwareResult to not let anyone trigger the CompletedSynchronously tripwire while the context is being captured.
221         [Conditional("DEBUG")]
222         protected void DebugProtectState(bool protect)
223         {
224 #if DEBUG
225             _ProtectState = protect;
226 #endif
227         }
228
229         // Interface property, returning synchronous completion status.
230         public bool CompletedSynchronously {
231             get {
232                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_CompletedSynchronously()");
233
234 #if DEBUG
235                 // Can't be called when state is protected.
236                 if (_ProtectState)
237                 {
238                     throw new InvalidOperationException("get_CompletedSynchronously called in protected state");
239                 }
240 #endif
241
242                 // If this returns greater than zero, it means it was incremented by InvokeCallback before anyone ever saw it.
243                 int result = m_IntCompleted;
244                 if (result == 0)
245                 {
246                     result = Interlocked.CompareExchange(ref m_IntCompleted, c_HighBit, 0);
247                 }
248                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_CompletedSynchronously() returns: "+((result>0)?"true":"false"));
249                 return result > 0;
250             }
251         }
252
253         // Interface property, returning completion status.
254         public bool IsCompleted {
255             get {
256                 GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::get_IsCompleted()");
257
258 #if DEBUG
259                 // Can't be called when state is protected.
260                 if (_ProtectState)
261                 {
262                     throw new InvalidOperationException("get_IsCompleted called in protected state");
263                 }
264 #endif
265
266                 // Look at just the low bits to see if it's been incremented.  If it hasn't, set the high bit
267                 // to show that it's been looked at.
268                 int result = m_IntCompleted;
269                 if (result == 0)
270                 {
271                     result = Interlocked.CompareExchange(ref m_IntCompleted, c_HighBit, 0);
272                 }
273                 return (result & ~c_HighBit) != 0;
274             }
275         }
276
277         // Use to see if something's completed without fixing CompletedSynchronously
278         internal bool InternalPeekCompleted
279         {
280             get
281             {
282                 return (m_IntCompleted & ~c_HighBit) != 0;
283             }
284         }
285
286         // Internal property for setting the IO result.
287         internal object Result {
288             get {
289                 return m_Result == DBNull.Value ? null : m_Result;
290             }
291             set {
292                 // Ideally this should never be called, since setting
293                 // the result object really makes sense when the IO completes.
294                 //
295                 // But if the result was set here (as a preemptive error or for some other reason),
296                 // then the "result" parameter passed to InvokeCallback() will be ignored.
297                 //
298
299                 // It's an error to call after the result has been completed or with DBNull.
300                 GlobalLog.Assert(value != DBNull.Value, "LazyAsyncResult#{0}::set_Result()|Result can't be set to DBNull - it's a special internal value.", ValidationHelper.HashString(this));
301                 GlobalLog.Assert(!InternalPeekCompleted, "LazyAsyncResult#{0}::set_Result()|Called on completed result.", ValidationHelper.HashString(this));
302                 m_Result = value;
303             }
304         }
305
306         internal bool EndCalled {
307             get {
308                 return m_EndCalled;
309             }
310             set {
311                 m_EndCalled = value;
312             }
313         }
314
315         // Internal property for setting the Win32 IO async error code.
316         internal int ErrorCode {
317             get {
318                 return m_ErrorCode;
319             }
320             set {
321                 m_ErrorCode = value;
322             }
323         }
324
325         // A method for completing the IO with a result
326         // and invoking the user's callback.
327         // Used by derived classes to pass context into an overridden Complete().  Useful
328         // for determining the 'winning' thread in case several may simultaneously call
329         // the equivalent of InvokeCallback().
330         protected void ProtectedInvokeCallback(object result, IntPtr userToken)
331         {
332             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::ProtectedInvokeCallback() result = " +
333                             (result is Exception? ((Exception)result).Message:  result == null? "<null>": result.ToString()) +
334                             ", userToken:" + userToken.ToString());
335
336             // Critical to disallow DBNull here - it could result in a stuck spinlock in WaitForCompletion.
337             if (result == DBNull.Value)
338             {
339                 throw new ArgumentNullException("result");
340             }
341
342 #if DEBUG
343             // Always safe to ask for the state now.
344             _ProtectState = false;
345 #endif
346
347             if ((m_IntCompleted & ~c_HighBit) == 0 && (Interlocked.Increment(ref m_IntCompleted) & ~c_HighBit) == 1)
348             {
349                 // DBNull.Value is used to guarantee that the first caller wins,
350                 // even if the result was set to null.
351                 if (m_Result == DBNull.Value)
352                     m_Result = result;
353
354                 // Does this need a memory barrier to be sure this thread gets the m_Event if it's set?  I don't think so
355                 // because the Interlockeds on m_IntCompleted/m_Event should serve as the barrier.
356                 ManualResetEvent asyncEvent = (ManualResetEvent) m_Event;
357                 if (asyncEvent != null)
358                 {
359                     try {
360                         asyncEvent.Set();
361                     }
362                     catch (ObjectDisposedException) {
363                         // Simply ignore this exception - There is apparently a rare race condition
364                         // where the event is disposed before the completion method is called.
365                     }
366                 }
367
368                 Complete(userToken);
369             }
370         }
371
372         // A method for completing the IO with a result
373         // and invoking the user's callback.
374         internal void InvokeCallback(object result)
375         {
376             ProtectedInvokeCallback(result, IntPtr.Zero);
377         }
378
379         // A method for completing the IO without a result
380         // and invoking the user's callback.
381         internal void InvokeCallback()
382         {
383             ProtectedInvokeCallback(null, IntPtr.Zero);
384         }
385
386         //
387         //  MUST NOT BE CALLED DIRECTLY
388         //  A protected method that does callback job and it is guaranteed to be called exactly once.
389         //  A derived overriding method must call the base class somewhere or the completion is lost.
390         //
391         protected virtual void Complete(IntPtr userToken)
392         {
393 #if !NET_PERF
394             bool offloaded = false;
395             ThreadContext threadContext = CurrentThreadContext;
396             try {
397                 ++threadContext.m_NestedIOCount;
398 #else
399             try
400             {
401 #endif
402                 if (m_AsyncCallback != null) {
403                     GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() invoking callback");
404
405 #if !NET_PERF
406                     if (threadContext.m_NestedIOCount >= c_ForceAsyncCount)
407                     {
408                         GlobalLog.Print("LazyAsyncResult::Complete *** OFFLOADED the user callback ***");
409                         ThreadPool.QueueUserWorkItem(new WaitCallback(WorkerThreadComplete));
410                         offloaded = true;
411                     }
412                     else
413 #endif
414                     {
415                         m_AsyncCallback(this);
416                     }
417                 }
418                 else  {
419                     GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::Complete() no callback to invoke");
420                 }
421             }
422             finally {
423 #if !NET_PERF
424                 --threadContext.m_NestedIOCount;
425
426                 // Never call this method unless interlocked m_IntCompleted check has succeeded (like in this case)
427                 if (!offloaded)
428 #endif
429                 {
430                     Cleanup();
431                 }
432             }
433         }
434
435
436
437 #if !NET_PERF
438         // Only called in the above method
439         void WorkerThreadComplete(object state)
440         {
441             try
442             {
443                 m_AsyncCallback(this);
444             }
445             finally
446             {
447                 Cleanup();
448             }
449         }
450 #endif
451
452         // Custom instance cleanup method.
453         // Derived types override this method to release unmanaged resources associated with an IO request.
454         protected virtual void Cleanup()
455         {
456 #if TRACK_LAR
457             _PendingResults.Remove(_MyIndex);
458 #endif
459         }
460
461         internal object InternalWaitForCompletion()
462         {
463             return WaitForCompletion(true);
464         }
465
466         /*
467         internal object InternalWaitForCompletionNoSideEffects()
468         {
469             return WaitForCompletion(false);
470         }
471         */
472
473         private object WaitForCompletion(bool snap) {
474             ManualResetEvent waitHandle = null;
475             bool createdByMe = false;
476             bool complete = snap ? IsCompleted : InternalPeekCompleted;
477
478             if (!complete)
479             {
480                 // Not done yet, so wait:
481                 waitHandle = (ManualResetEvent) m_Event;
482                 if (waitHandle == null)
483                 {
484                     createdByMe = LazilyCreateEvent(out waitHandle);
485                 }
486             }
487
488             if (waitHandle != null)
489             {
490                 try
491                 {
492                     GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::InternalWaitForCompletion() Waiting for completion m_Event#" + ValidationHelper.HashString(waitHandle));
493                     waitHandle.WaitOne(Timeout.Infinite, false);
494                 }
495                 catch (ObjectDisposedException)
496                 {
497                     // This can occur if this method is called from two different threads.
498                     // This possibility is the trade-off for not locking.
499                 }
500                 finally {
501                     // We also want to dispose the event although we can't unless we did wait on it here.
502                     if (createdByMe && !m_UserEvent)
503                     {
504                         // Does m_UserEvent need to be volatile (or m_Event set via Interlocked) in order
505                         // to avoid giving a user a disposed event?
506                         ManualResetEvent oldEvent = (ManualResetEvent) m_Event;
507                         m_Event = null;
508                         if (!m_UserEvent)
509                         {
510                             oldEvent.Close();
511                         }
512                     }
513                 }
514             }
515
516             // A race condition exists because InvokeCallback sets m_IntCompleted before m_Result (so that m_Result
517             // can benefit from the synchronization of m_IntCompleted).  That means you can get here before m_Result got
518             // set (although rarely - once every eight hours of stress).  Handle that case with a spin-lock.
519             while (m_Result == DBNull.Value)
520                 Thread.SpinWait(1);
521
522             GlobalLog.Print("LazyAsyncResult#" + ValidationHelper.HashString(this) + "::InternalWaitForCompletion() done: " +
523                             (m_Result is Exception? ((Exception)m_Result).Message:  m_Result == null? "<null>": m_Result.ToString()));
524
525             return m_Result;
526         }
527
528         // A general interface that is called to release unmanaged resources associated with the class.
529         // It completes the result but doesn't do any of the notifications.
530         internal void InternalCleanup()
531         {
532             if ((m_IntCompleted & ~c_HighBit) == 0 && (Interlocked.Increment(ref m_IntCompleted) & ~c_HighBit) == 1)
533             {
534                 // Set no result so that just in case there are waiters, they don't hang in the spin lock.
535                 m_Result = null;
536                 Cleanup();
537             }
538         }
539     }
540 }