VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/solaris/semeventwait-r0drv-solaris.h@ 33149

最後變更 在這個檔案從33149是 33149,由 vboxsync 提交於 14 年 前

IPRT/r0drv/solaris: Use timeout_generic for high resolution timeouts when available as it is cheaper and much kinder to the system.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 11.9 KB
 
1/* $Id: semeventwait-r0drv-solaris.h 33149 2010-10-15 11:26:24Z vboxsync $ */
2/** @file
3 * IPRT - Linux Ring-0 Driver Helpers for Abstracting Wait Queues,
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28#ifndef ___r0drv_solaris_semeventwait_r0drv_solaris_h
29#define ___r0drv_solaris_semeventwait_r0drv_solaris_h
30
31#include "the-solaris-kernel.h"
32
33#include <iprt/err.h>
34#include <iprt/string.h>
35#include <iprt/time.h>
36
37/**
38 * Solaris semaphore wait structure.
39 */
40typedef struct RTR0SEMSOLWAIT
41{
42 /** The absolute timeout given as nano seconds since the start of the
43 * monotonic clock. */
44 uint64_t uNsAbsTimeout;
45 /** The timeout in nano seconds relative to the start of the wait. */
46 uint64_t cNsRelTimeout;
47 /** The native timeout value. */
48 union
49 {
50 /** The timeout (abs lbolt) when fHighRes is false. */
51 clock_t lTimeout;
52 } u;
53 /** Set if we use high resolution timeouts. */
54 bool fHighRes;
55 /** Set if it's an indefinite wait. */
56 bool fIndefinite;
57 /** Set if we've already timed out.
58 * Set by rtR0SemSolWaitDoIt or rtR0SemSolWaitHighResTimeout, read by
59 * rtR0SemSolWaitHasTimedOut. */
60 bool volatile fTimedOut;
61 /** Whether the wait was interrupted. */
62 bool fInterrupted;
63 /** Interruptible or uninterruptible wait. */
64 bool fInterruptible;
65 /** The thread to wake up. */
66 kthread_t *pThread;
67 /** Cylic timer ID (used by the timeout callback). */
68 cyclic_id_t idCy;
69} RTR0SEMSOLWAIT;
70/** Pointer to a solaris semaphore wait structure. */
71typedef RTR0SEMSOLWAIT *PRTR0SEMSOLWAIT;
72
73
74/**
75 * Initializes a wait.
76 *
77 * The caller MUST check the wait condition BEFORE calling this function or the
78 * timeout logic will be flawed.
79 *
80 * @returns VINF_SUCCESS or VERR_TIMEOUT.
81 * @param pWait The wait structure.
82 * @param fFlags The wait flags.
83 * @param uTimeout The timeout.
84 * @param pWaitQueue The wait queue head.
85 */
86DECLINLINE(int) rtR0SemSolWaitInit(PRTR0SEMSOLWAIT pWait, uint32_t fFlags, uint64_t uTimeout)
87{
88 /*
89 * Process the flags and timeout.
90 */
91 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
92 {
93 if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
94 uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
95 ? uTimeout * UINT32_C(1000000)
96 : UINT64_MAX;
97 if (uTimeout == UINT64_MAX)
98 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
99 else
100 {
101 uint64_t u64Now;
102 if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
103 {
104 if (uTimeout == 0)
105 return VERR_TIMEOUT;
106
107 u64Now = RTTimeSystemNanoTS();
108 pWait->cNsRelTimeout = uTimeout;
109 pWait->uNsAbsTimeout = u64Now + uTimeout;
110 if (pWait->uNsAbsTimeout < u64Now) /* overflow */
111 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
112 }
113 else
114 {
115 u64Now = RTTimeSystemNanoTS();
116 if (u64Now >= uTimeout)
117 return VERR_TIMEOUT;
118
119 pWait->cNsRelTimeout = uTimeout - u64Now;
120 pWait->uNsAbsTimeout = uTimeout;
121 }
122 }
123 }
124
125 if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
126 {
127 pWait->fIndefinite = false;
128 if ( (fFlags & (RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_ABSOLUTE))
129 || pWait->cNsRelTimeout < UINT32_C(1000000000) / 100 /*Hz*/ * 4)
130 pWait->fHighRes = true;
131 else
132 {
133 uint64_t cTicks = NSEC_TO_TICK_ROUNDUP(uTimeout);
134 if (cTicks >= LONG_MAX)
135 fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
136 else
137 {
138 pWait->u.lTimeout = ddi_get_lbolt() + cTicks;
139 pWait->fHighRes = false;
140 }
141 }
142 }
143
144 if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
145 {
146 pWait->fIndefinite = true;
147 pWait->fHighRes = false;
148 pWait->uNsAbsTimeout = UINT64_MAX;
149 pWait->cNsRelTimeout = UINT64_MAX;
150 pWait->u.lTimeout = LONG_MAX;
151 }
152
153 pWait->fTimedOut = false;
154 pWait->fInterrupted = false;
155 pWait->fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
156 pWait->pThread = curthread;
157 pWait->idCy = CYCLIC_NONE;
158
159 return VINF_SUCCESS;
160}
161
162
163/**
164 * Cyclic timeout callback that sets the timeout indicator and wakes up the
165 * waiting thread.
166 *
167 * @param pvUser The wait structure.
168 */
169static void rtR0SemSolWaitHighResTimeout(void *pvUser)
170{
171 PRTR0SEMSOLWAIT pWait = (PRTR0SEMSOLWAIT)pvUser;
172 kthread_t *pThread = pWait->pThread;
173 if (VALID_PTR(pThread)) /* paranoia */
174 {
175 /* Note: Trying to take the cpu_lock here doesn't work. */
176 if (mutex_owner(&cpu_lock) == curthread)
177 {
178 cyclic_remove(pWait->idCy);
179 pWait->idCy = CYCLIC_NONE;
180 }
181 ASMAtomicWriteBool(&pWait->fTimedOut, true);
182 setrun(pThread);
183 }
184}
185
186
187/**
188 * Timeout callback that sets the timeout indicator and wakes up the waiting
189 * thread.
190 *
191 * @param pvUser The wait structure.
192 */
193static void rtR0SemSolWaitTimeout(void *pvUser)
194{
195 PRTR0SEMSOLWAIT pWait = (PRTR0SEMSOLWAIT)pvUser;
196 kthread_t *pThread = pWait->pThread;
197 if (VALID_PTR(pThread)) /* paranoia */
198 {
199 ASMAtomicWriteBool(&pWait->fTimedOut, true);
200 setrun(pThread);
201 }
202}
203
204
205/**
206 * Do the actual wait.
207 *
208 * @param pWait The wait structure.
209 * @param pCnd The condition variable to wait on.
210 * @param pMtx The mutex related to the condition variable.
211 * The caller has entered this.
212 */
213DECLINLINE(void) rtR0SemSolWaitDoIt(PRTR0SEMSOLWAIT pWait, kcondvar_t *pCnd, kmutex_t *pMtx)
214{
215 int rc = 1;
216 union
217 {
218 callout_id_t idCo;
219 timeout_id_t idTom;
220 } u;
221
222 /*
223 * Arm the timeout callback.
224 */
225 bool const fHasTimeout = !pWait->fIndefinite;
226 if (fHasTimeout)
227 {
228 if (pWait->fHighRes)
229 {
230 if (g_pfnrtR0Sol_timeout_generic != NULL)
231 {
232 /*
233 * High resolution timeout - arm a high resolution timeout callback
234 * for waking up the thread at the desired time.
235 */
236 u.idCo = g_pfnrtR0Sol_timeout_generic(CALLOUT_REALTIME, rtR0SemSolWaitTimeout, pWait,
237 pWait->uNsAbsTimeout, 50000 /*res*/,
238 CALLOUT_FLAG_ABSOLUTE);
239 }
240 else
241 {
242 /*
243 * High resolution timeout - arm a one-shot cyclic for waking up
244 * the thread at the desired time.
245 */
246 cyc_handler_t Cyh;
247 Cyh.cyh_arg = pWait;
248 Cyh.cyh_func = rtR0SemSolWaitHighResTimeout;
249 Cyh.cyh_level = CY_LOW_LEVEL; /// @todo try CY_LOCK_LEVEL and CY_HIGH_LEVEL?
250
251 cyc_time_t Cyt;
252 Cyt.cyt_when = pWait->uNsAbsTimeout;
253 Cyt.cyt_interval = UINT64_C(1000000000) * 60;
254
255 mutex_enter(&cpu_lock);
256 pWait->idCy = cyclic_add(&Cyh, &Cyt);
257 mutex_exit(&cpu_lock);
258 }
259 }
260 else
261 {
262 /*
263 * Normal timeout.
264 * We're better off with our own callback like on the timeout man page,
265 * than calling cv_timedwait[_sig]().
266 */
267 u.idTom = realtime_timeout(rtR0SemSolWaitTimeout, pWait, pWait->u.lTimeout);
268 }
269 }
270
271 /*
272 * Do the waiting.
273 */
274 if (pWait->fInterruptible)
275 rc = cv_wait_sig(pCnd, pMtx);
276 else
277 cv_wait(pCnd, pMtx);
278
279 /*
280 * Remove the timeout callback. Drop the lock while we're doing that
281 * to reduce lock contention - we don't need it yet anyway. (Too bad we
282 * are stuck with the cv_* API here, it's doing a little bit too much.)
283 */
284 if (fHasTimeout)
285 {
286 mutex_exit(pMtx);
287
288 if (pWait->fHighRes)
289 {
290 if (g_pfnrtR0Sol_timeout_generic != NULL)
291 g_pfnrtR0Sol_untimeout_generic(u.idCo, 0 /*nowait*/);
292 else
293 {
294 mutex_enter(&cpu_lock);
295 if (pWait->idCy != CYCLIC_NONE)
296 {
297 cyclic_remove(pWait->idCy);
298 pWait->idCy = CYCLIC_NONE;
299 }
300 mutex_exit(&cpu_lock);
301 }
302 }
303 else
304 untimeout(u.idTom);
305
306 mutex_enter(pMtx);
307 }
308
309 /*
310 * Above zero means normal wake-up.
311 * Interruption is signalled by 0, timeouts by -1.
312 */
313 if (RT_UNLIKELY(rc <= 0))
314 {
315 if (RT_LIKELY(rc == 0))
316 pWait->fInterrupted = true;
317 else
318 AssertMsgFailed(("rc=%d\n", rc)); /* no timeouts, see above! */
319 }
320}
321
322
323/**
324 * Checks if a solaris wait was interrupted.
325 *
326 * @returns true / false
327 * @param pWait The wait structure.
328 * @remarks This shall be called before the first rtR0SemSolWaitDoIt().
329 */
330DECLINLINE(bool) rtR0SemSolWaitWasInterrupted(PRTR0SEMSOLWAIT pWait)
331{
332 return pWait->fInterrupted;
333}
334
335
336/**
337 * Checks if a solaris wait has timed out.
338 *
339 * @returns true / false
340 * @param pWait The wait structure.
341 */
342DECLINLINE(bool) rtR0SemSolWaitHasTimedOut(PRTR0SEMSOLWAIT pWait)
343{
344 return pWait->fTimedOut;
345}
346
347
348/**
349 * Deletes a solaris wait.
350 *
351 * @param pWait The wait structure.
352 */
353DECLINLINE(void) rtR0SemSolWaitDelete(PRTR0SEMSOLWAIT pWait)
354{
355 pWait->pThread = NULL;
356}
357
358/**
359 * Enters the mutex, unpinning the underlying current thread if contended and
360 * we're on an interrupt thread.
361 *
362 * The unpinning is done to prevent a deadlock, see s this could lead to a
363 * deadlock (see #4259 for the full explanation)
364 *
365 * @param pMtx The mutex to enter.
366 */
367DECLINLINE(void) rtR0SemSolWaitEnterMutexWithUnpinningHack(kmutex_t *pMtx)
368{
369 int fAcquired = mutex_tryenter(pMtx);
370 if (!fAcquired)
371 {
372 /*
373 * Note! This assumes nobody is using the RTThreadPreemptDisable in an
374 * interrupt context and expects it to work right. The swtch will
375 * result in a voluntary preemption. To fix this, we would have to
376 * do our own counting in RTThreadPreemptDisable/Restore like we do
377 * on systems which doesn't do preemption (OS/2, linux, ...) and
378 * check whether preemption was disabled via RTThreadPreemptDisable
379 * or not and only call swtch if RTThreadPreemptDisable wasn't called.
380 */
381 if (curthread->t_intr && getpil() < DISP_LEVEL)
382 {
383 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
384 RTThreadPreemptDisable(&PreemptState);
385 preempt();
386 RTThreadPreemptRestore(&PreemptState);
387 }
388 mutex_enter(pMtx);
389 }
390}
391
392#endif
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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