diff options
Diffstat (limited to 'src/time/timer_create.c')
| -rw-r--r-- | src/time/timer_create.c | 101 | 
1 files changed, 57 insertions, 44 deletions
diff --git a/src/time/timer_create.c b/src/time/timer_create.c index ad7a2646..cc6c2236 100644 --- a/src/time/timer_create.c +++ b/src/time/timer_create.c @@ -1,6 +1,9 @@  #include <time.h>  #include <setjmp.h> +#include <limits.h> +#include <semaphore.h>  #include "pthread_impl.h" +#include "atomic.h"  struct ksigevent {  	union sigval sigev_value; @@ -10,7 +13,7 @@ struct ksigevent {  };  struct start_args { -	pthread_barrier_t b; +	sem_t sem1, sem2;  	struct sigevent *sev;  }; @@ -19,66 +22,60 @@ static void dummy_0()  }  weak_alias(dummy_0, __pthread_tsd_run_dtors); +static void timer_handler(int sig, siginfo_t *si, void *ctx) +{ +} +  static void cleanup_fromsig(void *p)  {  	pthread_t self = __pthread_self(); -	__pthread_tsd_run_dtors(self); +	__pthread_tsd_run_dtors(); +	__block_app_sigs(0); +	__syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGTIMER_SET, 0, _NSIG/8);  	self->cancel = 0;  	self->cancelbuf = 0;  	self->canceldisable = 0;  	self->cancelasync = 0; -	self->unblock_cancel = 0;  	__reset_tls();  	longjmp(p, 1);  } -static void timer_handler(int sig, siginfo_t *si, void *ctx) -{ -	pthread_t self = __pthread_self(); -	jmp_buf jb; -	void (*notify)(union sigval) = (void (*)(union sigval))self->start; -	union sigval val = { .sival_ptr = self->start_arg }; - -	if (!setjmp(jb) && si->si_code == SI_TIMER) { -		pthread_cleanup_push(cleanup_fromsig, jb); -		notify(val); -		pthread_cleanup_pop(1); -	} -} - -static void install_handler() -{ -	struct sigaction sa = { -		.sa_sigaction = timer_handler, -		.sa_flags = SA_SIGINFO | SA_RESTART -	}; -	__libc_sigaction(SIGTIMER, &sa, 0); -} -  static void *start(void *arg)  {  	pthread_t self = __pthread_self();  	struct start_args *args = arg; -	int id; +	jmp_buf jb; + +	void (*notify)(union sigval) = args->sev->sigev_notify_function; +	union sigval val = args->sev->sigev_value; -	/* Reuse no-longer-needed thread structure fields to avoid -	 * needing the timer address in the signal handler. */ -	self->start = (void *(*)(void *))args->sev->sigev_notify_function; -	self->start_arg = args->sev->sigev_value.sival_ptr; +	/* The two-way semaphore synchronization ensures that we see +	 * self->cancel set by the parent if timer creation failed or +	 * self->timer_id if it succeeded, and informs the parent that +	 * we are done accessing the arguments so that the parent can +	 * proceed past their block lifetime. */ +	while (sem_wait(&args->sem1)); +	sem_post(&args->sem2); -	pthread_barrier_wait(&args->b); -	if ((id = self->timer_id) >= 0) { -		__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, -			SIGTIMER_SET, 0, _NSIG/8); -		__wait(&self->timer_id, 0, id, 1); -		__syscall(SYS_timer_delete, id); +	if (self->cancel) +		return 0; +	for (;;) { +		siginfo_t si; +		while (sigwaitinfo(SIGTIMER_SET, &si) < 0); +		if (si.si_code == SI_TIMER && !setjmp(jb)) { +			pthread_cleanup_push(cleanup_fromsig, jb); +			notify(val); +			pthread_cleanup_pop(1); +		} +		if (self->timer_id < 0) break;  	} +	__syscall(SYS_timer_delete, self->timer_id & INT_MAX);  	return 0;  }  int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict res)  { -	static pthread_once_t once = PTHREAD_ONCE_INIT; +	static volatile int init = 0;  	pthread_t td;  	pthread_attr_t attr;  	int r; @@ -90,11 +87,15 @@ int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict  	switch (evp ? evp->sigev_notify : SIGEV_SIGNAL) {  	case SIGEV_NONE:  	case SIGEV_SIGNAL: +	case SIGEV_THREAD_ID:  		if (evp) {  			ksev.sigev_value = evp->sigev_value;  			ksev.sigev_signo = evp->sigev_signo;  			ksev.sigev_notify = evp->sigev_notify; -			ksev.sigev_tid = 0; +			if (evp->sigev_notify == SIGEV_THREAD_ID) +				ksev.sigev_tid = evp->sigev_notify_thread_id; +			else +				ksev.sigev_tid = 0;  			ksevp = &ksev;  		}  		if (syscall(SYS_timer_create, clk, ksevp, &timerid) < 0) @@ -102,16 +103,25 @@ int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict  		*res = (void *)(intptr_t)timerid;  		break;  	case SIGEV_THREAD: -		pthread_once(&once, install_handler); +		if (!init) { +			struct sigaction sa = { +				.sa_sigaction = timer_handler, +				.sa_flags = SA_SIGINFO | SA_RESTART +			}; +			__libc_sigaction(SIGTIMER, &sa, 0); +			a_store(&init, 1); +		}  		if (evp->sigev_notify_attributes)  			attr = *evp->sigev_notify_attributes;  		else  			pthread_attr_init(&attr);  		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); -		pthread_barrier_init(&args.b, 0, 2); +		sem_init(&args.sem1, 0, 0); +		sem_init(&args.sem2, 0, 0);  		args.sev = evp;  		__block_app_sigs(&set); +		__syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGTIMER_SET, 0, _NSIG/8);  		r = pthread_create(&td, &attr, start, &args);  		__restore_sigs(&set);  		if (r) { @@ -121,12 +131,15 @@ int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict  		ksev.sigev_value.sival_ptr = 0;  		ksev.sigev_signo = SIGTIMER; -		ksev.sigev_notify = 4; /* SIGEV_THREAD_ID */ +		ksev.sigev_notify = SIGEV_THREAD_ID;  		ksev.sigev_tid = td->tid; -		if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0) +		if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0) {  			timerid = -1; +			td->cancel = 1; +		}  		td->timer_id = timerid; -		pthread_barrier_wait(&args.b); +		sem_post(&args.sem1); +		while (sem_wait(&args.sem2));  		if (timerid < 0) return -1;  		*res = (void *)(INTPTR_MIN | (uintptr_t)td>>1);  		break;  | 
