semaphore.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /* Linuxthreads - a simple clone()-based implementation of Posix */
  2. /* threads for Linux. */
  3. /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
  4. /* */
  5. /* This program is free software; you can redistribute it and/or */
  6. /* modify it under the terms of the GNU Library General Public License */
  7. /* as published by the Free Software Foundation; either version 2 */
  8. /* of the License, or (at your option) any later version. */
  9. /* */
  10. /* This program is distributed in the hope that it will be useful, */
  11. /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
  12. /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
  13. /* GNU Library General Public License for more details. */
  14. /* Semaphores a la POSIX 1003.1b */
  15. #include <errno.h>
  16. #include "pthread.h"
  17. #include "semaphore.h"
  18. #include "internals.h"
  19. #include "spinlock.h"
  20. #include "restart.h"
  21. #include "queue.h"
  22. #include <not-cancel.h>
  23. int sem_init(sem_t *sem, int pshared, unsigned int value)
  24. {
  25. if (value > SEM_VALUE_MAX) {
  26. errno = EINVAL;
  27. return -1;
  28. }
  29. if (pshared) {
  30. errno = ENOSYS;
  31. return -1;
  32. }
  33. __pthread_init_lock(&sem->__sem_lock);
  34. sem->__sem_value = value;
  35. sem->__sem_waiting = NULL;
  36. return 0;
  37. }
  38. /* Function called by pthread_cancel to remove the thread from
  39. waiting inside sem_wait. */
  40. static int new_sem_extricate_func(void *obj, pthread_descr th)
  41. {
  42. volatile pthread_descr self = thread_self();
  43. sem_t *sem = obj;
  44. int did_remove = 0;
  45. __pthread_lock(&sem->__sem_lock, self);
  46. did_remove = remove_from_queue(&sem->__sem_waiting, th);
  47. __pthread_unlock(&sem->__sem_lock);
  48. return did_remove;
  49. }
  50. int sem_wait(sem_t * sem)
  51. {
  52. volatile pthread_descr self = thread_self();
  53. pthread_extricate_if extr;
  54. int already_canceled = 0;
  55. int spurious_wakeup_count;
  56. /* Set up extrication interface */
  57. extr.pu_object = sem;
  58. extr.pu_extricate_func = new_sem_extricate_func;
  59. __pthread_lock(&sem->__sem_lock, self);
  60. if (sem->__sem_value > 0) {
  61. sem->__sem_value--;
  62. __pthread_unlock(&sem->__sem_lock);
  63. return 0;
  64. }
  65. /* Register extrication interface */
  66. THREAD_SETMEM(self, p_sem_avail, 0);
  67. __pthread_set_own_extricate_if(self, &extr);
  68. /* Enqueue only if not already cancelled. */
  69. if (!(THREAD_GETMEM(self, p_canceled)
  70. && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
  71. enqueue(&sem->__sem_waiting, self);
  72. else
  73. already_canceled = 1;
  74. __pthread_unlock(&sem->__sem_lock);
  75. if (already_canceled) {
  76. __pthread_set_own_extricate_if(self, 0);
  77. __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
  78. }
  79. /* Wait for sem_post or cancellation, or fall through if already canceled */
  80. spurious_wakeup_count = 0;
  81. while (1)
  82. {
  83. suspend(self);
  84. if (THREAD_GETMEM(self, p_sem_avail) == 0
  85. && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
  86. || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
  87. {
  88. /* Count resumes that don't belong to us. */
  89. spurious_wakeup_count++;
  90. continue;
  91. }
  92. break;
  93. }
  94. __pthread_set_own_extricate_if(self, 0);
  95. /* Terminate only if the wakeup came from cancellation. */
  96. /* Otherwise ignore cancellation because we got the semaphore. */
  97. if (THREAD_GETMEM(self, p_woken_by_cancel)
  98. && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
  99. THREAD_SETMEM(self, p_woken_by_cancel, 0);
  100. __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
  101. }
  102. /* We got the semaphore */
  103. return 0;
  104. }
  105. int sem_trywait(sem_t * sem)
  106. {
  107. int retval;
  108. __pthread_lock(&sem->__sem_lock, NULL);
  109. if (sem->__sem_value == 0) {
  110. errno = EAGAIN;
  111. retval = -1;
  112. } else {
  113. sem->__sem_value--;
  114. retval = 0;
  115. }
  116. __pthread_unlock(&sem->__sem_lock);
  117. return retval;
  118. }
  119. int sem_post(sem_t * sem)
  120. {
  121. pthread_descr self = thread_self();
  122. pthread_descr th;
  123. struct pthread_request request;
  124. if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
  125. __pthread_lock(&sem->__sem_lock, self);
  126. if (sem->__sem_waiting == NULL) {
  127. if (sem->__sem_value >= SEM_VALUE_MAX) {
  128. /* Overflow */
  129. errno = ERANGE;
  130. __pthread_unlock(&sem->__sem_lock);
  131. return -1;
  132. }
  133. sem->__sem_value++;
  134. __pthread_unlock(&sem->__sem_lock);
  135. } else {
  136. th = dequeue(&sem->__sem_waiting);
  137. __pthread_unlock(&sem->__sem_lock);
  138. th->p_sem_avail = 1;
  139. WRITE_MEMORY_BARRIER();
  140. restart(th);
  141. }
  142. } else {
  143. /* If we're in signal handler, delegate post operation to
  144. the thread manager. */
  145. if (__pthread_manager_request < 0) {
  146. if (__pthread_initialize_manager() < 0) {
  147. errno = EAGAIN;
  148. return -1;
  149. }
  150. }
  151. request.req_kind = REQ_POST;
  152. request.req_args.post = sem;
  153. TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
  154. (char *) &request, sizeof(request)));
  155. }
  156. return 0;
  157. }
  158. int sem_getvalue(sem_t * sem, int * sval)
  159. {
  160. *sval = sem->__sem_value;
  161. return 0;
  162. }
  163. int sem_destroy(sem_t * sem)
  164. {
  165. if (sem->__sem_waiting != NULL) {
  166. __set_errno (EBUSY);
  167. return -1;
  168. }
  169. return 0;
  170. }
  171. sem_t *sem_open(const char *name, int oflag, ...)
  172. {
  173. __set_errno (ENOSYS);
  174. return SEM_FAILED;
  175. }
  176. int sem_close(sem_t *sem)
  177. {
  178. __set_errno (ENOSYS);
  179. return -1;
  180. }
  181. int sem_unlink(const char *name)
  182. {
  183. __set_errno (ENOSYS);
  184. return -1;
  185. }
  186. int sem_timedwait(sem_t *sem, const struct timespec *abstime)
  187. {
  188. pthread_descr self = thread_self();
  189. pthread_extricate_if extr;
  190. int already_canceled = 0;
  191. int spurious_wakeup_count;
  192. __pthread_lock(&sem->__sem_lock, self);
  193. if (sem->__sem_value > 0) {
  194. --sem->__sem_value;
  195. __pthread_unlock(&sem->__sem_lock);
  196. return 0;
  197. }
  198. if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
  199. /* The standard requires that if the function would block and the
  200. time value is illegal, the function returns with an error. */
  201. __pthread_unlock(&sem->__sem_lock);
  202. __set_errno (EINVAL);
  203. return -1;
  204. }
  205. /* Set up extrication interface */
  206. extr.pu_object = sem;
  207. extr.pu_extricate_func = new_sem_extricate_func;
  208. /* Register extrication interface */
  209. THREAD_SETMEM(self, p_sem_avail, 0);
  210. __pthread_set_own_extricate_if(self, &extr);
  211. /* Enqueue only if not already cancelled. */
  212. if (!(THREAD_GETMEM(self, p_canceled)
  213. && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
  214. enqueue(&sem->__sem_waiting, self);
  215. else
  216. already_canceled = 1;
  217. __pthread_unlock(&sem->__sem_lock);
  218. if (already_canceled) {
  219. __pthread_set_own_extricate_if(self, 0);
  220. __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
  221. }
  222. spurious_wakeup_count = 0;
  223. while (1)
  224. {
  225. if (timedsuspend(self, abstime) == 0) {
  226. int was_on_queue;
  227. /* __pthread_lock will queue back any spurious restarts that
  228. may happen to it. */
  229. __pthread_lock(&sem->__sem_lock, self);
  230. was_on_queue = remove_from_queue(&sem->__sem_waiting, self);
  231. __pthread_unlock(&sem->__sem_lock);
  232. if (was_on_queue) {
  233. __pthread_set_own_extricate_if(self, 0);
  234. __set_errno (ETIMEDOUT);
  235. return -1;
  236. }
  237. /* Eat the outstanding restart() from the signaller */
  238. suspend(self);
  239. }
  240. if (THREAD_GETMEM(self, p_sem_avail) == 0
  241. && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
  242. || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
  243. {
  244. /* Count resumes that don't belong to us. */
  245. spurious_wakeup_count++;
  246. continue;
  247. }
  248. break;
  249. }
  250. __pthread_set_own_extricate_if(self, 0);
  251. /* Terminate only if the wakeup came from cancellation. */
  252. /* Otherwise ignore cancellation because we got the semaphore. */
  253. if (THREAD_GETMEM(self, p_woken_by_cancel)
  254. && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
  255. THREAD_SETMEM(self, p_woken_by_cancel, 0);
  256. __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
  257. }
  258. /* We got the semaphore */
  259. return 0;
  260. }