VirtualBox

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

Last change on this file since 5006 was 4977, checked in by vboxsync, 17 years ago

Backed out most of 24659.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 44.1 KB
Line 
1/* $Id: TMAll.cpp 4977 2007-09-22 00:01:15Z 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 switch (pTimer->enmClock)
699 {
700 case TMCLOCK_VIRTUAL:
701 case TMCLOCK_VIRTUAL_SYNC:
702 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
703 return u64Ticks;
704
705 case TMCLOCK_REAL:
706 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
707 return u64Ticks * 1000000;
708
709 case TMCLOCK_TSC:
710 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
711 return 0;
712
713 default:
714 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
715 return 0;
716 }
717}
718
719
720/**
721 * Converts the specified timer clock time to microseconds.
722 *
723 * @returns microseconds.
724 * @param pTimer Timer handle as returned by one of the create functions.
725 * @param u64Ticks The clock ticks.
726 * @remark There could be rounding errors here. We just do a simple integere divide
727 * without any adjustments.
728 */
729TMDECL(uint64_t) TMTimerToMicro(PTMTIMER pTimer, uint64_t u64Ticks)
730{
731 switch (pTimer->enmClock)
732 {
733 case TMCLOCK_VIRTUAL:
734 case TMCLOCK_VIRTUAL_SYNC:
735 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
736 return u64Ticks / 1000;
737
738 case TMCLOCK_REAL:
739 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
740 return u64Ticks * 1000;
741
742 case TMCLOCK_TSC:
743 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
744 return 0;
745
746 default:
747 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
748 return 0;
749 }
750}
751
752
753/**
754 * Converts the specified timer clock time to milliseconds.
755 *
756 * @returns milliseconds.
757 * @param pTimer Timer handle as returned by one of the create functions.
758 * @param u64Ticks The clock ticks.
759 * @remark There could be rounding errors here. We just do a simple integere divide
760 * without any adjustments.
761 */
762TMDECL(uint64_t) TMTimerToMilli(PTMTIMER pTimer, uint64_t u64Ticks)
763{
764 switch (pTimer->enmClock)
765 {
766 case TMCLOCK_VIRTUAL:
767 case TMCLOCK_VIRTUAL_SYNC:
768 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
769 return u64Ticks / 1000000;
770
771 case TMCLOCK_REAL:
772 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
773 return u64Ticks;
774
775 case TMCLOCK_TSC:
776 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
777 return 0;
778
779 default:
780 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
781 return 0;
782 }
783}
784
785
786/**
787 * Converts the specified nanosecond timestamp to timer clock ticks.
788 *
789 * @returns timer clock ticks.
790 * @param pTimer Timer handle as returned by one of the create functions.
791 * @param u64NanoTS The nanosecond value ticks to convert.
792 * @remark There could be rounding and overflow errors here.
793 */
794TMDECL(uint64_t) TMTimerFromNano(PTMTIMER pTimer, uint64_t u64NanoTS)
795{
796 switch (pTimer->enmClock)
797 {
798 case TMCLOCK_VIRTUAL:
799 case TMCLOCK_VIRTUAL_SYNC:
800 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
801 return u64NanoTS;
802
803 case TMCLOCK_REAL:
804 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
805 return u64NanoTS / 1000000;
806
807 case TMCLOCK_TSC:
808 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
809 return 0;
810
811 default:
812 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
813 return 0;
814 }
815}
816
817
818/**
819 * Converts the specified microsecond timestamp to timer clock ticks.
820 *
821 * @returns timer clock ticks.
822 * @param pTimer Timer handle as returned by one of the create functions.
823 * @param u64MicroTS The microsecond value ticks to convert.
824 * @remark There could be rounding and overflow errors here.
825 */
826TMDECL(uint64_t) TMTimerFromMicro(PTMTIMER pTimer, uint64_t u64MicroTS)
827{
828 switch (pTimer->enmClock)
829 {
830 case TMCLOCK_VIRTUAL:
831 case TMCLOCK_VIRTUAL_SYNC:
832 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
833 return u64MicroTS * 1000;
834
835 case TMCLOCK_REAL:
836 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
837 return u64MicroTS / 1000;
838
839 case TMCLOCK_TSC:
840 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
841 return 0;
842
843 default:
844 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
845 return 0;
846 }
847}
848
849
850/**
851 * Converts the specified millisecond timestamp to timer clock ticks.
852 *
853 * @returns timer clock ticks.
854 * @param pTimer Timer handle as returned by one of the create functions.
855 * @param u64MilliTS The millisecond value ticks to convert.
856 * @remark There could be rounding and overflow errors here.
857 */
858TMDECL(uint64_t) TMTimerFromMilli(PTMTIMER pTimer, uint64_t u64MilliTS)
859{
860 switch (pTimer->enmClock)
861 {
862 case TMCLOCK_VIRTUAL:
863 case TMCLOCK_VIRTUAL_SYNC:
864 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
865 return u64MilliTS * 1000000;
866
867 case TMCLOCK_REAL:
868 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
869 return u64MilliTS;
870
871 case TMCLOCK_TSC:
872 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
873 return 0;
874
875 default:
876 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
877 return 0;
878 }
879}
880
881
882/**
883 * Get the expire time of the timer.
884 * Only valid for active timers.
885 *
886 * @returns Expire time of the timer.
887 * @param pTimer Timer handle as returned by one of the create functions.
888 */
889TMDECL(uint64_t) TMTimerGetExpire(PTMTIMER pTimer)
890{
891 int cRetries = 1000;
892 do
893 {
894 TMTIMERSTATE enmState = pTimer->enmState;
895 switch (enmState)
896 {
897 case TMTIMERSTATE_EXPIRED:
898 case TMTIMERSTATE_STOPPED:
899 case TMTIMERSTATE_PENDING_STOP:
900 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
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
905 case TMTIMERSTATE_ACTIVE:
906 case TMTIMERSTATE_PENDING_RESCHEDULE:
907 case TMTIMERSTATE_PENDING_SCHEDULE:
908 Log2(("TMTimerGetExpire: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
909 pTimer->u64Expire, pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
910 return pTimer->u64Expire;
911
912 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
913 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
914#ifdef IN_RING3
915 if (!RTThreadYield())
916 RTThreadSleep(1);
917#endif
918 break;
919
920 /*
921 * Invalid states.
922 */
923 case TMTIMERSTATE_PENDING_DESTROY:
924 case TMTIMERSTATE_PENDING_STOP_DESTROY:
925 case TMTIMERSTATE_FREE:
926 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
927 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
928 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
929 return ~(uint64_t)0;
930 default:
931 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
932 return ~(uint64_t)0;
933 }
934 } while (cRetries-- > 0);
935
936 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, HCSTRING(pTimer->pszDesc)));
937 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
938 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
939 return ~(uint64_t)0;
940}
941
942
943/**
944 * Checks if a timer is active or not.
945 *
946 * @returns True if active.
947 * @returns False if not active.
948 * @param pTimer Timer handle as returned by one of the create functions.
949 */
950TMDECL(bool) TMTimerIsActive(PTMTIMER pTimer)
951{
952 TMTIMERSTATE enmState = pTimer->enmState;
953 switch (enmState)
954 {
955 case TMTIMERSTATE_STOPPED:
956 case TMTIMERSTATE_EXPIRED:
957 case TMTIMERSTATE_PENDING_STOP:
958 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
959 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
960 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
961 return false;
962
963 case TMTIMERSTATE_ACTIVE:
964 case TMTIMERSTATE_PENDING_RESCHEDULE:
965 case TMTIMERSTATE_PENDING_SCHEDULE:
966 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
967 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
968 Log2(("TMTimerIsActive: returns true (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
969 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
970 return true;
971
972 /*
973 * Invalid states.
974 */
975 case TMTIMERSTATE_PENDING_DESTROY:
976 case TMTIMERSTATE_PENDING_STOP_DESTROY:
977 case TMTIMERSTATE_FREE:
978 AssertMsgFailed(("Invalid timer state %s (%s)\n", tmTimerState(enmState), HCSTRING(pTimer->pszDesc)));
979 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
980 pTimer, tmTimerState(pTimer->enmState), HCSTRING(pTimer->pszDesc)));
981 return false;
982 default:
983 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, HCSTRING(pTimer->pszDesc)));
984 return false;
985 }
986}
987
988
989/**
990 * Convert state to string.
991 *
992 * @returns Readonly status name.
993 * @param enmState State.
994 */
995const char *tmTimerState(TMTIMERSTATE enmState)
996{
997 switch (enmState)
998 {
999#define CASE(state) case state: return #state + sizeof("TMTIMERSTATE_") - 1
1000 CASE(TMTIMERSTATE_STOPPED);
1001 CASE(TMTIMERSTATE_ACTIVE);
1002 CASE(TMTIMERSTATE_EXPIRED);
1003 CASE(TMTIMERSTATE_PENDING_STOP);
1004 CASE(TMTIMERSTATE_PENDING_STOP_SCHEDULE);
1005 CASE(TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE);
1006 CASE(TMTIMERSTATE_PENDING_SCHEDULE);
1007 CASE(TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE);
1008 CASE(TMTIMERSTATE_PENDING_RESCHEDULE);
1009 CASE(TMTIMERSTATE_PENDING_STOP_DESTROY);
1010 CASE(TMTIMERSTATE_PENDING_DESTROY);
1011 CASE(TMTIMERSTATE_FREE);
1012 default:
1013 AssertMsgFailed(("Invalid state enmState=%d\n", enmState));
1014 return "Invalid state!";
1015#undef CASE
1016 }
1017}
1018
1019
1020/**
1021 * Schedules the given timer on the given queue.
1022 *
1023 * @param pQueue The timer queue.
1024 * @param pTimer The timer that needs scheduling.
1025 */
1026DECLINLINE(void) tmTimerQueueScheduleOne(PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
1027{
1028 /*
1029 * Processing.
1030 */
1031 switch (pTimer->enmState)
1032 {
1033 /*
1034 * Reschedule timer (in the active list).
1035 */
1036 case TMTIMERSTATE_PENDING_RESCHEDULE:
1037 {
1038 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1039 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1040 if (pPrev)
1041 TMTIMER_SET_NEXT(pPrev, pNext);
1042 else
1043 {
1044 TMTIMER_SET_HEAD(pQueue, pNext);
1045 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1046 }
1047 if (pNext)
1048 TMTIMER_SET_PREV(pNext, pPrev);
1049 pTimer->offNext = 0;
1050 pTimer->offPrev = 0;
1051 /* fall thru */
1052 }
1053
1054 /*
1055 * Schedule timer (insert into the active list).
1056 */
1057 case TMTIMERSTATE_PENDING_SCHEDULE:
1058 {
1059 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1060 TM_SET_STATE(pTimer, TMTIMERSTATE_ACTIVE);
1061 PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue);
1062 if (pCur)
1063 {
1064 const uint64_t u64Expire = pTimer->u64Expire;
1065 for (;; pCur = TMTIMER_GET_NEXT(pCur))
1066 {
1067 if (pCur->u64Expire > u64Expire)
1068 {
1069 const PTMTIMER pPrev = TMTIMER_GET_PREV(pCur);
1070 TMTIMER_SET_NEXT(pTimer, pCur);
1071 TMTIMER_SET_PREV(pTimer, pPrev);
1072 if (pPrev)
1073 TMTIMER_SET_NEXT(pPrev, pTimer);
1074 else
1075 {
1076 TMTIMER_SET_HEAD(pQueue, pTimer);
1077 pQueue->u64Expire = u64Expire;
1078 }
1079 TMTIMER_SET_PREV(pCur, pTimer);
1080 break;
1081 }
1082 else if (!pCur->offNext)
1083 {
1084 TMTIMER_SET_NEXT(pCur, pTimer);
1085 TMTIMER_SET_PREV(pTimer, pCur);
1086 break;
1087 }
1088 }
1089 }
1090 else
1091 {
1092 TMTIMER_SET_HEAD(pQueue, pTimer);
1093 pQueue->u64Expire = pTimer->u64Expire;
1094 }
1095 break;
1096 }
1097
1098 /*
1099 * Stop the timer in active list.
1100 */
1101 case TMTIMERSTATE_PENDING_STOP:
1102 {
1103 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1104 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1105 if (pPrev)
1106 TMTIMER_SET_NEXT(pPrev, pNext);
1107 else
1108 {
1109 TMTIMER_SET_HEAD(pQueue, pNext);
1110 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1111 }
1112 if (pNext)
1113 TMTIMER_SET_PREV(pNext, pPrev);
1114 pTimer->offNext = 0;
1115 pTimer->offPrev = 0;
1116 /* fall thru */
1117 }
1118
1119 /*
1120 * Stop the timer (not on the active list).
1121 */
1122 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1123 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1124 TM_SET_STATE(pTimer, TMTIMERSTATE_STOPPED);
1125 break;
1126
1127 /*
1128 * Stop & destroy the timer.
1129 */
1130 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1131 {
1132 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1133 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1134 if (pPrev)
1135 TMTIMER_SET_NEXT(pPrev, pNext);
1136 else
1137 {
1138 TMTIMER_SET_HEAD(pQueue, pNext);
1139 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1140 }
1141 if (pNext)
1142 TMTIMER_SET_PREV(pNext, pPrev);
1143 pTimer->offNext = 0;
1144 pTimer->offPrev = 0;
1145 /* fall thru */
1146 }
1147
1148 /*
1149 * Destroy the timer.
1150 */
1151 case TMTIMERSTATE_PENDING_DESTROY:
1152 {
1153 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1154 PVM pVM = pTimer->CTXALLSUFF(pVM);
1155 const PTMTIMER pBigPrev = (PTMTIMER)(pTimer->pBigPrev ? MMHyperR3ToCC(pVM, pTimer->pBigPrev) : NULL);
1156 const PTMTIMER pBigNext = (PTMTIMER)(pTimer->pBigNext ? MMHyperR3ToCC(pVM, pTimer->pBigNext) : NULL);
1157
1158 /* unlink from created list */
1159 if (pBigPrev)
1160 pBigPrev->pBigNext = pTimer->pBigNext;
1161 else
1162 pVM->tm.s.pCreated = pTimer->pBigNext;
1163 if (pBigNext)
1164 pBigNext->pBigPrev = pTimer->pBigPrev;
1165 pTimer->pBigNext = 0;
1166 pTimer->pBigPrev = 0;
1167
1168 /* free */
1169 Log2(("TM: Inserting %p into the free list ahead of %p!\n", pTimer, pVM->tm.s.pFree));
1170 pTimer->pBigNext = pVM->tm.s.pFree;
1171 pVM->tm.s.pFree = (PTMTIMERR3)MMHyperCCToR3(pVM, pTimer);
1172 TM_SET_STATE(pTimer, TMTIMERSTATE_FREE);
1173 break;
1174 }
1175
1176 /*
1177 * Postpone these until they get into the right state.
1178 */
1179 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1180 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1181 tmTimerLink(pQueue, pTimer);
1182 STAM_COUNTER_INC(&pTimer->CTXALLSUFF(pVM)->tm.s.CTXALLSUFF(StatPostponed));
1183 break;
1184
1185 /*
1186 * None of these can be in the schedule.
1187 */
1188 case TMTIMERSTATE_FREE:
1189 case TMTIMERSTATE_STOPPED:
1190 case TMTIMERSTATE_ACTIVE:
1191 case TMTIMERSTATE_EXPIRED:
1192 AssertMsgFailed(("Timer (%p) in the scheduling list has an invalid state %s (%d)!",
1193 pTimer, tmTimerState(pTimer->enmState), pTimer->enmState));
1194 break;
1195 }
1196}
1197
1198
1199/**
1200 * Schedules the specified timer queue.
1201 *
1202 * @param pVM The VM to run the timers for.
1203 * @param pQueue The queue to schedule.
1204 */
1205void tmTimerQueueSchedule(PVM pVM, PTMTIMERQUEUE pQueue)
1206{
1207 VM_ASSERT_EMT(pVM);
1208
1209 /*
1210 * Dequeue the scheduling list and iterate it.
1211 */
1212 int32_t offNext = ASMAtomicXchgS32(&pQueue->offSchedule, 0);
1213 Log2(("tmTimerQueueSchedule: pQueue=%p:{.enmClock=%d, offNext=%RI32}\n", pQueue, pQueue->enmClock, offNext));
1214 if (!offNext)
1215 return;
1216 PTMTIMER pNext = (PTMTIMER)((intptr_t)pQueue + offNext);
1217 while (pNext)
1218 {
1219 /*
1220 * Unlink the head timer and find the next one.
1221 */
1222 PTMTIMER pTimer = pNext;
1223 pNext = pNext->offScheduleNext ? (PTMTIMER)((intptr_t)pNext + pNext->offScheduleNext) : NULL;
1224 pTimer->offScheduleNext = 0;
1225
1226 /*
1227 * Do the scheduling.
1228 */
1229 Log2(("tmTimerQueueSchedule: pTimer=%p:{.enmState=%s, .enmClock=%d, .enmType=%d, .pszDesc=%s}\n",
1230 pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, HCSTRING(pTimer->pszDesc)));
1231 tmTimerQueueScheduleOne(pQueue, pTimer);
1232 Log2(("tmTimerQueueSchedule: new %s\n", tmTimerState(pTimer->enmState)));
1233 } /* foreach timer in current schedule batch. */
1234}
1235
1236
1237#ifdef VBOX_STRICT
1238/**
1239 * Checks that the timer queues are sane.
1240 *
1241 * @param pVM VM handle.
1242 */
1243void tmTimerQueuesSanityChecks(PVM pVM, const char *pszWhere)
1244{
1245 /*
1246 * Check the linking of the active lists.
1247 */
1248 for (int i = 0; i < TMCLOCK_MAX; i++)
1249 {
1250 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTXALLSUFF(paTimerQueues)[i];
1251 Assert((int)pQueue->enmClock == i);
1252 PTMTIMER pPrev = NULL;
1253 for (PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue); pCur; pPrev = pCur, pCur = TMTIMER_GET_NEXT(pCur))
1254 {
1255 AssertMsg((int)pCur->enmClock == i, ("%s: %d != %d\n", pszWhere, pCur->enmClock, i));
1256 AssertMsg(TMTIMER_GET_PREV(pCur) == pPrev, ("%s: %p != %p\n", pszWhere, TMTIMER_GET_PREV(pCur), pPrev));
1257 TMTIMERSTATE enmState = pCur->enmState;
1258 switch (enmState)
1259 {
1260 case TMTIMERSTATE_ACTIVE:
1261 AssertMsg(!pCur->offScheduleNext, ("%s: %RI32\n", pszWhere, pCur->offScheduleNext));
1262 break;
1263 case TMTIMERSTATE_PENDING_STOP:
1264 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1265 case TMTIMERSTATE_PENDING_RESCHEDULE:
1266 break;
1267 default:
1268 AssertMsgFailed(("%s: Invalid state enmState=%d %s\n", pszWhere, enmState, tmTimerState(enmState)));
1269 break;
1270 }
1271 }
1272 }
1273
1274
1275# ifdef IN_RING3
1276 /*
1277 * Do the big list and check that active timers all are in the active lists.
1278 */
1279 PTMTIMERR3 pPrev = NULL;
1280 for (PTMTIMERR3 pCur = pVM->tm.s.pCreated; pCur; pPrev = pCur, pCur = pCur->pBigNext)
1281 {
1282 Assert(pCur->pBigPrev == pPrev);
1283 Assert((unsigned)pCur->enmClock < (unsigned)TMCLOCK_MAX);
1284
1285 TMTIMERSTATE enmState = pCur->enmState;
1286 switch (enmState)
1287 {
1288 case TMTIMERSTATE_ACTIVE:
1289 case TMTIMERSTATE_PENDING_STOP:
1290 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1291 case TMTIMERSTATE_PENDING_RESCHEDULE:
1292 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1293 {
1294 PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTXALLSUFF(paTimerQueues)[pCur->enmClock]);
1295 Assert(pCur->offPrev || pCur == pCurAct);
1296 while (pCurAct && pCurAct != pCur)
1297 pCurAct = TMTIMER_GET_NEXT(pCurAct);
1298 Assert(pCurAct == pCur);
1299 break;
1300 }
1301
1302 case TMTIMERSTATE_PENDING_DESTROY:
1303 case TMTIMERSTATE_PENDING_SCHEDULE:
1304 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1305 case TMTIMERSTATE_STOPPED:
1306 case TMTIMERSTATE_EXPIRED:
1307 {
1308 Assert(!pCur->offNext);
1309 Assert(!pCur->offPrev);
1310 for (PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTXALLSUFF(paTimerQueues)[pCur->enmClock]);
1311 pCurAct;
1312 pCurAct = TMTIMER_GET_NEXT(pCurAct))
1313 {
1314 Assert(pCurAct != pCur);
1315 Assert(TMTIMER_GET_NEXT(pCurAct) != pCur);
1316 Assert(TMTIMER_GET_PREV(pCurAct) != pCur);
1317 }
1318 break;
1319 }
1320
1321 /* ignore */
1322 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1323 break;
1324
1325 /* shouldn't get here! */
1326 default:
1327 AssertMsgFailed(("Invalid state enmState=%d %s\n", enmState, tmTimerState(enmState)));
1328 break;
1329 }
1330 }
1331# endif /* IN_RING3 */
1332}
1333#endif /* !VBOX_STRICT */
1334
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