revert to deleting kernel-level timer from cancellation handler
[musl] / src / time / timer_create.c
index 2abec27..5d362c7 100644 (file)
@@ -11,41 +11,42 @@ struct ksigevent {
 struct start_args {
        pthread_barrier_t b;
        struct sigevent *sev;
-       timer_t t;
 };
 
-static void sighandler(int sig, siginfo_t *si, void *ctx)
+void __sigtimer_handler(pthread_t self)
 {
        int st;
-       timer_t t = si->si_value.sival_ptr;
+       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);
 }
 
-static void killtimer(void *arg)
+static void cleanup(void *p)
 {
-       timer_t t = arg;
-       if (t->timerid >= 0) __syscall(SYS_timer_delete, t->timerid);
+       pthread_t self = p;
+       __syscall(SYS_timer_delete, self->result);
 }
 
 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,
-       };
 
-       args->t = &t;
+       /* 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;
+       self->result = (void *)-1;
 
+       pthread_cleanup_push(cleanup, self);
        pthread_barrier_wait(&args->b);
-
-       pthread_cleanup_push(killtimer, &t);
        pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
        /* Loop on async-signal-safe cancellation point */
        for (;;) sleep(1);
-       pthread_cleanup_pop(1);
+       pthread_cleanup_pop(0);
        return 0;
 }
 
@@ -75,14 +76,9 @@ int timer_create(clockid_t clk, struct sigevent *evp, timer_t *res)
                ksev.sigev_tid = 0;
                if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0)
                        return -1;
-               if (!(t = calloc(1, sizeof *t))) {
-                       syscall(SYS_timer_delete, timerid);
-                       return -1;
-               }
-               t->timerid = timerid;
+               *res = (void *)(2*timerid+1);
                break;
        case SIGEV_THREAD:
-               if (!libc.sigtimer) libc.sigtimer = sighandler;
                if (sev.sigev_notify_attributes)
                        attr = *sev.sigev_notify_attributes;
                else
@@ -95,24 +91,23 @@ 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);
+               if (r < 0) {
                        pthread_cancel(td);
                        return -1;
                }
+               td->result = (void *)timerid;
+               *res = td;
                break;
        default:
                errno = EINVAL;
                return -1;
        }
 
-       *res = t;
        return 0;
 }