1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Runtime.InteropServices;
7 
8 namespace System.Threading
9 {
10     //
11     // Windows-specific implementation of Timer
12     //
13     internal partial class TimerQueue
14     {
15         private IntPtr _nativeTimer;
16 
17         [NativeCallable(CallingConvention = CallingConvention.StdCall)]
TimerCallback(IntPtr instance, IntPtr context, IntPtr timer)18         private static void TimerCallback(IntPtr instance, IntPtr context, IntPtr timer)
19         {
20             var wrapper = ThreadPoolCallbackWrapper.Enter();
21             Instance.FireNextTimers();
22             wrapper.Exit();
23         }
24 
SetTimer(uint actualDuration)25         private unsafe void SetTimer(uint actualDuration)
26         {
27             if (_nativeTimer == IntPtr.Zero)
28             {
29                 IntPtr nativeCallback = AddrofIntrinsics.AddrOf<Interop.mincore.TimerCallback>(TimerCallback);
30 
31                 _nativeTimer = Interop.mincore.CreateThreadpoolTimer(nativeCallback, IntPtr.Zero, IntPtr.Zero);
32                 if (_nativeTimer == IntPtr.Zero)
33                     throw new OutOfMemoryException();
34             }
35 
36             // Negative time indicates the amount of time to wait relative to the current time, in 100 nanosecond units
37             long dueTime = -10000 * (long)actualDuration;
38             Interop.mincore.SetThreadpoolTimer(_nativeTimer, &dueTime, 0, 0);
39         }
40 
41         //
42         // We need to keep our notion of time synchronized with the calls to SleepEx that drive
43         // the underlying native timer.  In Win8, SleepEx does not count the time the machine spends
44         // sleeping/hibernating.  Environment.TickCount (GetTickCount) *does* count that time,
45         // so we will get out of sync with SleepEx if we use that method.
46         //
47         // So, on Win8, we use QueryUnbiasedInterruptTime instead; this does not count time spent
48         // in sleep/hibernate mode.
49         //
50         private static int TickCount
51         {
52             get
53             {
54                 ulong time100ns;
55 
56                 bool result = Interop.mincore.QueryUnbiasedInterruptTime(out time100ns);
57                 Debug.Assert(result);
58 
59                 // convert to 100ns to milliseconds, and truncate to 32 bits.
60                 return (int)(uint)(time100ns / 10000);
61             }
62         }
63     }
64 
65     internal sealed partial class TimerQueueTimer
66     {
SignalNoCallbacksRunning()67         private void SignalNoCallbacksRunning()
68         {
69             Interop.mincore.SetEvent(_notifyWhenNoCallbacksRunning.SafeWaitHandle);
70         }
71     }
72 }
73