VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/solaris/semeventmulti-r0drv-solaris.c@ 24426

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

Solaris/sem-r0drv: fix for #4259 host deadlock.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.9 KB
Line 
1/* $Id: semeventmulti-r0drv-solaris.c 22991 2009-09-14 10:16:08Z vboxsync $ */
2/** @file
3 * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, Solaris.
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 * 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include "the-solaris-kernel.h"
36#include "internal/iprt.h"
37#include <iprt/semaphore.h>
38
39#include <iprt/assert.h>
40#include <iprt/asm.h>
41#include <iprt/err.h>
42#include <iprt/mem.h>
43#include <iprt/mp.h>
44#include <iprt/thread.h>
45#include "internal/magics.h"
46
47
48/*******************************************************************************
49* Structures and Typedefs *
50*******************************************************************************/
51/**
52 * FreeBSD multiple release event semaphore.
53 */
54typedef struct RTSEMEVENTMULTIINTERNAL
55{
56 /** Magic value (RTSEMEVENTMULTI_MAGIC). */
57 uint32_t volatile u32Magic;
58 /** The number of waiting threads. */
59 uint32_t volatile cWaiters;
60 /** Set if the event object is signaled. */
61 uint8_t volatile fSignaled;
62 /** The number of threads in the process of waking up. */
63 uint32_t volatile cWaking;
64 /** The Solaris mutex protecting this structure and pairing up the with the cv. */
65 kmutex_t Mtx;
66 /** The Solaris condition variable. */
67 kcondvar_t Cnd;
68} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
69
70
71
72RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
73{
74 Assert(sizeof(RTSEMEVENTMULTIINTERNAL) > sizeof(void *));
75 AssertPtrReturn(pEventMultiSem, VERR_INVALID_POINTER);
76 RT_ASSERT_PREEMPTIBLE();
77
78 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
79 if (pThis)
80 {
81 pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
82 pThis->cWaiters = 0;
83 pThis->cWaking = 0;
84 pThis->fSignaled = 0;
85 mutex_init(&pThis->Mtx, "IPRT Multiple Release Event Semaphore", MUTEX_DRIVER, (void *)ipltospl(DISP_LEVEL));
86 cv_init(&pThis->Cnd, "IPRT CV", CV_DRIVER, NULL);
87 *pEventMultiSem = pThis;
88 return VINF_SUCCESS;
89 }
90 return VERR_NO_MEMORY;
91}
92
93
94RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
95{
96 if (EventMultiSem == NIL_RTSEMEVENTMULTI) /* don't bitch */
97 return VERR_INVALID_HANDLE;
98 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
99 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
100 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
101 ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
102 VERR_INVALID_HANDLE);
103 RT_ASSERT_INTS_ON();
104
105 mutex_enter(&pThis->Mtx);
106 ASMAtomicIncU32(&pThis->u32Magic); /* make the handle invalid */
107 if (pThis->cWaiters > 0)
108 {
109 /* abort waiting thread, last man cleans up. */
110 ASMAtomicXchgU32(&pThis->cWaking, pThis->cWaking + pThis->cWaiters);
111 cv_broadcast(&pThis->Cnd);
112 mutex_exit(&pThis->Mtx);
113 }
114 else if (pThis->cWaking)
115 /* the last waking thread is gonna do the cleanup */
116 mutex_exit(&pThis->Mtx);
117 else
118 {
119 mutex_exit(&pThis->Mtx);
120 cv_destroy(&pThis->Cnd);
121 mutex_destroy(&pThis->Mtx);
122 RTMemFree(pThis);
123 }
124
125 return VINF_SUCCESS;
126}
127
128
129RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
130{
131 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
132 RT_ASSERT_PREEMPT_CPUID_VAR();
133
134 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
135 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
136 ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
137 VERR_INVALID_HANDLE);
138 RT_ASSERT_INTS_ON();
139
140 /*
141 * If we're in interrupt context we need to unpin the underlying current
142 * thread as this could lead to a deadlock (see #4259 for the full explanation)
143 *
144 * Note! See remarks about preemption in RTSemEventSignal.
145 */
146 int fAcquired = mutex_tryenter(&pThis->Mtx);
147 if (!fAcquired)
148 {
149 if (curthread->t_intr && getpil() < DISP_LEVEL)
150 {
151 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
152 RTThreadPreemptDisable(&PreemptState);
153 preempt();
154 RTThreadPreemptRestore(&PreemptState);
155 }
156 mutex_enter(&pThis->Mtx);
157 }
158
159 ASMAtomicXchgU8(&pThis->fSignaled, true);
160 if (pThis->cWaiters > 0)
161 {
162 ASMAtomicXchgU32(&pThis->cWaking, pThis->cWaking + pThis->cWaiters);
163 ASMAtomicXchgU32(&pThis->cWaiters, 0);
164 cv_broadcast(&pThis->Cnd);
165 }
166
167 mutex_exit(&pThis->Mtx);
168
169 RT_ASSERT_PREEMPT_CPUID();
170 return VINF_SUCCESS;
171}
172
173
174RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem)
175{
176 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
177 RT_ASSERT_PREEMPT_CPUID_VAR();
178
179 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
180 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
181 ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
182 VERR_INVALID_HANDLE);
183 RT_ASSERT_INTS_ON();
184
185 /*
186 * If we're in interrupt context we need to unpin the underlying current
187 * thread as this could lead to a deadlock (see #4259 for the full explanation)
188 *
189 * Note! See remarks about preemption in RTSemEventSignal.
190 */
191 int fAcquired = mutex_tryenter(&pThis->Mtx);
192 if (!fAcquired)
193 {
194 if (curthread->t_intr && getpil() < DISP_LEVEL)
195 {
196 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
197 RTThreadPreemptDisable(&PreemptState);
198 preempt();
199 RTThreadPreemptRestore(&PreemptState);
200 }
201 mutex_enter(&pThis->Mtx);
202 }
203
204 ASMAtomicXchgU8(&pThis->fSignaled, false);
205 mutex_exit(&pThis->Mtx);
206
207 RT_ASSERT_PREEMPT_CPUID();
208 return VINF_SUCCESS;
209}
210
211
212static int rtSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies, bool fInterruptible)
213{
214 int rc;
215 PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
216 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
217 AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
218 ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
219 VERR_INVALID_HANDLE);
220 if (cMillies)
221 RT_ASSERT_PREEMPTIBLE();
222
223 mutex_enter(&pThis->Mtx);
224
225 if (pThis->fSignaled)
226 rc = VINF_SUCCESS;
227 else if (!cMillies)
228 rc = VERR_TIMEOUT;
229 else
230 {
231 ASMAtomicIncU32(&pThis->cWaiters);
232
233 /*
234 * Translate milliseconds into ticks and go to sleep.
235 */
236 if (cMillies != RT_INDEFINITE_WAIT)
237 {
238 clock_t cTicks = drv_usectohz((clock_t)(cMillies * 1000L));
239 clock_t timeout = ddi_get_lbolt();
240 timeout += cTicks;
241 if (fInterruptible)
242 rc = cv_timedwait_sig(&pThis->Cnd, &pThis->Mtx, timeout);
243 else
244 rc = cv_timedwait(&pThis->Cnd, &pThis->Mtx, timeout);
245 }
246 else
247 {
248 if (fInterruptible)
249 rc = cv_wait_sig(&pThis->Cnd, &pThis->Mtx);
250 else
251 {
252 cv_wait(&pThis->Cnd, &pThis->Mtx);
253 rc = 1;
254 }
255 }
256 if (rc > 0)
257 {
258 /* Retured due to call to cv_signal() or cv_broadcast() */
259 if (RT_LIKELY(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC))
260 rc = VINF_SUCCESS;
261 else
262 {
263 rc = VERR_SEM_DESTROYED;
264 if (!ASMAtomicDecU32(&pThis->cWaking))
265 {
266 mutex_exit(&pThis->Mtx);
267 cv_destroy(&pThis->Cnd);
268 mutex_destroy(&pThis->Mtx);
269 RTMemFree(pThis);
270 return rc;
271 }
272 }
273 ASMAtomicDecU32(&pThis->cWaking);
274 }
275 else if (rc == -1)
276 {
277 /* Returned due to timeout being reached */
278 if (pThis->cWaiters > 0)
279 ASMAtomicDecU32(&pThis->cWaiters);
280 rc = VERR_TIMEOUT;
281 }
282 else
283 {
284 /* Returned due to pending signal */
285 if (pThis->cWaiters > 0)
286 ASMAtomicDecU32(&pThis->cWaiters);
287 rc = VERR_INTERRUPTED;
288 }
289 }
290
291 mutex_exit(&pThis->Mtx);
292 return rc;
293}
294
295
296RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
297{
298 return rtSemEventMultiWait(EventMultiSem, cMillies, false /* not interruptible */);
299}
300
301
302RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
303{
304 return rtSemEventMultiWait(EventMultiSem, cMillies, true /* interruptible */);
305}
306
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