VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/semevent-linux.cpp@ 14429

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

when building against glibc 2.6, use the Posix implementation of the event mutexes as the glibc futex bug is fixed there

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.1 KB
Line 
1/* $Id: semevent-linux.cpp 14429 2008-11-20 17:23:43Z vboxsync $ */
2/** @file
3 * IPRT - Event Semaphore, Linux (2.6.x+).
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.215389.xyz. 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#include <features.h>
32#if __GLIBC_PREREQ(2,6)
33
34/* glibc 2.6 fixed a serious bug in the mutex implementation
35 * The external refernce to epoll_pwait is a hack which prevents
36 * that we link against glibc < 2.6 */
37
38asm volatile (".global epoll_pwait");
39
40#include "r3/posix/semevent-posix.cpp"
41
42#else /* glibc < 2.6 */
43
44/*******************************************************************************
45* Header Files *
46*******************************************************************************/
47#include <iprt/semaphore.h>
48#include <iprt/assert.h>
49#include <iprt/alloc.h>
50#include <iprt/asm.h>
51#include <iprt/err.h>
52#include "internal/magics.h"
53
54#include <errno.h>
55#include <limits.h>
56#include <pthread.h>
57#include <unistd.h>
58#include <sys/time.h>
59#include <sys/syscall.h>
60#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
61# include <linux/futex.h>
62#else
63# define FUTEX_WAIT 0
64# define FUTEX_WAKE 1
65#endif
66
67
68/*******************************************************************************
69* Structures and Typedefs *
70*******************************************************************************/
71/**
72 * Linux (single wakup) event semaphore.
73 */
74struct RTSEMEVENTINTERNAL
75{
76 /** Magic value. */
77 intptr_t volatile iMagic;
78 /** The futex state variable.
79 * <0 means signaled.
80 * 0 means not signaled, no waiters.
81 * >0 means not signaled, and the value gives the number of waiters.
82 */
83 int32_t volatile cWaiters;
84};
85
86
87/**
88 * Wrapper for the futex syscall.
89 */
90static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
91{
92 errno = 0;
93 long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
94 if (rc < 0)
95 {
96 Assert(rc == -1);
97 rc = -errno;
98 }
99 return rc;
100}
101
102
103
104RTDECL(int) RTSemEventCreate(PRTSEMEVENT pEventSem)
105{
106 /*
107 * Allocate semaphore handle.
108 */
109 struct RTSEMEVENTINTERNAL *pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
110 if (pThis)
111 {
112 pThis->iMagic = RTSEMEVENT_MAGIC;
113 pThis->cWaiters = 0;
114 *pEventSem = pThis;
115 return VINF_SUCCESS;
116 }
117 return VERR_NO_MEMORY;
118}
119
120
121RTDECL(int) RTSemEventDestroy(RTSEMEVENT EventSem)
122{
123 /*
124 * Validate input.
125 */
126 if (EventSem == NIL_RTSEMEVENT) /* don't bitch */
127 return VERR_INVALID_HANDLE;
128 struct RTSEMEVENTINTERNAL *pThis = EventSem;
129 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
130 AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
131
132 /*
133 * Invalidate the semaphore and wake up anyone waiting on it.
134 */
135 ASMAtomicXchgSize(&pThis->iMagic, RTSEMEVENT_MAGIC | UINT32_C(0x80000000));
136 if (ASMAtomicXchgS32(&pThis->cWaiters, INT32_MIN / 2) > 0)
137 {
138 sys_futex(&pThis->cWaiters, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
139 usleep(1000);
140 }
141
142 /*
143 * Free the semaphore memory and be gone.
144 */
145 RTMemFree(pThis);
146 return VINF_SUCCESS;
147}
148
149
150RTDECL(int) RTSemEventSignal(RTSEMEVENT EventSem)
151{
152 /*
153 * Validate input.
154 */
155 struct RTSEMEVENTINTERNAL *pThis = EventSem;
156 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENT_MAGIC,
157 VERR_INVALID_HANDLE);
158 /*
159 * Try signal it.
160 */
161 for (unsigned i = 0;; i++)
162 {
163 int32_t iCur;
164 if (ASMAtomicCmpXchgExS32(&pThis->cWaiters, -1, 0, &iCur))
165 break; /* nobody is waiting */
166 else if (iCur < 0)
167 break; /* already signaled */
168 else
169 {
170 /* somebody is waiting, try wake up one of them. */
171 long cWoken = sys_futex(&pThis->cWaiters, FUTEX_WAKE, 1, NULL, NULL, 0);
172 if (RT_LIKELY(cWoken == 1))
173 {
174 ASMAtomicDecS32(&pThis->cWaiters);
175 break;
176 }
177 AssertMsg(cWoken == 0, ("%ld\n", cWoken));
178
179 /*
180 * This path is taken in two situations:
181 * 1) A waiting thread is returning from the sys_futex call with a
182 * non-zero return value.
183 * 2) There are two threads signaling the event at the
184 * same time and only one thread waiting.
185 *
186 * At this point we know that nobody is activly waiting on the event but
187 * at the same time, we are racing someone updating the state. The current
188 * strategy is to spin till the thread racing us is done, this is kind of
189 * brain dead and need fixing of course.
190 */
191 if (RT_UNLIKELY(i > 32))
192 {
193 if ((i % 128) == 127)
194 usleep(1000);
195 else if (!(i % 4))
196 pthread_yield();
197 else
198 AssertReleaseMsg(i < 4096, ("iCur=%#x pThis=%p\n", iCur, pThis));
199 }
200 }
201
202 /* Check the magic to fend off races with RTSemEventDestroy. */
203 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
204 return VERR_SEM_DESTROYED;
205 }
206 return VINF_SUCCESS;
207}
208
209
210static int rtSemEventWait(RTSEMEVENT EventSem, unsigned cMillies, bool fAutoResume)
211{
212 /*
213 * Validate input.
214 */
215 struct RTSEMEVENTINTERNAL *pThis = EventSem;
216 AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENT_MAGIC,
217 VERR_INVALID_HANDLE);
218
219 /*
220 * Quickly check whether it's signaled.
221 */
222 if (ASMAtomicCmpXchgS32(&pThis->cWaiters, 0, -1))
223 return VINF_SUCCESS;
224
225 /*
226 * Convert timeout value.
227 */
228 struct timespec ts;
229 struct timespec *pTimeout = NULL;
230 if (cMillies != RT_INDEFINITE_WAIT)
231 {
232 ts.tv_sec = cMillies / 1000;
233 ts.tv_nsec = (cMillies % 1000) * 1000000;
234 pTimeout = &ts;
235 }
236
237 /*
238 * The wait loop.
239 */
240 for (unsigned i = 0;; i++)
241 {
242 /*
243 * Announce that we're among the waiters.
244 */
245 int32_t iNew = ASMAtomicIncS32(&pThis->cWaiters);
246 if (iNew == 0)
247 return VINF_SUCCESS;
248 if (RT_LIKELY(iNew > 0))
249 {
250 /*
251 * Go to sleep.
252 */
253 long rc = sys_futex(&pThis->cWaiters, FUTEX_WAIT, iNew, pTimeout, NULL, 0);
254 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
255 return VERR_SEM_DESTROYED;
256
257 /* Did somebody wake us up from RTSemEventSignal()? */
258 if (rc == 0)
259 return VINF_SUCCESS;
260
261 /* No, then the kernel woke us up or we failed going to sleep. Adjust the accounting. */
262 iNew = ASMAtomicDecS32(&pThis->cWaiters);
263 Assert(iNew >= 0);
264
265 /*
266 * Act on the wakup code.
267 */
268 if (rc == -ETIMEDOUT)
269 {
270 Assert(pTimeout);
271 return VERR_TIMEOUT;
272 }
273 if (rc == -EWOULDBLOCK)
274 /* retry with new value. */;
275 else if (rc == -EINTR)
276 {
277 if (!fAutoResume)
278 return VERR_INTERRUPTED;
279 }
280 else
281 {
282 /* this shouldn't happen! */
283 AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
284 return RTErrConvertFromErrno(rc);
285 }
286 }
287 else
288 {
289 /* this can't happen. */
290 if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
291 return VERR_SEM_DESTROYED;
292 AssertReleaseMsgFailed(("iNew=%d\n", iNew));
293 }
294 }
295}
296
297
298RTDECL(int) RTSemEventWait(RTSEMEVENT EventSem, unsigned cMillies)
299{
300 int rc = rtSemEventWait(EventSem, cMillies, true);
301 Assert(rc != VERR_INTERRUPTED);
302 Assert(rc != VERR_TIMEOUT || cMillies != RT_INDEFINITE_WAIT);
303 return rc;
304}
305
306
307RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT EventSem, unsigned cMillies)
308{
309 return rtSemEventWait(EventSem, cMillies, false);
310}
311
312#endif /* glibc < 2.6 */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette