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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
#include <time.h>
#include <setjmp.h>
#include <limits.h>
#include <semaphore.h>
#include "pthread_impl.h"
#include "atomic.h"
struct ksigevent {
union sigval sigev_value;
int sigev_signo;
int sigev_notify;
int sigev_tid;
};
struct start_args {
sem_t sem1, sem2;
struct sigevent *sev;
};
static void dummy_0()
{
}
weak_alias(dummy_0, __pthread_tsd_run_dtors);
static void timer_handler(int sig, siginfo_t *si, void *ctx)
{
}
static void cleanup_fromsig(void *p)
{
pthread_t self = __pthread_self();
__pthread_tsd_run_dtors();
__block_app_sigs(0);
__syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGTIMER_SET, 0, _NSIG/8);
self->cancel = 0;
self->cancelbuf = 0;
self->canceldisable = 0;
self->cancelasync = 0;
__reset_tls();
longjmp(p, 1);
}
static void *start(void *arg)
{
pthread_t self = __pthread_self();
struct start_args *args = arg;
jmp_buf jb;
void (*notify)(union sigval) = args->sev->sigev_notify_function;
union sigval val = args->sev->sigev_value;
int cancel = 0;
/* Waiting on sem1 ensures we see self->timer_id. */
while (sem_wait(&args->sem1));
/* If self->timer_id is -1 while the parent thread is still in
* timer_create (before the sem_post to sem2) then the timer was
* never created. If it is -1 later after the sem_post then
* it was cancelled via timer_delete. */
if (self->timer_id < 0)
cancel = 1;
/* Incrementing sem2 informs the parent that we are done checking
* the initial value of self->timer_id, and accessing the arguments
* so that the parent can proceed past their block lifetime. */
sem_post(&args->sem2);
if (cancel)
return 0;
for (;;) {
siginfo_t si;
while (sigwaitinfo(SIGTIMER_SET, &si) < 0);
if (si.si_code == SI_TIMER && !setjmp(jb)) {
pthread_cleanup_push(cleanup_fromsig, jb);
notify(val);
pthread_cleanup_pop(1);
}
if (self->timer_id < 0) break;
}
__syscall(SYS_timer_delete, self->timer_id & INT_MAX);
return 0;
}
int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict res)
{
static volatile int init = 0;
pthread_t td;
pthread_attr_t attr;
int r;
struct start_args args;
struct ksigevent ksev, *ksevp=0;
int timerid;
sigset_t set;
int cs;
switch (evp ? evp->sigev_notify : SIGEV_SIGNAL) {
case SIGEV_NONE:
case SIGEV_SIGNAL:
case SIGEV_THREAD_ID:
if (evp) {
ksev.sigev_value = evp->sigev_value;
ksev.sigev_signo = evp->sigev_signo;
ksev.sigev_notify = evp->sigev_notify;
if (evp->sigev_notify == SIGEV_THREAD_ID)
ksev.sigev_tid = evp->sigev_notify_thread_id;
else
ksev.sigev_tid = 0;
ksevp = &ksev;
}
if (syscall(SYS_timer_create, clk, ksevp, &timerid) < 0)
return -1;
*res = (void *)(intptr_t)timerid;
break;
case SIGEV_THREAD:
if (!init) {
struct sigaction sa = {
.sa_sigaction = timer_handler,
.sa_flags = SA_SIGINFO | SA_RESTART
};
__libc_sigaction(SIGTIMER, &sa, 0);
a_store(&init, 1);
}
if (evp->sigev_notify_attributes)
attr = *evp->sigev_notify_attributes;
else
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
sem_init(&args.sem1, 0, 0);
sem_init(&args.sem2, 0, 0);
args.sev = evp;
__block_app_sigs(&set);
__syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGTIMER_SET, 0, _NSIG/8);
r = pthread_create(&td, &attr, start, &args);
__restore_sigs(&set);
if (r) {
errno = r;
return -1;
}
ksev.sigev_value.sival_ptr = 0;
ksev.sigev_signo = SIGTIMER;
ksev.sigev_notify = SIGEV_THREAD_ID;
ksev.sigev_tid = td->tid;
r = __syscall(SYS_timer_create, clk, &ksev, &timerid);
if (r < 0) {
timerid = -1;
}
td->timer_id = timerid;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
sem_post(&args.sem1);
while (sem_wait(&args.sem2));
pthread_setcancelstate(cs, 0);
if (timerid < 0) {
errno = -r;
return -1;
}
*res = (void *)(INTPTR_MIN | (uintptr_t)td>>1);
break;
default:
errno = EINVAL;
return -1;
}
return 0;
}
|