github.com/afumu/libc@v0.0.6/musl/src/time/timer_create.c (about)

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