Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

osx_sem_timedwait.c 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. /*
  2. * s e m _ t i m e d w a i t
  3. *
  4. * Function:
  5. * Implements a version of sem_timedwait().
  6. *
  7. * Description:
  8. * Not all systems implement sem_timedwait(), which is a version of
  9. * sem_wait() with a timeout. Mac OS X is one example, at least up to
  10. * and including version 10.6 (Leopard). If such a function is needed,
  11. * this code provides a reasonable implementation, which I think is
  12. * compatible with the standard version, although possibly less
  13. * efficient. It works by creating a thread that interrupts a normal
  14. * sem_wait() call after the specified timeout.
  15. *
  16. * Call:
  17. *
  18. * The Linux man pages say:
  19. *
  20. * #include <semaphore.h>
  21. *
  22. * int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  23. *
  24. * sem_timedwait() is the same as sem_wait(), except that abs_timeout
  25. * specifies a limit on the amount of time that the call should block if
  26. * the decrement cannot be immediately performed. The abs_timeout argument
  27. * points to a structure that specifies an absolute timeout in seconds and
  28. * nanoseconds since the Epoch (00:00:00, 1 January 1970). This structure
  29. * is defined as follows:
  30. *
  31. * struct timespec {
  32. * time_t tv_sec; Seconds
  33. * long tv_nsec; Nanoseconds [0 .. 999999999]
  34. * };
  35. *
  36. * If the timeout has already expired by the time of the call, and the
  37. * semaphore could not be locked immediately, then sem_timedwait() fails
  38. * with a timeout error (errno set to ETIMEDOUT).
  39. * If the operation can be performed immediately, then sem_timedwait()
  40. * never fails with a timeout error, regardless of the value of abs_timeout.
  41. * Furthermore, the validity of abs_timeout is not checked in this case.
  42. *
  43. * Limitations:
  44. *
  45. * The mechanism used involves sending a SIGUSR2 signal to the thread
  46. * calling sem_timedwait(). The handler for this signal is set to a null
  47. * routine which does nothing, and with any flags for the signal
  48. * (eg SA_RESTART) cleared. Note that this effective disabling of the
  49. * SIGUSR2 signal is a side-effect of using this routine, and means it
  50. * may not be a completely transparent plug-in replacement for a
  51. * 'normal' sig_timedwait() call. Since OS X does not declare the
  52. * sem_timedwait() call in its standard include files, the relevant
  53. * declaration (shown above in the man pages extract) will probably have
  54. * to be added to any code that uses this.
  55. *
  56. * Compiling:
  57. * This compiles and runs cleanly on OS X (10.6) with gcc with the
  58. * -Wall -ansi -pedantic flags. On Linux, using -ansi causes a sweep of
  59. * compiler complaints about the timespec structure, but it compiles
  60. * and works fine with just -Wall -pedantic. (Since Linux provides
  61. * sem_timedwait() anyway, this really isn't needed on Linux.) However,
  62. * since Linux provides sem_timedwait anyway, the sem_timedwait()
  63. * code in this file is only compiled on OS X, and is a null on other
  64. * systems.
  65. *
  66. * Testing:
  67. * This file contains a test program that exercises the sem_timedwait
  68. * code. It is compiled if the pre-processor variable TEST is defined.
  69. * For more details, see the comments for the test routine at the end
  70. * of the file.
  71. *
  72. * Author: Keith Shortridge, AAO.
  73. *
  74. * History:
  75. * 8th Sep 2009. Original version. KS.
  76. * 24th Sep 2009. Added test that the calling thread still exists before
  77. * trying to set the timed-out flag. KS.
  78. * 2nd Oct 2009. No longer restores the original SIGUSR2 signal handler.
  79. * See comments in the body of the code for more details.
  80. * Prototypes for now discontinued internal routines removed.
  81. * 12th Aug 2010. Added the cleanup handler, so that this code no longer
  82. * leaks resources if the calling thread is cancelled. KS.
  83. * 21st Sep 2011. Added copyright notice below. Modified header comments
  84. * to describe the use of SIGUSR2 more accurately in the
  85. * light of the 2/10/09 change above. Now undefs DEBUG
  86. * before defining it, to avoid any possible clash. KS.
  87. * 14th Feb 2012. Tidied out a number of TABs that had got into the
  88. * code. KS.
  89. * 6th May 2013. Copyright notice modified to one based on the MIT licence,
  90. * which is more permissive than the previous notice. KS.
  91. *
  92. * Copyright (c) Australian Astronomical Observatory (AAO), (2013).
  93. * Permission is hereby granted, free of charge, to any person obtaining a
  94. * copy of this software and associated documentation files (the "Software"),
  95. * to deal in the Software without restriction, including without limitation
  96. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  97. * and/or sell copies of the Software, and to permit persons to whom the
  98. * Software is furnished to do so, subject to the following conditions:
  99. *
  100. * The above copyright notice and this permission notice shall be included in
  101. * all copies or substantial portions of the Software.
  102. *
  103. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  104. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  105. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  106. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  107. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  108. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  109. * IN THE SOFTWARE.
  110. */
  111. #ifdef __APPLE__
  112. #include <semaphore.h>
  113. #include <time.h>
  114. #include <sys/time.h>
  115. #include <pthread.h>
  116. #include <errno.h>
  117. #include <signal.h>
  118. #include <stdio.h>
  119. #include <sys/types.h>
  120. #include <sys/stat.h>
  121. #include <sys/fcntl.h>
  122. #include <setjmp.h>
  123. /* Some useful definitions - TRUE and FALSE */
  124. #undef TRUE
  125. #define TRUE 1
  126. #undef FALSE
  127. #define FALSE 0
  128. /* A structure of type timeoutDetails is passed to the thread used to
  129. * implement the timeout.
  130. */
  131. typedef struct {
  132. struct timespec delay; /* Specifies the delay, relative to now */
  133. pthread_t callingThread; /* The thread doing the sem_wait call */
  134. volatile short *timedOutShort; /* Address of a flag set to indicate that
  135. * the timeout was triggered. */
  136. } timeoutDetails;
  137. /* A structure of type cleanupDetails is passed to the thread cleanup
  138. * routine which is called at the end of the routine or if the thread calling
  139. * it is cancelled.
  140. */
  141. typedef struct {
  142. pthread_t *threadIdAddr; /* Address of the variable that holds
  143. * the Id of the timeout thread. */
  144. struct sigaction *sigHandlerAddr; /* Address of the old signal action
  145. * handler. */
  146. volatile short *timedOutShort; /* Address of a flag set to indicate that
  147. * the timeout was triggered. */
  148. } cleanupDetails;
  149. /* Forward declarations of internal routines */
  150. static void* timeoutThreadMain (void* passedPtr);
  151. static int triggerSignal (int Signal, pthread_t Thread);
  152. static void ignoreSignal (int Signal);
  153. static void timeoutThreadCleanup (void* passedPtr);
  154. /* -------------------------------------------------------------------------- */
  155. /*
  156. * s e m _ t i m e d w a i t
  157. *
  158. * This is the main code for the sem_timedwait() implementation.
  159. */
  160. static int sem_timedwait (
  161. sem_t *sem,
  162. const struct timespec *abs_timeout)
  163. {
  164. int result = 0; /* Code returned by this routine 0 or -1 */
  165. /* "Under no circumstances shall the function fail if the semaphore
  166. * can be locked immediately". So we try to get it quickly to see if we
  167. * can avoid all the timeout overheads.
  168. */
  169. if (sem_trywait(sem) == 0) {
  170. /* Yes, got it immediately. */
  171. result = 0;
  172. } else {
  173. /* No, we've got to do it with a sem_wait() call and a thread to run
  174. * the timeout. First, work out the time from now to the specified
  175. * timeout, which we will pass to the timeout thread in a way that can
  176. * be used to pass to nanosleep(). So we need this in seconds and
  177. * nanoseconds. Along the way, we check for an invalid passed time,
  178. * and for one that's already expired.
  179. */
  180. if ((abs_timeout->tv_nsec < 0) || (abs_timeout->tv_nsec > 1000000000)) {
  181. /* Passed time is invalid */
  182. result = -1;
  183. errno = EINVAL;
  184. } else {
  185. struct timeval currentTime; /* Time now */
  186. long secsToWait,nsecsToWait; /* Seconds and nsec to delay */
  187. gettimeofday (&currentTime,NULL);
  188. secsToWait = abs_timeout->tv_sec - currentTime.tv_sec;
  189. nsecsToWait = (abs_timeout->tv_nsec - (currentTime.tv_usec * 1000));
  190. while (nsecsToWait < 0) {
  191. nsecsToWait += 1000000000;
  192. secsToWait--;
  193. }
  194. if ((secsToWait < 0) || ((secsToWait == 0) && (nsecsToWait < 0))) {
  195. /* Time has passed. Report an immediate timeout. */
  196. result = -1;
  197. errno = ETIMEDOUT;
  198. } else {
  199. /* We're going to have to do a sem_wait() with a timeout thread.
  200. * The thread will wait the specified time, then will issue a
  201. * SIGUSR2 signal that will interrupt the sem_wait() call.
  202. * We pass the thread the id of the current thread, the delay,
  203. * and the address of a flag to set on a timeout, so we can
  204. * distinguish an interrupt caused by the timeout thread from
  205. * one caused by some other signal.
  206. */
  207. volatile short timedOut; /* Flag to set on timeout */
  208. timeoutDetails details; /* All the stuff the thread must know */
  209. struct sigaction oldSignalAction; /* Current signal setting */
  210. pthread_t timeoutThread; /* Id of timeout thread */
  211. cleanupDetails cleaningDetails; /* What the cleanup routine needs */
  212. int oldCancelState; /* Previous cancellation state */
  213. int ignoreCancelState; /* Used in call, but ignored */
  214. int createStatus; /* Status of pthread_create() call */
  215. /* If the current thread is cancelled (and CML does do this)
  216. * we don't want to leave our timer thread running - if we've
  217. * started the thread we want to make sure we join it in order
  218. * to release its resources. So we set a cleanup handler to
  219. * do this. We pass it the address of the structure that will
  220. * hold all it needs to know. While we set all this up,
  221. * we prevent ourselves being cancelled, so all this data is
  222. * coherent.
  223. */
  224. pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,&oldCancelState);
  225. timeoutThread = (pthread_t) 0;
  226. cleaningDetails.timedOutShort = &timedOut;
  227. cleaningDetails.threadIdAddr = &timeoutThread;
  228. cleaningDetails.sigHandlerAddr = &oldSignalAction;
  229. pthread_cleanup_push (timeoutThreadCleanup,&cleaningDetails);
  230. /* Set up the details for the thread. Clear the timeout flag,
  231. * record the current SIGUSR2 action settings so we can restore
  232. * them later.
  233. */
  234. details.delay.tv_sec = secsToWait;
  235. details.delay.tv_nsec = nsecsToWait;
  236. details.callingThread = pthread_self();
  237. details.timedOutShort = &timedOut;
  238. timedOut = FALSE;
  239. sigaction (SIGUSR2,NULL,&oldSignalAction);
  240. /* Start up the timeout thread. Once we've done that, we can
  241. * restore the previous cancellation state.
  242. */
  243. createStatus = pthread_create(&timeoutThread,NULL,
  244. timeoutThreadMain, (void*)&details);
  245. pthread_setcancelstate (oldCancelState,&ignoreCancelState);
  246. if (createStatus < 0) {
  247. /* Failed to create thread. errno will already be set properly */
  248. result = -1;
  249. } else {
  250. /* Thread created OK. This is where we wait for the semaphore.
  251. */
  252. if (sem_wait(sem) == 0) {
  253. /* Got the semaphore OK. We return zero, and all's well. */
  254. result = 0;
  255. } else {
  256. /* If we got a -1 error from sem_wait(), it may be because
  257. * it was interrupted by a timeout, or failed for some
  258. * other reason. We check for the expected timeout
  259. * condition, which is an 'interrupted' status and the
  260. * timeout flag set by the timeout thread. We report that as
  261. * a timeout error. Anything else is some other error and
  262. * errno is already set properly.
  263. */
  264. result = -1;
  265. if (errno == EINTR) {
  266. if (timedOut) errno = ETIMEDOUT;
  267. }
  268. }
  269. }
  270. /* The cleanup routine - timeoutThreadCleanup() - packages up
  271. * any tidying up that is needed, including joining with the
  272. * timer thread. This will be called if the current thread is
  273. * cancelled, but we need it to happen anyway, so we set the
  274. * execute flag true here as we remove it from the list of
  275. * cleanup routines to be called. So normally, this line amounts
  276. * to calling timeoutThreadCleanup().
  277. */
  278. pthread_cleanup_pop (TRUE);
  279. }
  280. }
  281. }
  282. return (result);
  283. }
  284. /* -------------------------------------------------------------------------- */
  285. /*
  286. * t i m e o u t T h r e a d C l e a n u p
  287. *
  288. * This internal routine tidies up at the end of a sem_timedwait() call.
  289. * It is set as a cleanup routine for the current thread (not the timer
  290. * thread) so it is executed even if the thread is cancelled. This is
  291. * important, as we need to tidy up the timeout thread. If we took the
  292. * semaphore (in other words, if we didn't timeout) then the timer thread
  293. * will still be running, sitting in its nanosleep() call, and we need
  294. * to cancel it. If the timer thread did signal a timeout then it will
  295. * now be closing down. In either case, we need to join it (using a call
  296. * to pthread_join()) or its resources will never be released.
  297. * The single argument is a pointer to a cleanupDetails structure that has
  298. * all the routine needs to know.
  299. */
  300. static void timeoutThreadCleanup (void* passedPtr)
  301. {
  302. /* Get what we need from the structure we've been passed. */
  303. cleanupDetails *detailsPtr = (cleanupDetails*) passedPtr;
  304. short timedOut = *(detailsPtr->timedOutShort);
  305. pthread_t timeoutThread = *(detailsPtr->threadIdAddr);
  306. /* If we created the thread, stop it - doesn't matter if it's no longer
  307. * running, pthread_cancel can handle that. We make sure we wait for it
  308. * to complete, because it is this pthread_join() call that releases any
  309. * memory the thread may have allocated. Note that cancelling a thread is
  310. * generally not a good idea, because of the difficulty of cleaning up
  311. * after it, but this is a very simple thread that does nothing but call
  312. * nanosleep(), and that we can cancel quite happily.
  313. */
  314. if (!timedOut) pthread_cancel(timeoutThread);
  315. pthread_join(timeoutThread,NULL);
  316. /* The code originally restored the old action handler, which generally
  317. * was the default handler that caused the task to exit. Just occasionally,
  318. * there seem to be cases where the signal is still queued and ready to
  319. * trigger even though the thread that presumably sent it off just before
  320. * it was cancelled has finished. I had thought that once we'd joined
  321. * that thread, we could be sure of not seeing the signal, but that seems
  322. * not to be the case, and so restoring a handler that will allow the task
  323. * to crash is not a good idea, and so the line below has been commented
  324. * out.
  325. *
  326. * sigaction (SIGUSR2,detailsPtr->sigHandlerAddr,NULL);
  327. */
  328. }
  329. /* -------------------------------------------------------------------------- */
  330. /*
  331. * t i m e o u t T h r e a d M a i n
  332. *
  333. * This internal routine is the main code for the timeout thread.
  334. * The single argument is a pointer to a timeoutDetails structure that has
  335. * all the thread needs to know - thread to signal, delay time, and the
  336. * address of a flag to set if it triggers a timeout.
  337. */
  338. static void* timeoutThreadMain (void* passedPtr)
  339. {
  340. void* Return = (void*) 0;
  341. /* We grab all the data held in the calling thread right now. In some
  342. * cases, we find that the calling thread has vanished and released
  343. * its memory, including the details structure, by the time the timeout
  344. * expires, and then we get an access violation when we try to set the
  345. * 'timed out' flag.
  346. */
  347. timeoutDetails details = *((timeoutDetails*) passedPtr);
  348. struct timespec requestedDelay = details.delay;
  349. /* We do a nanosleep() for the specified delay, and then trigger a
  350. * timeout. Note that we allow for the case where the nanosleep() is
  351. * interrupted, and restart it for the remaining time. If the
  352. * thread that is doing the sem_wait() call gets the semaphore, it
  353. * will cancel this thread, which is fine as we aren't doing anything
  354. * other than a sleep and a signal.
  355. */
  356. for (;;) {
  357. struct timespec remainingDelay;
  358. if (nanosleep (&requestedDelay,&remainingDelay) == 0) {
  359. break;
  360. } else if (errno == EINTR) {
  361. requestedDelay = remainingDelay;
  362. } else {
  363. Return = (void*) errno;
  364. break;
  365. }
  366. }
  367. /* We've completed the delay without being cancelled, so we now trigger
  368. * the timeout by sending a signal to the calling thread. And that's it,
  369. * although we set the timeout flag first to indicate that it was us
  370. * that interrupted the sem_wait() call. One precaution: before we
  371. * try to set the timed-out flag, make sure the calling thread still
  372. * exists - this may not be the case if things are closing down a bit
  373. * messily. We check this quickly using a zero test signal.
  374. */
  375. if (pthread_kill(details.callingThread,0) == 0) {
  376. *(details.timedOutShort) = TRUE;
  377. if (triggerSignal (SIGUSR2,details.callingThread) < 0) {
  378. Return = (void*) errno;
  379. }
  380. }
  381. return Return;
  382. }
  383. /* -------------------------------------------------------------------------- */
  384. /*
  385. * t r i g g e r S i g n a l
  386. *
  387. * This is a general purpose routine that sends a specified signal to
  388. * a specified thread, setting up a signal handler that does nothing,
  389. * and then giving the signal. The only effect will be to interrupt any
  390. * operation that is currently blocking - in this case, we expect this to
  391. * be a sem_wait() call.
  392. */
  393. static int triggerSignal (int Signal, pthread_t Thread)
  394. {
  395. int Result = 0;
  396. struct sigaction SignalDetails;
  397. SignalDetails.sa_handler = ignoreSignal;
  398. SignalDetails.sa_flags = 0;
  399. (void) sigemptyset(&SignalDetails.sa_mask);
  400. if ((Result = sigaction(Signal,&SignalDetails,NULL)) == 0) {
  401. Result = pthread_kill(Thread,Signal);
  402. }
  403. return Result;
  404. }
  405. /* -------------------------------------------------------------------------- */
  406. /*
  407. * i g n o r e S i g n a l
  408. *
  409. * And this is the signal handler that does nothing. (It clears its argument,
  410. * but this has no effect and prevents a compiler warning about an unused
  411. * argument.)
  412. */
  413. static void ignoreSignal (int Signal) {
  414. Signal = 0;
  415. }
  416. #endif
  417. /* -------------------------------------------------------------------------- */
  418. /*
  419. * T e s t c o d e
  420. *
  421. * The rest of the code here is used to test sem_timedwait(), and is
  422. * compiled only if the pre-processor variable TEST is set. The test
  423. * program sets up a random timeout and a random delay after which a
  424. * test semaphore will become available. It starts a thread to release the
  425. * semaphore after the specified delay, and issues a sem_timedwait() call
  426. * to take the semaphore, with the specified timeout. It repeats this
  427. * several times, and finally reports the number of times the semaphore
  428. * was taken, the number of times it timed out, and the number of these
  429. * occurrences that were unexpected - ie a semaphore being taken although
  430. * the timeout was less than the delay before it was set, or vice versa.
  431. * The main() routine of the test returns the number of unexpected
  432. * occurrences, which will be zero if the code is working properly.
  433. *
  434. * To run:
  435. *
  436. * gcc -o timed -Wall -ansi -pedantic -DTEST sem_timedwait.c -lpthread
  437. * ./timed [count] [timescale]
  438. *
  439. * On some Linux systems, you may need to drop the -ansi - see comments
  440. * at start of file. On OS X systems, most tests up to a time frame of
  441. * 0.001 secs show nothing happening in an unexpected sequence. On a
  442. * Linux 2.4 system, with its lower time resolution, tests will show
  443. * occasional cases where things don't happen in the expected order, but
  444. * these are not counted as unexpected if the two random times are less
  445. * than 10 msec apart.
  446. */
  447. #ifdef TEST
  448. #include <stdio.h>
  449. #include <unistd.h>
  450. #include <stdlib.h>
  451. /* A giverDetails structure is used to pass the necessary information
  452. * to the thread that is started to release the semaphore.
  453. */
  454. typedef struct {
  455. sem_t *semAddr; /* Address of semaphore to release */
  456. float delaySecs; /* Time to wait before release */
  457. } giverDetails;
  458. /* -------------------------------------------------------------------------- */
  459. /*
  460. * g i v e r T h r e a d M a i n
  461. *
  462. * This is the main code for the thread that releases the semaphore after
  463. * a specified delay. The single argument is the address of a giverDetails
  464. * structure, which specifies the delay time and the semaphore to release.
  465. * If the sem_timedwait() call in the main thread times-out, this thread
  466. * is cancelled and so will not release the semaphore.
  467. */
  468. static void* giverThreadMain (void* passedPtr)
  469. {
  470. /* All we do is sleep the specified time and then release the semaphore */
  471. giverDetails *details = (giverDetails*) passedPtr;
  472. long uSecs = (long)(details->delaySecs * 1000000.0);
  473. usleep (uSecs);
  474. if (sem_post(details->semAddr) < 0) {
  475. perror ("sem_post");
  476. }
  477. return NULL;
  478. }
  479. /* -------------------------------------------------------------------------- */
  480. /*
  481. * m a i n
  482. *
  483. * The main test routine. This creates a semaphore, then sets up a series
  484. * of sem_timedwait() calls on it. For each call it starts a thread that
  485. * will release the semaphore after a random time, and specifies another
  486. * similar random time as the timeout for the sem_timedwait() call. It
  487. * then checks that what happens is what it expects.
  488. *
  489. * The pogram takes two optional arguments. The first is an integer giving
  490. * the nuber of sem_timedwait() calls it is to attempt (default 10) and the
  491. * second is a floatig point value that gives the time scale - it is the
  492. * maximum time in seconds for the two random times that are generated for
  493. * each sem_timedwait() call. The times should be fairly evenly distributed
  494. * between this value and a hard-coded minimum of 0.001 seconds. The default
  495. * time scale is 1.0.
  496. *
  497. * Note that on all systems, if the difference between the two random times
  498. * (the timeout and the delay before the semaphore is given) is comparable
  499. * with the scheduling resolution of the system - which is only 10
  500. * milliseconds on a standard Linux 2.4 kernel - you can expect to get
  501. * some cases where the 'wrong' timer goes off first. So these cases are
  502. * logged, but anything with a difference of less than 10 msec isn't
  503. * included in the unexpected count.
  504. */
  505. int main (
  506. int argc,
  507. char* argv[])
  508. {
  509. char semName[1024]; /* Semaphore name if we need to use sem_open */
  510. sem_t theSem; /* The semaphore itself */
  511. sem_t *semAddr; /* The address of the semaphore */
  512. struct timespec absTime; /* Absolute time at which we timeout */
  513. struct timeval currentTime; /* The time right now */
  514. pthread_t giverThread; /* Id for the thread that releases the sem */
  515. giverDetails details; /* Details passed to the giver thread */
  516. int randomShort; /* Random number in range 0..65535 */
  517. float randomDelaySecs; /* Random delay before semaphore given */
  518. float randomTimeoutSecs; /* Random timeout value */
  519. int intSecs; /* Integer number of seconds */
  520. long intNsecs; /* Nanoseconds in delay time */
  521. int msecs; /* Milliseconds between the two times */
  522. short retry; /* Controls the EAGAIN loop */
  523. int count; /* Number of tries at the semaphore so far */
  524. int takenCount = 0; /* Number ot time the semaphore was taken */
  525. int unexpectedCount = 0; /* Number of unexpected occurrences */
  526. int timeoutCount = 0; /* Number of times we timed out */
  527. float timeScaleSecs = 1.0; /* Time scale - from command line */
  528. int maxCount = 10; /* Times through the test loop - from command line */
  529. /* Get the command line arguments, the number of tries, and the time
  530. * scale to use.
  531. */
  532. if (argc >= 3) {
  533. timeScaleSecs = atof(argv[2]);
  534. }
  535. if (argc >= 2) {
  536. maxCount = atoi(argv[1]);
  537. }
  538. /* Creating a semaphore is awkward - some systems support sem_init(),
  539. * which is nice, but OS X only supports sem_open() and returns ENOSYS
  540. * for sem_init(). This code handles both cases. The semaphore is
  541. * created taken.
  542. */
  543. semName[0] = '\0';
  544. semAddr = &theSem;
  545. if (sem_init(semAddr,0,0) < 0) {
  546. if (errno == ENOSYS) {
  547. sprintf (semName,"/tmp/test_%ld.sem",(long)getpid());
  548. if ((semAddr = sem_open(semName,
  549. O_CREAT|O_EXCL,S_IWUSR | S_IRUSR,0))
  550. == (sem_t*)SEM_FAILED) {
  551. perror ("creating semaphore");
  552. }
  553. }
  554. }
  555. /* Loop through the specified number of tests. */
  556. for (count = 0; count < maxCount; count++) {
  557. /* Generate random times for the timeout and for the delay before
  558. * the semaphore is given. I'm only using the last 16 bits from random()
  559. * becasuse that's good enough for this and saves me worrying about
  560. * handling really big integers near the 32 bit limit. First for the
  561. * delay used by the giver thread.
  562. */
  563. randomShort = random() & 0xffff;
  564. randomDelaySecs = ((float)randomShort)/65536.0 * timeScaleSecs;
  565. if (randomDelaySecs < 0.001) randomDelaySecs = 0.001;
  566. details.semAddr = semAddr;
  567. details.delaySecs = randomDelaySecs;
  568. /* And now for the timeout, which has to be converted into an absolute
  569. * time from now in the form required by sem_timedwait.
  570. */
  571. randomShort = random() & 0xffff;
  572. randomTimeoutSecs = ((float)randomShort)/65536.0 * timeScaleSecs;
  573. if (randomTimeoutSecs < 0.001) randomTimeoutSecs = 0.001;
  574. intSecs = (int)randomTimeoutSecs;
  575. intNsecs = (long)((randomTimeoutSecs - (float)intSecs) * 1000000000.0);
  576. gettimeofday (&currentTime,NULL);
  577. absTime.tv_sec = currentTime.tv_sec + intSecs;
  578. absTime.tv_nsec = (currentTime.tv_usec * 1000) + intNsecs;
  579. while (absTime.tv_nsec > 1000000000) {
  580. absTime.tv_sec++;
  581. absTime.tv_nsec -= 1000000000;
  582. }
  583. /* Create the 'giver' thread, which will release the semaphore after
  584. * the specified delay time.
  585. */
  586. pthread_create(&giverThread,NULL,giverThreadMain,(void*)&details);
  587. /* Now try to take the semaphore and see what happens - timeout or
  588. * a taken semaphore? The retry loop handles any cases where an
  589. * EAGAIN problem is signalled.
  590. */
  591. retry = TRUE;
  592. while (retry) {
  593. retry = FALSE;
  594. if (sem_timedwait (semAddr,&absTime) == 0) {
  595. /* We got the semaphore. See if we expected to. */
  596. takenCount++;
  597. if (randomDelaySecs > randomTimeoutSecs) {
  598. msecs = (int)((randomDelaySecs - randomTimeoutSecs) * 1000.0);
  599. printf (
  600. "Sem taken first, delay %f timeout %f, diff %d msec\n",
  601. randomDelaySecs,randomTimeoutSecs,msecs);
  602. if (msecs < 10) {
  603. printf ("Time difference too short to count as an error\n");
  604. } else {
  605. unexpectedCount++;
  606. }
  607. }
  608. } else {
  609. /* We failed. See if this was a timeout, in which case see if
  610. * it was expected. If not, check for EAGAIN and retry, or log
  611. * an error in all other cases.
  612. */
  613. if (errno != ETIMEDOUT) {
  614. if (errno == EAGAIN) {
  615. retry = TRUE;
  616. } else {
  617. perror ("Timed wait");
  618. }
  619. } else {
  620. timeoutCount++;
  621. if (randomDelaySecs < randomTimeoutSecs) {
  622. msecs = (int)((randomTimeoutSecs - randomDelaySecs) * 1000.0);
  623. printf (
  624. "Timedout first, delay %f timeout %f, diff %d msec\n",
  625. randomDelaySecs,randomTimeoutSecs,msecs);
  626. if (msecs < 10) {
  627. printf (
  628. "Time difference too short to count as an error\n");
  629. } else {
  630. unexpectedCount++;
  631. }
  632. }
  633. }
  634. }
  635. }
  636. /* Cancel the giver thread if it's still running (ie a timeout or other
  637. * error), and wait for it to complete - that's needed to release its
  638. * resources.
  639. */
  640. pthread_cancel(giverThread);
  641. pthread_join(giverThread,NULL);
  642. /* Something to show we're still running. */
  643. if (((count + 1) % 25) == 0) {
  644. printf ("Tries: %d, Taken %d, Timedout %d, unexpected %d\n",
  645. count + 1,takenCount, timeoutCount,unexpectedCount);
  646. }
  647. /* And then back to try again. Note that the semaphore shuld now be
  648. * taken. Either it was releaed by the giver and we took it in the
  649. * sem_timedwait() call, or we timed out, in which case it's still
  650. * taken. So the next sem_timedwait() call will wait, just like this
  651. * one.
  652. */
  653. }
  654. printf ("Final results: Taken %d, Timedout %d, unexpected %d\n",
  655. takenCount,timeoutCount,unexpectedCount);
  656. return unexpectedCount;
  657. }
  658. #endif