VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/timer-win.cpp@ 89762

最後變更 在這個檔案從89762是 89762,由 vboxsync 提交於 4 年 前

Runtime/r3/win/timer-win.cpp: Implement RTTimerCreateEx, RTTimerStart and RTTimerStop as well as removing code paths unused for a very long time

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 15.2 KB
 
1/* $Id: timer-win.cpp 89762 2021-06-17 09:39:11Z vboxsync $ */
2/** @file
3 * IPRT - Timer.
4 */
5
6/*
7 * Copyright (C) 2006-2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_TIMER
32#define _WIN32_WINNT 0x0500
33#include <iprt/win/windows.h>
34
35#include <iprt/timer.h>
36#ifdef USE_CATCH_UP
37# include <iprt/time.h>
38#endif
39#include <iprt/alloc.h>
40#include <iprt/assert.h>
41#include <iprt/thread.h>
42#include <iprt/log.h>
43#include <iprt/asm.h>
44#include <iprt/semaphore.h>
45#include <iprt/err.h>
46#include "internal/magics.h"
47
48RT_C_DECLS_BEGIN
49/* from sysinternals. */
50NTSYSAPI LONG NTAPI NtSetTimerResolution(IN ULONG DesiredResolution, IN BOOLEAN SetResolution, OUT PULONG CurrentResolution);
51NTSYSAPI LONG NTAPI NtQueryTimerResolution(OUT PULONG MaximumResolution, OUT PULONG MinimumResolution, OUT PULONG CurrentResolution);
52RT_C_DECLS_END
53
54
55/*********************************************************************************************************************************
56* Structures and Typedefs *
57*********************************************************************************************************************************/
58/**
59 * The internal representation of a timer handle.
60 */
61typedef struct RTTIMER
62{
63 /** Magic.
64 * This is RTTIMER_MAGIC, but changes to something else before the timer
65 * is destroyed to indicate clearly that thread should exit. */
66 uint32_t volatile u32Magic;
67 /** Flag indicating the timer is suspended. */
68 bool volatile fSuspended;
69 /** Flag indicating that the timer has been destroyed. */
70 bool volatile fDestroyed;
71 /** User argument. */
72 void *pvUser;
73 /** Callback. */
74 PFNRTTIMER pfnTimer;
75 /** The current tick. */
76 uint64_t iTick;
77 /** The timer interval. 0 if one-shot. */
78 uint64_t u64NanoInterval;
79 /** The first shot interval. 0 if ASAP. */
80 uint64_t volatile u64NanoFirst;
81 /** Time handle. */
82 HANDLE hTimer;
83 /** USE_CATCH_UP: ns time of the next tick.
84 * !USE_CATCH_UP: -uMilliesInterval * 10000 */
85 LARGE_INTEGER llNext;
86 /** The thread handle of the timer thread. */
87 RTTHREAD Thread;
88 /** Event semaphore on which the thread is blocked. */
89 RTSEMEVENT Event;
90 /** The error/status of the timer.
91 * Initially -1, set to 0 when the timer have been successfully started, and
92 * to errno on failure in starting the timer. */
93 volatile int iError;
94} RTTIMER;
95
96
97
98/**
99 * Timer thread.
100 */
101static DECLCALLBACK(int) rttimerCallback(RTTHREAD hThreadSelf, void *pvArg)
102{
103 PRTTIMER pTimer = (PRTTIMER)(void *)pvArg;
104 Assert(pTimer->u32Magic == RTTIMER_MAGIC);
105
106 /*
107 * Bounce our priority up quite a bit.
108 */
109 if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
110 {
111 int rc = GetLastError();
112 AssertMsgFailed(("Failed to set priority class lasterror %d.\n", rc));
113 pTimer->iError = RTErrConvertFromWin32(rc);
114 RTThreadUserSignal(hThreadSelf);
115 return rc;
116 }
117
118 /*
119 * The work loop.
120 */
121 RTThreadUserSignal(hThreadSelf);
122
123 while ( !pTimer->fDestroyed
124 && pTimer->u32Magic == RTTIMER_MAGIC)
125 {
126 /*
127 * Wait for a start or destroy event.
128 */
129 if (pTimer->fSuspended)
130 {
131 int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
132 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
133 {
134 AssertRC(rc);
135 if (pTimer->fDestroyed)
136 continue;
137 RTThreadSleep(1000); /* Don't cause trouble! */
138 }
139 if ( pTimer->fSuspended
140 || pTimer->fDestroyed)
141 continue;
142 }
143
144 /*
145 * Start the waitable timer.
146 */
147 pTimer->llNext.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100;
148 LARGE_INTEGER ll;
149 if (pTimer->u64NanoFirst)
150 {
151 GetSystemTimeAsFileTime((LPFILETIME)&ll);
152 ll.QuadPart += pTimer->u64NanoFirst / 100;
153 pTimer->u64NanoFirst = 0;
154 }
155 else
156 ll.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100;
157 if (!SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE))
158 {
159 ASMAtomicXchgBool(&pTimer->fSuspended, true);
160 int rc = GetLastError();
161 AssertMsgFailed(("Failed to set timer, lasterr %d.\n", rc));
162 pTimer->iError = RTErrConvertFromWin32(rc);
163 RTThreadUserSignal(hThreadSelf);
164 continue; /* back to suspended mode. */
165 }
166 pTimer->iError = 0;
167 RTThreadUserSignal(hThreadSelf);
168
169 /*
170 * Timer Service Loop.
171 */
172 do
173 {
174 int rc = WaitForSingleObjectEx(pTimer->hTimer, INFINITE, FALSE);
175 if (pTimer->u32Magic != RTTIMER_MAGIC)
176 break;
177 if (rc == WAIT_OBJECT_0)
178 {
179 /*
180 * Callback the handler.
181 */
182 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
183
184 /*
185 * Rearm the timer handler.
186 */
187 ll = pTimer->llNext;
188 BOOL fRc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
189 AssertMsg(fRc || pTimer->u32Magic != RTTIMER_MAGIC, ("last error %d\n", GetLastError())); NOREF(fRc);
190 }
191 else
192 {
193 /*
194 * We failed during wait, so just signal the destructor and exit.
195 */
196 int rc2 = GetLastError();
197 RTThreadUserSignal(hThreadSelf);
198 AssertMsgFailed(("Wait on hTimer failed, rc=%d lasterr=%d\n", rc, rc2)); NOREF(rc2);
199 return -1;
200 }
201 } while (RT_LIKELY( !pTimer->fSuspended
202 && !pTimer->fDestroyed
203 && pTimer->u32Magic == RTTIMER_MAGIC));
204
205 /*
206 * Disable the timer.
207 */
208 int rc = CancelWaitableTimer (pTimer->hTimer);
209 AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError()));
210
211 /*
212 * ACK any pending suspend request.
213 */
214 if (!pTimer->fDestroyed)
215 {
216 pTimer->iError = 0;
217 RTThreadUserSignal(hThreadSelf);
218 }
219 }
220
221 /*
222 * Exit.
223 */
224 pTimer->iError = 0;
225 RTThreadUserSignal(hThreadSelf);
226 return VINF_SUCCESS;
227}
228
229
230RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
231{
232 /*
233 * We don't support the fancy MP features.
234 */
235 if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
236 return VERR_NOT_SUPPORTED;
237
238 /*
239 * On windows we'll have to set the timer resolution before
240 * we start the timer.
241 */
242 ULONG ulMax = UINT32_MAX;
243 ULONG ulMin = UINT32_MAX;
244 ULONG ulCur = UINT32_MAX;
245 ULONG ulReq = (ULONG)(u64NanoInterval / 100);
246 NtQueryTimerResolution(&ulMax, &ulMin, &ulCur);
247 Log(("NtQueryTimerResolution -> ulMax=%lu00ns ulMin=%lu00ns ulCur=%lu00ns\n", ulMax, ulMin, ulCur));
248 if (ulCur > ulMin && ulCur > ulReq)
249 {
250 ulReq = RT_MIN(ulMin, ulReq);
251 if (NtSetTimerResolution(ulReq, TRUE, &ulCur) >= 0)
252 Log(("Changed timer resolution to %lu*100ns.\n", ulReq));
253 else if (NtSetTimerResolution(10000, TRUE, &ulCur) >= 0)
254 Log(("Changed timer resolution to 1ms.\n"));
255 else if (NtSetTimerResolution(20000, TRUE, &ulCur) >= 0)
256 Log(("Changed timer resolution to 2ms.\n"));
257 else if (NtSetTimerResolution(40000, TRUE, &ulCur) >= 0)
258 Log(("Changed timer resolution to 4ms.\n"));
259 else if (ulMin <= 50000 && NtSetTimerResolution(ulMin, TRUE, &ulCur) >= 0)
260 Log(("Changed timer resolution to %lu *100ns.\n", ulMin));
261 else
262 {
263 AssertMsgFailed(("Failed to configure timer resolution!\n"));
264 return VERR_INTERNAL_ERROR;
265 }
266 }
267
268 /*
269 * Create new timer.
270 */
271 int rc = VERR_IPE_UNINITIALIZED_STATUS;
272 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
273 if (pTimer)
274 {
275 pTimer->u32Magic = RTTIMER_MAGIC;
276 pTimer->fSuspended = true;
277 pTimer->fDestroyed = false;
278 pTimer->Thread = NIL_RTTHREAD;
279 pTimer->pfnTimer = pfnTimer;
280 pTimer->pvUser = pvUser;
281 pTimer->u64NanoInterval = u64NanoInterval;
282
283 rc = RTSemEventCreate(&pTimer->Event);
284 AssertRC(rc);
285 if (RT_SUCCESS(rc))
286 {
287 /*
288 * Create Win32 event semaphore.
289 */
290 pTimer->iError = 0;
291 pTimer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
292 if (pTimer->hTimer)
293 {
294 /*
295 * Kick off the timer thread.
296 */
297 rc = RTThreadCreate(&pTimer->Thread, rttimerCallback, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
298 if (RT_SUCCESS(rc))
299 {
300 /*
301 * Wait for the timer to successfully create the timer
302 * If we don't get a response in 10 secs, then we assume we're screwed.
303 */
304 rc = RTThreadUserWait(pTimer->Thread, 10000);
305 if (RT_SUCCESS(rc))
306 {
307 rc = pTimer->iError;
308 if (RT_SUCCESS(rc))
309 {
310 *ppTimer = pTimer;
311 return VINF_SUCCESS;
312 }
313 }
314
315 /* bail out */
316 ASMAtomicXchgBool(&pTimer->fDestroyed, true);
317 ASMAtomicXchgU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
318 RTThreadWait(pTimer->Thread, 45*1000, NULL);
319 CancelWaitableTimer(pTimer->hTimer);
320 }
321 CloseHandle(pTimer->hTimer);
322 }
323 RTSemEventDestroy(pTimer->Event);
324 pTimer->Event = NIL_RTSEMEVENT;
325 }
326
327 RTMemFree(pTimer);
328 }
329 else
330 rc = VERR_NO_MEMORY;
331 return rc;
332}
333
334
335RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
336{
337 /* NULL is ok. */
338 if (!pTimer)
339 return VINF_SUCCESS;
340
341 int rc = VINF_SUCCESS;
342 AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
343 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
344 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
345
346 /*
347 * Signal that we want the thread to exit.
348 */
349 ASMAtomicWriteBool(&pTimer->fDestroyed, true);
350 ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
351
352 /*
353 * Suspend the timer if it's running.
354 */
355 if (!pTimer->fSuspended)
356 {
357 LARGE_INTEGER ll = {0};
358 ll.LowPart = 100;
359 rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
360 AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError()));
361 }
362
363 rc = RTSemEventSignal(pTimer->Event);
364 AssertRC(rc);
365
366 /*
367 * Wait for the thread to exit.
368 * And if it don't wanna exit, we'll get kill it.
369 */
370 rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
371 if (RT_FAILURE(rc))
372 TerminateThread((HANDLE)RTThreadGetNative(pTimer->Thread), UINT32_MAX);
373
374 /*
375 * Free resource.
376 */
377 rc = CloseHandle(pTimer->hTimer);
378 AssertMsg(rc, ("CloseHandle lasterr=%d\n", GetLastError()));
379
380 RTSemEventDestroy(pTimer->Event);
381 pTimer->Event = NIL_RTSEMEVENT;
382
383 RTMemFree(pTimer);
384 return rc;
385}
386
387
388RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
389{
390 /*
391 * Validate input.
392 */
393 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
394 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
395 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
396
397 RTThreadUserReset(pTimer->Thread);
398
399 /*
400 * Already running?
401 */
402 if (!ASMAtomicXchgBool(&pTimer->fSuspended, false))
403 return VERR_TIMER_ACTIVE;
404 LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
405
406 /*
407 * Tell the thread to start servicing the timer.
408 * Wait for it to ACK the request to avoid reset races.
409 */
410 ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First);
411 ASMAtomicUoWriteU64(&pTimer->iTick, 0);
412 int rc = RTSemEventSignal(pTimer->Event);
413 if (RT_SUCCESS(rc))
414 {
415 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
416 AssertRC(rc);
417 RTThreadUserReset(pTimer->Thread);
418 }
419 else
420 AssertRC(rc);
421
422 if (RT_FAILURE(rc))
423 ASMAtomicXchgBool(&pTimer->fSuspended, true);
424 return rc;
425}
426
427
428RTDECL(int) RTTimerStop(PRTTIMER pTimer)
429{
430 /*
431 * Validate input.
432 */
433 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
434 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
435
436 RTThreadUserReset(pTimer->Thread);
437
438 /*
439 * Already running?
440 */
441 if (ASMAtomicXchgBool(&pTimer->fSuspended, true))
442 return VERR_TIMER_SUSPENDED;
443 LogFlow(("RTTimerStop: pTimer=%p\n", pTimer));
444
445 /*
446 * Tell the thread to stop servicing the timer.
447 */
448 int rc = VINF_SUCCESS;
449 if (RTThreadSelf() != pTimer->Thread)
450 {
451 LARGE_INTEGER ll = {0};
452 ll.LowPart = 100;
453 rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
454 AssertMsg(rc, ("SetWaitableTimer lasterr=%d\n", GetLastError()));
455 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
456 AssertRC(rc);
457 RTThreadUserReset(pTimer->Thread);
458 }
459
460 return rc;
461}
462
463
464RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
465{
466 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
467 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
468 NOREF(u64NanoInterval);
469 return VERR_NOT_SUPPORTED;
470}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette