VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/semeventmulti-posix.cpp@ 16522

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

Fix a couple of words doubled in comments. No code changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.2 KB
Line 
1/* $Id: semeventmulti-posix.cpp 14318 2008-11-18 16:56:53Z vboxsync $ */
2/** @file
3 * IPRT - Multiple Release Event Semaphore, POSIX.
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* Header Files *
33*******************************************************************************/
34#include <iprt/semaphore.h>
35#include <iprt/assert.h>
36#include <iprt/alloc.h>
37#include <iprt/asm.h>
38#include <iprt/err.h>
39
40#include <errno.h>
41#include <pthread.h>
42#include <unistd.h>
43#include <sys/time.h>
44
45
46/*******************************************************************************
47* Structures and Typedefs *
48*******************************************************************************/
49/** Posix internal representation of a Mutex Multi semaphore.
50 * The POSIX implementation uses a mutex and a condition variable to implement
51 * the automatic reset event semaphore semantics. */
52struct RTSEMEVENTMULTIINTERNAL
53{
54 /** pthread condition. */
55 pthread_cond_t Cond;
56 /** pthread mutex which protects the condition and the event state. */
57 pthread_mutex_t Mutex;
58 /** The state of the semaphore.
59 * This is operated while owning mutex and using atomic updating. */
60 volatile uint32_t u32State;
61 /** Number of waiters. */
62 volatile uint32_t cWaiters;
63};
64
65/** The valus of the u32State variable in RTSEMEVENTMULTIINTERNAL.
66 * @{ */
67/** The object isn't initialized. */
68#define EVENTMULTI_STATE_UNINITIALIZED 0
69/** The semaphore is signaled. */
70#define EVENTMULTI_STATE_SIGNALED 0xff00ff00
71/** The semaphore is not signaled. */
72#define EVENTMULTI_STATE_NOT_SIGNALED 0x00ff00ff
73/** @} */
74
75
76
77/**
78 * Validate an event multi semaphore handle passed to one of the interface.
79 *
80 * @returns true if valid.
81 * @returns false if invalid.
82 * @param pThis Pointer to the event semaphore to validate.
83 */
84inline bool rtsemEventMultiValid(struct RTSEMEVENTMULTIINTERNAL *pThis)
85{
86 if ((uintptr_t)pThis < 0x10000)
87 return false;
88
89 uint32_t u32 = pThis->u32State; /* this is volatile, so a explicit read like this is needed. */
90 if ( u32 != EVENTMULTI_STATE_NOT_SIGNALED
91 && u32 != EVENTMULTI_STATE_SIGNALED)
92 return false;
93
94 return true;
95}
96
97
98RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI pEventMultiSem)
99{
100 int rc;
101
102 /*
103 * Allocate semaphore handle.
104 */
105 struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
106 if (pThis)
107 {
108 /*
109 * Create the condition variable.
110 */
111 pthread_condattr_t CondAttr;
112 rc = pthread_condattr_init(&CondAttr);
113 if (!rc)
114 {
115 rc = pthread_cond_init(&pThis->Cond, &CondAttr);
116 if (!rc)
117 {
118 /*
119 * Create the semaphore.
120 */
121 pthread_mutexattr_t MutexAttr;
122 rc = pthread_mutexattr_init(&MutexAttr);
123 if (!rc)
124 {
125 rc = pthread_mutex_init(&pThis->Mutex, &MutexAttr);
126 if (!rc)
127 {
128 pthread_mutexattr_destroy(&MutexAttr);
129 pthread_condattr_destroy(&CondAttr);
130
131 ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_NOT_SIGNALED);
132 ASMAtomicXchgU32(&pThis->cWaiters, 0);
133
134 *pEventMultiSem = pThis;
135 return VINF_SUCCESS;
136 }
137
138 pthread_mutexattr_destroy(&MutexAttr);
139 }
140 pthread_cond_destroy(&pThis->Cond);
141 }
142 pthread_condattr_destroy(&CondAttr);
143 }
144
145 rc = RTErrConvertFromErrno(rc);
146 RTMemFree(pThis);
147 }
148 else
149 rc = VERR_NO_MEMORY;
150
151 return rc;
152
153}
154
155
156RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI EventMultiSem)
157{
158 /*
159 * Validate handle.
160 */
161 if (!rtsemEventMultiValid(EventMultiSem))
162 {
163 AssertMsgFailed(("Invalid handle %p!\n", EventMultiSem));
164 return VERR_INVALID_HANDLE;
165 }
166
167 /*
168 * Abort all waiters forcing them to return failure.
169 */
170 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
171 int rc;
172 for (int i = 30; i > 0; i--)
173 {
174 ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_UNINITIALIZED);
175 rc = pthread_cond_destroy(&pThis->Cond);
176 if (rc != EBUSY)
177 break;
178 pthread_cond_broadcast(&pThis->Cond);
179 usleep(1000);
180 }
181 if (rc)
182 {
183 AssertMsgFailed(("Failed to destroy event sem %p, rc=%d.\n", EventMultiSem, rc));
184 return RTErrConvertFromErrno(rc);
185 }
186
187 /*
188 * Destroy the semaphore
189 * If it's busy we'll wait a bit to give the threads a chance to be scheduled.
190 */
191 for (int i = 30; i > 0; i--)
192 {
193 rc = pthread_mutex_destroy(&pThis->Mutex);
194 if (rc != EBUSY)
195 break;
196 usleep(1000);
197 }
198 if (rc)
199 {
200 AssertMsgFailed(("Failed to destroy event sem %p, rc=%d. (mutex)\n", EventMultiSem, rc));
201 return RTErrConvertFromErrno(rc);
202 }
203
204 /*
205 * Free the semaphore memory and be gone.
206 */
207 RTMemFree(pThis);
208 return VINF_SUCCESS;
209}
210
211
212RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI EventMultiSem)
213{
214 /*
215 * Validate input.
216 */
217 if (!rtsemEventMultiValid(EventMultiSem))
218 {
219 AssertMsgFailed(("Invalid handle %p!\n", EventMultiSem));
220 return VERR_INVALID_HANDLE;
221 }
222
223 /*
224 * Lock the mutex semaphore.
225 */
226 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
227 int rc = pthread_mutex_lock(&pThis->Mutex);
228 if (rc)
229 {
230 AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", EventMultiSem, rc));
231 return RTErrConvertFromErrno(rc);
232 }
233
234 /*
235 * Check the state.
236 */
237 if (pThis->u32State == EVENTMULTI_STATE_NOT_SIGNALED)
238 {
239 ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_SIGNALED);
240 rc = pthread_cond_broadcast(&pThis->Cond);
241 AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d.\n", EventMultiSem, rc));
242 }
243 else if (pThis->u32State == EVENTMULTI_STATE_SIGNALED)
244 {
245 rc = pthread_cond_broadcast(&pThis->Cond); /* give'm another kick... */
246 AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d. (2)\n", EventMultiSem, rc));
247 }
248 else
249 rc = VERR_SEM_DESTROYED;
250
251 /*
252 * Release the mutex and return.
253 */
254 int rc2 = pthread_mutex_unlock(&pThis->Mutex);
255 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", EventMultiSem, rc));
256 if (rc)
257 return RTErrConvertFromErrno(rc);
258 if (rc2)
259 return RTErrConvertFromErrno(rc2);
260
261 return VINF_SUCCESS;
262}
263
264
265RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI EventMultiSem)
266{
267 /*
268 * Validate input.
269 */
270 if (!rtsemEventMultiValid(EventMultiSem))
271 {
272 AssertMsgFailed(("Invalid handle %p!\n", EventMultiSem));
273 return VERR_INVALID_HANDLE;
274 }
275
276 /*
277 * Lock the mutex semaphore.
278 */
279 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
280 int rc = pthread_mutex_lock(&pThis->Mutex);
281 if (rc)
282 {
283 AssertMsgFailed(("Failed to lock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
284 return RTErrConvertFromErrno(rc);
285 }
286
287 /*
288 * Check the state.
289 */
290 if (pThis->u32State == EVENTMULTI_STATE_SIGNALED)
291 ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_NOT_SIGNALED);
292 else if (pThis->u32State != EVENTMULTI_STATE_NOT_SIGNALED)
293 rc = VERR_SEM_DESTROYED;
294
295 /*
296 * Release the mutex and return.
297 */
298 rc = pthread_mutex_unlock(&pThis->Mutex);
299 if (rc)
300 {
301 AssertMsgFailed(("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
302 return RTErrConvertFromErrno(rc);
303 }
304
305 return VINF_SUCCESS;
306
307}
308
309
310static int rtSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies, bool fAutoResume)
311{
312 /*
313 * Validate input.
314 */
315 if (!rtsemEventMultiValid(EventMultiSem))
316 {
317 AssertMsgFailed(("Invalid handle %p!\n", EventMultiSem));
318 return VERR_INVALID_HANDLE;
319 }
320
321 /*
322 * Timed or indefinite wait?
323 */
324 struct RTSEMEVENTMULTIINTERNAL *pThis = EventMultiSem;
325 if (cMillies == RT_INDEFINITE_WAIT)
326 {
327 /* take mutex */
328 int rc = pthread_mutex_lock(&pThis->Mutex);
329 if (rc)
330 {
331 AssertMsgFailed(("Failed to lock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
332 return RTErrConvertFromErrno(rc);
333 }
334 ASMAtomicIncU32(&pThis->cWaiters);
335
336 for (;;)
337 {
338 /* check state. */
339 if (pThis->u32State == EVENTMULTI_STATE_SIGNALED)
340 {
341 ASMAtomicDecU32(&pThis->cWaiters);
342 rc = pthread_mutex_unlock(&pThis->Mutex);
343 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
344 return VINF_SUCCESS;
345 }
346 if (pThis->u32State == EVENTMULTI_STATE_UNINITIALIZED)
347 {
348 rc = pthread_mutex_unlock(&pThis->Mutex);
349 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
350 return VERR_SEM_DESTROYED;
351 }
352
353 /* wait */
354 rc = pthread_cond_wait(&pThis->Cond, &pThis->Mutex);
355 if (rc)
356 {
357 AssertMsgFailed(("Failed to wait on event multi sem %p, rc=%d.\n", EventMultiSem, rc));
358 ASMAtomicDecU32(&pThis->cWaiters);
359 int rc2 = pthread_mutex_unlock(&pThis->Mutex);
360 AssertMsg(!rc2, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc2)); NOREF(rc2);
361 return RTErrConvertFromErrno(rc);
362 }
363 }
364 }
365 else
366 {
367 /*
368 * Get current time and calc end of wait time.
369 */
370 /** @todo Something is braindead here. we're getting occational timeouts after no time has
371 * elapsed on linux 2.6.23. (ata code typically)
372 *
373 * The general problem here is that we're using the realtime clock, i.e. the wall clock
374 * that is subject to ntp updates and user alteration, so we will have to compenstate
375 * for this by using RTTimeMilliTS together with the clock_gettime()/gettimeofday() call.
376 * Joy, oh joy. */
377 struct timespec ts = {0,0};
378#ifdef RT_OS_DARWIN
379 struct timeval tv = {0,0};
380 gettimeofday(&tv, NULL);
381 ts.tv_sec = tv.tv_sec;
382 ts.tv_nsec = tv.tv_usec * 1000;
383#else
384 clock_gettime(CLOCK_REALTIME, &ts);
385#endif
386 if (cMillies != 0)
387 {
388 ts.tv_nsec += (cMillies % 1000) * 1000000;
389 ts.tv_sec += cMillies / 1000;
390 if (ts.tv_nsec >= 1000000000)
391 {
392 ts.tv_nsec -= 1000000000;
393 ts.tv_sec++;
394 }
395 }
396
397 /* take mutex */
398#ifdef RT_OS_DARWIN
399 int rc = pthread_mutex_lock(&pThis->Mutex);
400#else
401 int rc = pthread_mutex_timedlock(&pThis->Mutex, &ts);
402#endif
403 if (rc)
404 {
405 AssertMsg(rc == ETIMEDOUT, ("Failed to lock event multi sem %p, rc=%d.\n", EventMultiSem, rc));
406 return RTErrConvertFromErrno(rc);
407 }
408 ASMAtomicIncU32(&pThis->cWaiters);
409
410 for (;;)
411 {
412 /* check state. */
413 if (pThis->u32State == EVENTMULTI_STATE_SIGNALED)
414 {
415 ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_NOT_SIGNALED);
416 ASMAtomicDecU32(&pThis->cWaiters);
417 rc = pthread_mutex_unlock(&pThis->Mutex);
418 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
419 return VINF_SUCCESS;
420 }
421 if (pThis->u32State == EVENTMULTI_STATE_UNINITIALIZED)
422 {
423 rc = pthread_mutex_unlock(&pThis->Mutex);
424 AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc)); NOREF(rc);
425 return VERR_SEM_DESTROYED;
426 }
427
428 /* wait */
429 rc = pthread_cond_timedwait(&pThis->Cond, &pThis->Mutex, &ts);
430 if (rc && (rc != EINTR || !fAutoResume)) /* according to SuS this function shall not return EINTR, but linux man page says differently. */
431 {
432 AssertMsg(rc == ETIMEDOUT, ("Failed to wait on event multi sem %p, rc=%d.\n", EventMultiSem, rc));
433 ASMAtomicDecU32(&pThis->cWaiters);
434 int rc2 = pthread_mutex_unlock(&pThis->Mutex);
435 AssertMsg(!rc2, ("Failed to unlock event multi sem %p, rc=%d.\n", EventMultiSem, rc2)); NOREF(rc2);
436 return RTErrConvertFromErrno(rc);
437 }
438 }
439 }
440}
441
442
443RTDECL(int) RTSemEventMultiWait(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
444{
445 int rc = rtSemEventMultiWait(EventMultiSem, cMillies, true);
446 Assert(rc != VERR_INTERRUPTED);
447 return rc;
448}
449
450
451RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI EventMultiSem, unsigned cMillies)
452{
453 return rtSemEventMultiWait(EventMultiSem, cMillies, false);
454}
455
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