VirtualBox

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

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

New method for HWACCM called TMCpuTickCanUseRealTSC() (completely untested).

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