VirtualBox

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

Last change on this file since 5393 was 5167, checked in by vboxsync, 17 years ago

New halt method.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 47.7 KB
Line 
1/* $Id: TMAll.cpp 5167 2007-10-05 13:33:36Z 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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * 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 clock.
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 * Stop the timer.
609 * Use TMR3TimerArm() to "un-stop" the timer.
610 *
611 * @returns VBox status.
612 * @param pTimer Timer handle as returned by one of the create functions.
613 */
614TMDECL(int) TMTimerStop(PTMTIMER pTimer)
615{
616 STAM_PROFILE_START(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
617 /** @todo see if this function needs optimizing. */
618 int cRetries = 1000;
619 do
620 {
621 /*
622 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
623 */
624 TMTIMERSTATE enmState = pTimer->enmState;
625 Log2(("TMTimerStop: pTimer=%p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
626 pTimer, tmTimerState(enmState), HCSTRING(pTimer->pszDesc), cRetries));
627 switch (enmState)
628 {
629 case TMTIMERSTATE_EXPIRED:
630 //AssertMsgFailed(("You don't stop an expired timer dude!\n"));
631 return VERR_INVALID_PARAMETER;
632
633 case TMTIMERSTATE_STOPPED:
634 case TMTIMERSTATE_PENDING_STOP:
635 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
636 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
637 return VINF_SUCCESS;
638
639 case TMTIMERSTATE_PENDING_SCHEDULE:
640 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, enmState))
641 {
642 Assert(!pTimer->offPrev);
643 Assert(!pTimer->offNext);
644 tmSchedule(pTimer);
645 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
646 return VINF_SUCCESS;
647 }
648
649 case TMTIMERSTATE_PENDING_RESCHEDULE:
650 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
651 {
652 tmSchedule(pTimer);
653 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
654 return VINF_SUCCESS;
655 }
656 break;
657
658 case TMTIMERSTATE_ACTIVE:
659 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
660 {
661 tmSchedule(pTimer);
662 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
663 return VINF_SUCCESS;
664 }
665 break;
666
667 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
668 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
669#ifdef IN_RING3
670 if (!RTThreadYield())
671 RTThreadSleep(1);
672#else
673/**@todo call host and yield cpu after a while. */
674#endif
675 break;
676
677 /*
678 * Invalid states.
679 */
680 case TMTIMERSTATE_PENDING_DESTROY:
681 case TMTIMERSTATE_PENDING_STOP_DESTROY:
682 case TMTIMERSTATE_FREE:
683 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
684 return VERR_TM_INVALID_STATE;
685 default:
686 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
687 return VERR_TM_UNKNOWN_STATE;
688 }
689 } while (cRetries-- > 0);
690
691 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, HCSTRING(pTimer->pszDesc)));
692 STAM_PROFILE_STOP(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
693 return VERR_INTERNAL_ERROR;
694}
695
696
697/**
698 * Get the current clock time.
699 * Handy for calculating the new expire time.
700 *
701 * @returns Current clock time.
702 * @param pTimer Timer handle as returned by one of the create functions.
703 */
704TMDECL(uint64_t) TMTimerGet(PTMTIMER pTimer)
705{
706 uint64_t u64;
707 PVM pVM = pTimer->CTXALLSUFF(pVM);
708 switch (pTimer->enmClock)
709 {
710 case TMCLOCK_VIRTUAL:
711 u64 = TMVirtualGet(pVM);
712 break;
713 case TMCLOCK_VIRTUAL_SYNC:
714 u64 = TMVirtualSyncGet(pVM);
715 break;
716 case TMCLOCK_REAL:
717 u64 = TMRealGet(pVM);
718 break;
719 case TMCLOCK_TSC:
720 u64 = TMCpuTickGet(pVM);
721 break;
722
723 default:
724 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
725 return ~(uint64_t)0;
726 }
727 //Log2(("TMTimerGet: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
728 // u64, pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
729 return u64;
730}
731
732
733/**
734 * Get the freqency of the timer clock.
735 *
736 * @returns Clock frequency (as Hz of course).
737 * @param pTimer Timer handle as returned by one of the create functions.
738 */
739TMDECL(uint64_t) TMTimerGetFreq(PTMTIMER pTimer)
740{
741 switch (pTimer->enmClock)
742 {
743 case TMCLOCK_VIRTUAL:
744 case TMCLOCK_VIRTUAL_SYNC:
745 return TMCLOCK_FREQ_VIRTUAL;
746
747 case TMCLOCK_REAL:
748 return TMCLOCK_FREQ_REAL;
749
750 case TMCLOCK_TSC:
751 return TMCpuTicksPerSecond(pTimer->CTXALLSUFF(pVM));
752
753 default:
754 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
755 return 0;
756 }
757}
758
759
760/**
761 * Get the current clock time as nanoseconds.
762 *
763 * @returns The timer clock as nanoseconds.
764 * @param pTimer Timer handle as returned by one of the create functions.
765 */
766TMDECL(uint64_t) TMTimerGetNano(PTMTIMER pTimer)
767{
768 return TMTimerToNano(pTimer, TMTimerGet(pTimer));
769}
770
771
772/**
773 * Get the current clock time as microseconds.
774 *
775 * @returns The timer clock as microseconds.
776 * @param pTimer Timer handle as returned by one of the create functions.
777 */
778TMDECL(uint64_t) TMTimerGetMicro(PTMTIMER pTimer)
779{
780 return TMTimerToMicro(pTimer, TMTimerGet(pTimer));
781}
782
783
784/**
785 * Get the current clock time as milliseconds.
786 *
787 * @returns The timer clock as milliseconds.
788 * @param pTimer Timer handle as returned by one of the create functions.
789 */
790TMDECL(uint64_t) TMTimerGetMilli(PTMTIMER pTimer)
791{
792 return TMTimerToMilli(pTimer, TMTimerGet(pTimer));
793}
794
795
796/**
797 * Converts the specified timer clock time to nanoseconds.
798 *
799 * @returns nanoseconds.
800 * @param pTimer Timer handle as returned by one of the create functions.
801 * @param u64Ticks The clock ticks.
802 * @remark There could be rounding errors here. We just do a simple integere divide
803 * without any adjustments.
804 */
805TMDECL(uint64_t) TMTimerToNano(PTMTIMER pTimer, uint64_t u64Ticks)
806{
807 switch (pTimer->enmClock)
808 {
809 case TMCLOCK_VIRTUAL:
810 case TMCLOCK_VIRTUAL_SYNC:
811 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
812 return u64Ticks;
813
814 case TMCLOCK_REAL:
815 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
816 return u64Ticks * 1000000;
817
818 case TMCLOCK_TSC:
819 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
820 return 0;
821
822 default:
823 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
824 return 0;
825 }
826}
827
828
829/**
830 * Converts the specified timer clock time to microseconds.
831 *
832 * @returns microseconds.
833 * @param pTimer Timer handle as returned by one of the create functions.
834 * @param u64Ticks The clock ticks.
835 * @remark There could be rounding errors here. We just do a simple integere divide
836 * without any adjustments.
837 */
838TMDECL(uint64_t) TMTimerToMicro(PTMTIMER pTimer, uint64_t u64Ticks)
839{
840 switch (pTimer->enmClock)
841 {
842 case TMCLOCK_VIRTUAL:
843 case TMCLOCK_VIRTUAL_SYNC:
844 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
845 return u64Ticks / 1000;
846
847 case TMCLOCK_REAL:
848 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
849 return u64Ticks * 1000;
850
851 case TMCLOCK_TSC:
852 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
853 return 0;
854
855 default:
856 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
857 return 0;
858 }
859}
860
861
862/**
863 * Converts the specified timer clock time to milliseconds.
864 *
865 * @returns milliseconds.
866 * @param pTimer Timer handle as returned by one of the create functions.
867 * @param u64Ticks The clock ticks.
868 * @remark There could be rounding errors here. We just do a simple integere divide
869 * without any adjustments.
870 */
871TMDECL(uint64_t) TMTimerToMilli(PTMTIMER pTimer, uint64_t u64Ticks)
872{
873 switch (pTimer->enmClock)
874 {
875 case TMCLOCK_VIRTUAL:
876 case TMCLOCK_VIRTUAL_SYNC:
877 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
878 return u64Ticks / 1000000;
879
880 case TMCLOCK_REAL:
881 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
882 return u64Ticks;
883
884 case TMCLOCK_TSC:
885 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
886 return 0;
887
888 default:
889 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
890 return 0;
891 }
892}
893
894
895/**
896 * Converts the specified nanosecond timestamp to timer clock ticks.
897 *
898 * @returns timer clock ticks.
899 * @param pTimer Timer handle as returned by one of the create functions.
900 * @param u64NanoTS The nanosecond value ticks to convert.
901 * @remark There could be rounding and overflow errors here.
902 */
903TMDECL(uint64_t) TMTimerFromNano(PTMTIMER pTimer, uint64_t u64NanoTS)
904{
905 switch (pTimer->enmClock)
906 {
907 case TMCLOCK_VIRTUAL:
908 case TMCLOCK_VIRTUAL_SYNC:
909 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
910 return u64NanoTS;
911
912 case TMCLOCK_REAL:
913 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
914 return u64NanoTS / 1000000;
915
916 case TMCLOCK_TSC:
917 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
918 return 0;
919
920 default:
921 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
922 return 0;
923 }
924}
925
926
927/**
928 * Converts the specified microsecond timestamp to timer clock ticks.
929 *
930 * @returns timer clock ticks.
931 * @param pTimer Timer handle as returned by one of the create functions.
932 * @param u64MicroTS The microsecond value ticks to convert.
933 * @remark There could be rounding and overflow errors here.
934 */
935TMDECL(uint64_t) TMTimerFromMicro(PTMTIMER pTimer, uint64_t u64MicroTS)
936{
937 switch (pTimer->enmClock)
938 {
939 case TMCLOCK_VIRTUAL:
940 case TMCLOCK_VIRTUAL_SYNC:
941 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
942 return u64MicroTS * 1000;
943
944 case TMCLOCK_REAL:
945 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
946 return u64MicroTS / 1000;
947
948 case TMCLOCK_TSC:
949 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
950 return 0;
951
952 default:
953 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
954 return 0;
955 }
956}
957
958
959/**
960 * Converts the specified millisecond timestamp to timer clock ticks.
961 *
962 * @returns timer clock ticks.
963 * @param pTimer Timer handle as returned by one of the create functions.
964 * @param u64MilliTS The millisecond value ticks to convert.
965 * @remark There could be rounding and overflow errors here.
966 */
967TMDECL(uint64_t) TMTimerFromMilli(PTMTIMER pTimer, uint64_t u64MilliTS)
968{
969 switch (pTimer->enmClock)
970 {
971 case TMCLOCK_VIRTUAL:
972 case TMCLOCK_VIRTUAL_SYNC:
973 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
974 return u64MilliTS * 1000000;
975
976 case TMCLOCK_REAL:
977 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
978 return u64MilliTS;
979
980 case TMCLOCK_TSC:
981 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
982 return 0;
983
984 default:
985 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
986 return 0;
987 }
988}
989
990
991/**
992 * Get the expire time of the timer.
993 * Only valid for active timers.
994 *
995 * @returns Expire time of the timer.
996 * @param pTimer Timer handle as returned by one of the create functions.
997 */
998TMDECL(uint64_t) TMTimerGetExpire(PTMTIMER pTimer)
999{
1000 int cRetries = 1000;
1001 do
1002 {
1003 TMTIMERSTATE enmState = pTimer->enmState;
1004 switch (enmState)
1005 {
1006 case TMTIMERSTATE_EXPIRED:
1007 case TMTIMERSTATE_STOPPED:
1008 case TMTIMERSTATE_PENDING_STOP:
1009 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1010 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1011 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1012 return ~(uint64_t)0;
1013
1014 case TMTIMERSTATE_ACTIVE:
1015 case TMTIMERSTATE_PENDING_RESCHEDULE:
1016 case TMTIMERSTATE_PENDING_SCHEDULE:
1017 Log2(("TMTimerGetExpire: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1018 pTimer->u64Expire, pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1019 return pTimer->u64Expire;
1020
1021 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1022 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1023#ifdef IN_RING3
1024 if (!RTThreadYield())
1025 RTThreadSleep(1);
1026#endif
1027 break;
1028
1029 /*
1030 * Invalid states.
1031 */
1032 case TMTIMERSTATE_PENDING_DESTROY:
1033 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1034 case TMTIMERSTATE_FREE:
1035 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
1036 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1037 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1038 return ~(uint64_t)0;
1039 default:
1040 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
1041 return ~(uint64_t)0;
1042 }
1043 } while (cRetries-- > 0);
1044
1045 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, HCSTRING(pTimer->pszDesc)));
1046 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1047 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1048 return ~(uint64_t)0;
1049}
1050
1051
1052/**
1053 * Checks if a timer is active or not.
1054 *
1055 * @returns True if active.
1056 * @returns False if not active.
1057 * @param pTimer Timer handle as returned by one of the create functions.
1058 */
1059TMDECL(bool) TMTimerIsActive(PTMTIMER pTimer)
1060{
1061 TMTIMERSTATE enmState = pTimer->enmState;
1062 switch (enmState)
1063 {
1064 case TMTIMERSTATE_STOPPED:
1065 case TMTIMERSTATE_EXPIRED:
1066 case TMTIMERSTATE_PENDING_STOP:
1067 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1068 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1069 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1070 return false;
1071
1072 case TMTIMERSTATE_ACTIVE:
1073 case TMTIMERSTATE_PENDING_RESCHEDULE:
1074 case TMTIMERSTATE_PENDING_SCHEDULE:
1075 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1076 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1077 Log2(("TMTimerIsActive: returns true (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1078 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1079 return true;
1080
1081 /*
1082 * Invalid states.
1083 */
1084 case TMTIMERSTATE_PENDING_DESTROY:
1085 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1086 case TMTIMERSTATE_FREE:
1087 AssertMsgFailed(("Invalid timer state %s (%s)\n", tmTimerState(enmState), HCSTRING(pTimer->pszDesc)));
1088 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1089 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
1090 return false;
1091 default:
1092 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
1093 return false;
1094 }
1095}
1096
1097
1098/**
1099 * Convert state to string.
1100 *
1101 * @returns Readonly status name.
1102 * @param enmState State.
1103 */
1104const char *tmTimerState(TMTIMERSTATE enmState)
1105{
1106 switch (enmState)
1107 {
1108#define CASE(state) case state: return #state + sizeof("TMTIMERSTATE_") - 1
1109 CASE(TMTIMERSTATE_STOPPED);
1110 CASE(TMTIMERSTATE_ACTIVE);
1111 CASE(TMTIMERSTATE_EXPIRED);
1112 CASE(TMTIMERSTATE_PENDING_STOP);
1113 CASE(TMTIMERSTATE_PENDING_STOP_SCHEDULE);
1114 CASE(TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE);
1115 CASE(TMTIMERSTATE_PENDING_SCHEDULE);
1116 CASE(TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE);
1117 CASE(TMTIMERSTATE_PENDING_RESCHEDULE);
1118 CASE(TMTIMERSTATE_PENDING_STOP_DESTROY);
1119 CASE(TMTIMERSTATE_PENDING_DESTROY);
1120 CASE(TMTIMERSTATE_FREE);
1121 default:
1122 AssertMsgFailed(("Invalid state enmState=%d\n", enmState));
1123 return "Invalid state!";
1124#undef CASE
1125 }
1126}
1127
1128
1129/**
1130 * Schedules the given timer on the given queue.
1131 *
1132 * @param pQueue The timer queue.
1133 * @param pTimer The timer that needs scheduling.
1134 */
1135DECLINLINE(void) tmTimerQueueScheduleOne(PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
1136{
1137 /*
1138 * Processing.
1139 */
1140 switch (pTimer->enmState)
1141 {
1142 /*
1143 * Reschedule timer (in the active list).
1144 */
1145 case TMTIMERSTATE_PENDING_RESCHEDULE:
1146 {
1147 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1148 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1149 if (pPrev)
1150 TMTIMER_SET_NEXT(pPrev, pNext);
1151 else
1152 {
1153 TMTIMER_SET_HEAD(pQueue, pNext);
1154 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1155 }
1156 if (pNext)
1157 TMTIMER_SET_PREV(pNext, pPrev);
1158 pTimer->offNext = 0;
1159 pTimer->offPrev = 0;
1160 /* fall thru */
1161 }
1162
1163 /*
1164 * Schedule timer (insert into the active list).
1165 */
1166 case TMTIMERSTATE_PENDING_SCHEDULE:
1167 {
1168 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1169 TM_SET_STATE(pTimer, TMTIMERSTATE_ACTIVE);
1170 PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue);
1171 if (pCur)
1172 {
1173 const uint64_t u64Expire = pTimer->u64Expire;
1174 for (;; pCur = TMTIMER_GET_NEXT(pCur))
1175 {
1176 if (pCur->u64Expire > u64Expire)
1177 {
1178 const PTMTIMER pPrev = TMTIMER_GET_PREV(pCur);
1179 TMTIMER_SET_NEXT(pTimer, pCur);
1180 TMTIMER_SET_PREV(pTimer, pPrev);
1181 if (pPrev)
1182 TMTIMER_SET_NEXT(pPrev, pTimer);
1183 else
1184 {
1185 TMTIMER_SET_HEAD(pQueue, pTimer);
1186 pQueue->u64Expire = u64Expire;
1187 }
1188 TMTIMER_SET_PREV(pCur, pTimer);
1189 break;
1190 }
1191 else if (!pCur->offNext)
1192 {
1193 TMTIMER_SET_NEXT(pCur, pTimer);
1194 TMTIMER_SET_PREV(pTimer, pCur);
1195 break;
1196 }
1197 }
1198 }
1199 else
1200 {
1201 TMTIMER_SET_HEAD(pQueue, pTimer);
1202 pQueue->u64Expire = pTimer->u64Expire;
1203 }
1204 break;
1205 }
1206
1207 /*
1208 * Stop the timer in active list.
1209 */
1210 case TMTIMERSTATE_PENDING_STOP:
1211 {
1212 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1213 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1214 if (pPrev)
1215 TMTIMER_SET_NEXT(pPrev, pNext);
1216 else
1217 {
1218 TMTIMER_SET_HEAD(pQueue, pNext);
1219 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1220 }
1221 if (pNext)
1222 TMTIMER_SET_PREV(pNext, pPrev);
1223 pTimer->offNext = 0;
1224 pTimer->offPrev = 0;
1225 /* fall thru */
1226 }
1227
1228 /*
1229 * Stop the timer (not on the active list).
1230 */
1231 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1232 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1233 TM_SET_STATE(pTimer, TMTIMERSTATE_STOPPED);
1234 break;
1235
1236 /*
1237 * Stop & destroy the timer.
1238 */
1239 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1240 {
1241 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1242 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1243 if (pPrev)
1244 TMTIMER_SET_NEXT(pPrev, pNext);
1245 else
1246 {
1247 TMTIMER_SET_HEAD(pQueue, pNext);
1248 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1249 }
1250 if (pNext)
1251 TMTIMER_SET_PREV(pNext, pPrev);
1252 pTimer->offNext = 0;
1253 pTimer->offPrev = 0;
1254 /* fall thru */
1255 }
1256
1257 /*
1258 * Destroy the timer.
1259 */
1260 case TMTIMERSTATE_PENDING_DESTROY:
1261 {
1262 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1263 PVM pVM = pTimer->CTXALLSUFF(pVM);
1264 const PTMTIMER pBigPrev = (PTMTIMER)(pTimer->pBigPrev ? MMHyperR3ToCC(pVM, pTimer->pBigPrev) : NULL);
1265 const PTMTIMER pBigNext = (PTMTIMER)(pTimer->pBigNext ? MMHyperR3ToCC(pVM, pTimer->pBigNext) : NULL);
1266
1267 /* unlink from created list */
1268 if (pBigPrev)
1269 pBigPrev->pBigNext = pTimer->pBigNext;
1270 else
1271 pVM->tm.s.pCreated = pTimer->pBigNext;
1272 if (pBigNext)
1273 pBigNext->pBigPrev = pTimer->pBigPrev;
1274 pTimer->pBigNext = 0;
1275 pTimer->pBigPrev = 0;
1276
1277 /* free */
1278 Log2(("TM: Inserting %p into the free list ahead of %p!\n", pTimer, pVM->tm.s.pFree));
1279 pTimer->pBigNext = pVM->tm.s.pFree;
1280 pVM->tm.s.pFree = (PTMTIMERR3)MMHyperCCToR3(pVM, pTimer);
1281 TM_SET_STATE(pTimer, TMTIMERSTATE_FREE);
1282 break;
1283 }
1284
1285 /*
1286 * Postpone these until they get into the right state.
1287 */
1288 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1289 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1290 tmTimerLink(pQueue, pTimer);
1291 STAM_COUNTER_INC(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatPostponed));
1292 break;
1293
1294 /*
1295 * None of these can be in the schedule.
1296 */
1297 case TMTIMERSTATE_FREE:
1298 case TMTIMERSTATE_STOPPED:
1299 case TMTIMERSTATE_ACTIVE:
1300 case TMTIMERSTATE_EXPIRED:
1301 AssertMsgFailed(("Timer (%p) in the scheduling list has an invalid state %s (%d)!",
1302 pTimer, tmTimerState(pTimer->enmState), pTimer->enmState));
1303 break;
1304 }
1305}
1306
1307
1308/**
1309 * Schedules the specified timer queue.
1310 *
1311 * @param pVM The VM to run the timers for.
1312 * @param pQueue The queue to schedule.
1313 */
1314void tmTimerQueueSchedule(PVM pVM, PTMTIMERQUEUE pQueue)
1315{
1316 VM_ASSERT_EMT(pVM);
1317
1318 /*
1319 * Dequeue the scheduling list and iterate it.
1320 */
1321 int32_t offNext = ASMAtomicXchgS32(&pQueue->offSchedule, 0);
1322 Log2(("tmTimerQueueSchedule: pQueue=%p:{.enmClock=%d, offNext=%RI32}\n", pQueue, pQueue->enmClock, offNext));
1323 if (!offNext)
1324 return;
1325 PTMTIMER pNext = (PTMTIMER)((intptr_t)pQueue + offNext);
1326 while (pNext)
1327 {
1328 /*
1329 * Unlink the head timer and find the next one.
1330 */
1331 PTMTIMER pTimer = pNext;
1332 pNext = pNext->offScheduleNext ? (PTMTIMER)((intptr_t)pNext + pNext->offScheduleNext) : NULL;
1333 pTimer->offScheduleNext = 0;
1334
1335 /*
1336 * Do the scheduling.
1337 */
1338 Log2(("tmTimerQueueSchedule: pTimer=%p:{.enmState=%s, .enmClock=%d, .enmType=%d, .pszDesc=%s}\n",
1339 pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, HCSTRING(pTimer->pszDesc)));
1340 tmTimerQueueScheduleOne(pQueue, pTimer);
1341 Log2(("tmTimerQueueSchedule: new %s\n", tmTimerState(pTimer->enmState)));
1342 } /* foreach timer in current schedule batch. */
1343}
1344
1345
1346#ifdef VBOX_STRICT
1347/**
1348 * Checks that the timer queues are sane.
1349 *
1350 * @param pVM VM handle.
1351 */
1352void tmTimerQueuesSanityChecks(PVM pVM, const char *pszWhere)
1353{
1354 /*
1355 * Check the linking of the active lists.
1356 */
1357 for (int i = 0; i < TMCLOCK_MAX; i++)
1358 {
1359 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTXALLSUFF(paTimerQueues)[i];
1360 Assert((int)pQueue->enmClock == i);
1361 PTMTIMER pPrev = NULL;
1362 for (PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue); pCur; pPrev = pCur, pCur = TMTIMER_GET_NEXT(pCur))
1363 {
1364 AssertMsg((int)pCur->enmClock == i, ("%s: %d != %d\n", pszWhere, pCur->enmClock, i));
1365 AssertMsg(TMTIMER_GET_PREV(pCur) == pPrev, ("%s: %p != %p\n", pszWhere, TMTIMER_GET_PREV(pCur), pPrev));
1366 TMTIMERSTATE enmState = pCur->enmState;
1367 switch (enmState)
1368 {
1369 case TMTIMERSTATE_ACTIVE:
1370 AssertMsg(!pCur->offScheduleNext, ("%s: %RI32\n", pszWhere, pCur->offScheduleNext));
1371 break;
1372 case TMTIMERSTATE_PENDING_STOP:
1373 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1374 case TMTIMERSTATE_PENDING_RESCHEDULE:
1375 break;
1376 default:
1377 AssertMsgFailed(("%s: Invalid state enmState=%d %s\n", pszWhere, enmState, tmTimerState(enmState)));
1378 break;
1379 }
1380 }
1381 }
1382
1383
1384# ifdef IN_RING3
1385 /*
1386 * Do the big list and check that active timers all are in the active lists.
1387 */
1388 PTMTIMERR3 pPrev = NULL;
1389 for (PTMTIMERR3 pCur = pVM->tm.s.pCreated; pCur; pPrev = pCur, pCur = pCur->pBigNext)
1390 {
1391 Assert(pCur->pBigPrev == pPrev);
1392 Assert((unsigned)pCur->enmClock < (unsigned)TMCLOCK_MAX);
1393
1394 TMTIMERSTATE enmState = pCur->enmState;
1395 switch (enmState)
1396 {
1397 case TMTIMERSTATE_ACTIVE:
1398 case TMTIMERSTATE_PENDING_STOP:
1399 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1400 case TMTIMERSTATE_PENDING_RESCHEDULE:
1401 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1402 {
1403 PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTXALLSUFF(paTimerQueues)[pCur->enmClock]);
1404 Assert(pCur->offPrev || pCur == pCurAct);
1405 while (pCurAct && pCurAct != pCur)
1406 pCurAct = TMTIMER_GET_NEXT(pCurAct);
1407 Assert(pCurAct == pCur);
1408 break;
1409 }
1410
1411 case TMTIMERSTATE_PENDING_DESTROY:
1412 case TMTIMERSTATE_PENDING_SCHEDULE:
1413 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1414 case TMTIMERSTATE_STOPPED:
1415 case TMTIMERSTATE_EXPIRED:
1416 {
1417 Assert(!pCur->offNext);
1418 Assert(!pCur->offPrev);
1419 for (PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTXALLSUFF(paTimerQueues)[pCur->enmClock]);
1420 pCurAct;
1421 pCurAct = TMTIMER_GET_NEXT(pCurAct))
1422 {
1423 Assert(pCurAct != pCur);
1424 Assert(TMTIMER_GET_NEXT(pCurAct) != pCur);
1425 Assert(TMTIMER_GET_PREV(pCurAct) != pCur);
1426 }
1427 break;
1428 }
1429
1430 /* ignore */
1431 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1432 break;
1433
1434 /* shouldn't get here! */
1435 default:
1436 AssertMsgFailed(("Invalid state enmState=%d %s\n", enmState, tmTimerState(enmState)));
1437 break;
1438 }
1439 }
1440# endif /* IN_RING3 */
1441}
1442#endif /* !VBOX_STRICT */
1443
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