The Labs \ Source Viewer \ SSCLI \ System.Threading \ TimerBase

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*=============================================================================
  16. **
  17. ** Class: TimerQueue
  18. **
  19. **
  20. ** Purpose: Class for creating and managing a threadpool
  21. **
  22. **
  23. =============================================================================*/
  24. namespace System.Threading
  25. {
  26.     using System.Threading;
  27.     using System;
  28.     using System.Security;
  29.     using System.Security.Permissions;
  30.     using Microsoft.Win32;
  31.     using System.Runtime.CompilerServices;
  32.     using System.Runtime.InteropServices;
  33.     using System.Runtime.ConstrainedExecution;
  34.    
  35.     internal class _TimerCallback
  36.     {
  37.         TimerCallback _timerCallback;
  38.         ExecutionContext _executionContext;
  39.         object _state;
  40.         static internal ContextCallback _ccb = new ContextCallback(TimerCallback_Context);
  41.         static internal void TimerCallback_Context(object state)
  42.         {
  43.             _TimerCallback helper = (_TimerCallback)state;
  44.             helper._timerCallback(helper._state);
  45.            
  46.         }
  47.        
  48.         internal _TimerCallback(TimerCallback timerCallback, object state, ref StackCrawlMark stackMark)
  49.         {
  50.             _timerCallback = timerCallback;
  51.             _state = state;
  52.             if (!ExecutionContext.IsFlowSuppressed()) {
  53.                 _executionContext = ExecutionContext.Capture(ref stackMark);
  54.                 ExecutionContext.ClearSyncContext(_executionContext);
  55.             }
  56.         }
  57.        
  58.         // call back helper
  59.         static internal void PerformTimerCallback(object state)
  60.         {
  61.             _TimerCallback helper = (_TimerCallback)state;
  62.            
  63.             BCLDebug.Assert(helper != null, "Null state passed to PerformTimerCallback!");
  64.             // call directly if EC flow is suppressed
  65.             if (helper._executionContext == null) {
  66.                 TimerCallback callback = helper._timerCallback;
  67.                 callback(helper._state);
  68.             }
  69.             else {
  70.                 // From this point on we can use useExecutionContext for this callback
  71.                 ExecutionContext.Run(helper._executionContext.CreateCopy(), _ccb, helper);
  72.             }
  73.         }
  74.     }
  75.    
  76.     [System.Runtime.InteropServices.ComVisible(true)]
  77.     public delegate void TimerCallback(object state);
  78.    
  79.     [HostProtection(Synchronization = true, ExternalThreading = true)]
  80.     internal sealed class TimerBase : CriticalFinalizerObject, IDisposable
  81.     {
  82.         #pragma warning disable 169
  83.         private IntPtr timerHandle;
  84.         private IntPtr delegateInfo;
  85.         #pragma warning restore 169
  86.         private int timerDeleted;
  87.         private int m_lock = 0;
  88.        
  89.         ~TimerBase()
  90.         {
  91.             // lock(this) cannot be used reliably in Cer since thin lock could be
  92.             // promoted to syncblock and that is not a guaranteed operation
  93.             bool bLockTaken = false;
  94.             do {
  95.                 if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) {
  96.                     bLockTaken = true;
  97.                     try {
  98.                         DeleteTimerNative(null);
  99.                     }
  100.                     finally {
  101.                         m_lock = 0;
  102.                     }
  103.                 }
  104.                 Thread.SpinWait(1);
  105.                 // yield to processor
  106.             }
  107.             while (!bLockTaken);
  108.         }
  109.        
  110.         internal void AddTimer(TimerCallback callback, object state, UInt32 dueTime, UInt32 period, ref StackCrawlMark stackMark)
  111.         {
  112.             if (callback != null) {
  113.                 _TimerCallback callbackHelper = new _TimerCallback(callback, state, ref stackMark);
  114.                 state = (object)callbackHelper;
  115.                 AddTimerNative(state, dueTime, period, ref stackMark);
  116.                 timerDeleted = 0;
  117.             }
  118.             else {
  119.                 throw new ArgumentNullException("TimerCallback");
  120.             }
  121.         }
  122.        
  123.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  124.         internal bool ChangeTimer(UInt32 dueTime, UInt32 period)
  125.         {
  126.             bool status = false;
  127.             bool bLockTaken = false;
  128.            
  129.             // prepare here to prevent threadabort from occuring which could
  130.             // destroy m_lock state. lock(this) can't be used due to critical
  131.             // finalizer and thinlock/syncblock escalation.
  132.             RuntimeHelpers.PrepareConstrainedRegions();
  133.             try {
  134.             }
  135.             finally {
  136.                 do {
  137.                     if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) {
  138.                         bLockTaken = true;
  139.                         try {
  140.                             if (timerDeleted != 0)
  141.                                 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
  142.                             status = ChangeTimerNative(dueTime, period);
  143.                         }
  144.                         finally {
  145.                             m_lock = 0;
  146.                         }
  147.                     }
  148.                     Thread.SpinWait(1);
  149.                     // yield to processor
  150.                 }
  151.                 while (!bLockTaken);
  152.             }
  153.             return status;
  154.            
  155.         }
  156.        
  157.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  158.         internal bool Dispose(WaitHandle notifyObject)
  159.         {
  160.             bool status = false;
  161.             bool bLockTaken = false;
  162.             RuntimeHelpers.PrepareConstrainedRegions();
  163.             try {
  164.             }
  165.             finally {
  166.                 do {
  167.                     if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) {
  168.                         bLockTaken = true;
  169.                         try {
  170.                             status = DeleteTimerNative(notifyObject.SafeWaitHandle);
  171.                         }
  172.                         finally {
  173.                             m_lock = 0;
  174.                         }
  175.                     }
  176.                     Thread.SpinWait(1);
  177.                     // yield to processor
  178.                 }
  179.                 while (!bLockTaken);
  180.                 GC.SuppressFinalize(this);
  181.             }
  182.            
  183.             return status;
  184.         }
  185.        
  186.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  187.         public void Dispose()
  188.         {
  189.             bool bLockTaken = false;
  190.             RuntimeHelpers.PrepareConstrainedRegions();
  191.             try {
  192.             }
  193.             finally {
  194.                 do {
  195.                     if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) {
  196.                         bLockTaken = true;
  197.                         try {
  198.                             DeleteTimerNative(null);
  199.                         }
  200.                         finally {
  201.                             m_lock = 0;
  202.                         }
  203.                     }
  204.                     Thread.SpinWait(1);
  205.                     // yield to processor
  206.                 }
  207.                 while (!bLockTaken);
  208.                 GC.SuppressFinalize(this);
  209.             }
  210.         }
  211.        
  212.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  213.         private extern void AddTimerNative(object state, UInt32 dueTime, UInt32 period, ref StackCrawlMark stackMark);
  214.        
  215.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  216.         private extern bool ChangeTimerNative(UInt32 dueTime, UInt32 period);
  217.        
  218.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  219.         private extern bool DeleteTimerNative(SafeHandle notifyObject);
  220.        
  221.     }
  222.    
  223.     [HostProtection(Synchronization = true, ExternalThreading = true)]
  224.     [System.Runtime.InteropServices.ComVisible(true)]
  225.     public sealed class Timer : MarshalByRefObject, IDisposable
  226.     {
  227.         private const UInt32 MAX_SUPPORTED_TIMEOUT = (uint)4294967294u;
  228.         private TimerBase timerBase;
  229.        
  230.         public Timer(TimerCallback callback, object state, int dueTime, int period)
  231.         {
  232.             if (dueTime < -1)
  233.                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  234.             if (period < -1)
  235.                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  236.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  237.            
  238.             TimerSetup(callback, state, (UInt32)dueTime, (UInt32)period, ref stackMark);
  239.         }
  240.        
  241.         public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
  242.         {
  243.             long dueTm = (long)dueTime.TotalMilliseconds;
  244.             if (dueTm < -1)
  245.                 throw new ArgumentOutOfRangeException("dueTm", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  246.             if (dueTm > MAX_SUPPORTED_TIMEOUT)
  247.                 throw new ArgumentOutOfRangeException("dueTm", Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
  248.            
  249.             long periodTm = (long)period.TotalMilliseconds;
  250.             if (periodTm < -1)
  251.                 throw new ArgumentOutOfRangeException("periodTm", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  252.             if (periodTm > MAX_SUPPORTED_TIMEOUT)
  253.                 throw new ArgumentOutOfRangeException("periodTm", Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
  254.            
  255.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  256.             TimerSetup(callback, state, (UInt32)dueTm, (UInt32)periodTm, ref stackMark);
  257.         }
  258.        
  259.         [CLSCompliant(false)]
  260.         public Timer(TimerCallback callback, object state, UInt32 dueTime, UInt32 period)
  261.         {
  262.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  263.             TimerSetup(callback, state, dueTime, period, ref stackMark);
  264.         }
  265.        
  266.         public Timer(TimerCallback callback, object state, long dueTime, long period)
  267.         {
  268.             if (dueTime < -1)
  269.                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  270.             if (period < -1)
  271.                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  272.             if (dueTime > MAX_SUPPORTED_TIMEOUT)
  273.                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
  274.             if (period > MAX_SUPPORTED_TIMEOUT)
  275.                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
  276.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  277.             TimerSetup(callback, state, (UInt32)dueTime, (UInt32)period, ref stackMark);
  278.         }
  279.        
  280.         public Timer(TimerCallback callback)
  281.         {
  282.             int dueTime = -1;
  283.             // we want timer to be registered, but not activated. Requires caller to call
  284.             int period = -1;
  285.             // Change after a timer instance is created. This is to avoid the potential
  286.             // for a timer to be fired before the returned value is assigned to the variable,
  287.             // potentially causing the callback to reference a bogus value (if passing the timer to the callback).
  288.             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  289.             TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period, ref stackMark);
  290.         }
  291.        
  292.         private void TimerSetup(TimerCallback callback, object state, UInt32 dueTime, UInt32 period, ref StackCrawlMark stackMark)
  293.         {
  294.             timerBase = new TimerBase();
  295.             timerBase.AddTimer(callback, state, (UInt32)dueTime, (UInt32)period, ref stackMark);
  296.         }
  297.        
  298.         public bool Change(int dueTime, int period)
  299.         {
  300.             if (dueTime < -1)
  301.                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  302.             if (period < -1)
  303.                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  304.            
  305.             return timerBase.ChangeTimer((UInt32)dueTime, (UInt32)period);
  306.         }
  307.        
  308.         public bool Change(TimeSpan dueTime, TimeSpan period)
  309.         {
  310.             return Change((long)dueTime.TotalMilliseconds, (long)period.TotalMilliseconds);
  311.         }
  312.        
  313.         [CLSCompliant(false)]
  314.         public bool Change(UInt32 dueTime, UInt32 period)
  315.         {
  316.             return timerBase.ChangeTimer(dueTime, period);
  317.         }
  318.        
  319.         public bool Change(long dueTime, long period)
  320.         {
  321.             if (dueTime < -1)
  322.                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  323.             if (period < -1)
  324.                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
  325.             if (dueTime > MAX_SUPPORTED_TIMEOUT)
  326.                 throw new ArgumentOutOfRangeException("dueTime", Environment.GetResourceString("ArgumentOutOfRange_TimeoutTooLarge"));
  327.             if (period > MAX_SUPPORTED_TIMEOUT)
  328.                 throw new ArgumentOutOfRangeException("period", Environment.GetResourceString("ArgumentOutOfRange_PeriodTooLarge"));
  329.            
  330.             return timerBase.ChangeTimer((UInt32)dueTime, (UInt32)period);
  331.         }
  332.        
  333.         public bool Dispose(WaitHandle notifyObject)
  334.         {
  335.             if (notifyObject == null)
  336.                 throw new ArgumentNullException("notifyObject");
  337.             return timerBase.Dispose(notifyObject);
  338.         }
  339.        
  340.        
  341.         public void Dispose()
  342.         {
  343.             timerBase.Dispose();
  344.         }
  345.     }
  346. }

Developer Fusion