VirtualBox

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

Last change on this file since 33150 was 33150, checked in by vboxsync, 15 years ago

IPRT/r0drv/solaris: Some more adjustments.

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