31
31
#include <features.h>
32
#if __GLIBC_PREREQ(2,6)
32
#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS)
35
35
* glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
36
* linux specific event semaphores code in order to work around the bug. As it
37
* turns out, this code seems to have an unresolved issue (#2599), so we'll
38
* fall back on the pthread based implementation if glibc is known to contain
36
* linux specific event semaphores code in order to work around the bug. We
37
* will fall back on the pthread-based implementation if glibc is known to
38
* contain the bug fix.
41
40
* The external refernce to epoll_pwait is a hack which prevents that we link
42
41
* against glibc < 2.6.
81
81
/** Magic value. */
82
82
intptr_t volatile iMagic;
83
83
/** The futex state variable.
85
* 0 means not signaled, no waiters.
86
* >0 means not signaled, and the value gives the number of waiters.
84
* 0 means not signalled.
86
uint32_t volatile fSignalled;
87
/** The number of waiting threads */
88
88
int32_t volatile cWaiters;
93
93
* Wrapper for the futex syscall.
95
static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
95
static long sys_futex(uint32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
98
98
long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
140
141
ASMAtomicXchgSize(&pThis->iMagic, RTSEMEVENT_MAGIC | UINT32_C(0x80000000));
141
142
if (ASMAtomicXchgS32(&pThis->cWaiters, INT32_MIN / 2) > 0)
143
sys_futex(&pThis->cWaiters, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
144
sys_futex(&pThis->fSignalled, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
160
161
struct RTSEMEVENTINTERNAL *pThis = EventSem;
161
162
AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENT_MAGIC,
162
163
VERR_INVALID_HANDLE);
166
for (unsigned i = 0;; i++)
169
if (ASMAtomicCmpXchgExS32(&pThis->cWaiters, -1, 0, &iCur))
170
break; /* nobody is waiting */
172
break; /* already signaled */
175
/* somebody is waiting, try wake up one of them. */
176
long cWoken = sys_futex(&pThis->cWaiters, FUTEX_WAKE, 1, NULL, NULL, 0);
177
if (RT_LIKELY(cWoken == 1))
179
ASMAtomicDecS32(&pThis->cWaiters);
182
AssertMsg(cWoken == 0, ("%ld\n", cWoken));
185
* This path is taken in two situations:
186
* 1) A waiting thread is returning from the sys_futex call with a
187
* non-zero return value.
188
* 2) There are two threads signaling the event at the
189
* same time and only one thread waiting.
191
* At this point we know that nobody is activly waiting on the event but
192
* at the same time, we are racing someone updating the state. The current
193
* strategy is to spin till the thread racing us is done, this is kind of
194
* brain dead and need fixing of course.
196
if (RT_UNLIKELY(i > 32))
198
if ((i % 128) == 127)
203
AssertReleaseMsg(i < 4096, ("iCur=%#x pThis=%p\n", iCur, pThis));
207
/* Check the magic to fend off races with RTSemEventDestroy. */
208
if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
209
return VERR_SEM_DESTROYED;
165
ASMAtomicWriteU32(&pThis->fSignalled, 1);
166
if (ASMAtomicReadS32(&pThis->cWaiters) < 1)
169
/* somebody is waiting, try wake up one of them. */
170
long cWoken = sys_futex(&pThis->fSignalled, FUTEX_WAKE, 1, NULL, NULL, 0);
171
if (RT_LIKELY(cWoken >= 0))
174
if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
175
return VERR_SEM_DESTROYED;
177
return VERR_INVALID_PARAMETER;
225
191
* Quickly check whether it's signaled.
227
if (ASMAtomicCmpXchgS32(&pThis->cWaiters, 0, -1))
193
if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
228
194
return VINF_SUCCESS;
231
* Convert timeout value.
197
* Convert the timeout value.
233
199
struct timespec ts;
234
200
struct timespec *pTimeout = NULL;
201
uint64_t u64End = 0; /* shut up gcc */
235
202
if (cMillies != RT_INDEFINITE_WAIT)
237
206
ts.tv_sec = cMillies / 1000;
238
207
ts.tv_nsec = (cMillies % 1000) * 1000000;
208
u64End = RTTimeSystemNanoTS() + cMillies * 1000000;
212
ASMAtomicIncS32(&pThis->cWaiters);
245
for (unsigned i = 0;; i++)
217
int rc = VINF_SUCCESS;
248
* Announce that we're among the waiters.
250
int32_t iNew = ASMAtomicIncS32(&pThis->cWaiters);
253
if (RT_LIKELY(iNew > 0))
258
long rc = sys_futex(&pThis->cWaiters, FUTEX_WAIT, iNew, pTimeout, NULL, 0);
259
if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
260
return VERR_SEM_DESTROYED;
262
/* Did somebody wake us up from RTSemEventSignal()? */
266
/* No, then the kernel woke us up or we failed going to sleep. Adjust the accounting. */
267
iNew = ASMAtomicDecS32(&pThis->cWaiters);
271
* Act on the wakup code.
273
if (rc == -ETIMEDOUT)
278
if (rc == -EWOULDBLOCK)
279
/* retry with new value. */;
280
else if (rc == -EINTR)
283
return VERR_INTERRUPTED;
287
/* this shouldn't happen! */
288
AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
289
return RTErrConvertFromErrno(rc);
220
long lrc = sys_futex(&pThis->fSignalled, FUTEX_WAIT, 0, pTimeout, NULL, 0);
221
if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
223
rc = VERR_SEM_DESTROYED;
227
if (RT_LIKELY(lrc == 0 || lrc == -EWOULDBLOCK))
229
/* successful wakeup or fSignalled > 0 in the meantime */
230
if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
233
else if (lrc == -ETIMEDOUT)
238
else if (lrc == -EINTR)
242
rc = VERR_INTERRUPTED;
294
/* this can't happen. */
295
if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
296
return VERR_SEM_DESTROYED;
297
AssertReleaseMsgFailed(("iNew=%d\n", iNew));
248
/* this shouldn't happen! */
249
AssertMsgFailed(("rc=%ld errno=%d\n", lrc, errno));
250
rc = RTErrConvertFromErrno(lrc);
253
/* adjust the relative timeout */
256
int64_t i64Diff = u64End - RTTimeSystemNanoTS();
262
ts.tv_sec = i64Diff / 1000000000;
263
ts.tv_nsec = i64Diff % 1000000000;
267
ASMAtomicDecS32(&pThis->cWaiters);