VirtualBox

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

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

rebranding: IPRT files again.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.2 KB
Line 
1/* $Id: semevent-posix.cpp 8245 2008-04-21 17:24:28Z 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 (!rtsemEventValid(EventSem))
170 {
171 AssertMsgFailed(("Invalid handle %p!\n", EventSem));
172 return VERR_INVALID_HANDLE;
173 }
174
175 /*
176 * Abort all waiters forcing them to return failure.
177 *
178 */
179 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
180 int rc;
181 for (int i = 30; i > 0; i--)
182 {
183 ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_UNINITIALIZED);
184 rc = pthread_cond_destroy(&pIntEventSem->Cond);
185 if (rc != EBUSY)
186 break;
187 pthread_cond_broadcast(&pIntEventSem->Cond);
188 usleep(1000);
189 } while (rc == EBUSY);
190 if (rc)
191 {
192 AssertMsgFailed(("Failed to destroy event sem %p, rc=%d.\n", EventSem, rc));
193 return RTErrConvertFromErrno(rc);
194 }
195
196 /*
197 * Destroy the semaphore
198 * If it's busy we'll wait a bit to give the threads a chance to be scheduled.
199 */
200 for (int i = 30; i > 0; i--)
201 {
202 rc = pthread_mutex_destroy(&pIntEventSem->Mutex);
203 if (rc != EBUSY)
204 break;
205 usleep(1000);
206 }
207 if (rc)
208 {
209 AssertMsgFailed(("Failed to destroy event sem %p, rc=%d. (mutex)\n", EventSem, rc));
210 return RTErrConvertFromErrno(rc);
211 }
212
213 /*
214 * Free the semaphore memory and be gone.
215 */
216 RTMemFree(pIntEventSem);
217 return VINF_SUCCESS;
218}
219
220
221RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
222{
223 /*
224 * Validate input.
225 */
226 if (!rtsemEventValid(EventSem))
227 {
228 AssertMsgFailed(("Invalid handle %p!\n", EventSem));
229 return VERR_INVALID_HANDLE;
230 }
231
232 /*
233 * Lock the mutex semaphore.
234 */
235 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
236 int rc = pthread_mutex_lock(&pIntEventSem->Mutex);
237 if (rc)
238 {
239 AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
240 return RTErrConvertFromErrno(rc);
241 }
242
243 /*
244 * Check the state.
245 */
246 if (pIntEventSem->u32State == EVENT_STATE_NOT_SIGNALED)
247 {
248 ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_SIGNALED);
249 rc = pthread_cond_signal(&pIntEventSem->Cond);
250 AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d.\n", EventSem, rc));
251 }
252 else if (pIntEventSem->u32State == EVENT_STATE_SIGNALED)
253 {
254 rc = pthread_cond_signal(&pIntEventSem->Cond); /* give'm another kick... */
255 AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d. (2)\n", EventSem, rc));
256 }
257 else
258 rc = VERR_SEM_DESTROYED;
259
260 /*
261 * Release the mutex and return.
262 */
263 int rc2 = pthread_mutex_unlock(&pIntEventSem->Mutex);
264 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc));
265 if (rc)
266 return RTErrConvertFromErrno(rc);
267 if (rc2)
268 return RTErrConvertFromErrno(rc2);
269
270 return VINF_SUCCESS;
271}
272
273
274static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
275{
276 /*
277 * Validate input.
278 */
279 if (!rtsemEventValid(EventSem))
280 {
281 AssertMsgFailed(("Invalid handle %p!\n", EventSem));
282 return VERR_INVALID_HANDLE;
283 }
284
285 /*
286 * Timed or indefinite wait?
287 */
288 struct RTSEMEVENTINTERNAL *pIntEventSem = EventSem;
289 if (cMillies == RT_INDEFINITE_WAIT)
290 {
291 /* for fairness, yield before going to sleep. */
292 if ( ASMAtomicIncU32(&pIntEventSem->cWaiters) > 1
293 && pIntEventSem->u32State == EVENT_STATE_SIGNALED)
294 pthread_yield();
295
296 /* take mutex */
297 int rc = pthread_mutex_lock(&pIntEventSem->Mutex);
298 if (rc)
299 {
300 ASMAtomicDecU32(&pIntEventSem->cWaiters);
301 AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
302 return RTErrConvertFromErrno(rc);
303 }
304
305 for (;;)
306 {
307 /* check state. */
308 if (pIntEventSem->u32State == EVENT_STATE_SIGNALED)
309 {
310 ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_NOT_SIGNALED);
311 ASMAtomicDecU32(&pIntEventSem->cWaiters);
312 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
313 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
314 return VINF_SUCCESS;
315 }
316 if (pIntEventSem->u32State == EVENT_STATE_UNINITIALIZED)
317 {
318 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
319 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
320 return VERR_SEM_DESTROYED;
321 }
322
323 /* wait */
324 rc = pthread_cond_wait(&pIntEventSem->Cond, &pIntEventSem->Mutex);
325 if (rc)
326 {
327 AssertMsgFailed(("Failed to wait on event sem %p, rc=%d.\n", EventSem, rc));
328 ASMAtomicDecU32(&pIntEventSem->cWaiters);
329 int rc2 = pthread_mutex_unlock(&pIntEventSem->Mutex);
330 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc2)); NOREF(rc2);
331 return RTErrConvertFromErrno(rc);
332 }
333 }
334 }
335 else
336 {
337 /*
338 * Get current time and calc end of wait time.
339 */
340 struct timespec ts = {0,0};
341#ifdef RT_OS_DARWIN
342 struct timeval tv = {0,0};
343 gettimeofday(&tv, NULL);
344 ts.tv_sec = tv.tv_sec;
345 ts.tv_nsec = tv.tv_usec * 1000;
346#else
347 clock_gettime(CLOCK_REALTIME, &ts);
348#endif
349 if (cMillies != 0)
350 {
351 ts.tv_nsec += (cMillies % 1000) * 1000000;
352 ts.tv_sec += cMillies / 1000;
353 if (ts.tv_nsec >= 1000000000)
354 {
355 ts.tv_nsec -= 1000000000;
356 ts.tv_sec++;
357 }
358 }
359
360 /* for fairness, yield before going to sleep. */
361 if (ASMAtomicIncU32(&pIntEventSem->cWaiters) > 1)
362 pthread_yield();
363
364 /* take mutex */
365#ifdef RT_OS_DARWIN
366 int rc = pthread_mutex_lock(&pIntEventSem->Mutex);
367#else
368 int rc = pthread_mutex_timedlock(&pIntEventSem->Mutex, &ts);
369#endif
370 if (rc)
371 {
372 ASMAtomicDecU32(&pIntEventSem->cWaiters);
373 AssertMsg(rc == ETIMEDOUT, ("Failed to lock event sem %p, rc=%d.\n", EventSem, rc));
374 return RTErrConvertFromErrno(rc);
375 }
376
377 for (;;)
378 {
379 /* check state. */
380 if (pIntEventSem->u32State == EVENT_STATE_SIGNALED)
381 {
382 ASMAtomicXchgU32(&pIntEventSem->u32State, EVENT_STATE_NOT_SIGNALED);
383 ASMAtomicDecU32(&pIntEventSem->cWaiters);
384 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
385 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
386 return VINF_SUCCESS;
387 }
388 if (pIntEventSem->u32State == EVENT_STATE_UNINITIALIZED)
389 {
390 rc = pthread_mutex_unlock(&pIntEventSem->Mutex);
391 AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", EventSem, rc)); NOREF(rc);
392 return VERR_SEM_DESTROYED;
393 }
394
395 /* wait */
396 rc = pthread_cond_timedwait(&pIntEventSem->Cond, &pIntEventSem->Mutex, &ts);
397 if (rc && (rc != EINTR || !fAutoResume)) /* according to SuS this function shall not return EINTR, but linux man page says differently. */
398 {
399 AssertMsg(rc == ETIMEDOUT, ("Failed to wait on event sem %p, rc=%d.\n", EventSem, rc));
400 ASMAtomicDecU32(&pIntEventSem->cWaiters);
401 int rc2 = pthread_mutex_unlock(&pIntEventSem->Mutex);
402 AssertMsg(!rc2, ("Failed to unlock event sem %p, rc2=%d.\n", EventSem, rc2)); NOREF(rc2);
403 return RTErrConvertFromErrno(rc);
404 }
405 } /* for (;;) */
406 }
407}
408
409
410RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
411{
412 int rc = rtSemEventWait(EventSem, cMillies, true);
413 Assert(rc != VERR_INTERRUPTED);
414 return rc;
415}
416
417
418RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
419{
420 return rtSemEventWait(EventSem, cMillies, false);
421}
422
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