VirtualBox

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

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

We're better off using the generic timer implementation on darwin. I get 201 ticks max with the posix one, while the generic manages more than 430 and only fails on the 1ms interval test. (tried three tstTimer instances in parallel, no trouble)

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