1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System.Threading
{
internal partial class TimerQueue
{
#region interface to native per-AppDomain timer
#if !FEATURE_PAL
private static int TickCount
{
get
{
// We need to keep our notion of time synchronized with the calls to SleepEx that drive
// the underlying native timer. In Win8, SleepEx does not count the time the machine spends
// sleeping/hibernating. Environment.TickCount (GetTickCount) *does* count that time,
// so we will get out of sync with SleepEx if we use that method.
//
// So, on Win8, we use QueryUnbiasedInterruptTime instead; this does not count time spent
// in sleep/hibernate mode.
if (Environment.IsWindows8OrAbove)
{
ulong time100ns;
bool result = Interop.Kernel32.QueryUnbiasedInterruptTime(out time100ns);
if (!result)
throw Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
// convert to 100ns to milliseconds, and truncate to 32 bits.
return (int)(uint)(time100ns / 10000);
}
else
{
return Environment.TickCount;
}
}
}
#endif
// We use a SafeHandle to ensure that the native timer is destroyed when the AppDomain is unloaded.
private sealed class AppDomainTimerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public AppDomainTimerSafeHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return DeleteAppDomainTimer(handle);
}
}
private readonly int _id; // TimerQueues[_id] == this
private AppDomainTimerSafeHandle m_appDomainTimer;
private TimerQueue(int id)
{
_id = id;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool SetTimer(uint actualDuration)
{
if (m_appDomainTimer == null || m_appDomainTimer.IsInvalid)
{
Debug.Assert(!_isTimerScheduled);
Debug.Assert(_id >= 0 && _id < Instances.Length && this == Instances[_id]);
m_appDomainTimer = CreateAppDomainTimer(actualDuration, _id);
return !m_appDomainTimer.IsInvalid;
}
else
{
return ChangeAppDomainTimer(m_appDomainTimer, actualDuration);
}
}
// The VM calls this when a native timer fires.
internal static void AppDomainTimerCallback(int id)
{
Debug.Assert(id >= 0 && id < Instances.Length && Instances[id]._id == id);
Instances[id].FireNextTimers();
}
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern AppDomainTimerSafeHandle CreateAppDomainTimer(uint dueTime, int id);
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern bool ChangeAppDomainTimer(AppDomainTimerSafeHandle handle, uint dueTime);
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern bool DeleteAppDomainTimer(IntPtr handle);
#endregion
}
}
|