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