timer_create.c raw

   1  #include <time.h>
   2  #include <setjmp.h>
   3  #include <limits.h>
   4  #include "pthread_impl.h"
   5  #include "atomic.h"
   6  
   7  struct ksigevent {
   8  	union sigval sigev_value;
   9  	int sigev_signo;
  10  	int sigev_notify;
  11  	int sigev_tid;
  12  };
  13  
  14  struct start_args {
  15  	pthread_barrier_t b;
  16  	struct sigevent *sev;
  17  };
  18  
  19  static void dummy_0()
  20  {
  21  }
  22  weak_alias(dummy_0, __pthread_tsd_run_dtors);
  23  
  24  static void cleanup_fromsig(void *p)
  25  {
  26  	pthread_t self = __pthread_self();
  27  	__pthread_tsd_run_dtors();
  28  	self->cancel = 0;
  29  	self->cancelbuf = 0;
  30  	self->canceldisable = 0;
  31  	self->cancelasync = 0;
  32  	__reset_tls();
  33  	longjmp(p, 1);
  34  }
  35  
  36  static void *start(void *arg)
  37  {
  38  	pthread_t self = __pthread_self();
  39  	struct start_args *args = arg;
  40  	jmp_buf jb;
  41  
  42  	void (*notify)(union sigval) = args->sev->sigev_notify_function;
  43  	union sigval val = args->sev->sigev_value;
  44  
  45  	pthread_barrier_wait(&args->b);
  46  	for (;;) {
  47  		siginfo_t si;
  48  		while (sigwaitinfo(SIGTIMER_SET, &si) < 0);
  49  		if (si.si_code == SI_TIMER && !setjmp(jb)) {
  50  			pthread_cleanup_push(cleanup_fromsig, jb);
  51  			notify(val);
  52  			pthread_cleanup_pop(1);
  53  		}
  54  		if (self->timer_id < 0) break;
  55  	}
  56  	__syscall(SYS_timer_delete, self->timer_id & INT_MAX);
  57  	return 0;
  58  }
  59  
  60  int timer_create(clockid_t clk, struct sigevent *restrict evp, timer_t *restrict res)
  61  {
  62  	volatile static int init = 0;
  63  	pthread_t td;
  64  	pthread_attr_t attr;
  65  	int r;
  66  	struct start_args args;
  67  	struct ksigevent ksev, *ksevp=0;
  68  	int timerid;
  69  	sigset_t set;
  70  
  71  	switch (evp ? evp->sigev_notify : SIGEV_SIGNAL) {
  72  	case SIGEV_NONE:
  73  	case SIGEV_SIGNAL:
  74  	case SIGEV_THREAD_ID:
  75  		if (evp) {
  76  			ksev.sigev_value = evp->sigev_value;
  77  			ksev.sigev_signo = evp->sigev_signo;
  78  			ksev.sigev_notify = evp->sigev_notify;
  79  			if (evp->sigev_notify == SIGEV_THREAD_ID)
  80  				ksev.sigev_tid = evp->sigev_notify_thread_id;
  81  			else
  82  				ksev.sigev_tid = 0;
  83  			ksevp = &ksev;
  84  		}
  85  		if (syscall(SYS_timer_create, clk, ksevp, &timerid) < 0)
  86  			return -1;
  87  		*res = (void *)(intptr_t)timerid;
  88  		break;
  89  	case SIGEV_THREAD:
  90  		if (!init) {
  91  			struct sigaction sa = { .sa_handler = SIG_DFL };
  92  			__libc_sigaction(SIGTIMER, &sa, 0);
  93  			a_store(&init, 1);
  94  		}
  95  		if (evp->sigev_notify_attributes)
  96  			attr = *evp->sigev_notify_attributes;
  97  		else
  98  			pthread_attr_init(&attr);
  99  		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 100  		pthread_barrier_init(&args.b, 0, 2);
 101  		args.sev = evp;
 102  
 103  		__block_app_sigs(&set);
 104  		__syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGTIMER_SET, 0, _NSIG/8);
 105  		r = pthread_create(&td, &attr, start, &args);
 106  		__restore_sigs(&set);
 107  		if (r) {
 108  			errno = r;
 109  			return -1;
 110  		}
 111  
 112  		ksev.sigev_value.sival_ptr = 0;
 113  		ksev.sigev_signo = SIGTIMER;
 114  		ksev.sigev_notify = SIGEV_THREAD_ID;
 115  		ksev.sigev_tid = td->tid;
 116  		if (syscall(SYS_timer_create, clk, &ksev, &timerid) < 0)
 117  			timerid = -1;
 118  		td->timer_id = timerid;
 119  		pthread_barrier_wait(&args.b);
 120  		if (timerid < 0) return -1;
 121  		*res = (void *)(INTPTR_MIN | (uintptr_t)td>>1);
 122  		break;
 123  	default:
 124  		errno = EINVAL;
 125  		return -1;
 126  	}
 127  
 128  	return 0;
 129  }
 130