VirtualBox

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

Last change on this file since 847 was 23, checked in by vboxsync, 18 years ago

string.h & stdio.h + header cleanups.

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