VirtualBox

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

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