timer_create.c 3.5 KB

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