VirtualBox

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

Last change on this file since 4953 was 4917, checked in by vboxsync, 17 years ago

Don't trigger breakpoints in ring 0. (AssertRelease & co)

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