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