VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c@ 22582

Last change on this file since 22582 was 22582, checked in by vboxsync, 16 years ago

Runtime/semevent{multi}-r0drv-freebsd:

Replace spin/default mutexes with sleep queues and
our spinlock implementation and unify the code
of both variants.
Spin mutexes can't be interrupted because they
disable interrupts and default mutexes.
Fixes panics if the kernel is compiled with WITNESS and INVARIANTS.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.7 KB
Line 
1/* $Id: semeventmulti-r0drv-freebsd.c 22582 2009-08-30 20:47:57Z vboxsync $ */
2/** @file
3 * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, FreeBSD.
4 */
5
6/*
7 * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include "the-freebsd-kernel.h"
35
36#include <iprt/semaphore.h>
37#include <iprt/alloc.h>
38#include <iprt/asm.h>
39#include <iprt/assert.h>
40#include <iprt/err.h>
41#include <iprt/spinlock.h>
42
43#include "internal/magics.h"
44
45
46/*******************************************************************************
47* Structures and Typedefs *
48*******************************************************************************/
49/**
50 * FreeBSD multiple release event semaphore.
51 */
52typedef struct RTSEMEVENTMULTIINTERNAL
53{
54 /** Magic value (RTSEMEVENTMULTI_MAGIC). */
55 uint32_t volatile u32Magic;
56 /** The number of waiting threads. */
57 uint32_t volatile cWaiters;
58 /** Set if the event object is signaled. */
59 uint8_t volatile fSignaled;
60 /** The number of threads in the process of waking up. */
61 uint32_t volatile cWaking;
62 /** Spinlock protecting this structure. */
63 RTSPINLOCK hSpinLock;
64} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
65
66
67RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
68{
69 Assert(sizeof(RTSEMEVENTMULTIINTERNAL) > sizeof(void *));
70 AssertPtrReturn(pEventMultiSem, VERR_INVALID_POINTER);
71
72 PRTSEMEVENTMULTIINTERNAL pEventMultiInt = (PRTSEMEVENTMULTIINTERNAL)RTMemAllocZ(sizeof(*pEventMultiInt));
73 if (pEventMultiInt)
74 {
75 pEventMultiInt->u32Magic = RTSEMEVENTMULTI_MAGIC;
76 pEventMultiInt->cWaiters = 0;
77 pEventMultiInt->cWaking = 0;
78 pEventMultiInt->fSignaled = 0;
79 int rc = RTSpinlockCreate(&pEventMultiInt->hSpinLock);
80 if (RT_SUCCESS(rc))
81 {
82 *pEventMultiSem = pEventMultiInt;
83 return VINF_SUCCESS;
84 }
85
86 RTMemFree(pEventMultiInt);
87 return rc;
88 }
89 return VERR_NO_MEMORY;
90}
91
92
93RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
94{
95 if (EventMultiSem == NIL_RTSEMEVENTMULTI) /* don't bitch */
96 return VERR_INVALID_HANDLE;
97 PRTSEMEVENTMULTIINTERNAL pEventMultiInt = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
98 RTSPINLOCKTMP Tmp;
99
100 AssertPtrReturn(pEventMultiInt, VERR_INVALID_HANDLE);
101 AssertMsgReturn(pEventMultiInt->u32Magic == RTSEMEVENTMULTI_MAGIC,
102 ("pEventMultiInt=%p u32Magic=%#x\n", pEventMultiInt, pEventMultiInt->u32Magic),
103 VERR_INVALID_HANDLE);
104
105 RTSpinlockAcquire(pEventMultiInt->hSpinLock, &Tmp);
106 ASMAtomicIncU32(&pEventMultiInt->u32Magic); /* make the handle invalid */
107 if (pEventMultiInt->cWaiters > 0)
108 {
109 /* abort waiting thread, last man cleans up. */
110 ASMAtomicXchgU32(&pEventMultiInt->cWaking, pEventMultiInt->cWaking + pEventMultiInt->cWaiters);
111 sleepq_lock(pEventMultiInt);
112 sleepq_broadcast(pEventMultiInt, SLEEPQ_CONDVAR, 0, 0);
113 sleepq_release(pEventMultiInt);
114 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
115 }
116 else if (pEventMultiInt->cWaking)
117 /* the last waking thread is gonna do the cleanup */
118 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
119 else
120 {
121 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
122 RTSpinlockDestroy(pEventMultiInt->hSpinLock);
123 RTMemFree(pEventMultiInt);
124 }
125
126 return VINF_SUCCESS;
127}
128
129
130RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
131{
132 RTSPINLOCKTMP Tmp;
133 PRTSEMEVENTMULTIINTERNAL pEventMultiInt = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
134 AssertPtrReturn(pEventMultiInt, VERR_INVALID_HANDLE);
135 AssertMsgReturn(pEventMultiInt->u32Magic == RTSEMEVENTMULTI_MAGIC,
136 ("pEventMultiInt=%p u32Magic=%#x\n", pEventMultiInt, pEventMultiInt->u32Magic),
137 VERR_INVALID_HANDLE);
138
139 RTSpinlockAcquire(pEventMultiInt->hSpinLock, &Tmp);
140
141 ASMAtomicXchgU8(&pEventMultiInt->fSignaled, true);
142 if (pEventMultiInt->cWaiters > 0)
143 {
144 ASMAtomicXchgU32(&pEventMultiInt->cWaking, pEventMultiInt->cWaking + pEventMultiInt->cWaiters);
145 ASMAtomicXchgU32(&pEventMultiInt->cWaiters, 0);
146 sleepq_lock(pEventMultiInt);
147 int fWakeupSwapProc = sleepq_signal(pEventMultiInt, SLEEPQ_CONDVAR, 0, 0);
148 sleepq_release(pEventMultiInt);
149 if (fWakeupSwapProc)
150 kick_proc0();
151 }
152
153 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
154 return VINF_SUCCESS;
155}
156
157
158RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem)
159{
160 RTSPINLOCKTMP Tmp;
161 PRTSEMEVENTMULTIINTERNAL pEventMultiInt = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
162 AssertPtrReturn(pEventMultiInt, VERR_INVALID_HANDLE);
163 AssertMsgReturn(pEventMultiInt->u32Magic == RTSEMEVENTMULTI_MAGIC,
164 ("pEventMultiInt=%p u32Magic=%#x\n", pEventMultiInt, pEventMultiInt->u32Magic),
165 VERR_INVALID_HANDLE);
166
167 RTSpinlockAcquire(pEventMultiInt->hSpinLock, &Tmp);
168 ASMAtomicXchgU8(&pEventMultiInt->fSignaled, false);
169 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
170 return VINF_SUCCESS;
171}
172
173
174static int rtSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies, bool fInterruptible)
175{
176 int rc;
177 RTSPINLOCKTMP Tmp;
178 PRTSEMEVENTMULTIINTERNAL pEventMultiInt = (PRTSEMEVENTMULTIINTERNAL)EventMultiSem;
179 AssertPtrReturn(pEventMultiInt, VERR_INVALID_HANDLE);
180 AssertMsgReturn(pEventMultiInt->u32Magic == RTSEMEVENTMULTI_MAGIC,
181 ("pEventMultiInt=%p u32Magic=%#x\n", pEventMultiInt, pEventMultiInt->u32Magic),
182 VERR_INVALID_HANDLE);
183
184 RTSpinlockAcquire(pEventMultiInt->hSpinLock, &Tmp);
185
186 if (pEventMultiInt->fSignaled)
187 rc = VINF_SUCCESS;
188 else
189 {
190 if (cMillies == 0)
191 rc = VERR_TIMEOUT;
192 else
193 {
194 ASMAtomicIncU32(&pEventMultiInt->cWaiters);
195
196 int fFlags = SLEEPQ_CONDVAR;
197
198 if (fInterruptible)
199 fFlags |= SLEEPQ_INTERRUPTIBLE;
200
201 sleepq_lock(pEventMultiInt);
202 sleepq_add(pEventMultiInt, NULL, "IPRT Event Semaphore", fFlags, 0);
203
204 if (cMillies != RT_INDEFINITE_WAIT)
205 {
206 /*
207 * Translate milliseconds into ticks and go to sleep.
208 */
209 struct timeval tv;
210
211 tv.tv_sec = cMillies / 1000;
212 tv.tv_usec = (cMillies % 1000) * 1000;
213
214 sleepq_set_timeout(pEventMultiInt, tvtohz(&tv));
215
216 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
217
218 if (fInterruptible)
219 rc = sleepq_timedwait_sig(pEventMultiInt, 0);
220 else
221 rc = sleepq_timedwait(pEventMultiInt, 0);
222 }
223 else
224 {
225 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
226
227 if (fInterruptible)
228 rc = sleepq_wait_sig(pEventMultiInt, 0);
229 else
230 {
231 rc = 0;
232 sleepq_wait(pEventMultiInt, 0);
233 }
234 }
235
236 RTSpinlockAcquire(pEventMultiInt->hSpinLock, &Tmp);
237
238 switch (rc)
239 {
240 case 0:
241 if (pEventMultiInt->u32Magic == RTSEMEVENTMULTI_MAGIC)
242 {
243 ASMAtomicDecU32(&pEventMultiInt->cWaking);
244 rc = VINF_SUCCESS;
245 }
246 else
247 {
248 rc = VERR_SEM_DESTROYED; /** @todo this isn't necessarily correct, we've
249 * could've woken up just before destruction... */
250 if (!ASMAtomicDecU32(&pEventMultiInt->cWaking))
251 {
252 /* The event was destroyed, as the last thread do the cleanup.
253 we don't actually know whether */
254 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
255 RTSpinlockDestroy(pEventMultiInt->hSpinLock);
256 RTMemFree(pEventMultiInt);
257 return rc;
258 }
259 }
260 break;
261
262 case EWOULDBLOCK:
263 Assert(cMillies != RT_INDEFINITE_WAIT);
264 if (pEventMultiInt->cWaiters > 0)
265 ASMAtomicDecU32(&pEventMultiInt->cWaiters);
266 rc = VERR_TIMEOUT;
267 break;
268
269 case EINTR:
270 case ERESTART:
271 Assert(fInterruptible);
272 if (pEventMultiInt->cWaiters > 0)
273 ASMAtomicDecU32(&pEventMultiInt->cWaiters);
274 rc = VERR_INTERRUPTED;
275 break;
276
277 default:
278 AssertMsgFailed(("sleepq_* -> %d\n", rc));
279 rc = VERR_GENERAL_FAILURE;
280 break;
281 }
282 }
283 }
284
285 RTSpinlockRelease(pEventMultiInt->hSpinLock, &Tmp);
286 return rc;
287}
288
289
290RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
291{
292 return rtSemEventMultiWait(EventMultiSem, cMillies, false /* not interruptible */);
293}
294
295
296RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
297{
298 return rtSemEventMultiWait(EventMultiSem, cMillies, true /* interruptible */);
299}
300
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