PageRenderTime 91ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/referencesource/mscorlib/system/threading/timer.cs

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