summaryrefslogtreecommitdiff
path: root/src/time
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-03-29 13:01:25 -0400
committerRich Felker <dalias@aerifal.cx>2011-03-29 13:01:25 -0400
commit80c4dcd2535a2d7f01eb539b0358dc51b0c0e122 (patch)
tree45bd30741a6f37bd0191467663dfbe5fb108c471 /src/time
parentbf619d82c82052741323aa63c107fbd346c8aaba (diff)
downloadmusl-80c4dcd2535a2d7f01eb539b0358dc51b0c0e122.tar.gz
implement POSIX timers
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.
Diffstat (limited to 'src/time')
-rw-r--r--src/time/timer_create.c110
-rw-r--r--src/time/timer_delete.c12
-rw-r--r--src/time/timer_getoverrun.c7
-rw-r--r--src/time/timer_gettime.c7
-rw-r--r--src/time/timer_settime.c7
5 files changed, 143 insertions, 0 deletions
diff --git a/src/time/timer_create.c b/src/time/timer_create.c
new file mode 100644
index 00000000..1ac1906b
--- /dev/null
+++ b/src/time/timer_create.c
@@ -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
index 00000000..d7c7670f
--- /dev/null
+++ b/src/time/timer_delete.c
@@ -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
index 00000000..1334e451
--- /dev/null
+++ b/src/time/timer_getoverrun.c
@@ -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
index 00000000..3d3156a0
--- /dev/null
+++ b/src/time/timer_gettime.c
@@ -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
index 00000000..d109570b
--- /dev/null
+++ b/src/time/timer_settime.c
@@ -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);
+}