diff options
-rw-r--r-- | include/limits.h | 1 | ||||
-rw-r--r-- | include/signal.h | 22 | ||||
-rw-r--r-- | src/time/timer_create.c | 110 | ||||
-rw-r--r-- | src/time/timer_delete.c | 12 | ||||
-rw-r--r-- | src/time/timer_getoverrun.c | 7 | ||||
-rw-r--r-- | src/time/timer_gettime.c | 7 | ||||
-rw-r--r-- | src/time/timer_settime.c | 7 |
7 files changed, 160 insertions, 6 deletions
diff --git a/include/limits.h b/include/limits.h index 55ad8231..3953aea5 100644 --- a/include/limits.h +++ b/include/limits.h @@ -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... */ diff --git a/include/signal.h b/include/signal.h index f5e87c78..6116fb43 100644 --- a/include/signal.h +++ b/include/signal.h @@ -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 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); +} |