VirtualBox

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

Last change on this file since 8170 was 8155, checked in by vboxsync, 16 years ago

The Big Sun Rebranding Header Change

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