1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| | #include "pthread_impl.h"
#include <threads.h>
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;
}
|