pthread_cond_wait.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /*
  2. * pthread_cond_wait.c
  3. *
  4. * Description:
  5. * This translation unit implements condition variables and their primitives.
  6. *
  7. *
  8. * --------------------------------------------------------------------------
  9. *
  10. * Pthreads-embedded (PTE) - POSIX Threads Library for embedded systems
  11. * Copyright(C) 2008 Jason Schmidlapp
  12. *
  13. * Contact Email: [email protected]
  14. *
  15. *
  16. * Based upon Pthreads-win32 - POSIX Threads Library for Win32
  17. * Copyright(C) 1998 John E. Bossom
  18. * Copyright(C) 1999,2005 Pthreads-win32 contributors
  19. *
  20. * Contact Email: [email protected]
  21. *
  22. * The original list of contributors to the Pthreads-win32 project
  23. * is contained in the file CONTRIBUTORS.ptw32 included with the
  24. * source code distribution. The list can also be seen at the
  25. * following World Wide Web location:
  26. * http://sources.redhat.com/pthreads-win32/contributors.html
  27. *
  28. * This library is free software; you can redistribute it and/or
  29. * modify it under the terms of the GNU Lesser General Public
  30. * License as published by the Free Software Foundation; either
  31. * version 2 of the License, or (at your option) any later version.
  32. *
  33. * This library is distributed in the hope that it will be useful,
  34. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  35. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  36. * Lesser General Public License for more details.
  37. *
  38. * You should have received a copy of the GNU Lesser General Public
  39. * License along with this library in the file COPYING.LIB;
  40. * if not, write to the Free Software Foundation, Inc.,
  41. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  42. *
  43. * -------------------------------------------------------------
  44. * Algorithm:
  45. * The algorithm used in this implementation is that developed by
  46. * Alexander Terekhov in colaboration with Louis Thomas. The bulk
  47. * of the discussion is recorded in the file README.CV, which contains
  48. * several generations of both colaborators original algorithms. The final
  49. * algorithm used here is the one referred to as
  50. *
  51. * Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
  52. *
  53. * presented below in pseudo-code as it appeared:
  54. *
  55. *
  56. * given:
  57. * semBlockLock - bin.semaphore
  58. * semBlockQueue - semaphore
  59. * mtxExternal - mutex or CS
  60. * mtxUnblockLock - mutex or CS
  61. * nWaitersGone - int
  62. * nWaitersBlocked - int
  63. * nWaitersToUnblock - int
  64. *
  65. * wait( timeout ) {
  66. *
  67. * [auto: register int result ] // error checking omitted
  68. * [auto: register int nSignalsWasLeft ]
  69. * [auto: register int nWaitersWasGone ]
  70. *
  71. * sem_wait( semBlockLock );
  72. * nWaitersBlocked++;
  73. * sem_post( semBlockLock );
  74. *
  75. * unlock( mtxExternal );
  76. * bTimedOut = sem_wait( semBlockQueue,timeout );
  77. *
  78. * lock( mtxUnblockLock );
  79. * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
  80. * if ( bTimeout ) { // timeout (or canceled)
  81. * if ( 0 != nWaitersBlocked ) {
  82. * nWaitersBlocked--;
  83. * }
  84. * else {
  85. * nWaitersGone++; // count spurious wakeups.
  86. * }
  87. * }
  88. * if ( 0 == --nWaitersToUnblock ) {
  89. * if ( 0 != nWaitersBlocked ) {
  90. * sem_post( semBlockLock ); // open the gate.
  91. * nSignalsWasLeft = 0; // do not open the gate
  92. * // below again.
  93. * }
  94. * else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
  95. * nWaitersGone = 0;
  96. * }
  97. * }
  98. * }
  99. * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
  100. * // spurious semaphore :-)
  101. * sem_wait( semBlockLock );
  102. * nWaitersBlocked -= nWaitersGone; // something is going on here
  103. * // - test of timeouts? :-)
  104. * sem_post( semBlockLock );
  105. * nWaitersGone = 0;
  106. * }
  107. * unlock( mtxUnblockLock );
  108. *
  109. * if ( 1 == nSignalsWasLeft ) {
  110. * if ( 0 != nWaitersWasGone ) {
  111. * // sem_adjust( semBlockQueue,-nWaitersWasGone );
  112. * while ( nWaitersWasGone-- ) {
  113. * sem_wait( semBlockQueue ); // better now than spurious later
  114. * }
  115. * } sem_post( semBlockLock ); // open the gate
  116. * }
  117. *
  118. * lock( mtxExternal );
  119. *
  120. * return ( bTimedOut ) ? ETIMEOUT : 0;
  121. * }
  122. *
  123. * signal(bAll) {
  124. *
  125. * [auto: register int result ]
  126. * [auto: register int nSignalsToIssue]
  127. *
  128. * lock( mtxUnblockLock );
  129. *
  130. * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
  131. * if ( 0 == nWaitersBlocked ) { // NO-OP
  132. * return unlock( mtxUnblockLock );
  133. * }
  134. * if (bAll) {
  135. * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
  136. * nWaitersBlocked = 0;
  137. * }
  138. * else {
  139. * nSignalsToIssue = 1;
  140. * nWaitersToUnblock++;
  141. * nWaitersBlocked--;
  142. * }
  143. * }
  144. * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
  145. * sem_wait( semBlockLock ); // close the gate
  146. * if ( 0 != nWaitersGone ) {
  147. * nWaitersBlocked -= nWaitersGone;
  148. * nWaitersGone = 0;
  149. * }
  150. * if (bAll) {
  151. * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
  152. * nWaitersBlocked = 0;
  153. * }
  154. * else {
  155. * nSignalsToIssue = nWaitersToUnblock = 1;
  156. * nWaitersBlocked--;
  157. * }
  158. * }
  159. * else { // NO-OP
  160. * return unlock( mtxUnblockLock );
  161. * }
  162. *
  163. * unlock( mtxUnblockLock );
  164. * sem_post( semBlockQueue,nSignalsToIssue );
  165. * return result;
  166. * }
  167. * -------------------------------------------------------------
  168. *
  169. * Algorithm 9 / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
  170. *
  171. * presented below in pseudo-code; basically 8a...
  172. * ...BUT W/O "spurious wakes" prevention:
  173. *
  174. *
  175. * given:
  176. * semBlockLock - bin.semaphore
  177. * semBlockQueue - semaphore
  178. * mtxExternal - mutex or CS
  179. * mtxUnblockLock - mutex or CS
  180. * nWaitersGone - int
  181. * nWaitersBlocked - int
  182. * nWaitersToUnblock - int
  183. *
  184. * wait( timeout ) {
  185. *
  186. * [auto: register int result ] // error checking omitted
  187. * [auto: register int nSignalsWasLeft ]
  188. *
  189. * sem_wait( semBlockLock );
  190. * ++nWaitersBlocked;
  191. * sem_post( semBlockLock );
  192. *
  193. * unlock( mtxExternal );
  194. * bTimedOut = sem_wait( semBlockQueue,timeout );
  195. *
  196. * lock( mtxUnblockLock );
  197. * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
  198. * --nWaitersToUnblock;
  199. * }
  200. * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
  201. * // spurious semaphore :-)
  202. * sem_wait( semBlockLock );
  203. * nWaitersBlocked -= nWaitersGone; // something is going on here
  204. * // - test of timeouts? :-)
  205. * sem_post( semBlockLock );
  206. * nWaitersGone = 0;
  207. * }
  208. * unlock( mtxUnblockLock );
  209. *
  210. * if ( 1 == nSignalsWasLeft ) {
  211. * sem_post( semBlockLock ); // open the gate
  212. * }
  213. *
  214. * lock( mtxExternal );
  215. *
  216. * return ( bTimedOut ) ? ETIMEOUT : 0;
  217. * }
  218. *
  219. * signal(bAll) {
  220. *
  221. * [auto: register int result ]
  222. * [auto: register int nSignalsToIssue]
  223. *
  224. * lock( mtxUnblockLock );
  225. *
  226. * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
  227. * if ( 0 == nWaitersBlocked ) { // NO-OP
  228. * return unlock( mtxUnblockLock );
  229. * }
  230. * if (bAll) {
  231. * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
  232. * nWaitersBlocked = 0;
  233. * }
  234. * else {
  235. * nSignalsToIssue = 1;
  236. * ++nWaitersToUnblock;
  237. * --nWaitersBlocked;
  238. * }
  239. * }
  240. * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
  241. * sem_wait( semBlockLock ); // close the gate
  242. * if ( 0 != nWaitersGone ) {
  243. * nWaitersBlocked -= nWaitersGone;
  244. * nWaitersGone = 0;
  245. * }
  246. * if (bAll) {
  247. * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
  248. * nWaitersBlocked = 0;
  249. * }
  250. * else {
  251. * nSignalsToIssue = nWaitersToUnblock = 1;
  252. * --nWaitersBlocked;
  253. * }
  254. * }
  255. * else { // NO-OP
  256. * return unlock( mtxUnblockLock );
  257. * }
  258. *
  259. * unlock( mtxUnblockLock );
  260. * sem_post( semBlockQueue,nSignalsToIssue );
  261. * return result;
  262. * }
  263. * -------------------------------------------------------------
  264. *
  265. */
  266. #include "pthread.h"
  267. #include "implement.h"
  268. /*
  269. * Arguments for cond_wait_cleanup, since we can only pass a
  270. * single void * to it.
  271. */
  272. typedef struct
  273. {
  274. pthread_mutex_t *mutexPtr;
  275. pthread_cond_t cv;
  276. int *resultPtr;
  277. } pte_cond_wait_cleanup_args_t;
  278. static void
  279. pte_cond_wait_cleanup (void *args)
  280. {
  281. pte_cond_wait_cleanup_args_t *cleanup_args =
  282. (pte_cond_wait_cleanup_args_t *) args;
  283. pthread_cond_t cv = cleanup_args->cv;
  284. int *resultPtr = cleanup_args->resultPtr;
  285. int nSignalsWasLeft;
  286. int result;
  287. /*
  288. * Whether we got here as a result of signal/broadcast or because of
  289. * timeout on wait or thread cancellation we indicate that we are no
  290. * longer waiting. The waiter is responsible for adjusting waiters
  291. * (to)unblock(ed) counts (protected by unblock lock).
  292. */
  293. if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)
  294. {
  295. *resultPtr = result;
  296. return;
  297. }
  298. if (0 != (nSignalsWasLeft = cv->nWaitersToUnblock))
  299. {
  300. --(cv->nWaitersToUnblock);
  301. }
  302. else if (INT_MAX / 2 == ++(cv->nWaitersGone))
  303. {
  304. /* Use the non-cancellable version of sem_wait() */
  305. // if (sem_wait_nocancel (&(cv->semBlockLock)) != 0)
  306. if (sem_wait (&(cv->semBlockLock)) != 0)
  307. {
  308. *resultPtr = errno;
  309. /*
  310. * This is a fatal error for this CV,
  311. * so we deliberately don't unlock
  312. * cv->mtxUnblockLock before returning.
  313. */
  314. return;
  315. }
  316. cv->nWaitersBlocked -= cv->nWaitersGone;
  317. if (sem_post (&(cv->semBlockLock)) != 0)
  318. {
  319. *resultPtr = errno;
  320. /*
  321. * This is a fatal error for this CV,
  322. * so we deliberately don't unlock
  323. * cv->mtxUnblockLock before returning.
  324. */
  325. return;
  326. }
  327. cv->nWaitersGone = 0;
  328. }
  329. if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) != 0)
  330. {
  331. *resultPtr = result;
  332. return;
  333. }
  334. if (1 == nSignalsWasLeft)
  335. {
  336. if (sem_post (&(cv->semBlockLock)) != 0)
  337. {
  338. *resultPtr = errno;
  339. return;
  340. }
  341. }
  342. /*
  343. * XSH: Upon successful return, the mutex has been locked and is owned
  344. * by the calling thread.
  345. */
  346. if ((result = pthread_mutex_lock (cleanup_args->mutexPtr)) != 0)
  347. {
  348. *resultPtr = result;
  349. }
  350. } /* pte_cond_wait_cleanup */
  351. static int
  352. pte_cond_timedwait (pthread_cond_t * cond,
  353. pthread_mutex_t * mutex, const struct timespec *abstime)
  354. {
  355. int result = 0;
  356. pthread_cond_t cv;
  357. pte_cond_wait_cleanup_args_t cleanup_args;
  358. if (cond == NULL || *cond == NULL)
  359. {
  360. return EINVAL;
  361. }
  362. /*
  363. * We do a quick check to see if we need to do more work
  364. * to initialise a static condition variable. We check
  365. * again inside the guarded section of pte_cond_check_need_init()
  366. * to avoid race conditions.
  367. */
  368. if (*cond == PTHREAD_COND_INITIALIZER)
  369. {
  370. result = pte_cond_check_need_init (cond);
  371. }
  372. if (result != 0 && result != EBUSY)
  373. {
  374. return result;
  375. }
  376. cv = *cond;
  377. /* Thread can be cancelled in sem_wait() but this is OK */
  378. if (sem_wait (&(cv->semBlockLock)) != 0)
  379. {
  380. return errno;
  381. }
  382. ++(cv->nWaitersBlocked);
  383. if (sem_post (&(cv->semBlockLock)) != 0)
  384. {
  385. return errno;
  386. }
  387. /*
  388. * Setup this waiter cleanup handler
  389. */
  390. cleanup_args.mutexPtr = mutex;
  391. cleanup_args.cv = cv;
  392. cleanup_args.resultPtr = &result;
  393. pthread_cleanup_push (pte_cond_wait_cleanup, (void *) &cleanup_args);
  394. /*
  395. * Now we can release 'mutex' and...
  396. */
  397. if ((result = pthread_mutex_unlock (mutex)) == 0)
  398. {
  399. /*
  400. * ...wait to be awakened by
  401. * pthread_cond_signal, or
  402. * pthread_cond_broadcast, or
  403. * timeout, or
  404. * thread cancellation
  405. *
  406. * Note:
  407. *
  408. * sem_timedwait is a cancellation point,
  409. * hence providing the mechanism for making
  410. * pthread_cond_wait a cancellation point.
  411. * We use the cleanup mechanism to ensure we
  412. * re-lock the mutex and adjust (to)unblock(ed) waiters
  413. * counts if we are cancelled, timed out or signalled.
  414. */
  415. if (sem_timedwait (&(cv->semBlockQueue), abstime) != 0)
  416. {
  417. result = errno;
  418. }
  419. }
  420. /*
  421. * Always cleanup
  422. */
  423. pthread_cleanup_pop (1);
  424. /*
  425. * "result" can be modified by the cleanup handler.
  426. */
  427. return result;
  428. } /* pte_cond_timedwait */
  429. int
  430. pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)
  431. /*
  432. * ------------------------------------------------------
  433. * DOCPUBLIC
  434. * This function waits on a condition variable until
  435. * awakened by a signal or broadcast.
  436. *
  437. * Caller MUST be holding the mutex lock; the
  438. * lock is released and the caller is blocked waiting
  439. * on 'cond'. When 'cond' is signaled, the mutex
  440. * is re-acquired before returning to the caller.
  441. *
  442. * PARAMETERS
  443. * cond
  444. * pointer to an instance of pthread_cond_t
  445. *
  446. * mutex
  447. * pointer to an instance of pthread_mutex_t
  448. *
  449. *
  450. * DESCRIPTION
  451. * This function waits on a condition variable until
  452. * awakened by a signal or broadcast.
  453. *
  454. * NOTES:
  455. *
  456. * 1) The function must be called with 'mutex' LOCKED
  457. * by the calling thread, or undefined behaviour
  458. * will result.
  459. *
  460. * 2) This routine atomically releases 'mutex' and causes
  461. * the calling thread to block on the condition variable.
  462. * The blocked thread may be awakened by
  463. * pthread_cond_signal or
  464. * pthread_cond_broadcast.
  465. *
  466. * Upon successful completion, the 'mutex' has been locked and
  467. * is owned by the calling thread.
  468. *
  469. *
  470. * RESULTS
  471. * 0 caught condition; mutex released,
  472. * EINVAL 'cond' or 'mutex' is invalid,
  473. * EINVAL different mutexes for concurrent waits,
  474. * EINVAL mutex is not held by the calling thread,
  475. *
  476. * ------------------------------------------------------
  477. */
  478. {
  479. /*
  480. * The NULL abstime arg means INFINITE waiting.
  481. */
  482. return (pte_cond_timedwait (cond, mutex, NULL));
  483. } /* pthread_cond_wait */
  484. int
  485. pthread_cond_timedwait (pthread_cond_t * cond,
  486. pthread_mutex_t * mutex,
  487. const struct timespec *abstime)
  488. /*
  489. * ------------------------------------------------------
  490. * DOCPUBLIC
  491. * This function waits on a condition variable either until
  492. * awakened by a signal or broadcast; or until the time
  493. * specified by abstime passes.
  494. *
  495. * PARAMETERS
  496. * cond
  497. * pointer to an instance of pthread_cond_t
  498. *
  499. * mutex
  500. * pointer to an instance of pthread_mutex_t
  501. *
  502. * abstime
  503. * pointer to an instance of (const struct timespec)
  504. *
  505. *
  506. * DESCRIPTION
  507. * This function waits on a condition variable either until
  508. * awakened by a signal or broadcast; or until the time
  509. * specified by abstime passes.
  510. *
  511. * NOTES:
  512. * 1) The function must be called with 'mutex' LOCKED
  513. * by the calling thread, or undefined behaviour
  514. * will result.
  515. *
  516. * 2) This routine atomically releases 'mutex' and causes
  517. * the calling thread to block on the condition variable.
  518. * The blocked thread may be awakened by
  519. * pthread_cond_signal or
  520. * pthread_cond_broadcast.
  521. *
  522. *
  523. * RESULTS
  524. * 0 caught condition; mutex released,
  525. * EINVAL 'cond', 'mutex', or abstime is invalid,
  526. * EINVAL different mutexes for concurrent waits,
  527. * EINVAL mutex is not held by the calling thread,
  528. * ETIMEDOUT abstime ellapsed before cond was signaled.
  529. *
  530. * ------------------------------------------------------
  531. */
  532. {
  533. if (abstime == NULL)
  534. {
  535. return EINVAL;
  536. }
  537. return (pte_cond_timedwait (cond, mutex, abstime));
  538. } /* pthread_cond_timedwait */