VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/semevent-posix.cpp@ 10854

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

RTSemEventDestroy: Don't bitch on NIL_RTSEMEVENT.

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