VirtualBox

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

Last change on this file since 7689 was 6738, checked in by vboxsync, 17 years ago

split up the linux and posix semaphore implementations (ring-3) to avoid code duplication and make it easier to select one or the other for each of the semaphore types.

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