VirtualBox

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

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

LED and Network Config interfaces added. Fix in TMAll.cpp: Assertions in PENDING_STOP_SCHEDULE could be
triggered erroneously when a timer stop is being scheduled by EMT and another thread sets the timer.

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