about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/limits.h1
-rw-r--r--include/signal.h22
-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
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);
+}