summaryrefslogtreecommitdiff
path: root/src/time
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-03-30 12:06:39 -0400
committerRich Felker <dalias@aerifal.cx>2011-03-30 12:06:39 -0400
commitb8be64c43da207a2f497c1c5b5720e4a2027348a (patch)
tree943491b9a0212fe048fbe3c8d8d71202fe883867 /src/time
parenta1eb8cb5dab06dd23c256d03d82ef6b0efc4b6c6 (diff)
downloadmusl-b8be64c43da207a2f497c1c5b5720e4a2027348a.tar.gz
optimize timer creation and possibly protect against some minor races
the major idea of this patch is not to depend on having the timer pointer delivered to the signal handler, and instead use the thread pointer to get the callback function address and argument. this way, the parent thread can make the timer_create syscall while the child thread is starting, and it should never have to block waiting for the barrier.
Diffstat (limited to 'src/time')
-rw-r--r--src/time/timer_create.c33
1 files changed, 19 insertions, 14 deletions
diff --git a/src/time/timer_create.c b/src/time/timer_create.c
index 2abec278..89099dd6 100644
--- a/src/time/timer_create.c
+++ b/src/time/timer_create.c
@@ -17,9 +17,12 @@ struct start_args {
static void sighandler(int sig, siginfo_t *si, void *ctx)
{
int st;
- timer_t t = si->si_value.sival_ptr;
+ pthread_t self = __pthread_self();
+ void (*notify)(union sigval) = (void (*)(union sigval))self->start;
+ union sigval val = { .sival_ptr = self->start_arg };
+
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &st);
- t->notify(t->val);
+ notify(val);
pthread_setcancelstate(st, 0);
}
@@ -31,17 +34,18 @@ static void killtimer(void *arg)
static void *start(void *arg)
{
+ pthread_t self = __pthread_self();
struct start_args *args = arg;
- struct __timer t = {
- .notify = args->sev->sigev_notify_function,
- .val = args->sev->sigev_value,
- };
+ struct __timer t = { .timerid = -1 };
+ /* Reuse no-longer-needed thread structure fields to avoid
+ * needing the timer address in the signal handler. */
+ self->start = (void *(*)(void *))args->sev->sigev_notify_function;
+ self->start_arg = args->sev->sigev_value.sival_ptr;
args->t = &t;
- pthread_barrier_wait(&args->b);
-
pthread_cleanup_push(killtimer, &t);
+ pthread_barrier_wait(&args->b);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
/* Loop on async-signal-safe cancellation point */
for (;;) sleep(1);
@@ -95,18 +99,19 @@ int timer_create(clockid_t clk, struct sigevent *evp, timer_t *res)
errno = r;
return -1;
}
- pthread_barrier_wait(&args.b);
- t = args.t;
- t->thread = td;
- ksev.sigev_value.sival_ptr = t;
+ ksev.sigev_value.sival_ptr = 0;
ksev.sigev_signo = SIGCANCEL;
ksev.sigev_notify = 4; /* SIGEV_THREAD_ID */
ksev.sigev_tid = td->tid;
- if (syscall(SYS_timer_create, clk, &ksev, &t->timerid) < 0) {
- t->timerid = -1;
+ r = syscall(SYS_timer_create, clk, &ksev, &timerid);
+ pthread_barrier_wait(&args.b);
+ t = args.t;
+ if (r < 0) {
pthread_cancel(td);
return -1;
}
+ t->timerid = timerid;
+ t->thread = td;
break;
default:
errno = EINVAL;