sig_timedwait.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /****************************************************************************
  2. * sched/signal/sig_timedwait.c
  3. *
  4. * Copyright (C) 2007-2009, 2012-2017 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <nuttx/compiler.h>
  40. #include <stdint.h>
  41. #include <string.h>
  42. #include <signal.h>
  43. #include <time.h>
  44. #include <assert.h>
  45. #include <debug.h>
  46. #include <sched.h>
  47. #include <errno.h>
  48. #include <nuttx/irq.h>
  49. #include <nuttx/arch.h>
  50. #include <nuttx/wdog.h>
  51. #include <nuttx/signal.h>
  52. #include <nuttx/cancelpt.h>
  53. #include "sched/sched.h"
  54. #include "signal/signal.h"
  55. #include "clock/clock.h"
  56. /****************************************************************************
  57. * Pre-processor Definitions
  58. ****************************************************************************/
  59. /* These are special values of si_signo that mean that either the wait was
  60. * awakened with a timeout, or the wait was canceled... not the receipt of a
  61. * signal.
  62. */
  63. #define SIG_CANCEL_TIMEOUT 0xfe
  64. #define SIG_WAIT_TIMEOUT 0xff
  65. /****************************************************************************
  66. * Private Functions
  67. ****************************************************************************/
  68. /****************************************************************************
  69. * Name: nxsig_timeout
  70. *
  71. * Description:
  72. * A timeout elapsed while waiting for signals to be queued.
  73. *
  74. * Assumptions:
  75. * This function executes in the context of the timer interrupt handler.
  76. * Local interrupts are assumed to be disabled on entry.
  77. *
  78. ****************************************************************************/
  79. static void nxsig_timeout(int argc, wdparm_t itcb)
  80. {
  81. #ifdef CONFIG_SMP
  82. irqstate_t flags;
  83. #endif
  84. /* On many small machines, pointers are encoded and cannot be simply cast
  85. * from uint32_t to struct tcb_s *. The following union works around this
  86. * (see wdogparm_t). This odd logic could be conditioned on
  87. * CONFIG_CAN_CAST_POINTERS, but it is not too bad in any case.
  88. */
  89. union
  90. {
  91. FAR struct tcb_s *wtcb;
  92. wdparm_t itcb;
  93. } u;
  94. u.itcb = itcb;
  95. DEBUGASSERT(u.wtcb);
  96. #ifdef CONFIG_SMP
  97. /* We must be in a critical section in order to call up_unblock_task()
  98. * below. If we are running on a single CPU architecture, then we know
  99. * interrupts a disabled an there is no need to explicitly call
  100. * enter_critical_section(). However, in the SMP case,
  101. * enter_critical_section() does much more than just disable interrupts on
  102. * the local CPU; it also manages spinlocks to assure the stability of the
  103. * TCB that we are manipulating.
  104. */
  105. flags = enter_critical_section();
  106. #endif
  107. /* There may be a race condition -- make sure the task is
  108. * still waiting for a signal
  109. */
  110. if (u.wtcb->task_state == TSTATE_WAIT_SIG)
  111. {
  112. u.wtcb->sigunbinfo.si_signo = SIG_WAIT_TIMEOUT;
  113. u.wtcb->sigunbinfo.si_code = SI_TIMER;
  114. u.wtcb->sigunbinfo.si_errno = ETIMEDOUT;
  115. u.wtcb->sigunbinfo.si_value.sival_int = 0;
  116. #ifdef CONFIG_SCHED_HAVE_PARENT
  117. u.wtcb->sigunbinfo.si_pid = 0; /* Not applicable */
  118. u.wtcb->sigunbinfo.si_status = OK;
  119. #endif
  120. up_unblock_task(u.wtcb);
  121. }
  122. #ifdef CONFIG_SMP
  123. leave_critical_section(flags);
  124. #endif
  125. }
  126. /****************************************************************************
  127. * Public Functions
  128. ****************************************************************************/
  129. /****************************************************************************
  130. * Name: nxsig_wait_irq
  131. *
  132. * Description:
  133. * An error event has occurred and the signal wait must be terminated with
  134. * an error.
  135. *
  136. ****************************************************************************/
  137. #ifdef CONFIG_CANCELLATION_POINTS
  138. void nxsig_wait_irq(FAR struct tcb_s *wtcb, int errcode)
  139. {
  140. #ifdef CONFIG_SMP
  141. irqstate_t flags;
  142. /* We must be in a critical section in order to call up_unblock_task()
  143. * below. If we are running on a single CPU architecture, then we know
  144. * interrupts a disabled an there is no need to explicitly call
  145. * enter_critical_section(). However, in the SMP case,
  146. * enter_critical_section() does much more than just disable interrupts on
  147. * the local CPU; it also manages spinlocks to assure the stability of the
  148. * TCB that we are manipulating.
  149. */
  150. flags = enter_critical_section();
  151. #endif
  152. /* There may be a race condition -- make sure the task is
  153. * still waiting for a signal
  154. */
  155. if (wtcb->task_state == TSTATE_WAIT_SIG)
  156. {
  157. wtcb->sigunbinfo.si_signo = SIG_CANCEL_TIMEOUT;
  158. wtcb->sigunbinfo.si_code = SI_USER;
  159. wtcb->sigunbinfo.si_errno = errcode;
  160. wtcb->sigunbinfo.si_value.sival_int = 0;
  161. #ifdef CONFIG_SCHED_HAVE_PARENT
  162. wtcb->sigunbinfo.si_pid = 0; /* Not applicable */
  163. wtcb->sigunbinfo.si_status = OK;
  164. #endif
  165. up_unblock_task(wtcb);
  166. }
  167. #ifdef CONFIG_SMP
  168. leave_critical_section(flags);
  169. #endif
  170. }
  171. #endif /* CONFIG_CANCELLATION_POINTS */
  172. /****************************************************************************
  173. * Name: nxsig_timedwait
  174. *
  175. * Description:
  176. * This function selects the pending signal set specified by the argument
  177. * set. If multiple signals are pending in set, it will remove and return
  178. * the lowest numbered one. If no signals in set are pending at the time
  179. * of the call, the calling process will be suspended until one of the
  180. * signals in set becomes pending, OR until the process is interrupted by
  181. * an unblocked signal, OR until the time interval specified by timeout
  182. * (if any), has expired. If timeout is NULL, then the timeout interval
  183. * is forever.
  184. *
  185. * If the info argument is non-NULL, the selected signal number is stored
  186. * in the si_signo member and the cause of the signal is store din the
  187. * si_code member. The content of si_value is only meaningful if the
  188. * signal was generated by sigqueue() (or nxsig_queue).
  189. *
  190. * This is an internal OS interface. It is functionally equivalent to
  191. * sigtimedwait() except that:
  192. *
  193. * - It is not a cancellaction point, and
  194. * - It does not modify the errno value.
  195. *
  196. * Input Parameters:
  197. * set - The pending signal set.
  198. * info - The returned value (may be NULL).
  199. * timeout - The amount of time to wait (may be NULL)
  200. *
  201. * Returned Value:
  202. * This is an internal OS interface and should not be used by applications.
  203. * It follows the NuttX internal error return policy: Zero (OK) is
  204. * returned on success. A negated errno value is returned on failure.
  205. *
  206. * EAGAIN - No signal specified by set was generated within the specified
  207. * timeout period.
  208. * EINTR - The wait was interrupted by an unblocked, caught signal.
  209. *
  210. ****************************************************************************/
  211. int nxsig_timedwait(FAR const sigset_t *set, FAR struct siginfo *info,
  212. FAR const struct timespec *timeout)
  213. {
  214. FAR struct tcb_s *rtcb = this_task();
  215. sigset_t intersection;
  216. FAR sigpendq_t *sigpend;
  217. irqstate_t flags;
  218. int32_t waitticks;
  219. int ret;
  220. DEBUGASSERT(set != NULL && rtcb->waitdog == NULL);
  221. /* Several operations must be performed below: We must determine if any
  222. * signal is pending and, if not, wait for the signal. Since signals can
  223. * be posted from the interrupt level, there is a race condition that
  224. * can only be eliminated by disabling interrupts!
  225. */
  226. flags = enter_critical_section();
  227. /* Check if there is a pending signal corresponding to one of the
  228. * signals in the pending signal set argument.
  229. */
  230. intersection = *set & nxsig_pendingset(rtcb);
  231. if (intersection != NULL_SIGNAL_SET)
  232. {
  233. /* One or more of the signals in intersections is sufficient to cause
  234. * us to not wait. Pick the lowest numbered signal and mark it not
  235. * pending.
  236. */
  237. sigpend = nxsig_remove_pendingsignal(rtcb, nxsig_lowest(&intersection));
  238. DEBUGASSERT(sigpend);
  239. /* Return the signal info to the caller if so requested */
  240. if (info != NULL)
  241. {
  242. memcpy(info, &sigpend->info, sizeof(struct siginfo));
  243. }
  244. /* The return value is the number of the signal that awakened us */
  245. ret = sigpend->info.si_signo;
  246. /* Then dispose of the pending signal structure properly */
  247. nxsig_release_pendingsignal(sigpend);
  248. leave_critical_section(flags);
  249. }
  250. /* We will have to wait for a signal to be posted to this task. */
  251. else
  252. {
  253. #ifdef CONFIG_CANCELLATION_POINTS
  254. /* nxsig_timedwait() is not a cancellation point, but it may be called
  255. * from a cancellation point. So if a cancellation is pending, we
  256. * must exit immediately without waiting.
  257. */
  258. if (check_cancellation_point())
  259. {
  260. /* If there is a pending cancellation, then do not perform
  261. * the wait. Exit now with ECANCELED.
  262. */
  263. leave_critical_section(flags);
  264. return -ECANCELED;
  265. }
  266. #endif
  267. /* Save the set of pending signals to wait for */
  268. rtcb->sigwaitmask = *set;
  269. /* Check if we should wait for the timeout */
  270. if (timeout != NULL)
  271. {
  272. /* Convert the timespec to system clock ticks, making sure that
  273. * the resulting delay is greater than or equal to the requested
  274. * time in nanoseconds.
  275. */
  276. #ifdef CONFIG_HAVE_LONG_LONG
  277. uint64_t waitticks64 = ((uint64_t)timeout->tv_sec * NSEC_PER_SEC +
  278. (uint64_t)timeout->tv_nsec + NSEC_PER_TICK - 1) /
  279. NSEC_PER_TICK;
  280. DEBUGASSERT(waitticks64 <= UINT32_MAX);
  281. waitticks = (uint32_t)waitticks64;
  282. #else
  283. uint32_t waitmsec;
  284. DEBUGASSERT(timeout->tv_sec < UINT32_MAX / MSEC_PER_SEC);
  285. waitmsec = timeout->tv_sec * MSEC_PER_SEC +
  286. (timeout->tv_nsec + NSEC_PER_MSEC - 1) / NSEC_PER_MSEC;
  287. waitticks = MSEC2TICK(waitmsec);
  288. #endif
  289. /* Create a watchdog */
  290. rtcb->waitdog = wd_create();
  291. DEBUGASSERT(rtcb->waitdog);
  292. if (rtcb->waitdog)
  293. {
  294. /* This little bit of nonsense is necessary for some
  295. * processors where sizeof(pointer) < sizeof(uint32_t).
  296. * see wdog.h.
  297. */
  298. union wdparm_u wdparm;
  299. wdparm.pvarg = (FAR void *)rtcb;
  300. /* Start the watchdog */
  301. (void)wd_start(rtcb->waitdog, waitticks,
  302. (wdentry_t)nxsig_timeout, 1, wdparm.pvarg);
  303. /* Now wait for either the signal or the watchdog, but
  304. * first, make sure this is not the idle task,
  305. * descheduling that isn't going to end well.
  306. */
  307. DEBUGASSERT(NULL != rtcb->flink);
  308. up_block_task(rtcb, TSTATE_WAIT_SIG);
  309. /* We no longer need the watchdog */
  310. wd_delete(rtcb->waitdog);
  311. rtcb->waitdog = NULL;
  312. }
  313. /* REVISIT: And do what if there are no watchdog timers? The wait
  314. * will fail and we will return something bogus.
  315. */
  316. }
  317. /* No timeout, just wait */
  318. else
  319. {
  320. /* And wait until one of the unblocked signals is posted,
  321. * but first make sure this is not the idle task,
  322. * descheduling that isn't going to end well.
  323. */
  324. DEBUGASSERT(NULL != rtcb->flink);
  325. up_block_task(rtcb, TSTATE_WAIT_SIG);
  326. }
  327. /* We are running again, clear the sigwaitmask */
  328. rtcb->sigwaitmask = NULL_SIGNAL_SET;
  329. /* When we awaken, the cause will be in the TCB. Get the signal number
  330. * or timeout) that awakened us.
  331. */
  332. if (GOOD_SIGNO(rtcb->sigunbinfo.si_signo))
  333. {
  334. /* We were awakened by a signal... but is it one of the signals that
  335. * we were waiting for?
  336. */
  337. if (sigismember(set, rtcb->sigunbinfo.si_signo))
  338. {
  339. /* Yes.. the return value is the number of the signal that
  340. * awakened us.
  341. */
  342. ret = rtcb->sigunbinfo.si_signo;
  343. }
  344. else
  345. {
  346. /* No... then report the EINTR error */
  347. ret = -EINTR;
  348. }
  349. }
  350. else
  351. {
  352. /* Otherwise, we must have been awakened by the timeout or,
  353. * perhaps, the wait was cancelled.
  354. */
  355. #ifdef CONFIG_CANCELLATION_POINTS
  356. if (rtcb->sigunbinfo.si_signo == SIG_CANCEL_TIMEOUT)
  357. {
  358. /* The wait was canceled */
  359. ret = -rtcb->sigunbinfo.si_errno;
  360. DEBUGASSERT(ret < 0);
  361. }
  362. else
  363. #endif
  364. {
  365. /* We were awakened by a timeout. Set EAGAIN and return an
  366. * error.
  367. */
  368. DEBUGASSERT(rtcb->sigunbinfo.si_signo == SIG_WAIT_TIMEOUT);
  369. ret = -EAGAIN;
  370. }
  371. }
  372. /* Return the signal info to the caller if so requested */
  373. if (info)
  374. {
  375. memcpy(info, &rtcb->sigunbinfo, sizeof(struct siginfo));
  376. }
  377. leave_critical_section(flags);
  378. }
  379. return ret;
  380. }
  381. /****************************************************************************
  382. * Name: sigtimedwait
  383. *
  384. * Description:
  385. * This function selects the pending signal set specified by the argument
  386. * set. If multiple signals are pending in set, it will remove and return
  387. * the lowest numbered one. If no signals in set are pending at the time
  388. * of the call, the calling process will be suspended until one of the
  389. * signals in set becomes pending, OR until the process is interrupted by
  390. * an unblocked signal, OR until the time interval specified by timeout
  391. * (if any), has expired. If timeout is NULL, then the timeout interval
  392. * is forever.
  393. *
  394. * If the info argument is non-NULL, the selected signal number is stored
  395. * in the si_signo member and the cause of the signal is store din the
  396. * si_code member. The content of si_value is only meaningful if the
  397. * signal was generated by sigqueue().
  398. *
  399. * The following values for si_code are defined in signal.h:
  400. * SI_USER - Signal sent from kill, raise, or abort
  401. * SI_QUEUE - Signal sent from sigqueue
  402. * SI_TIMER - Signal is result of timer expiration
  403. * SI_ASYNCIO - Signal is the result of asynch IO completion
  404. * SI_MESGQ - Signal generated by arrival of a message on an
  405. * empty message queue.
  406. *
  407. * Input Parameters:
  408. * set - The pending signal set.
  409. * info - The returned value (may be NULL).
  410. * timeout - The amount of time to wait (may be NULL)
  411. *
  412. * Returned Value:
  413. * Signal number that cause the wait to be terminated, otherwise -1 (ERROR)
  414. * is returned with errno set to either:
  415. *
  416. * EAGAIN - No signal specified by set was generated within the specified
  417. * timeout period.
  418. * EINTR - The wait was interrupted by an unblocked, caught signal.
  419. *
  420. ****************************************************************************/
  421. int sigtimedwait(FAR const sigset_t *set, FAR struct siginfo *info,
  422. FAR const struct timespec *timeout)
  423. {
  424. int ret;
  425. /* sigtimedwait() is a cancellation point */
  426. (void)enter_cancellation_point();
  427. /* Let nxsig_timedwait() do the work. */
  428. ret = nxsig_timedwait(set, info, timeout);
  429. if (ret < 0)
  430. {
  431. set_errno(-ret);
  432. ret = ERROR;
  433. }
  434. leave_cancellation_point();
  435. return ret;
  436. }