VirtualBox

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

Last change on this file since 3254 was 2981, checked in by vboxsync, 17 years ago

InnoTek -> innotek: all the headers and comments.

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