#include "pthread_impl.h" #include void __pthread_testcancel(void); int __clock_gettime(clockid_t clk, struct timespec *ts); int __thrd_wait(volatile int *addr, int val, const struct timespec *at); static int __cnd_timedwait(__cnd_t *restrict c, __mtx_t *restrict mut, const struct timespec *restrict ts, int seq) { int e = EINTR, r; __mtx_unlock(mut); /* In a first phase, we ignore all wakeups if there hasn't been any signal or broadcast. No legitimate wakeup will be lost by that, since we can only be target of such a wakeup if we have been rescheduled to the tok queue or the one of the mutex. But this is only the case when a signal or broadcast has occured. */ while (c->seq == seq) { e = __thrd_wait(&c->seq, seq, ts); if (e && e != EINTR && e != EWOULDBLOCK) goto RELOCK; } if (e == ETIMEDOUT) goto RELOCK; /* From here on a signal or broadcast has happened after our sequence point. */ do { int token = c->tok; /* a wakeup has happened since our entry to cnd_timedwait, get a token if some is available */ while (token > 0) { int prev = a_cas(&c->tok, token, token - 1); if (prev == token) goto RELOCK; token = prev; } e = __thrd_wait(&c->tok, token, ts); } while (!e || e == EINTR || e == EWOULDBLOCK); if (e != ETIMEDOUT) return thrd_error; RELOCK: if ((r=__mtx_lock(mut))) return r; switch (e) { case 0: return thrd_success; case ETIMEDOUT: return thrd_timedout; default: return thrd_error; } } int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mut, const struct timespec *restrict ts) { int ret = thrd_error; int seq = 0; __mtx_t * m = __mtx_getref(mut); if (m) { __cnd_t * c = __cnd_getref(cond, m, &seq); if (c) { ret = __cnd_timedwait(c, m, ts, seq); __cnd_unref(c); } __mtx_unref(m); } return ret; }