Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / timer.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 //
7 // <OWNER>[....]</OWNER>
8
9 using Microsoft.Win32;
10 using Microsoft.Win32.SafeHandles;
11
12 namespace System.Threading 
13 {
14     using System;
15     using System.Security;
16     using System.Security.Permissions;
17     using System.Runtime.CompilerServices;
18     using System.Runtime.InteropServices;
19     using System.Runtime.ConstrainedExecution;
20     using System.Runtime.Versioning;
21     using System.Diagnostics.Contracts;
22     using System.Diagnostics.Tracing;
23
24
25         
26     [System.Runtime.InteropServices.ComVisible(true)]
27     public delegate void TimerCallback(Object state);
28
29     //
30     // TimerQueue maintains a list of active timers in this AppDomain.  We use a single native timer, supplied by the VM,
31     // to schedule all managed timers in the AppDomain.
32     //
33     // Perf assumptions:  We assume that timers are created and destroyed frequently, but rarely actually fire.
34     // There are roughly two types of timer:
35     //
36     //  - timeouts for operations.  These are created and destroyed very frequently, but almost never fire, because
37     //    the whole point is that the timer only fires if something has gone wrong.
38     //
39     //  - scheduled background tasks.  These typically do fire, but they usually have quite long durations.
40     //    So the impact of spending a few extra cycles to fire these is negligible.
41     //
42     // Because of this, we want to choose a data structure with very fast insert and delete times, but we can live
43     // with linear traversal times when firing timers.
44     //
45     // The data structure we've chosen is an unordered doubly-linked list of active timers.  This gives O(1) insertion
46     // and removal, and O(N) traversal when finding expired timers.
47     //
48     // Note that all instance methods of this class require that the caller hold a lock on TimerQueue.Instance.
49     //
50     class TimerQueue
51     {
52         #region singleton pattern implementation
53
54         // The one-and-only TimerQueue for the AppDomain.
55         static TimerQueue s_queue = new TimerQueue();
56
57         public static TimerQueue Instance
58         {
59             get { return s_queue; }
60         }
61
62         private TimerQueue()
63         {
64             // empty private constructor to ensure we remain a singleton.
65         }
66
67         #endregion
68
69         #region interface to native per-AppDomain timer
70
71         //
72         // We need to keep our notion of time synchronized with the calls to SleepEx that drive
73         // the underlying native timer.  In Win8, SleepEx does not count the time the machine spends
74         // sleeping/hibernating.  Environment.TickCount (GetTickCount) *does* count that time,
75         // so we will get out of [....] with SleepEx if we use that method.
76         //
77         // So, on Win8, we use QueryUnbiasedInterruptTime instead; this does not count time spent
78         // in sleep/hibernate mode.
79         //
80         private static int TickCount
81         {
82             [SecuritySafeCritical]
83             get
84             {
85 #if !MONO
86                 if (Environment.IsWindows8OrAbove)
87                 {
88                     ulong time100ns;
89
90                     bool result = Win32Native.QueryUnbiasedInterruptTime(out time100ns);
91                     if (!result)
92                         throw Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
93
94                     // convert to 100ns to milliseconds, and truncate to 32 bits.
95                     return (int)(uint)(time100ns / 10000);
96                 }
97                 else
98 #endif
99                 {
100                     return Environment.TickCount;
101                 }
102             }
103         }
104
105         //
106         // We use a SafeHandle to ensure that the native timer is destroyed when the AppDomain is unloaded.
107         //
108         [SecurityCritical]
109         class AppDomainTimerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
110         {
111             public AppDomainTimerSafeHandle()
112                 : base(true)
113             {
114             }
115
116             [SecurityCritical]
117             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
118             protected override bool ReleaseHandle()
119             {
120                 return DeleteAppDomainTimer(handle);
121             }
122         }
123
124         [SecurityCritical]
125         AppDomainTimerSafeHandle m_appDomainTimer;
126
127         bool m_isAppDomainTimerScheduled;
128         int m_currentAppDomainTimerStartTicks;
129         uint m_currentAppDomainTimerDuration;
130
131         [SecuritySafeCritical]
132         private bool EnsureAppDomainTimerFiresBy(uint requestedDuration)
133         {
134             //
135             // The VM's timer implementation does not work well for very long-duration timers.
136             // See kb 950807.
137             // So we'll limit our native timer duration to a "small" value.
138             // This may cause us to attempt to fire timers early, but that's ok - 
139             // we'll just see that none of our timers has actually reached its due time,
140             // and schedule the native timer again.
141             //
142             const uint maxPossibleDuration = 0x0fffffff;
143             uint actualDuration = Math.Min(requestedDuration, maxPossibleDuration);
144
145             if (m_isAppDomainTimerScheduled)
146             {
147                 uint elapsed = (uint)(TickCount - m_currentAppDomainTimerStartTicks);
148                 if (elapsed >= m_currentAppDomainTimerDuration)
149                     return true; //the timer's about to fire
150
151                 uint remainingDuration = m_currentAppDomainTimerDuration - elapsed;
152                 if (actualDuration >= remainingDuration)
153                     return true; //the timer will fire earlier than this request
154             }
155
156             // If Pause is underway then do not schedule the timers
157             // A later update during resume will re-schedule
158             if(m_pauseTicks != 0)
159             {
160                 Contract.Assert(!m_isAppDomainTimerScheduled);
161                 Contract.Assert(m_appDomainTimer == null);
162                 return true;
163             }
164  
165             if (m_appDomainTimer == null || m_appDomainTimer.IsInvalid)
166             {
167                 Contract.Assert(!m_isAppDomainTimerScheduled);
168
169                 m_appDomainTimer = CreateAppDomainTimer(actualDuration);
170                 if (!m_appDomainTimer.IsInvalid)
171                 {
172                     m_isAppDomainTimerScheduled = true;
173                     m_currentAppDomainTimerStartTicks = TickCount;
174                     m_currentAppDomainTimerDuration = actualDuration;
175                     return true;
176                 }
177                 else
178                 {
179                     return false;
180                 }
181             }
182             else
183             {
184                 if (ChangeAppDomainTimer(m_appDomainTimer, actualDuration))
185                 {
186                     m_isAppDomainTimerScheduled = true;
187                     m_currentAppDomainTimerStartTicks = TickCount;
188                     m_currentAppDomainTimerDuration = actualDuration;
189                     return true;
190                 }
191                 else
192                 {
193                     return false;
194                 }
195             }
196         }
197
198         //
199         // The VM calls this when the native timer fires.
200         //
201         [SecuritySafeCritical]
202         internal static void AppDomainTimerCallback()
203         {
204             Instance.FireNextTimers();
205         }
206
207         [System.Security.SecurityCritical]
208         [ResourceExposure(ResourceScope.None)]
209         [MethodImplAttribute(MethodImplOptions.InternalCall)]
210         [SuppressUnmanagedCodeSecurity]
211         static extern AppDomainTimerSafeHandle CreateAppDomainTimer(uint dueTime);
212
213         [System.Security.SecurityCritical]
214         [ResourceExposure(ResourceScope.None)]
215         [MethodImplAttribute(MethodImplOptions.InternalCall)]
216         [SuppressUnmanagedCodeSecurity]
217         static extern bool ChangeAppDomainTimer(AppDomainTimerSafeHandle handle, uint dueTime);
218
219         [System.Security.SecurityCritical]
220         [ResourceExposure(ResourceScope.None)]
221         [MethodImplAttribute(MethodImplOptions.InternalCall)]
222         [SuppressUnmanagedCodeSecurity]
223         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
224         static extern bool DeleteAppDomainTimer(IntPtr handle);
225
226         #endregion
227
228         #region Firing timers
229
230         //
231         // The list of timers
232         //
233         TimerQueueTimer m_timers;
234
235
236         volatile int m_pauseTicks = 0; // Time when Pause was called
237
238         [SecurityCritical]
239         internal void Pause()
240         {
241             lock(this)
242             {
243                 // Delete the native timer so that no timers are fired in the Pause zone
244                 if(m_appDomainTimer != null && !m_appDomainTimer.IsInvalid)
245                 {
246                     m_appDomainTimer.Dispose();
247                     m_appDomainTimer = null;
248                     m_isAppDomainTimerScheduled = false;
249                     m_pauseTicks = TickCount;
250                 }
251             }
252         }
253
254         [SecurityCritical]
255         internal void Resume()
256         {
257             //
258             // Update timers to adjust their due-time to accomodate Pause/Resume
259             //
260             lock (this)
261             {
262                 // prevent ThreadAbort while updating state
263                 try { }
264                 finally
265                 {
266                     int pauseTicks = m_pauseTicks;
267                     m_pauseTicks = 0; // Set this to 0 so that now timers can be scheduled
268
269                     int resumedTicks = TickCount;
270                     int pauseDuration = resumedTicks - pauseTicks;
271
272                     bool haveTimerToSchedule = false;
273                     uint nextAppDomainTimerDuration = uint.MaxValue;      
274             
275                     TimerQueueTimer timer = m_timers;
276                     while (timer != null)
277                     {
278                         Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite);
279                         Contract.Assert(resumedTicks >= timer.m_startTicks);
280
281                         uint elapsed; // How much of the timer dueTime has already elapsed
282
283                         // Timers started before the paused event has to be sufficiently delayed to accomodate 
284                         // for the Pause time. However, timers started after the Paused event shouldnt be adjusted. 
285                         // E.g. ones created by the app in its Activated event should fire when it was designated.
286                         // The Resumed event which is where this routine is executing is after this Activated and hence 
287                         // shouldn't delay this timer
288
289                         if(timer.m_startTicks <= pauseTicks)
290                             elapsed = (uint)(pauseTicks - timer.m_startTicks);
291                         else
292                             elapsed = (uint)(resumedTicks - timer.m_startTicks);
293
294                         // Handling the corner cases where a Timer was already due by the time Resume is happening,
295                         // We shouldn't delay those timers. 
296                         // Example is a timer started in App's Activated event with a very small duration
297                         timer.m_dueTime = (timer.m_dueTime > elapsed) ? timer.m_dueTime - elapsed : 0;;
298                         timer.m_startTicks = resumedTicks; // re-baseline
299
300                         if (timer.m_dueTime < nextAppDomainTimerDuration)
301                         {
302                             haveTimerToSchedule = true;
303                             nextAppDomainTimerDuration = timer.m_dueTime;
304                         }
305
306                         timer = timer.m_next;
307                     }
308                     
309                     if (haveTimerToSchedule)
310                     {
311                         EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration);
312                     }
313                 }
314             }
315         }
316
317
318         //
319         // Fire any timers that have expired, and update the native timer to schedule the rest of them.
320         //
321         private void FireNextTimers()
322         {
323             //
324             // we fire the first timer on this thread; any other timers that might have fired are queued
325             // to the ThreadPool.
326             //
327             TimerQueueTimer timerToFireOnThisThread = null;
328
329             lock (this)
330             {
331                 // prevent ThreadAbort while updating state
332                 try { }
333                 finally
334                 {
335                     //
336                     // since we got here, that means our previous timer has fired.
337                     //
338                     m_isAppDomainTimerScheduled = false;
339                     bool haveTimerToSchedule = false;
340                     uint nextAppDomainTimerDuration = uint.MaxValue;
341
342                     int nowTicks = TickCount;
343
344                     //
345                     // Sweep through all timers.  The ones that have reached their due time
346                     // will fire.  We will calculate the next native timer due time from the
347                     // other timers.
348                     //
349                     TimerQueueTimer timer = m_timers;
350                     while (timer != null)
351                     {
352                         Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite);
353
354                         uint elapsed = (uint)(nowTicks - timer.m_startTicks);
355                         if (elapsed >= timer.m_dueTime)
356                         {
357                             //
358                             // Remember the next timer in case we delete this one
359                             //
360                             TimerQueueTimer nextTimer = timer.m_next;
361
362                             if (timer.m_period != Timeout.UnsignedInfinite)
363                             {
364                                 timer.m_startTicks = nowTicks;
365                                 timer.m_dueTime = timer.m_period;
366
367                                 //
368                                 // This is a repeating timer; schedule it to run again.
369                                 //
370                                 if (timer.m_dueTime < nextAppDomainTimerDuration)
371                                 {
372                                     haveTimerToSchedule = true;
373                                     nextAppDomainTimerDuration = timer.m_dueTime;
374                                 }
375                             }
376                             else
377                             {
378                                 //
379                                 // Not repeating; remove it from the queue
380                                 //
381                                 DeleteTimer(timer);
382                             }
383
384                             //
385                             // If this is the first timer, we'll fire it on this thread.  Otherwise, queue it
386                             // to the ThreadPool.
387                             //
388                             if (timerToFireOnThisThread == null)
389                                 timerToFireOnThisThread = timer;
390                             else
391                                 QueueTimerCompletion(timer);
392
393                             timer = nextTimer;
394                         }
395                         else
396                         {
397                             //
398                             // This timer hasn't fired yet.  Just update the next time the native timer fires.
399                             //
400                             uint remaining = timer.m_dueTime - elapsed;
401                             if (remaining < nextAppDomainTimerDuration)
402                             {
403                                 haveTimerToSchedule = true;
404                                 nextAppDomainTimerDuration = remaining;
405                             }
406                             timer = timer.m_next;
407                         }
408                     }
409
410                     if (haveTimerToSchedule)
411                         EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration);
412                 }
413             }
414
415             //
416             // Fire the user timer outside of the lock!
417             //
418             if (timerToFireOnThisThread != null)
419                 timerToFireOnThisThread.Fire();
420         }
421
422         [SecuritySafeCritical]
423         private static void QueueTimerCompletion(TimerQueueTimer timer)
424         {
425             WaitCallback callback = s_fireQueuedTimerCompletion;
426             if (callback == null)
427                 s_fireQueuedTimerCompletion = callback = new WaitCallback(FireQueuedTimerCompletion);
428
429             // Can use "unsafe" variant because we take care of capturing and restoring
430             // the ExecutionContext.
431             ThreadPool.UnsafeQueueUserWorkItem(callback, timer);
432         }
433
434         private static WaitCallback s_fireQueuedTimerCompletion;
435
436         private static void FireQueuedTimerCompletion(object state)
437         {
438             ((TimerQueueTimer)state).Fire();
439         }
440
441         #endregion
442
443         #region Queue implementation
444
445         public bool UpdateTimer(TimerQueueTimer timer, uint dueTime, uint period)
446         {
447             if (timer.m_dueTime == Timeout.UnsignedInfinite)
448             {
449                 // the timer is not in the list; add it (as the head of the list).
450                 timer.m_next = m_timers;
451                 timer.m_prev = null;
452                 if (timer.m_next != null)
453                     timer.m_next.m_prev = timer;
454                 m_timers = timer;
455             }
456             timer.m_dueTime = dueTime;
457             timer.m_period = (period == 0) ? Timeout.UnsignedInfinite : period;
458             timer.m_startTicks = TickCount;
459             return EnsureAppDomainTimerFiresBy(dueTime);
460         }
461
462         public void DeleteTimer(TimerQueueTimer timer)
463         {
464             if (timer.m_dueTime != Timeout.UnsignedInfinite)
465             {
466                 if (timer.m_next != null)
467                     timer.m_next.m_prev = timer.m_prev;
468                 if (timer.m_prev != null)
469                     timer.m_prev.m_next = timer.m_next;
470                 if (m_timers == timer)
471                     m_timers = timer.m_next;
472
473                 timer.m_dueTime = Timeout.UnsignedInfinite;
474                 timer.m_period = Timeout.UnsignedInfinite;
475                 timer.m_startTicks = 0;
476                 timer.m_prev = null;
477                 timer.m_next = null;
478             }
479         }
480
481         #endregion
482     }
483
484     //
485     // A timer in our TimerQueue.
486     //
487     sealed class TimerQueueTimer
488     {
489         //
490         // All fields of this class are protected by a lock on TimerQueue.Instance.
491         //
492         // The first four fields are maintained by TimerQueue itself.
493         //
494         internal TimerQueueTimer m_next;
495         internal TimerQueueTimer m_prev;
496
497         //
498         // The time, according to TimerQueue.TickCount, when this timer's current interval started.
499         //
500         internal int m_startTicks;
501
502         //
503         // Timeout.UnsignedInfinite if we are not going to fire.  Otherwise, the offset from m_startTime when we will fire.
504         //
505         internal uint m_dueTime;
506
507         //
508         // Timeout.UnsignedInfinite if we are a single-shot timer.  Otherwise, the repeat interval.
509         //
510         internal uint m_period;
511
512         //
513         // Info about the user's callback
514         //
515         readonly TimerCallback m_timerCallback;
516         readonly Object m_state;
517         readonly ExecutionContext m_executionContext;
518
519
520         //
521         // When Timer.Dispose(WaitHandle) is used, we need to signal the wait handle only
522         // after all pending callbacks are complete.  We set m_canceled to prevent any callbacks that
523         // are already queued from running.  We track the number of callbacks currently executing in 
524         // m_callbacksRunning.  We set m_notifyWhenNoCallbacksRunning only when m_callbacksRunning
525         // reaches zero.
526         //
527         int m_callbacksRunning;
528         volatile bool m_canceled;
529         volatile WaitHandle m_notifyWhenNoCallbacksRunning;
530
531
532         [SecurityCritical]
533         internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period, ref StackCrawlMark stackMark)
534         {
535             m_timerCallback = timerCallback;
536             m_state = state;
537             m_dueTime = Timeout.UnsignedInfinite;
538             m_period = Timeout.UnsignedInfinite;
539
540             if (!ExecutionContext.IsFlowSuppressed())
541             {
542                 m_executionContext = ExecutionContext.Capture(
543                     ref stackMark,
544                     ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
545             }
546
547             //
548             // After the following statement, the timer may fire.  No more manipulation of timer state outside of
549             // the lock is permitted beyond this point!
550             //
551             if (dueTime != Timeout.UnsignedInfinite)
552                 Change(dueTime, period);
553         }
554
555
556         internal bool Change(uint dueTime, uint period)
557         {
558             bool success;
559
560             lock (TimerQueue.Instance)
561             {
562                 if (m_canceled)
563                     throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
564
565                 // prevent ThreadAbort while updating state
566                 try { }
567                 finally
568                 {
569                     m_period = period;
570
571                     if (dueTime == Timeout.UnsignedInfinite)
572                     {
573                         TimerQueue.Instance.DeleteTimer(this);
574                         success = true;
575                     }
576                     else
577                     {
578 #if !MONO
579                         if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer))
580                             FrameworkEventSource.Log.ThreadTransferSendObj(this, 1, string.Empty, true);
581 #endif
582                         success = TimerQueue.Instance.UpdateTimer(this, dueTime, period);
583                     }
584                 }
585             }
586
587             return success;
588         }
589
590
591         public void Close()
592         {
593             lock (TimerQueue.Instance)
594             {
595                 // prevent ThreadAbort while updating state
596                 try { }
597                 finally
598                 {
599                     if (!m_canceled)
600                     {
601                         m_canceled = true;
602                         TimerQueue.Instance.DeleteTimer(this);
603                     }
604                 }
605             }
606         }
607
608
609         public bool Close(WaitHandle toSignal)
610         {
611             bool success;
612             bool shouldSignal = false;
613
614             lock (TimerQueue.Instance)
615             {
616                 // prevent ThreadAbort while updating state
617                 try { }
618                 finally
619                 {
620                     if (m_canceled)
621                     {
622                         success = false;
623                     }
624                     else
625                     {
626                         m_canceled = true;
627                         m_notifyWhenNoCallbacksRunning = toSignal;
628                         TimerQueue.Instance.DeleteTimer(this);
629
630                         if (m_callbacksRunning == 0)
631                             shouldSignal = true;
632
633                         success = true;
634                     }
635                 }
636             }
637
638             if (shouldSignal)
639                 SignalNoCallbacksRunning();
640
641             return success;
642         }
643
644
645         internal void Fire()
646         {
647             bool canceled = false;
648
649             lock (TimerQueue.Instance)
650             {
651                 // prevent ThreadAbort while updating state
652                 try { }
653                 finally
654                 {
655                     canceled = m_canceled;
656                     if (!canceled)
657                         m_callbacksRunning++;
658                 }
659             }
660
661             if (canceled)
662                 return;
663
664             CallCallback();
665
666             bool shouldSignal = false;
667             lock (TimerQueue.Instance)
668             {
669                 // prevent ThreadAbort while updating state
670                 try { }
671                 finally
672                 {
673                     m_callbacksRunning--;
674                     if (m_canceled && m_callbacksRunning == 0 && m_notifyWhenNoCallbacksRunning != null)
675                         shouldSignal = true;
676                 }
677             }
678
679             if (shouldSignal)
680                 SignalNoCallbacksRunning();
681         }
682
683         [SecuritySafeCritical]
684         internal void SignalNoCallbacksRunning()
685         {
686 #if !MONO
687             Win32Native.SetEvent(m_notifyWhenNoCallbacksRunning.SafeWaitHandle);
688 #else
689             NativeEventCalls.SetEvent_internal (m_notifyWhenNoCallbacksRunning.SafeWaitHandle.DangerousGetHandle ());
690 #endif
691         }
692
693         [SecuritySafeCritical]
694         internal void CallCallback()
695         {
696 #if !MONO
697             if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer))
698                 FrameworkEventSource.Log.ThreadTransferReceiveObj(this, 1, string.Empty);
699 #endif
700
701             // call directly if EC flow is suppressed
702             if (m_executionContext == null)
703             {
704                 m_timerCallback(m_state);
705             }
706             else
707             {
708                 using (ExecutionContext executionContext = 
709                     m_executionContext.IsPreAllocatedDefault ? m_executionContext : m_executionContext.CreateCopy())
710                 {
711                     ContextCallback callback = s_callCallbackInContext;
712                     if (callback == null)
713                         s_callCallbackInContext = callback = new ContextCallback(CallCallbackInContext);
714                     
715                     ExecutionContext.Run(
716                         executionContext,
717                         callback,
718                         this,  // state
719                         true); // ignoreSyncCtx
720                 }
721             }
722         }
723
724         [SecurityCritical]
725         private static ContextCallback s_callCallbackInContext;
726
727         [SecurityCritical]
728         private static void CallCallbackInContext(object state)
729         {
730             TimerQueueTimer t = (TimerQueueTimer)state;
731             t.m_timerCallback(t.m_state);
732         }
733     }
734
735     //
736     // TimerHolder serves as an intermediary between Timer and TimerQueueTimer, releasing the TimerQueueTimer 
737     // if the Timer is collected.
738     // This is necessary because Timer itself cannot use its finalizer for this purpose.  If it did,
739     // then users could control timer lifetimes using GC.SuppressFinalize/ReRegisterForFinalize.
740     // You might ask, wouldn't that be a good thing?  Maybe (though it would be even better to offer this
741     // via first-class APIs), but Timer has never offered this, and adding it now would be a breaking
742     // change, because any code that happened to be suppressing finalization of Timer objects would now
743     // unwittingly be changing the lifetime of those timers.
744     //
745     sealed class TimerHolder
746     {
747         internal TimerQueueTimer m_timer;
748         
749         public TimerHolder(TimerQueueTimer timer) 
750         { 
751             m_timer = timer; 
752         }
753
754         ~TimerHolder() 
755         { 
756             //
757             // If shutdown has started, another thread may be suspended while holding the timer lock.
758             // So we can't safely close the timer.  
759             //
760             // Similarly, we should not close the timer during AD-unload's live-object finalization phase.
761             // A rude abort may have prevented us from releasing the lock.
762             //
763             // Note that in either case, the Timer still won't fire, because ThreadPool threads won't be
764             // allowed to run in this AppDomain.
765             //
766             if (Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload())
767                 return;
768
769             m_timer.Close(); 
770         }
771
772         public void Close()
773         {
774             m_timer.Close();
775             GC.SuppressFinalize(this);
776         }
777
778         public bool Close(WaitHandle notifyObject)
779         {
780             bool result = m_timer.Close(notifyObject);
781             GC.SuppressFinalize(this);
782             return result;
783         }
784
785     }
786
787
788     [HostProtection(Synchronization=true, ExternalThreading=true)]
789     [System.Runtime.InteropServices.ComVisible(true)]
790 #if FEATURE_REMOTING
791     public sealed class Timer : MarshalByRefObject, IDisposable
792 #else // FEATURE_REMOTING
793     public sealed class Timer : IDisposable
794 #endif // FEATURE_REMOTING
795     {
796         private const UInt32 MAX_SUPPORTED_TIMEOUT = (uint)0xfffffffe;
797
798         private TimerHolder m_timer;
799
800         [SecuritySafeCritical]
801         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
802         public Timer(TimerCallback callback, 
803                      Object        state,  
804                      int           dueTime,
805                      int           period)
806         {
807             if (dueTime < -1)
808                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
809             if (period < -1 )
810                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
811             Contract.EndContractBlock();
812             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
813
814             TimerSetup(callback,state,(UInt32)dueTime,(UInt32)period,ref stackMark);
815         }
816
817         [SecuritySafeCritical]
818         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
819         public Timer(TimerCallback callback, 
820                      Object        state,  
821                      TimeSpan      dueTime,
822                      TimeSpan      period)
823         {                
824             long dueTm = (long)dueTime.TotalMilliseconds;
825             if (dueTm < -1)
826                 throw new ArgumentOutOfRangeException("dueTm",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
827             if (dueTm > MAX_SUPPORTED_TIMEOUT)
828                 throw new ArgumentOutOfRangeException("dueTm",Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
829
830             long periodTm = (long)period.TotalMilliseconds;
831             if (periodTm < -1)
832                 throw new ArgumentOutOfRangeException("periodTm",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
833             if (periodTm > MAX_SUPPORTED_TIMEOUT)
834                 throw new ArgumentOutOfRangeException("periodTm",Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
835
836             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
837             TimerSetup(callback,state,(UInt32)dueTm,(UInt32)periodTm,ref stackMark);
838         }
839
840         [CLSCompliant(false)]
841         [SecuritySafeCritical]
842         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
843         public Timer(TimerCallback callback, 
844                      Object        state,  
845                      UInt32        dueTime,
846                      UInt32        period)
847         {
848             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
849             TimerSetup(callback,state,dueTime,period,ref stackMark);
850         }
851
852         [SecuritySafeCritical]
853         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable                                        
854         public Timer(TimerCallback callback, 
855                      Object        state,  
856                      long          dueTime,
857                      long          period)
858         {
859             if (dueTime < -1)
860                 throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
861             if (period < -1)
862                 throw new ArgumentOutOfRangeException("period",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
863             if (dueTime > MAX_SUPPORTED_TIMEOUT)
864                 throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
865             if (period > MAX_SUPPORTED_TIMEOUT)
866                 throw new ArgumentOutOfRangeException("period",Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
867             Contract.EndContractBlock();
868             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
869             TimerSetup(callback,state,(UInt32) dueTime, (UInt32) period,ref stackMark);
870         }
871
872         [SecuritySafeCritical]
873         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
874         public Timer(TimerCallback callback)
875         {
876             int dueTime = -1;    // we want timer to be registered, but not activated.  Requires caller to call
877             int period = -1;    // Change after a timer instance is created.  This is to avoid the potential
878                                 // for a timer to be fired before the returned value is assigned to the variable,
879                                 // potentially causing the callback to reference a bogus value (if passing the timer to the callback). 
880             
881             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
882             TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period, ref stackMark);
883         }
884
885         [SecurityCritical]
886         private void TimerSetup(TimerCallback callback,
887                                 Object state, 
888                                 UInt32 dueTime,
889                                 UInt32 period,
890                                 ref StackCrawlMark stackMark)
891         {
892             if (callback == null)
893                 throw new ArgumentNullException("TimerCallback");
894             Contract.EndContractBlock();
895
896             m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period, ref stackMark));
897         }
898
899         [SecurityCritical]
900         internal static void Pause()
901         {
902             TimerQueue.Instance.Pause();
903         }
904
905         [SecurityCritical]
906         internal static void Resume()
907         {
908             TimerQueue.Instance.Resume();
909         }
910      
911         public bool Change(int dueTime, int period)
912         {
913             if (dueTime < -1 )
914                 throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
915             if (period < -1)
916                 throw new ArgumentOutOfRangeException("period",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
917             Contract.EndContractBlock();
918
919             return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period);
920         }
921
922         public bool Change(TimeSpan dueTime, TimeSpan period)
923         {
924             return Change((long) dueTime.TotalMilliseconds, (long) period.TotalMilliseconds);
925         }
926
927         [CLSCompliant(false)]
928         public bool Change(UInt32 dueTime, UInt32 period)
929         {
930             return m_timer.m_timer.Change(dueTime, period);
931         }
932
933         public bool Change(long dueTime, long period)
934         {
935             if (dueTime < -1 )
936                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
937             if (period < -1)
938                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
939             if (dueTime > MAX_SUPPORTED_TIMEOUT)
940                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
941             if (period > MAX_SUPPORTED_TIMEOUT)
942                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
943             Contract.EndContractBlock();
944
945             return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period);
946         }
947     
948         public bool Dispose(WaitHandle notifyObject)
949         {
950             if (notifyObject==null)
951                 throw new ArgumentNullException("notifyObject");
952             Contract.EndContractBlock();
953
954             return m_timer.Close(notifyObject);
955         }
956          
957         public void Dispose()
958         {
959             m_timer.Close();
960         }
961
962         internal void KeepRootedWhileScheduled()
963         {
964             GC.SuppressFinalize(m_timer);
965         }
966     }
967 }