summaryrefslogtreecommitdiff
path: root/src/time
diff options
context:
space:
mode:
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);
+}