semaphore.c 8.1 KB

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