VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/timer-generic.cpp@ 1536

Last change on this file since 1536 was 1536, checked in by vboxsync, 18 years ago

Some ring-3 adjustments.

  • Property svn:keywords set to Id
File size: 9.8 KB
Line 
1/** $Id: timer-generic.cpp 1536 2007-03-16 17:02:02Z vboxsync $ */
2/** @file
3 * InnoTek Portable Runtime - Timers, Generic.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include <iprt/timer.h>
27#include <iprt/thread.h>
28#include <iprt/err.h>
29#include <iprt/assert.h>
30#include <iprt/alloc.h>
31#include <iprt/asm.h>
32#include <iprt/semaphore.h>
33#include <iprt/time.h>
34#include <iprt/log.h>
35
36
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41/**
42 * The internal representation of a timer handle.
43 */
44typedef struct RTTIMER
45{
46 /** Magic.
47 * This is RTTIMER_MAGIC, but changes to something else before the timer
48 * is destroyed to indicate clearly that thread should exit. */
49 uint32_t volatile u32Magic;
50 /** Flag indicating the the timer is suspended. */
51 uint8_t volatile fSuspended;
52 /** Flag indicating that the timer has been destroyed. */
53 uint8_t volatile fDestroyed;
54 /** Callback. */
55 PFNRTTIMER pfnTimer;
56 /** User argument. */
57 void *pvUser;
58 /** The timer thread. */
59 RTTHREAD Thread;
60 /** Event semaphore on which the thread is blocked. */
61 RTSEMEVENT Event;
62 /** The timer interval. 0 if one-shot. */
63 uint64_t u64NanoInterval;
64 /** The start of the current run.
65 * This is used to calculate when the timer ought to fire the next time. */
66 uint64_t volatile u64StartTS;
67 /** The start of the current run.
68 * This is used to calculate when the timer ought to fire the next time. */
69 uint64_t volatile u64NextTS;
70 /** The current tick number (since u64StartTS). */
71 uint64_t volatile iTick;
72} RTTIMER;
73/** Magic number for timer handles. (Jared Mason Diamond) */
74#define RTTIMER_MAGIC 0x19370910
75
76
77/*******************************************************************************
78* Internal Functions *
79*******************************************************************************/
80static DECLCALLBACK(int) rtTimerThread(RTTHREAD Thread, void *pvUser);
81
82
83RTDECL(int) RTTimerCreate(PRTTIMER *ppTimer, unsigned uMilliesInterval, PFNRTTIMER pfnTimer, void *pvUser)
84{
85 int rc = RTTimerCreateEx(ppTimer, uMilliesInterval * UINT64_C(1000000), 0, pfnTimer, pvUser);
86 if (RT_SUCCESS(rc))
87 {
88 rc = RTTimerStart(*ppTimer, 0);
89 if (RT_SUCCESS(rc))
90 return rc;
91 int rc2 = RTTimerDestroy(*ppTimer); AssertRC(rc2);
92 *ppTimer = NULL;
93 }
94 return rc;
95}
96
97
98RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
99{
100 *ppTimer = NULL;
101
102 /*
103 * Allocate and initialize the timer handle.
104 */
105 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
106 if (!pTimer)
107 return VERR_NO_MEMORY;
108
109 pTimer->u32Magic = RTTIMER_MAGIC;
110 pTimer->fSuspended = true;
111 pTimer->fDestroyed = false;
112 pTimer->pfnTimer = pfnTimer;
113 pTimer->pvUser = pvUser;
114 pTimer->Thread = NIL_RTTHREAD;
115 pTimer->Event = NIL_RTSEMEVENT;
116 pTimer->u64NanoInterval = u64NanoInterval;
117 pTimer->u64StartTS = 0;
118
119 int rc = RTSemEventCreate(&pTimer->Event);
120 if (RT_SUCCESS(rc))
121 {
122 rc = RTThreadCreate(&pTimer->Thread, rtTimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "TIMER");
123 if (RT_SUCCESS(rc))
124 {
125 *ppTimer = pTimer;
126 return VINF_SUCCESS;
127 }
128
129 pTimer->u32Magic = 0;
130 RTSemEventDestroy(pTimer->Event);
131 pTimer->Event = NIL_RTSEMEVENT;
132 }
133 RTMemFree(pTimer);
134
135 return rc;
136}
137
138
139/**
140 * Validates the timer handle.
141 *
142 * @returns true if valid, false if invalid.
143 * @param pTimer The handle.
144 */
145DECLINLINE(bool) rtTimerIsValid(PRTTIMER pTimer)
146{
147 AssertReturn(VALID_PTR(pTimer), false);
148 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, false);
149 AssertReturn(!pTimer->fDestroyed, false);
150 return true;
151}
152
153
154RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
155{
156 /* It's ok to pass NULL pointer. */
157 if (pTimer == /*NIL_RTTIMER*/ NULL)
158 return VINF_SUCCESS;
159 if (!rtTimerIsValid(pTimer))
160 return VERR_INVALID_HANDLE;
161
162 /*
163 * If the timer is active, we just flag it to self destruct on the next tick.
164 * If it's suspended we can safely set the destroy flag and signal it.
165 */
166 RTTHREAD Thread = pTimer->Thread;
167 if (!pTimer->fSuspended)
168 {
169 ASMAtomicXchgU8(&pTimer->fSuspended, true);
170 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
171 }
172 else
173 {
174 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
175 int rc = RTSemEventSignal(pTimer->Event);
176 if (rc == VERR_ALREADY_POSTED)
177 rc = VINF_SUCCESS;
178 AssertRC(rc);
179 }
180
181 RTThreadWait(Thread, 250, NULL);
182 return VINF_SUCCESS;
183}
184
185
186RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
187{
188 if (!rtTimerIsValid(pTimer))
189 return VERR_INVALID_HANDLE;
190 if (!pTimer->fSuspended)
191 return VERR_TIMER_ACTIVE;
192
193 /*
194 * Calc when it should start fireing and give the thread a kick so it get going.
195 */
196 u64First += RTTimeNanoTS();
197 ASMAtomicXchgU64(&pTimer->iTick, 0);
198 ASMAtomicXchgU64(&pTimer->u64StartTS, u64First);
199 ASMAtomicXchgU64(&pTimer->u64NextTS, u64First);
200 ASMAtomicXchgU8(&pTimer->fSuspended, false);
201 int rc = RTSemEventSignal(pTimer->Event);
202 if (rc == VERR_ALREADY_POSTED)
203 rc = VINF_SUCCESS;
204 AssertRC(rc);
205 return rc;
206}
207
208
209RTDECL(int) RTTimerStop(PRTTIMER pTimer)
210{
211 if (!rtTimerIsValid(pTimer))
212 return VERR_INVALID_HANDLE;
213 if (pTimer->fSuspended)
214 return VERR_TIMER_SUSPENDED;
215
216 /*
217 * Mark it as suspended and kick the thread.
218 */
219 ASMAtomicXchgU8(&pTimer->fSuspended, true);
220 int rc = RTSemEventSignal(pTimer->Event);
221 if (rc == VERR_ALREADY_POSTED)
222 rc = VINF_SUCCESS;
223 AssertRC(rc);
224 return rc;
225}
226
227
228static DECLCALLBACK(int) rtTimerThread(RTTHREAD Thread, void *pvUser)
229{
230 PRTTIMER pTimer = (PRTTIMER)pvUser;
231
232 /*
233 * The loop.
234 */
235 while (!pTimer->fDestroyed)
236 {
237 if (pTimer->fSuspended)
238 {
239 int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
240 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
241 {
242 AssertRC(rc);
243 RTThreadSleep(1000); /* Don't cause trouble! */
244 }
245 }
246 else
247 {
248 const uint64_t u64NanoTS = RTTimeNanoTS();
249 if (u64NanoTS >= pTimer->u64NextTS)
250 {
251 pTimer->iTick++;
252 pTimer->pfnTimer(pTimer, pTimer->pvUser);
253
254 /* status changed? */
255 if (pTimer->fSuspended || pTimer->fDestroyed)
256 continue;
257
258 /* one shot? */
259 if (!pTimer->u64NanoInterval)
260 {
261 ASMAtomicXchgU8(&pTimer->fSuspended, true);
262 continue;
263 }
264
265 /* calc the next time we should fire. */
266 pTimer->u64NextTS = pTimer->u64StartTS + pTimer->iTick * pTimer->u64NanoInterval;
267 if (pTimer->u64NextTS < u64NanoTS)
268#ifdef IN_RING3 /* In ring-3 we'll catch up lost ticks immediately. */
269 pTimer->u64NextTS = u64NanoTS + 1;
270#else
271 pTimer->u64NextTS = u64NanoTS + RTTimerGetSystemGranularity() / 2;
272#endif
273 }
274
275 /* block. */
276 uint64_t cNanoSeconds = pTimer->u64NextTS - u64NanoTS;
277#ifdef IN_RING3 /* In ring-3 we'll catch up lost ticks immediately. */
278 if (cNanoSeconds > 10)
279#endif
280 {
281 int rc = RTSemEventWait(pTimer->Event, cNanoSeconds < 1000000 ? 1 : cNanoSeconds / 1000000);
282 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED && rc != VERR_TIMEOUT)
283 {
284 AssertRC(rc);
285 RTThreadSleep(1000); /* Don't cause trouble! */
286 }
287 }
288 }
289 }
290
291 /*
292 * Release the timer resources.
293 */
294 ASMAtomicIncU32(&pTimer->u32Magic); /* make the handle invalid. */
295 int rc = RTSemEventDestroy(pTimer->Event); AssertRC(rc);
296 pTimer->Event = NIL_RTSEMEVENT;
297 pTimer->Thread = NIL_RTTHREAD;
298 RTMemFree(pTimer);
299
300 return VINF_SUCCESS;
301}
302
303
304
305
306RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
307{
308 return 10000000; /* 10ms */
309}
310
311
312RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
313{
314 return VERR_NOT_SUPPORTED;
315}
316
317
318RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
319{
320 return VERR_NOT_SUPPORTED;
321}
322
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette