implement POSIX timers
authorRich Felker <dalias@aerifal.cx>
Tue, 29 Mar 2011 17:01:25 +0000 (13:01 -0400)
committerRich Felker <dalias@aerifal.cx>
Tue, 29 Mar 2011 17:01:25 +0000 (13:01 -0400)
this implementation is superior to the glibc/nptl implementation, in
that it gives true realtime behavior. there is no risk of timer
expiration events being lost due to failed thread creation or failed
malloc, because the thread is created as time creation time, and
reused until the timer is deleted.

include/limits.h
include/signal.h
src/time/timer_create.c [new file with mode: 0644]
src/time/timer_delete.c [new file with mode: 0644]
src/time/timer_getoverrun.c [new file with mode: 0644]
src/time/timer_gettime.c [new file with mode: 0644]
src/time/timer_settime.c [new file with mode: 0644]

index 55ad82315b39193f845a7cd512c5a689fc97471a..3953aea53533895e4ac4f51e8af40d68b1bb4541 100644 (file)
@@ -31,6 +31,7 @@
 #define PTHREAD_DESTRUCTOR_ITERATIONS 4
 #define SEM_VALUE_MAX 0x7fffffff
 #define SEM_NSEMS_MAX 256
+#define DELAYTIMER_MAX 0x7fffffff
 
 /* Arbitrary numbers... */
 
index f5e87c783c059c32d9538ee5ea348bbdfe87dd86..6116fb430178a5d3affef1a980f58230689308f2 100644 (file)
@@ -17,6 +17,7 @@ extern "C" {
 #define __NEED_uid_t
 #define __NEED_struct_timespec
 #define __NEED_pthread_t
+#define __NEED_pthread_attr_t
 #define __NEED_time_t
 #define __NEED_clock_t
 #define __NEED_sigset_t
@@ -24,8 +25,7 @@ extern "C" {
 
 #include <bits/alltypes.h>
 
-struct sigaction
-{
+struct sigaction {
        union {
                void (*sa_handler)(int);
                void (*sa_sigaction)(int, siginfo_t *, void *);
@@ -37,19 +37,29 @@ struct sigaction
 #define sa_handler   __sa_handler.sa_handler
 #define sa_sigaction __sa_handler.sa_sigaction
 
-typedef struct
-{
+typedef struct {
        void *ss_sp;
        int ss_flags;
        size_t ss_size;
 } stack_t;
 
-union sigval
-{
+union sigval {
        int sival_int;
        void *sival_ptr;
 };
 
+struct sigevent {
+       union sigval sigev_value;
+       int sigev_signo;
+       int sigev_notify;
+       void (*sigev_notify_function)(union sigval);
+       pthread_attr_t *sigev_notify_attributes;
+};
+
+#define SIGEV_SIGNAL 0
+#define SIGEV_NONE 1
+#define SIGEV_THREAD 2
+
 int __libc_current_sigrtmin(void);
 int __libc_current_sigrtmax(void);
 
diff --git a/src/time/timer_create.c b/src/time/timer_create.c
new file mode 100644 (file)
index 0000000..1ac1906
--- /dev/null
@@ -0,0 +1,110 @@
+#include <time.h>
+#include "pthread_impl.h"
+
+struct ksigevent {
+       union sigval sigev_value;
+       int sigev_signo;
+       int sigev_notify;
+       int sigev_tid;
+};
+
+struct start_args {
+       pthread_barrier_t b;
+       struct sigevent *sev;
+       timer_t t;
+};
+
+static void sighandler(int sig, siginfo_t *si, void *ctx)
+{
+       int st;
+       timer_t t = si->si_value.sival_ptr;
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &st);
+       t->notify(t->val);
+       pthread_setcancelstate(st, 0);
+}
+
+static void killtimer(void *arg)
+{
+       timer_t t = arg;
+       if (t->timerid >= 0) __syscall(SYS_timer_delete, t->timerid);
+}
+
+static void *start(void *arg)
+{
+       struct start_args *args = arg;
+       struct __timer t = {
+               .notify = args->sev->sigev_notify_function,
+               .val = args->sev->sigev_value,
+       };
+
+       args->t = &t;
+
+       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);
+       return 0;
+}
+
+int timer_create(clockid_t clk, struct sigevent *evp, timer_t *res)
+{
+       struct sigevent sev = { 
+               .sigev_notify = SIGEV_SIGNAL,
+               .sigev_signo = SIGALRM
+       };
+       pthread_t td;
+       pthread_attr_t attr;
+       int r;
+       struct start_args args;
+       timer_t t;
+       struct ksigevent ksev;
+
+       if (evp) sev = *evp;
+
+       switch (sev.sigev_notify) {
+       case SIGEV_NONE:
+       case SIGEV_SIGNAL:
+               if (!(t = calloc(1, sizeof *t)))
+                       return -1;
+               ksev.sigev_value = evp ? sev.sigev_value
+                       : (union sigval){.sival_ptr=t};
+               ksev.sigev_signo = sev.sigev_signo;
+               ksev.sigev_notify = sev.sigev_notify;
+               ksev.sigev_tid = 0;
+               break;
+       case SIGEV_THREAD:
+               if (sev.sigev_notify_attributes)
+                       attr = *sev.sigev_notify_attributes;
+               else
+                       pthread_attr_init(&attr);
+               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+               pthread_barrier_init(&args.b, 0, 2);
+               args.sev = &sev;
+               r = pthread_create(&td, &attr, start, &args);
+               if (r) {
+                       errno = r;
+                       return -1;
+               }
+               pthread_barrier_wait(&args.b);
+               t = args.t;
+               t->thread = td;
+               ksev.sigev_value.sival_ptr = t;
+               ksev.sigev_signo = SIGCANCEL;
+               ksev.sigev_notify = 4; /* SIGEV_THREAD_ID */
+               ksev.sigev_tid = td->tid;
+               if (!libc.sigtimer) libc.sigtimer = sighandler;
+               break;
+       }
+
+       t->timerid = -1;
+       if (syscall(SYS_timer_create, clk, &ksev, &t->timerid) < 0) {
+               timer_delete(t);
+               return -1;
+       }
+
+       *res = t;
+       return 0;
+}
diff --git a/src/time/timer_delete.c b/src/time/timer_delete.c
new file mode 100644 (file)
index 0000000..d7c7670
--- /dev/null
@@ -0,0 +1,12 @@
+#include <time.h>
+#include "pthread_impl.h"
+
+int timer_delete(timer_t t)
+{
+       if (t->thread) pthread_cancel(t->thread);
+       else {
+               if (t->timerid >= 0) __syscall(SYS_timer_delete, t->timerid);
+               free(t);
+       }
+       return 0;
+}
diff --git a/src/time/timer_getoverrun.c b/src/time/timer_getoverrun.c
new file mode 100644 (file)
index 0000000..1334e45
--- /dev/null
@@ -0,0 +1,7 @@
+#include <time.h>
+#include "pthread_impl.h"
+
+int timer_getoverrun(timer_t t)
+{
+       return syscall(SYS_timer_getoverrun, t->timerid);
+}
diff --git a/src/time/timer_gettime.c b/src/time/timer_gettime.c
new file mode 100644 (file)
index 0000000..3d3156a
--- /dev/null
@@ -0,0 +1,7 @@
+#include <time.h>
+#include "pthread_impl.h"
+
+int timer_gettime(timer_t t, struct itimerspec *val)
+{
+       return syscall(SYS_timer_gettime, t->timerid, val);
+}
diff --git a/src/time/timer_settime.c b/src/time/timer_settime.c
new file mode 100644 (file)
index 0000000..d109570
--- /dev/null
@@ -0,0 +1,7 @@
+#include <time.h>
+#include "pthread_impl.h"
+
+int timer_settime(timer_t t, int flags, const struct itimerspec *val, struct itimerspec *old)
+{
+       return syscall(SYS_timer_settime, t->timerid, flags, val, old);
+}