VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/TMAll.cpp@ 7802

Last change on this file since 7802 was 7109, checked in by vboxsync, 17 years ago

Added TMTimerSetNano and TMTimerSetMicro.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 51.2 KB
Line 
1/* $Id: TMAll.cpp 7109 2008-02-25 14:38:50Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager, all contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.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
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_TM
23#include <VBox/tm.h>
24#include <VBox/mm.h>
25#ifdef IN_RING3
26# include <VBox/rem.h>
27#endif
28#include "TMInternal.h"
29#include <VBox/vm.h>
30
31#include <VBox/param.h>
32#include <VBox/err.h>
33#include <VBox/log.h>
34#include <VBox/sup.h>
35#include <iprt/time.h>
36#include <iprt/assert.h>
37#include <iprt/asm.h>
38#ifdef IN_RING3
39# include <iprt/thread.h>
40#endif
41
42
43/**
44 * Schedule the queue which was changed.
45 */
46DECLINLINE(void) tmSchedule(PTMTIMER pTimer)
47{
48 PVM pVM = pTimer->CTXALLSUFF(pVM);
49 if (VM_IS_EMT(pVM))
50 {
51 STAM_PROFILE_START(&pVM->tm.s.CTXALLSUFF(StatScheduleOne), a);
52 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTXALLSUFF(paTimerQueues)[pTimer->enmClock];
53 Log3(("tmSchedule: tmTimerQueueSchedule\n"));
54 tmTimerQueueSchedule(pVM, pQueue);
55#ifdef VBOX_STRICT
56 tmTimerQueuesSanityChecks(pVM, "tmSchedule");
57#endif
58 STAM_PROFILE_STOP(&pVM->tm.s.CTXALLSUFF(StatScheduleOne), a);
59 }
60 else if (!VM_FF_ISSET(pVM, VM_FF_TIMER)) /**@todo only do this when arming the timer. */
61 {
62 STAM_COUNTER_INC(&pVM->tm.s.StatScheduleSetFF);
63 VM_FF_SET(pVM, VM_FF_TIMER);
64#ifdef IN_RING3
65 REMR3NotifyTimerPending(pVM);
66 VMR3NotifyFF(pVM, true);
67#endif
68 }
69}
70
71
72/**
73 * Try change the state to enmStateNew from enmStateOld
74 * and link the timer into the scheduling queue.
75 *
76 * @returns Success indicator.
77 * @param pTimer Timer in question.
78 * @param enmStateNew The new timer state.
79 * @param enmStateOld The old timer state.
80 */
81DECLINLINE(bool) tmTimerTry(PTMTIMER pTimer, TMTIMERSTATE enmStateNew, TMTIMERSTATE enmStateOld)
82{
83 /*
84 * Attempt state change.
85 */
86 bool fRc;
87 TM_TRY_SET_STATE(pTimer, enmStateNew, enmStateOld, fRc);
88 return fRc;
89}
90
91
92/**
93 * Links the timer onto the scheduling queue.
94 *
95 * @param pQueue The timer queue the timer belongs to.
96 * @param pTimer The timer.
97 */
98DECLINLINE(void) tmTimerLink(PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
99{
100 Assert(!pTimer->offScheduleNext);
101 const int32_t offHeadNew = (intptr_t)pTimer - (intptr_t)pQueue;
102 int32_t offHead;
103 do
104 {
105 offHead = pQueue->offSchedule;
106 if (offHead)
107 pTimer->offScheduleNext = ((intptr_t)pQueue + offHead) - (intptr_t)pTimer;
108 else
109 pTimer->offScheduleNext = 0;
110 } while (!ASMAtomicCmpXchgS32(&pQueue->offSchedule, offHeadNew, offHead));
111}
112
113
114/**
115 * Try change the state to enmStateNew from enmStateOld
116 * and link the timer into the scheduling queue.
117 *
118 * @returns Success indicator.
119 * @param pTimer Timer in question.
120 * @param enmStateNew The new timer state.
121 * @param enmStateOld The old timer state.
122 */
123DECLINLINE(bool) tmTimerTryWithLink(PTMTIMER pTimer, TMTIMERSTATE enmStateNew, TMTIMERSTATE enmStateOld)
124{
125 if (tmTimerTry(pTimer, enmStateNew, enmStateOld))
126 {
127 tmTimerLink(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(paTimerQueues)[pTimer->enmClock], pTimer);
128 return true;
129 }
130 return false;
131}
132
133
134#ifdef VBOX_HIGH_RES_TIMERS_HACK
135/**
136 * Set FF if we've passed the next virtual event.
137 *
138 * This function is called before FFs are checked in the inner execution EM loops.
139 *
140 * @returns Virtual timer ticks to the next event.
141 * @param pVM Pointer to the shared VM structure.
142 * @thread The emulation thread.
143 */
144TMDECL(uint64_t) TMTimerPoll(PVM pVM)
145{
146 /*
147 * Return straight away if the timer FF is already set.
148 */
149 if (VM_FF_ISSET(pVM, VM_FF_TIMER))
150 {
151 STAM_COUNTER_INC(&pVM->tm.s.StatPollAlreadySet);
152 return 0;
153 }
154
155 /*
156 * Get current time and check the expire times of the two relevant queues.
157 */
158 const uint64_t u64Now = TMVirtualGet(pVM);
159
160 /*
161 * TMCLOCK_VIRTUAL
162 */
163 const uint64_t u64Expire1 = pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire;
164 const int64_t i64Delta1 = u64Expire1 - u64Now;
165 if (i64Delta1 <= 0)
166 {
167 LogFlow(("TMTimerPoll: expire1=%RU64 <= now=%RU64\n", u64Expire1, u64Now));
168 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtual);
169 VM_FF_SET(pVM, VM_FF_TIMER);
170#ifdef IN_RING3
171 REMR3NotifyTimerPending(pVM);
172#endif
173 return 0;
174 }
175
176 /*
177 * TMCLOCK_VIRTUAL_SYNC
178 * This isn't quite as stright forward if in a catch-up, not only do
179 * we have to adjust the 'now' but when have to adjust the delta as well.
180 */
181 const uint64_t u64Expire2 = pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
182 uint64_t u64VirtualSyncNow;
183 if (!pVM->tm.s.fVirtualSyncTicking)
184 u64VirtualSyncNow = pVM->tm.s.u64VirtualSync;
185 else
186 {
187 if (!pVM->tm.s.fVirtualSyncCatchUp)
188 u64VirtualSyncNow = u64Now - pVM->tm.s.offVirtualSync;
189 else
190 {
191 uint64_t off = pVM->tm.s.offVirtualSync;
192 uint64_t u64Delta = u64Now - pVM->tm.s.u64VirtualSyncCatchUpPrev;
193 if (RT_LIKELY(!(u64Delta >> 32)))
194 {
195 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage, 100);
196 if (off > u64Sub + pVM->tm.s.offVirtualSyncGivenUp)
197 off -= u64Sub;
198 else
199 off = pVM->tm.s.offVirtualSyncGivenUp;
200 }
201 u64VirtualSyncNow = u64Now - off;
202 }
203 }
204 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
205 if (i64Delta2 <= 0)
206 {
207 LogFlow(("TMTimerPoll: expire2=%RU64 <= now=%RU64\n", u64Expire2, u64Now));
208 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtualSync);
209 VM_FF_SET(pVM, VM_FF_TIMER);
210#ifdef IN_RING3
211 REMR3NotifyTimerPending(pVM);
212#endif
213 return 0;
214 }
215 if (pVM->tm.s.fVirtualSyncCatchUp)
216 i64Delta2 = ASMMultU64ByU32DivByU32(i64Delta2, 100, pVM->tm.s.u32VirtualSyncCatchUpPercentage + 100);
217
218 /*
219 * Return the time left to the next event.
220 */
221 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
222 return RT_MIN(i64Delta1, i64Delta2);
223}
224
225
226/**
227 * Set FF if we've passed the next virtual event.
228 *
229 * This function is called before FFs are checked in the inner execution EM loops.
230 *
231 * @returns The GIP timestamp of the next event.
232 * 0 if the next event has already expired.
233 * @param pVM Pointer to the shared VM structure.
234 * @param pVM Pointer to the shared VM structure.
235 * @param pu64Delta Where to store the delta.
236 * @thread The emulation thread.
237 */
238TMDECL(uint64_t) TMTimerPollGIP(PVM pVM, uint64_t *pu64Delta)
239{
240 /*
241 * Return straight away if the timer FF is already set.
242 */
243 if (VM_FF_ISSET(pVM, VM_FF_TIMER))
244 {
245 STAM_COUNTER_INC(&pVM->tm.s.StatPollAlreadySet);
246 *pu64Delta = 0;
247 return 0;
248 }
249
250 /*
251 * Get current time and check the expire times of the two relevant queues.
252 */
253 const uint64_t u64Now = TMVirtualGet(pVM);
254
255 /*
256 * TMCLOCK_VIRTUAL
257 */
258 const uint64_t u64Expire1 = pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire;
259 const int64_t i64Delta1 = u64Expire1 - u64Now;
260 if (i64Delta1 <= 0)
261 {
262 LogFlow(("TMTimerPoll: expire1=%RU64 <= now=%RU64\n", u64Expire1, u64Now));
263 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtual);
264 VM_FF_SET(pVM, VM_FF_TIMER);
265#ifdef IN_RING3
266 REMR3NotifyTimerPending(pVM);
267#endif
268 *pu64Delta = 0;
269 return 0;
270 }
271
272 /*
273 * TMCLOCK_VIRTUAL_SYNC
274 * This isn't quite as stright forward if in a catch-up, not only do
275 * we have to adjust the 'now' but when have to adjust the delta as well.
276 */
277 const uint64_t u64Expire2 = pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
278 uint64_t u64VirtualSyncNow;
279 if (!pVM->tm.s.fVirtualSyncTicking)
280 u64VirtualSyncNow = pVM->tm.s.u64VirtualSync;
281 else
282 {
283 if (!pVM->tm.s.fVirtualSyncCatchUp)
284 u64VirtualSyncNow = u64Now - pVM->tm.s.offVirtualSync;
285 else
286 {
287 uint64_t off = pVM->tm.s.offVirtualSync;
288 uint64_t u64Delta = u64Now - pVM->tm.s.u64VirtualSyncCatchUpPrev;
289 if (RT_LIKELY(!(u64Delta >> 32)))
290 {
291 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage, 100);
292 if (off > u64Sub + pVM->tm.s.offVirtualSyncGivenUp)
293 off -= u64Sub;
294 else
295 off = pVM->tm.s.offVirtualSyncGivenUp;
296 }
297 u64VirtualSyncNow = u64Now - off;
298 }
299 }
300 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
301 if (i64Delta2 <= 0)
302 {
303 LogFlow(("TMTimerPoll: expire2=%RU64 <= now=%RU64\n", u64Expire2, u64Now));
304 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtualSync);
305 VM_FF_SET(pVM, VM_FF_TIMER);
306#ifdef IN_RING3
307 REMR3NotifyTimerPending(pVM);
308#endif
309 *pu64Delta = 0;
310 return 0;
311 }
312 if (pVM->tm.s.fVirtualSyncCatchUp)
313 i64Delta2 = ASMMultU64ByU32DivByU32(i64Delta2, 100, pVM->tm.s.u32VirtualSyncCatchUpPercentage + 100);
314
315 /*
316 * Return the GIP time of the next event.
317 * This is the reverse of what tmVirtualGetRaw is doing.
318 */
319 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
320 uint64_t u64GipTime = RT_MIN(i64Delta1, i64Delta2);
321 *pu64Delta = u64GipTime;
322 u64GipTime += u64Now + pVM->tm.s.u64VirtualOffset;
323 if (RT_UNLIKELY(!pVM->tm.s.fVirtualWarpDrive))
324 {
325 u64GipTime -= pVM->tm.s.u64VirtualWarpDriveStart; /* the start is GIP time. */
326 u64GipTime *= 100;
327 u64GipTime /= pVM->tm.s.u32VirtualWarpDrivePercentage;
328 u64GipTime += pVM->tm.s.u64VirtualWarpDriveStart;
329 }
330 return u64GipTime;
331}
332#endif
333
334
335/**
336 * Gets the host context ring-3 pointer of the timer.
337 *
338 * @returns HC R3 pointer.
339 * @param pTimer Timer handle as returned by one of the create functions.
340 */
341TMDECL(PTMTIMERR3) TMTimerR3Ptr(PTMTIMER pTimer)
342{
343 return (PTMTIMERR3)MMHyperCCToR3(pTimer->CTXALLSUFF(pVM), pTimer);
344}
345
346
347/**
348 * Gets the host context ring-0 pointer of the timer.
349 *
350 * @returns HC R0 pointer.
351 * @param pTimer Timer handle as returned by one of the create functions.
352 */
353TMDECL(PTMTIMERR0) TMTimerR0Ptr(PTMTIMER pTimer)
354{
355 return (PTMTIMERR0)MMHyperCCToR0(pTimer->CTXALLSUFF(pVM), pTimer);
356}
357
358
359/**
360 * Gets the GC pointer of the timer.
361 *
362 * @returns GC pointer.
363 * @param pTimer Timer handle as returned by one of the create functions.
364 */
365TMDECL(PTMTIMERGC) TMTimerGCPtr(PTMTIMER pTimer)
366{
367 return (PTMTIMERGC)MMHyperCCToGC(pTimer->CTXALLSUFF(pVM), pTimer);
368}
369
370
371/**
372 * Destroy a timer
373 *
374 * @returns VBox status.
375 * @param pTimer Timer handle as returned by one of the create functions.
376 */
377TMDECL(int) TMTimerDestroy(PTMTIMER pTimer)
378{
379 int cRetries = 1000;
380 do
381 {
382 /*
383 * Change to any of the DESTROY states if valid.
384 */
385 TMTIMERSTATE enmState = pTimer->enmState;
386 Log2(("TMTimerDestroy: pTimer=%p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
387 pTimer, tmTimerState(enmState), HCSTRING(pTimer->pszDesc), cRetries));
388 switch (enmState)
389 {
390 case TMTIMERSTATE_EXPIRED:
391 if (!VM_IS_EMT(pTimer->CTXALLSUFF(pVM)))
392 {
393 AssertMsgFailed(("Attempted timer destruction from other thread while expire pending! (%s)\n", HCSTRING(pTimer->pszDesc)));
394 return VERR_INVALID_PARAMETER;
395 }
396 /* fall thru */
397 case TMTIMERSTATE_STOPPED:
398 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_DESTROY, enmState))
399 {
400 tmSchedule(pTimer);
401 return VINF_SUCCESS;
402 }
403 break;
404
405 case TMTIMERSTATE_ACTIVE:
406 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_STOP_DESTROY, enmState))
407 {
408 tmSchedule(pTimer);
409 return VINF_SUCCESS;
410 }
411 break;
412
413 case TMTIMERSTATE_PENDING_STOP:
414 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
415 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_DESTROY, enmState))
416 {
417 tmSchedule(pTimer);
418 return VINF_SUCCESS;
419 }
420 break;
421
422 case TMTIMERSTATE_PENDING_DESTROY:
423 case TMTIMERSTATE_PENDING_STOP_DESTROY:
424 AssertMsgFailed(("How many times do you think you can destroy the same timer... (%s)\n", HCSTRING(pTimer->pszDesc)));
425 return VERR_INVALID_PARAMETER;
426
427 case TMTIMERSTATE_PENDING_RESCHEDULE:
428 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_DESTROY, enmState))
429 {
430 tmSchedule(pTimer);
431 return VINF_SUCCESS;
432 }
433 break;
434
435 case TMTIMERSTATE_PENDING_SCHEDULE:
436 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_DESTROY, enmState))
437 {
438 tmSchedule(pTimer);
439 return VINF_SUCCESS;
440 }
441 break;
442
443 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
444 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
445#ifdef IN_RING3
446 if (!RTThreadYield())
447 RTThreadSleep(1);
448#endif
449 break;
450
451 /*
452 * Invalid states.
453 */
454 case TMTIMERSTATE_FREE:
455 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
456 return VERR_TM_INVALID_STATE;
457 default:
458 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
459 return VERR_TM_UNKNOWN_STATE;
460 }
461 } while (cRetries-- > 0);
462
463 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, HCSTRING(pTimer->pszDesc)));
464 return VERR_INTERNAL_ERROR;
465}
466
467
468/**
469 * Arm a timer with a (new) expire time.
470 *
471 * @returns VBox status.
472 * @param pTimer Timer handle as returned by one of the create functions.
473 * @param u64Expire New expire time.
474 */
475TMDECL(int) TMTimerSet(PTMTIMER pTimer, uint64_t u64Expire)
476{
477 STAM_PROFILE_START(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerSet), a);
478
479 /** @todo find the most frequently used paths and make them skip tmSchedule and tmTimerTryWithLink. */
480 int cRetries = 1000;
481 do
482 {
483 /*
484 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
485 */
486 TMTIMERSTATE enmState = pTimer->enmState;
487 Log2(("TMTimerSet: pTimer=%p:{.enmState=%s, .pszDesc='%s'} cRetries=%d u64Expire=%llu\n",
488 pTimer, tmTimerState(enmState), HCSTRING(pTimer->pszDesc), cRetries, u64Expire));
489 switch (enmState)
490 {
491 case TMTIMERSTATE_EXPIRED:
492 case TMTIMERSTATE_STOPPED:
493 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
494 {
495 Assert(!pTimer->offPrev);
496 Assert(!pTimer->offNext);
497 AssertMsg( pTimer->enmClock != TMCLOCK_VIRTUAL_SYNC
498 || pTimer->CTXALLSUFF(pVM)->tm.s.fVirtualSyncTicking
499 || u64Expire >= pTimer->CTXALLSUFF(pVM)->tm.s.u64VirtualSync,
500 ("%RU64 < %RU64 %s\n", u64Expire, pTimer->CTXALLSUFF(pVM)->tm.s.u64VirtualSync, R3STRING(pTimer->pszDesc)));
501 pTimer->u64Expire = u64Expire;
502 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
503 tmSchedule(pTimer);
504 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerSet), a);
505 return VINF_SUCCESS;
506 }
507 break;
508
509 case TMTIMERSTATE_PENDING_SCHEDULE:
510 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
511 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
512 {
513 Assert(!pTimer->offPrev);
514 Assert(!pTimer->offNext);
515 pTimer->u64Expire = u64Expire;
516 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
517 tmSchedule(pTimer);
518 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerSet), a);
519 return VINF_SUCCESS;
520 }
521 break;
522
523
524 case TMTIMERSTATE_ACTIVE:
525 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
526 {
527 pTimer->u64Expire = u64Expire;
528 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
529 tmSchedule(pTimer);
530 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerSet), a);
531 return VINF_SUCCESS;
532 }
533 break;
534
535 case TMTIMERSTATE_PENDING_RESCHEDULE:
536 case TMTIMERSTATE_PENDING_STOP:
537 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
538 {
539 pTimer->u64Expire = u64Expire;
540 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
541 tmSchedule(pTimer);
542 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerSet), a);
543 return VINF_SUCCESS;
544 }
545 break;
546
547
548 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
549 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
550#ifdef IN_RING3
551 if (!RTThreadYield())
552 RTThreadSleep(1);
553#else
554/** @todo call host context and yield after a couple of iterations */
555#endif
556 break;
557
558 /*
559 * Invalid states.
560 */
561 case TMTIMERSTATE_PENDING_DESTROY:
562 case TMTIMERSTATE_PENDING_STOP_DESTROY:
563 case TMTIMERSTATE_FREE:
564 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
565 return VERR_TM_INVALID_STATE;
566 default:
567 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
568 return VERR_TM_UNKNOWN_STATE;
569 }
570 } while (cRetries-- > 0);
571
572 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, HCSTRING(pTimer->pszDesc)));
573 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerSet), a);
574 return VERR_INTERNAL_ERROR;
575}
576
577
578/**
579 * Arm a timer with a (new) expire time relative to current time.
580 *
581 * @returns VBox status.
582 * @param pTimer Timer handle as returned by one of the create functions.
583 * @param cMilliesToNext Number of millieseconds to the next tick.
584 */
585TMDECL(int) TMTimerSetMillies(PTMTIMER pTimer, uint32_t cMilliesToNext)
586{
587 PVM pVM = pTimer->CTXALLSUFF(pVM);
588 switch (pTimer->enmClock)
589 {
590 case TMCLOCK_VIRTUAL:
591 return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualGet(pVM));
592 case TMCLOCK_VIRTUAL_SYNC:
593 return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualSyncGet(pVM));
594 case TMCLOCK_REAL:
595 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
596 return TMTimerSet(pTimer, cMilliesToNext + TMRealGet(pVM));
597 case TMCLOCK_TSC:
598 return TMTimerSet(pTimer, cMilliesToNext * pVM->tm.s.cTSCTicksPerSecond / 1000 + TMCpuTickGet(pVM));
599
600 default:
601 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
602 return VERR_INTERNAL_ERROR;
603 }
604}
605
606
607/**
608 * Arm a timer with a (new) expire time relative to current time.
609 *
610 * @returns VBox status.
611 * @param pTimer Timer handle as returned by one of the create functions.
612 * @param cMicrosToNext Number of microseconds to the next tick.
613 */
614TMDECL(int) TMTimerSetMicro(PTMTIMER pTimer, uint64_t cMicrosToNext)
615{
616 PVM pVM = pTimer->CTXALLSUFF(pVM);
617 switch (pTimer->enmClock)
618 {
619 case TMCLOCK_VIRTUAL:
620 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
621 return TMTimerSet(pTimer, cMicrosToNext * 1000 + TMVirtualGet(pVM));
622
623 case TMCLOCK_VIRTUAL_SYNC:
624 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
625 return TMTimerSet(pTimer, cMicrosToNext * 1000 + TMVirtualSyncGet(pVM));
626
627 case TMCLOCK_REAL:
628 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
629 return TMTimerSet(pTimer, cMicrosToNext / 1000 + TMRealGet(pVM));
630
631 case TMCLOCK_TSC:
632 return TMTimerSet(pTimer, TMTimerFromMicro(pTimer, cMicrosToNext) + TMCpuTickGet(pVM));
633
634 default:
635 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
636 return VERR_INTERNAL_ERROR;
637 }
638}
639
640
641/**
642 * Arm a timer with a (new) expire time relative to current time.
643 *
644 * @returns VBox status.
645 * @param pTimer Timer handle as returned by one of the create functions.
646 * @param cNanosToNext Number of nanoseconds to the next tick.
647 */
648TMDECL(int) TMTimerSetNano(PTMTIMER pTimer, uint64_t cNanosToNext)
649{
650 PVM pVM = pTimer->CTXALLSUFF(pVM);
651 switch (pTimer->enmClock)
652 {
653 case TMCLOCK_VIRTUAL:
654 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
655 return TMTimerSet(pTimer, cNanosToNext + TMVirtualGet(pVM));
656
657 case TMCLOCK_VIRTUAL_SYNC:
658 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
659 return TMTimerSet(pTimer, cNanosToNext + TMVirtualSyncGet(pVM));
660
661 case TMCLOCK_REAL:
662 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
663 return TMTimerSet(pTimer, cNanosToNext / 1000000 + TMRealGet(pVM));
664
665 case TMCLOCK_TSC:
666 return TMTimerSet(pTimer, TMTimerFromNano(pTimer, cNanosToNext) + TMCpuTickGet(pVM));
667
668 default:
669 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
670 return VERR_INTERNAL_ERROR;
671 }
672}
673
674
675/**
676 * Stop the timer.
677 * Use TMR3TimerArm() to "un-stop" the timer.
678 *
679 * @returns VBox status.
680 * @param pTimer Timer handle as returned by one of the create functions.
681 */
682TMDECL(int) TMTimerStop(PTMTIMER pTimer)
683{
684 STAM_PROFILE_START(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
685 /** @todo see if this function needs optimizing. */
686 int cRetries = 1000;
687 do
688 {
689 /*
690 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
691 */
692 TMTIMERSTATE enmState = pTimer->enmState;
693 Log2(("TMTimerStop: pTimer=%p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
694 pTimer, tmTimerState(enmState), HCSTRING(pTimer->pszDesc), cRetries));
695 switch (enmState)
696 {
697 case TMTIMERSTATE_EXPIRED:
698 //AssertMsgFailed(("You don't stop an expired timer dude!\n"));
699 return VERR_INVALID_PARAMETER;
700
701 case TMTIMERSTATE_STOPPED:
702 case TMTIMERSTATE_PENDING_STOP:
703 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
704 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
705 return VINF_SUCCESS;
706
707 case TMTIMERSTATE_PENDING_SCHEDULE:
708 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, enmState))
709 {
710 Assert(!pTimer->offPrev);
711 Assert(!pTimer->offNext);
712 tmSchedule(pTimer);
713 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
714 return VINF_SUCCESS;
715 }
716
717 case TMTIMERSTATE_PENDING_RESCHEDULE:
718 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
719 {
720 tmSchedule(pTimer);
721 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
722 return VINF_SUCCESS;
723 }
724 break;
725
726 case TMTIMERSTATE_ACTIVE:
727 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
728 {
729 tmSchedule(pTimer);
730 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
731 return VINF_SUCCESS;
732 }
733 break;
734
735 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
736 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
737#ifdef IN_RING3
738 if (!RTThreadYield())
739 RTThreadSleep(1);
740#else
741/**@todo call host and yield cpu after a while. */
742#endif
743 break;
744
745 /*
746 * Invalid states.
747 */
748 case TMTIMERSTATE_PENDING_DESTROY:
749 case TMTIMERSTATE_PENDING_STOP_DESTROY:
750 case TMTIMERSTATE_FREE:
751 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
752 return VERR_TM_INVALID_STATE;
753 default:
754 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
755 return VERR_TM_UNKNOWN_STATE;
756 }
757 } while (cRetries-- > 0);
758
759 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, HCSTRING(pTimer->pszDesc)));
760 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
761 return VERR_INTERNAL_ERROR;
762}
763
764
765/**
766 * Get the current clock time.
767 * Handy for calculating the new expire time.
768 *
769 * @returns Current clock time.
770 * @param pTimer Timer handle as returned by one of the create functions.
771 */
772TMDECL(uint64_t) TMTimerGet(PTMTIMER pTimer)
773{
774 uint64_t u64;
775 PVM pVM = pTimer->CTXALLSUFF(pVM);
776 switch (pTimer->enmClock)
777 {
778 case TMCLOCK_VIRTUAL:
779 u64 = TMVirtualGet(pVM);
780 break;
781 case TMCLOCK_VIRTUAL_SYNC:
782 u64 = TMVirtualSyncGet(pVM);
783 break;
784 case TMCLOCK_REAL:
785 u64 = TMRealGet(pVM);
786 break;
787 case TMCLOCK_TSC:
788 u64 = TMCpuTickGet(pVM);
789 break;
790
791 default:
792 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
793 return ~(uint64_t)0;
794 }
795 //Log2(("TMTimerGet: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
796 // u64, pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
797 return u64;
798}
799
800
801/**
802 * Get the freqency of the timer clock.
803 *
804 * @returns Clock frequency (as Hz of course).
805 * @param pTimer Timer handle as returned by one of the create functions.
806 */
807TMDECL(uint64_t) TMTimerGetFreq(PTMTIMER pTimer)
808{
809 switch (pTimer->enmClock)
810 {
811 case TMCLOCK_VIRTUAL:
812 case TMCLOCK_VIRTUAL_SYNC:
813 return TMCLOCK_FREQ_VIRTUAL;
814
815 case TMCLOCK_REAL:
816 return TMCLOCK_FREQ_REAL;
817
818 case TMCLOCK_TSC:
819 return TMCpuTicksPerSecond(pTimer->CTXALLSUFF(pVM));
820
821 default:
822 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
823 return 0;
824 }
825}
826
827
828/**
829 * Get the current clock time as nanoseconds.
830 *
831 * @returns The timer clock as nanoseconds.
832 * @param pTimer Timer handle as returned by one of the create functions.
833 */
834TMDECL(uint64_t) TMTimerGetNano(PTMTIMER pTimer)
835{
836 return TMTimerToNano(pTimer, TMTimerGet(pTimer));
837}
838
839
840/**
841 * Get the current clock time as microseconds.
842 *
843 * @returns The timer clock as microseconds.
844 * @param pTimer Timer handle as returned by one of the create functions.
845 */
846TMDECL(uint64_t) TMTimerGetMicro(PTMTIMER pTimer)
847{
848 return TMTimerToMicro(pTimer, TMTimerGet(pTimer));
849}
850
851
852/**
853 * Get the current clock time as milliseconds.
854 *
855 * @returns The timer clock as milliseconds.
856 * @param pTimer Timer handle as returned by one of the create functions.
857 */
858TMDECL(uint64_t) TMTimerGetMilli(PTMTIMER pTimer)
859{
860 return TMTimerToMilli(pTimer, TMTimerGet(pTimer));
861}
862
863
864/**
865 * Converts the specified timer clock time to nanoseconds.
866 *
867 * @returns nanoseconds.
868 * @param pTimer Timer handle as returned by one of the create functions.
869 * @param u64Ticks The clock ticks.
870 * @remark There could be rounding errors here. We just do a simple integere divide
871 * without any adjustments.
872 */
873TMDECL(uint64_t) TMTimerToNano(PTMTIMER pTimer, uint64_t u64Ticks)
874{
875 switch (pTimer->enmClock)
876 {
877 case TMCLOCK_VIRTUAL:
878 case TMCLOCK_VIRTUAL_SYNC:
879 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
880 return u64Ticks;
881
882 case TMCLOCK_REAL:
883 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
884 return u64Ticks * 1000000;
885
886 case TMCLOCK_TSC:
887 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
888 return 0;
889
890 default:
891 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
892 return 0;
893 }
894}
895
896
897/**
898 * Converts the specified timer clock time to microseconds.
899 *
900 * @returns microseconds.
901 * @param pTimer Timer handle as returned by one of the create functions.
902 * @param u64Ticks The clock ticks.
903 * @remark There could be rounding errors here. We just do a simple integere divide
904 * without any adjustments.
905 */
906TMDECL(uint64_t) TMTimerToMicro(PTMTIMER pTimer, uint64_t u64Ticks)
907{
908 switch (pTimer->enmClock)
909 {
910 case TMCLOCK_VIRTUAL:
911 case TMCLOCK_VIRTUAL_SYNC:
912 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
913 return u64Ticks / 1000;
914
915 case TMCLOCK_REAL:
916 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
917 return u64Ticks * 1000;
918
919 case TMCLOCK_TSC:
920 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
921 return 0;
922
923 default:
924 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
925 return 0;
926 }
927}
928
929
930/**
931 * Converts the specified timer clock time to milliseconds.
932 *
933 * @returns milliseconds.
934 * @param pTimer Timer handle as returned by one of the create functions.
935 * @param u64Ticks The clock ticks.
936 * @remark There could be rounding errors here. We just do a simple integere divide
937 * without any adjustments.
938 */
939TMDECL(uint64_t) TMTimerToMilli(PTMTIMER pTimer, uint64_t u64Ticks)
940{
941 switch (pTimer->enmClock)
942 {
943 case TMCLOCK_VIRTUAL:
944 case TMCLOCK_VIRTUAL_SYNC:
945 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
946 return u64Ticks / 1000000;
947
948 case TMCLOCK_REAL:
949 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
950 return u64Ticks;
951
952 case TMCLOCK_TSC:
953 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
954 return 0;
955
956 default:
957 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
958 return 0;
959 }
960}
961
962
963/**
964 * Converts the specified nanosecond timestamp to timer clock ticks.
965 *
966 * @returns timer clock ticks.
967 * @param pTimer Timer handle as returned by one of the create functions.
968 * @param u64NanoTS The nanosecond value ticks to convert.
969 * @remark There could be rounding and overflow errors here.
970 */
971TMDECL(uint64_t) TMTimerFromNano(PTMTIMER pTimer, uint64_t u64NanoTS)
972{
973 switch (pTimer->enmClock)
974 {
975 case TMCLOCK_VIRTUAL:
976 case TMCLOCK_VIRTUAL_SYNC:
977 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
978 return u64NanoTS;
979
980 case TMCLOCK_REAL:
981 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
982 return u64NanoTS / 1000000;
983
984 case TMCLOCK_TSC:
985 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
986 return 0;
987
988 default:
989 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
990 return 0;
991 }
992}
993
994
995/**
996 * Converts the specified microsecond timestamp to timer clock ticks.
997 *
998 * @returns timer clock ticks.
999 * @param pTimer Timer handle as returned by one of the create functions.
1000 * @param u64MicroTS The microsecond value ticks to convert.
1001 * @remark There could be rounding and overflow errors here.
1002 */
1003TMDECL(uint64_t) TMTimerFromMicro(PTMTIMER pTimer, uint64_t u64MicroTS)
1004{
1005 switch (pTimer->enmClock)
1006 {
1007 case TMCLOCK_VIRTUAL:
1008 case TMCLOCK_VIRTUAL_SYNC:
1009 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1010 return u64MicroTS * 1000;
1011
1012 case TMCLOCK_REAL:
1013 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1014 return u64MicroTS / 1000;
1015
1016 case TMCLOCK_TSC:
1017 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1018 return 0;
1019
1020 default:
1021 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1022 return 0;
1023 }
1024}
1025
1026
1027/**
1028 * Converts the specified millisecond timestamp to timer clock ticks.
1029 *
1030 * @returns timer clock ticks.
1031 * @param pTimer Timer handle as returned by one of the create functions.
1032 * @param u64MilliTS The millisecond value ticks to convert.
1033 * @remark There could be rounding and overflow errors here.
1034 */
1035TMDECL(uint64_t) TMTimerFromMilli(PTMTIMER pTimer, uint64_t u64MilliTS)
1036{
1037 switch (pTimer->enmClock)
1038 {
1039 case TMCLOCK_VIRTUAL:
1040 case TMCLOCK_VIRTUAL_SYNC:
1041 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1042 return u64MilliTS * 1000000;
1043
1044 case TMCLOCK_REAL:
1045 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1046 return u64MilliTS;
1047
1048 case TMCLOCK_TSC:
1049 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1050 return 0;
1051
1052 default:
1053 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1054 return 0;
1055 }
1056}
1057
1058
1059/**
1060 * Get the expire time of the timer.
1061 * Only valid for active timers.
1062 *
1063 * @returns Expire time of the timer.
1064 * @param pTimer Timer handle as returned by one of the create functions.
1065 */
1066TMDECL(uint64_t) TMTimerGetExpire(PTMTIMER pTimer)
1067{
1068 int cRetries = 1000;
1069 do
1070 {
1071 TMTIMERSTATE enmState = pTimer->enmState;
1072 switch (enmState)
1073 {
1074 case TMTIMERSTATE_EXPIRED:
1075 case TMTIMERSTATE_STOPPED:
1076 case TMTIMERSTATE_PENDING_STOP:
1077 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1078 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1079 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1080 return ~(uint64_t)0;
1081
1082 case TMTIMERSTATE_ACTIVE:
1083 case TMTIMERSTATE_PENDING_RESCHEDULE:
1084 case TMTIMERSTATE_PENDING_SCHEDULE:
1085 Log2(("TMTimerGetExpire: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1086 pTimer->u64Expire, pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1087 return pTimer->u64Expire;
1088
1089 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1090 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1091#ifdef IN_RING3
1092 if (!RTThreadYield())
1093 RTThreadSleep(1);
1094#endif
1095 break;
1096
1097 /*
1098 * Invalid states.
1099 */
1100 case TMTIMERSTATE_PENDING_DESTROY:
1101 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1102 case TMTIMERSTATE_FREE:
1103 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
1104 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1105 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1106 return ~(uint64_t)0;
1107 default:
1108 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
1109 return ~(uint64_t)0;
1110 }
1111 } while (cRetries-- > 0);
1112
1113 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, HCSTRING(pTimer->pszDesc)));
1114 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1115 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1116 return ~(uint64_t)0;
1117}
1118
1119
1120/**
1121 * Checks if a timer is active or not.
1122 *
1123 * @returns True if active.
1124 * @returns False if not active.
1125 * @param pTimer Timer handle as returned by one of the create functions.
1126 */
1127TMDECL(bool) TMTimerIsActive(PTMTIMER pTimer)
1128{
1129 TMTIMERSTATE enmState = pTimer->enmState;
1130 switch (enmState)
1131 {
1132 case TMTIMERSTATE_STOPPED:
1133 case TMTIMERSTATE_EXPIRED:
1134 case TMTIMERSTATE_PENDING_STOP:
1135 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1136 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1137 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1138 return false;
1139
1140 case TMTIMERSTATE_ACTIVE:
1141 case TMTIMERSTATE_PENDING_RESCHEDULE:
1142 case TMTIMERSTATE_PENDING_SCHEDULE:
1143 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1144 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1145 Log2(("TMTimerIsActive: returns true (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1146 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1147 return true;
1148
1149 /*
1150 * Invalid states.
1151 */
1152 case TMTIMERSTATE_PENDING_DESTROY:
1153 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1154 case TMTIMERSTATE_FREE:
1155 AssertMsgFailed(("Invalid timer state %s (%s)\n", tmTimerState(enmState), HCSTRING(pTimer->pszDesc)));
1156 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1157 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1158 return false;
1159 default:
1160 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
1161 return false;
1162 }
1163}
1164
1165
1166/**
1167 * Convert state to string.
1168 *
1169 * @returns Readonly status name.
1170 * @param enmState State.
1171 */
1172const char *tmTimerState(TMTIMERSTATE enmState)
1173{
1174 switch (enmState)
1175 {
1176#define CASE(state) case state: return #state + sizeof("TMTIMERSTATE_") - 1
1177 CASE(TMTIMERSTATE_STOPPED);
1178 CASE(TMTIMERSTATE_ACTIVE);
1179 CASE(TMTIMERSTATE_EXPIRED);
1180 CASE(TMTIMERSTATE_PENDING_STOP);
1181 CASE(TMTIMERSTATE_PENDING_STOP_SCHEDULE);
1182 CASE(TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE);
1183 CASE(TMTIMERSTATE_PENDING_SCHEDULE);
1184 CASE(TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE);
1185 CASE(TMTIMERSTATE_PENDING_RESCHEDULE);
1186 CASE(TMTIMERSTATE_PENDING_STOP_DESTROY);
1187 CASE(TMTIMERSTATE_PENDING_DESTROY);
1188 CASE(TMTIMERSTATE_FREE);
1189 default:
1190 AssertMsgFailed(("Invalid state enmState=%d\n", enmState));
1191 return "Invalid state!";
1192#undef CASE
1193 }
1194}
1195
1196
1197/**
1198 * Schedules the given timer on the given queue.
1199 *
1200 * @param pQueue The timer queue.
1201 * @param pTimer The timer that needs scheduling.
1202 */
1203DECLINLINE(void) tmTimerQueueScheduleOne(PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
1204{
1205 /*
1206 * Processing.
1207 */
1208 unsigned cRetries = 2;
1209 do
1210 {
1211 TMTIMERSTATE enmState = pTimer->enmState;
1212 switch (enmState)
1213 {
1214 /*
1215 * Reschedule timer (in the active list).
1216 */
1217 case TMTIMERSTATE_PENDING_RESCHEDULE:
1218 {
1219 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE, TMTIMERSTATE_PENDING_RESCHEDULE)))
1220 break; /* retry */
1221
1222 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1223 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1224 if (pPrev)
1225 TMTIMER_SET_NEXT(pPrev, pNext);
1226 else
1227 {
1228 TMTIMER_SET_HEAD(pQueue, pNext);
1229 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1230 }
1231 if (pNext)
1232 TMTIMER_SET_PREV(pNext, pPrev);
1233 pTimer->offNext = 0;
1234 pTimer->offPrev = 0;
1235 /* fall thru */
1236 }
1237
1238 /*
1239 * Schedule timer (insert into the active list).
1240 */
1241 case TMTIMERSTATE_PENDING_SCHEDULE:
1242 {
1243 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1244 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, TMTIMERSTATE_PENDING_SCHEDULE)))
1245 break; /* retry */
1246
1247 PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue);
1248 if (pCur)
1249 {
1250 const uint64_t u64Expire = pTimer->u64Expire;
1251 for (;; pCur = TMTIMER_GET_NEXT(pCur))
1252 {
1253 if (pCur->u64Expire > u64Expire)
1254 {
1255 const PTMTIMER pPrev = TMTIMER_GET_PREV(pCur);
1256 TMTIMER_SET_NEXT(pTimer, pCur);
1257 TMTIMER_SET_PREV(pTimer, pPrev);
1258 if (pPrev)
1259 TMTIMER_SET_NEXT(pPrev, pTimer);
1260 else
1261 {
1262 TMTIMER_SET_HEAD(pQueue, pTimer);
1263 pQueue->u64Expire = u64Expire;
1264 }
1265 TMTIMER_SET_PREV(pCur, pTimer);
1266 return;
1267 }
1268 if (!pCur->offNext)
1269 {
1270 TMTIMER_SET_NEXT(pCur, pTimer);
1271 TMTIMER_SET_PREV(pTimer, pCur);
1272 return;
1273 }
1274 }
1275 }
1276 else
1277 {
1278 TMTIMER_SET_HEAD(pQueue, pTimer);
1279 pQueue->u64Expire = pTimer->u64Expire;
1280 }
1281 return;
1282 }
1283
1284 /*
1285 * Stop the timer in active list.
1286 */
1287 case TMTIMERSTATE_PENDING_STOP:
1288 {
1289 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, TMTIMERSTATE_PENDING_STOP)))
1290 break; /* retry */
1291
1292 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1293 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1294 if (pPrev)
1295 TMTIMER_SET_NEXT(pPrev, pNext);
1296 else
1297 {
1298 TMTIMER_SET_HEAD(pQueue, pNext);
1299 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1300 }
1301 if (pNext)
1302 TMTIMER_SET_PREV(pNext, pPrev);
1303 pTimer->offNext = 0;
1304 pTimer->offPrev = 0;
1305 /* fall thru */
1306 }
1307
1308 /*
1309 * Stop the timer (not on the active list).
1310 */
1311 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1312 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1313 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_STOPPED, TMTIMERSTATE_PENDING_STOP_SCHEDULE)))
1314 break;
1315 return;
1316
1317 /*
1318 * Stop & destroy the timer.
1319 */
1320 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1321 {
1322 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1323 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1324 if (pPrev)
1325 TMTIMER_SET_NEXT(pPrev, pNext);
1326 else
1327 {
1328 TMTIMER_SET_HEAD(pQueue, pNext);
1329 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1330 }
1331 if (pNext)
1332 TMTIMER_SET_PREV(pNext, pPrev);
1333 pTimer->offNext = 0;
1334 pTimer->offPrev = 0;
1335 /* fall thru */
1336 }
1337
1338 /*
1339 * Destroy the timer.
1340 */
1341 case TMTIMERSTATE_PENDING_DESTROY:
1342 {
1343 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1344 PVM pVM = pTimer->CTXALLSUFF(pVM);
1345 const PTMTIMER pBigPrev = (PTMTIMER)(pTimer->pBigPrev ? MMHyperR3ToCC(pVM, pTimer->pBigPrev) : NULL);
1346 const PTMTIMER pBigNext = (PTMTIMER)(pTimer->pBigNext ? MMHyperR3ToCC(pVM, pTimer->pBigNext) : NULL);
1347
1348 /* unlink from created list */
1349 if (pBigPrev)
1350 pBigPrev->pBigNext = pTimer->pBigNext;
1351 else
1352 pVM->tm.s.pCreated = pTimer->pBigNext;
1353 if (pBigNext)
1354 pBigNext->pBigPrev = pTimer->pBigPrev;
1355 pTimer->pBigNext = 0;
1356 pTimer->pBigPrev = 0;
1357
1358 /* free */
1359 Log2(("TM: Inserting %p into the free list ahead of %p!\n", pTimer, pVM->tm.s.pFree));
1360 pTimer->pBigNext = pVM->tm.s.pFree;
1361 pVM->tm.s.pFree = (PTMTIMERR3)MMHyperCCToR3(pVM, pTimer);
1362 TM_SET_STATE(pTimer, TMTIMERSTATE_FREE);
1363 return;
1364 }
1365
1366 /*
1367 * Postpone these until they get into the right state.
1368 */
1369 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1370 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1371 tmTimerLink(pQueue, pTimer);
1372 STAM_COUNTER_INC(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatPostponed));
1373 return;
1374
1375 /*
1376 * None of these can be in the schedule.
1377 */
1378 case TMTIMERSTATE_FREE:
1379 case TMTIMERSTATE_STOPPED:
1380 case TMTIMERSTATE_ACTIVE:
1381 case TMTIMERSTATE_EXPIRED:
1382 default:
1383 AssertMsgFailed(("Timer (%p) in the scheduling list has an invalid state %s (%d)!",
1384 pTimer, tmTimerState(pTimer->enmState), pTimer->enmState));
1385 return;
1386 }
1387 } while (cRetries-- > 0);
1388}
1389
1390
1391/**
1392 * Schedules the specified timer queue.
1393 *
1394 * @param pVM The VM to run the timers for.
1395 * @param pQueue The queue to schedule.
1396 */
1397void tmTimerQueueSchedule(PVM pVM, PTMTIMERQUEUE pQueue)
1398{
1399 VM_ASSERT_EMT(pVM);
1400
1401 /*
1402 * Dequeue the scheduling list and iterate it.
1403 */
1404 int32_t offNext = ASMAtomicXchgS32(&pQueue->offSchedule, 0);
1405 Log2(("tmTimerQueueSchedule: pQueue=%p:{.enmClock=%d, offNext=%RI32}\n", pQueue, pQueue->enmClock, offNext));
1406 if (!offNext)
1407 return;
1408 PTMTIMER pNext = (PTMTIMER)((intptr_t)pQueue + offNext);
1409 while (pNext)
1410 {
1411 /*
1412 * Unlink the head timer and find the next one.
1413 */
1414 PTMTIMER pTimer = pNext;
1415 pNext = pNext->offScheduleNext ? (PTMTIMER)((intptr_t)pNext + pNext->offScheduleNext) : NULL;
1416 pTimer->offScheduleNext = 0;
1417
1418 /*
1419 * Do the scheduling.
1420 */
1421 Log2(("tmTimerQueueSchedule: pTimer=%p:{.enmState=%s, .enmClock=%d, .enmType=%d, .pszDesc=%s}\n",
1422 pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, HCSTRING(pTimer->pszDesc)));
1423 tmTimerQueueScheduleOne(pQueue, pTimer);
1424 Log2(("tmTimerQueueSchedule: new %s\n", tmTimerState(pTimer->enmState)));
1425 } /* foreach timer in current schedule batch. */
1426}
1427
1428
1429#ifdef VBOX_STRICT
1430/**
1431 * Checks that the timer queues are sane.
1432 *
1433 * @param pVM VM handle.
1434 */
1435void tmTimerQueuesSanityChecks(PVM pVM, const char *pszWhere)
1436{
1437 /*
1438 * Check the linking of the active lists.
1439 */
1440 for (int i = 0; i < TMCLOCK_MAX; i++)
1441 {
1442 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTXALLSUFF(paTimerQueues)[i];
1443 Assert((int)pQueue->enmClock == i);
1444 PTMTIMER pPrev = NULL;
1445 for (PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue); pCur; pPrev = pCur, pCur = TMTIMER_GET_NEXT(pCur))
1446 {
1447 AssertMsg((int)pCur->enmClock == i, ("%s: %d != %d\n", pszWhere, pCur->enmClock, i));
1448 AssertMsg(TMTIMER_GET_PREV(pCur) == pPrev, ("%s: %p != %p\n", pszWhere, TMTIMER_GET_PREV(pCur), pPrev));
1449 TMTIMERSTATE enmState = pCur->enmState;
1450 switch (enmState)
1451 {
1452 case TMTIMERSTATE_ACTIVE:
1453 AssertMsg(!pCur->offScheduleNext, ("%s: %RI32\n", pszWhere, pCur->offScheduleNext));
1454 break;
1455 case TMTIMERSTATE_PENDING_STOP:
1456 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1457 case TMTIMERSTATE_PENDING_RESCHEDULE:
1458 break;
1459 default:
1460 AssertMsgFailed(("%s: Invalid state enmState=%d %s\n", pszWhere, enmState, tmTimerState(enmState)));
1461 break;
1462 }
1463 }
1464 }
1465
1466
1467# ifdef IN_RING3
1468 /*
1469 * Do the big list and check that active timers all are in the active lists.
1470 */
1471 PTMTIMERR3 pPrev = NULL;
1472 for (PTMTIMERR3 pCur = pVM->tm.s.pCreated; pCur; pPrev = pCur, pCur = pCur->pBigNext)
1473 {
1474 Assert(pCur->pBigPrev == pPrev);
1475 Assert((unsigned)pCur->enmClock < (unsigned)TMCLOCK_MAX);
1476
1477 TMTIMERSTATE enmState = pCur->enmState;
1478 switch (enmState)
1479 {
1480 case TMTIMERSTATE_ACTIVE:
1481 case TMTIMERSTATE_PENDING_STOP:
1482 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1483 case TMTIMERSTATE_PENDING_RESCHEDULE:
1484 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1485 {
1486 PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTXALLSUFF(paTimerQueues)[pCur->enmClock]);
1487 Assert(pCur->offPrev || pCur == pCurAct);
1488 while (pCurAct && pCurAct != pCur)
1489 pCurAct = TMTIMER_GET_NEXT(pCurAct);
1490 Assert(pCurAct == pCur);
1491 break;
1492 }
1493
1494 case TMTIMERSTATE_PENDING_DESTROY:
1495 case TMTIMERSTATE_PENDING_SCHEDULE:
1496 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1497 case TMTIMERSTATE_STOPPED:
1498 case TMTIMERSTATE_EXPIRED:
1499 {
1500 Assert(!pCur->offNext);
1501 Assert(!pCur->offPrev);
1502 for (PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTXALLSUFF(paTimerQueues)[pCur->enmClock]);
1503 pCurAct;
1504 pCurAct = TMTIMER_GET_NEXT(pCurAct))
1505 {
1506 Assert(pCurAct != pCur);
1507 Assert(TMTIMER_GET_NEXT(pCurAct) != pCur);
1508 Assert(TMTIMER_GET_PREV(pCurAct) != pCur);
1509 }
1510 break;
1511 }
1512
1513 /* ignore */
1514 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1515 break;
1516
1517 /* shouldn't get here! */
1518 default:
1519 AssertMsgFailed(("Invalid state enmState=%d %s\n", enmState, tmTimerState(enmState)));
1520 break;
1521 }
1522 }
1523# endif /* IN_RING3 */
1524}
1525#endif /* !VBOX_STRICT */
1526
Note: See TracBrowser for help on using the repository browser.

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