3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // <OWNER>[....]</OWNER>
10 using Microsoft.Win32.SafeHandles;
12 namespace System.Threading
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;
26 [System.Runtime.InteropServices.ComVisible(true)]
27 public delegate void TimerCallback(Object state);
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.
33 // Perf assumptions: We assume that timers are created and destroyed frequently, but rarely actually fire.
34 // There are roughly two types of timer:
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.
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.
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.
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.
48 // Note that all instance methods of this class require that the caller hold a lock on TimerQueue.Instance.
52 #region singleton pattern implementation
54 // The one-and-only TimerQueue for the AppDomain.
55 static TimerQueue s_queue = new TimerQueue();
57 public static TimerQueue Instance
59 get { return s_queue; }
64 // empty private constructor to ensure we remain a singleton.
69 #region interface to native per-AppDomain timer
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.
77 // So, on Win8, we use QueryUnbiasedInterruptTime instead; this does not count time spent
78 // in sleep/hibernate mode.
80 private static int TickCount
82 [SecuritySafeCritical]
86 if (Environment.IsWindows8OrAbove)
90 bool result = Win32Native.QueryUnbiasedInterruptTime(out time100ns);
92 throw Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
94 // convert to 100ns to milliseconds, and truncate to 32 bits.
95 return (int)(uint)(time100ns / 10000);
100 return Environment.TickCount;
106 // We use a SafeHandle to ensure that the native timer is destroyed when the AppDomain is unloaded.
109 class AppDomainTimerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
111 public AppDomainTimerSafeHandle()
117 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
118 protected override bool ReleaseHandle()
120 return DeleteAppDomainTimer(handle);
125 AppDomainTimerSafeHandle m_appDomainTimer;
127 bool m_isAppDomainTimerScheduled;
128 int m_currentAppDomainTimerStartTicks;
129 uint m_currentAppDomainTimerDuration;
131 [SecuritySafeCritical]
132 private bool EnsureAppDomainTimerFiresBy(uint requestedDuration)
135 // The VM's timer implementation does not work well for very long-duration timers.
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.
142 const uint maxPossibleDuration = 0x0fffffff;
143 uint actualDuration = Math.Min(requestedDuration, maxPossibleDuration);
145 if (m_isAppDomainTimerScheduled)
147 uint elapsed = (uint)(TickCount - m_currentAppDomainTimerStartTicks);
148 if (elapsed >= m_currentAppDomainTimerDuration)
149 return true; //the timer's about to fire
151 uint remainingDuration = m_currentAppDomainTimerDuration - elapsed;
152 if (actualDuration >= remainingDuration)
153 return true; //the timer will fire earlier than this request
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)
160 Contract.Assert(!m_isAppDomainTimerScheduled);
161 Contract.Assert(m_appDomainTimer == null);
165 if (m_appDomainTimer == null || m_appDomainTimer.IsInvalid)
167 Contract.Assert(!m_isAppDomainTimerScheduled);
169 m_appDomainTimer = CreateAppDomainTimer(actualDuration);
170 if (!m_appDomainTimer.IsInvalid)
172 m_isAppDomainTimerScheduled = true;
173 m_currentAppDomainTimerStartTicks = TickCount;
174 m_currentAppDomainTimerDuration = actualDuration;
184 if (ChangeAppDomainTimer(m_appDomainTimer, actualDuration))
186 m_isAppDomainTimerScheduled = true;
187 m_currentAppDomainTimerStartTicks = TickCount;
188 m_currentAppDomainTimerDuration = actualDuration;
199 // The VM calls this when the native timer fires.
201 [SecuritySafeCritical]
202 internal static void AppDomainTimerCallback()
204 Instance.FireNextTimers();
207 [System.Security.SecurityCritical]
208 [ResourceExposure(ResourceScope.None)]
209 [MethodImplAttribute(MethodImplOptions.InternalCall)]
210 [SuppressUnmanagedCodeSecurity]
211 static extern AppDomainTimerSafeHandle CreateAppDomainTimer(uint dueTime);
213 [System.Security.SecurityCritical]
214 [ResourceExposure(ResourceScope.None)]
215 [MethodImplAttribute(MethodImplOptions.InternalCall)]
216 [SuppressUnmanagedCodeSecurity]
217 static extern bool ChangeAppDomainTimer(AppDomainTimerSafeHandle handle, uint dueTime);
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);
228 #region Firing timers
231 // The list of timers
233 TimerQueueTimer m_timers;
236 volatile int m_pauseTicks = 0; // Time when Pause was called
239 internal void Pause()
243 // Delete the native timer so that no timers are fired in the Pause zone
244 if(m_appDomainTimer != null && !m_appDomainTimer.IsInvalid)
246 m_appDomainTimer.Dispose();
247 m_appDomainTimer = null;
248 m_isAppDomainTimerScheduled = false;
249 m_pauseTicks = TickCount;
255 internal void Resume()
258 // Update timers to adjust their due-time to accomodate Pause/Resume
262 // prevent ThreadAbort while updating state
266 int pauseTicks = m_pauseTicks;
267 m_pauseTicks = 0; // Set this to 0 so that now timers can be scheduled
269 int resumedTicks = TickCount;
270 int pauseDuration = resumedTicks - pauseTicks;
272 bool haveTimerToSchedule = false;
273 uint nextAppDomainTimerDuration = uint.MaxValue;
275 TimerQueueTimer timer = m_timers;
276 while (timer != null)
278 Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite);
279 Contract.Assert(resumedTicks >= timer.m_startTicks);
281 uint elapsed; // How much of the timer dueTime has already elapsed
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
289 if(timer.m_startTicks <= pauseTicks)
290 elapsed = (uint)(pauseTicks - timer.m_startTicks);
292 elapsed = (uint)(resumedTicks - timer.m_startTicks);
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
300 if (timer.m_dueTime < nextAppDomainTimerDuration)
302 haveTimerToSchedule = true;
303 nextAppDomainTimerDuration = timer.m_dueTime;
306 timer = timer.m_next;
309 if (haveTimerToSchedule)
311 EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration);
319 // Fire any timers that have expired, and update the native timer to schedule the rest of them.
321 private void FireNextTimers()
324 // we fire the first timer on this thread; any other timers that might have fired are queued
325 // to the ThreadPool.
327 TimerQueueTimer timerToFireOnThisThread = null;
331 // prevent ThreadAbort while updating state
336 // since we got here, that means our previous timer has fired.
338 m_isAppDomainTimerScheduled = false;
339 bool haveTimerToSchedule = false;
340 uint nextAppDomainTimerDuration = uint.MaxValue;
342 int nowTicks = TickCount;
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
349 TimerQueueTimer timer = m_timers;
350 while (timer != null)
352 Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite);
354 uint elapsed = (uint)(nowTicks - timer.m_startTicks);
355 if (elapsed >= timer.m_dueTime)
358 // Remember the next timer in case we delete this one
360 TimerQueueTimer nextTimer = timer.m_next;
362 if (timer.m_period != Timeout.UnsignedInfinite)
364 timer.m_startTicks = nowTicks;
365 timer.m_dueTime = timer.m_period;
368 // This is a repeating timer; schedule it to run again.
370 if (timer.m_dueTime < nextAppDomainTimerDuration)
372 haveTimerToSchedule = true;
373 nextAppDomainTimerDuration = timer.m_dueTime;
379 // Not repeating; remove it from the queue
385 // If this is the first timer, we'll fire it on this thread. Otherwise, queue it
386 // to the ThreadPool.
388 if (timerToFireOnThisThread == null)
389 timerToFireOnThisThread = timer;
391 QueueTimerCompletion(timer);
398 // This timer hasn't fired yet. Just update the next time the native timer fires.
400 uint remaining = timer.m_dueTime - elapsed;
401 if (remaining < nextAppDomainTimerDuration)
403 haveTimerToSchedule = true;
404 nextAppDomainTimerDuration = remaining;
406 timer = timer.m_next;
410 if (haveTimerToSchedule)
411 EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration);
416 // Fire the user timer outside of the lock!
418 if (timerToFireOnThisThread != null)
419 timerToFireOnThisThread.Fire();
422 [SecuritySafeCritical]
423 private static void QueueTimerCompletion(TimerQueueTimer timer)
425 WaitCallback callback = s_fireQueuedTimerCompletion;
426 if (callback == null)
427 s_fireQueuedTimerCompletion = callback = new WaitCallback(FireQueuedTimerCompletion);
429 // Can use "unsafe" variant because we take care of capturing and restoring
430 // the ExecutionContext.
431 ThreadPool.UnsafeQueueUserWorkItem(callback, timer);
434 private static WaitCallback s_fireQueuedTimerCompletion;
436 private static void FireQueuedTimerCompletion(object state)
438 ((TimerQueueTimer)state).Fire();
443 #region Queue implementation
445 public bool UpdateTimer(TimerQueueTimer timer, uint dueTime, uint period)
447 if (timer.m_dueTime == Timeout.UnsignedInfinite)
449 // the timer is not in the list; add it (as the head of the list).
450 timer.m_next = m_timers;
452 if (timer.m_next != null)
453 timer.m_next.m_prev = timer;
456 timer.m_dueTime = dueTime;
457 timer.m_period = (period == 0) ? Timeout.UnsignedInfinite : period;
458 timer.m_startTicks = TickCount;
459 return EnsureAppDomainTimerFiresBy(dueTime);
462 public void DeleteTimer(TimerQueueTimer timer)
464 if (timer.m_dueTime != Timeout.UnsignedInfinite)
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;
473 timer.m_dueTime = Timeout.UnsignedInfinite;
474 timer.m_period = Timeout.UnsignedInfinite;
475 timer.m_startTicks = 0;
485 // A timer in our TimerQueue.
487 sealed class TimerQueueTimer
490 // All fields of this class are protected by a lock on TimerQueue.Instance.
492 // The first four fields are maintained by TimerQueue itself.
494 internal TimerQueueTimer m_next;
495 internal TimerQueueTimer m_prev;
498 // The time, according to TimerQueue.TickCount, when this timer's current interval started.
500 internal int m_startTicks;
503 // Timeout.UnsignedInfinite if we are not going to fire. Otherwise, the offset from m_startTime when we will fire.
505 internal uint m_dueTime;
508 // Timeout.UnsignedInfinite if we are a single-shot timer. Otherwise, the repeat interval.
510 internal uint m_period;
513 // Info about the user's callback
515 readonly TimerCallback m_timerCallback;
516 readonly Object m_state;
517 readonly ExecutionContext m_executionContext;
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
527 int m_callbacksRunning;
528 volatile bool m_canceled;
529 volatile WaitHandle m_notifyWhenNoCallbacksRunning;
533 internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period, ref StackCrawlMark stackMark)
535 m_timerCallback = timerCallback;
537 m_dueTime = Timeout.UnsignedInfinite;
538 m_period = Timeout.UnsignedInfinite;
540 if (!ExecutionContext.IsFlowSuppressed())
542 m_executionContext = ExecutionContext.Capture(
544 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
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!
551 if (dueTime != Timeout.UnsignedInfinite)
552 Change(dueTime, period);
556 internal bool Change(uint dueTime, uint period)
560 lock (TimerQueue.Instance)
563 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
565 // prevent ThreadAbort while updating state
571 if (dueTime == Timeout.UnsignedInfinite)
573 TimerQueue.Instance.DeleteTimer(this);
579 if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer))
580 FrameworkEventSource.Log.ThreadTransferSendObj(this, 1, string.Empty, true);
582 success = TimerQueue.Instance.UpdateTimer(this, dueTime, period);
593 lock (TimerQueue.Instance)
595 // prevent ThreadAbort while updating state
602 TimerQueue.Instance.DeleteTimer(this);
609 public bool Close(WaitHandle toSignal)
612 bool shouldSignal = false;
614 lock (TimerQueue.Instance)
616 // prevent ThreadAbort while updating state
627 m_notifyWhenNoCallbacksRunning = toSignal;
628 TimerQueue.Instance.DeleteTimer(this);
630 if (m_callbacksRunning == 0)
639 SignalNoCallbacksRunning();
647 bool canceled = false;
649 lock (TimerQueue.Instance)
651 // prevent ThreadAbort while updating state
655 canceled = m_canceled;
657 m_callbacksRunning++;
666 bool shouldSignal = false;
667 lock (TimerQueue.Instance)
669 // prevent ThreadAbort while updating state
673 m_callbacksRunning--;
674 if (m_canceled && m_callbacksRunning == 0 && m_notifyWhenNoCallbacksRunning != null)
680 SignalNoCallbacksRunning();
683 [SecuritySafeCritical]
684 internal void SignalNoCallbacksRunning()
687 Win32Native.SetEvent(m_notifyWhenNoCallbacksRunning.SafeWaitHandle);
689 NativeEventCalls.SetEvent_internal (m_notifyWhenNoCallbacksRunning.SafeWaitHandle.DangerousGetHandle ());
693 [SecuritySafeCritical]
694 internal void CallCallback()
697 if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer))
698 FrameworkEventSource.Log.ThreadTransferReceiveObj(this, 1, string.Empty);
701 // call directly if EC flow is suppressed
702 if (m_executionContext == null)
704 m_timerCallback(m_state);
708 using (ExecutionContext executionContext =
709 m_executionContext.IsPreAllocatedDefault ? m_executionContext : m_executionContext.CreateCopy())
711 ContextCallback callback = s_callCallbackInContext;
712 if (callback == null)
713 s_callCallbackInContext = callback = new ContextCallback(CallCallbackInContext);
715 ExecutionContext.Run(
719 true); // ignoreSyncCtx
725 private static ContextCallback s_callCallbackInContext;
728 private static void CallCallbackInContext(object state)
730 TimerQueueTimer t = (TimerQueueTimer)state;
731 t.m_timerCallback(t.m_state);
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.
745 sealed class TimerHolder
747 internal TimerQueueTimer m_timer;
749 public TimerHolder(TimerQueueTimer timer)
757 // If shutdown has started, another thread may be suspended while holding the timer lock.
758 // So we can't safely close the timer.
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.
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.
766 if (Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload())
775 GC.SuppressFinalize(this);
778 public bool Close(WaitHandle notifyObject)
780 bool result = m_timer.Close(notifyObject);
781 GC.SuppressFinalize(this);
788 [HostProtection(Synchronization=true, ExternalThreading=true)]
789 [System.Runtime.InteropServices.ComVisible(true)]
791 public sealed class Timer : MarshalByRefObject, IDisposable
792 #else // FEATURE_REMOTING
793 public sealed class Timer : IDisposable
794 #endif // FEATURE_REMOTING
796 private const UInt32 MAX_SUPPORTED_TIMEOUT = (uint)0xfffffffe;
798 private TimerHolder m_timer;
800 [SecuritySafeCritical]
801 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
802 public Timer(TimerCallback callback,
808 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
810 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
811 Contract.EndContractBlock();
812 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
814 TimerSetup(callback,state,(UInt32)dueTime,(UInt32)period,ref stackMark);
817 [SecuritySafeCritical]
818 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
819 public Timer(TimerCallback callback,
824 long dueTm = (long)dueTime.TotalMilliseconds;
826 throw new ArgumentOutOfRangeException("dueTm",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
827 if (dueTm > MAX_SUPPORTED_TIMEOUT)
828 throw new ArgumentOutOfRangeException("dueTm",Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
830 long periodTm = (long)period.TotalMilliseconds;
832 throw new ArgumentOutOfRangeException("periodTm",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
833 if (periodTm > MAX_SUPPORTED_TIMEOUT)
834 throw new ArgumentOutOfRangeException("periodTm",Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
836 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
837 TimerSetup(callback,state,(UInt32)dueTm,(UInt32)periodTm,ref stackMark);
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,
848 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
849 TimerSetup(callback,state,dueTime,period,ref stackMark);
852 [SecuritySafeCritical]
853 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
854 public Timer(TimerCallback callback,
860 throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
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);
872 [SecuritySafeCritical]
873 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
874 public Timer(TimerCallback callback)
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).
881 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
882 TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period, ref stackMark);
886 private void TimerSetup(TimerCallback callback,
890 ref StackCrawlMark stackMark)
892 if (callback == null)
893 throw new ArgumentNullException("TimerCallback");
894 Contract.EndContractBlock();
896 m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period, ref stackMark));
900 internal static void Pause()
902 TimerQueue.Instance.Pause();
906 internal static void Resume()
908 TimerQueue.Instance.Resume();
911 public bool Change(int dueTime, int period)
914 throw new ArgumentOutOfRangeException("dueTime",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
916 throw new ArgumentOutOfRangeException("period",Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
917 Contract.EndContractBlock();
919 return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period);
922 public bool Change(TimeSpan dueTime, TimeSpan period)
924 return Change((long) dueTime.TotalMilliseconds, (long) period.TotalMilliseconds);
927 [CLSCompliant(false)]
928 public bool Change(UInt32 dueTime, UInt32 period)
930 return m_timer.m_timer.Change(dueTime, period);
933 public bool Change(long dueTime, long period)
936 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
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();
945 return m_timer.m_timer.Change((UInt32)dueTime, (UInt32)period);
948 public bool Dispose(WaitHandle notifyObject)
950 if (notifyObject==null)
951 throw new ArgumentNullException("notifyObject");
952 Contract.EndContractBlock();
954 return m_timer.Close(notifyObject);
957 public void Dispose()
962 internal void KeepRootedWhileScheduled()
964 GC.SuppressFinalize(m_timer);