event.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /*
  2. * Copyright (C) 2001-2006 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. */
  32. /*!
  33. * \file os/event.c
  34. * \brief Event management routines.
  35. *
  36. * This kernel module provides thread synchronization.
  37. *
  38. * \verbatim
  39. *
  40. * $Log$
  41. * Revision 1.26 2009/01/17 15:37:52 haraldkipp
  42. * Added some NUTASSERT macros to check function parameters.
  43. *
  44. * Revision 1.25 2008/08/11 07:00:33 haraldkipp
  45. * BSD types replaced by stdint types (feature request #1282721).
  46. *
  47. * Revision 1.24 2006/06/28 14:38:34 haraldkipp
  48. * Event and timer handling re-design, again. This fixes a bug, which
  49. * possibly existed since version 3.9.8 and freezes threads under heavy
  50. * load. After several people reported this problem, Michael Jones and
  51. * Henrik Maier finally detected the cause and came up with a solution.
  52. * However, this fix let interrupt latency times depend on the number
  53. * of running threads again and a new solution was implemented, which
  54. * not only avoids this problem but further decreases interrupt latencies
  55. * by adding an event post counter to the THREADINFO structure. This
  56. * counter frees the interrupt routines from dealing with linked lists
  57. * and frees the kernel from dealing with linked lists concurrently
  58. * modified by interrupts. Furthermore, timeout timers are now released
  59. * early. Michael Jones reported, that previous versions suffer from low
  60. * memory situations while processing many events within short periods.
  61. * The timer list is now double linked to reduce removal time. Internally
  62. * timeout condition is now flagged by setting the timer handle to
  63. * SIGNALED.
  64. * Unfortunately new bugs were introduced with this re-design. Special
  65. * thanks to Michael Jones, who located the "exact spot of the crime" and
  66. * proofed, that his final fixes let Nut/OS behave quite well under heavy
  67. * traffic storms. This new version will probably help also, if you
  68. * experienced long term instability.
  69. * LGPL copyright removed after all the initial sources had been finally
  70. * replaced by BSDL'ed code.
  71. * Last not least the documentation had been updated.
  72. *
  73. * Revision 1.23 2005/08/18 15:33:54 christianwelzel
  74. * Fixed bug in handling of NUTDEBUG.
  75. *
  76. * Revision 1.22 2005/07/21 14:23:17 freckle
  77. * inlined NutEventPostFromIrq call in NutEventPostAsync to reduce CS
  78. *
  79. * Revision 1.21 2005/07/20 09:19:45 haraldkipp
  80. * Use native heap calls to avoid dependencies
  81. *
  82. * Revision 1.20 2005/07/13 15:25:50 freckle
  83. * Rewrote NutEventWait to get short critical sections
  84. *
  85. * Revision 1.19 2005/07/12 18:44:05 freckle
  86. * Removed unnecessary critical sections in NutEventPost, NutEventWait
  87. *
  88. * Revision 1.18 2005/07/12 18:04:12 freckle
  89. * Reverted NutEventWait back to 1.15 but kept critical section in
  90. * NutEventTimeout + changed CS in NutEventWaitNext
  91. *
  92. * Revision 1.15 2005/06/10 12:59:26 freckle
  93. * corrected NuEventBroadcastAsync documentation.
  94. * NutEventBroadcast uses NutEventPostAsync which disables IRQsCVS:
  95. * ----------------------------------------------------------------------
  96. *
  97. * Revision 1.14 2005/02/21 12:37:57 phblum
  98. * Removed tabs and added semicolons after NUTTRACER macros
  99. *
  100. * Revision 1.13 2005/02/17 14:45:10 phblum
  101. * In NutEventWait(), replaced NutThreadRemoveQueue by direct removal of first thread in queue.
  102. *
  103. * Revision 1.12 2005/02/16 19:53:17 haraldkipp
  104. * Ready-to-run queue handling removed from interrupt context.
  105. *
  106. * Revision 1.11 2005/01/24 22:34:35 freckle
  107. * Added new tracer by Phlipp Blum <blum@tik.ee.ethz.ch>
  108. *
  109. * Revision 1.10 2005/01/24 21:11:21 freckle
  110. * renamed NutEventPostFromIRQ into NutEventPostFromIrq
  111. *
  112. * Revision 1.9 2005/01/21 16:49:44 freckle
  113. * Seperated calls to NutEventPostAsync between Threads and IRQs
  114. *
  115. * Revision 1.8 2005/01/19 17:59:45 freckle
  116. * Improved interrupt performance by reducing some critical section
  117. *
  118. * Revision 1.7 2005/01/02 10:07:10 haraldkipp
  119. * Replaced platform dependant formats in debug outputs.
  120. *
  121. * Revision 1.6 2004/07/20 08:33:28 freckle
  122. * Fixed NutPostEvent to give up CPU if there is another thread ready with
  123. * same priority to match the documentation
  124. * Also removed 'SWP..' NBUTDEBUG output, as switching is done calling NutThreadYield()
  125. *
  126. * Revision 1.5 2004/04/07 12:13:58 haraldkipp
  127. * Matthias Ringwald's *nix emulation added
  128. *
  129. * Revision 1.4 2004/03/19 09:05:12 jdubiec
  130. * Fixed format strings declarations for AVR.
  131. *
  132. * Revision 1.3 2004/03/16 16:48:45 haraldkipp
  133. * Added Jan Dubiec's H8/300 port.
  134. *
  135. * Revision 1.2 2003/07/20 18:27:44 haraldkipp
  136. * Explain how to disable timeout.
  137. *
  138. * Revision 1.1.1.1 2003/05/09 14:41:49 haraldkipp
  139. * Initial using 3.2.1
  140. *
  141. * Revision 1.20 2003/04/21 17:07:41 harald
  142. * Comments added
  143. *
  144. * Revision 1.19 2003/03/31 14:46:41 harald
  145. * Broadcasts clear signaled queues
  146. *
  147. * Revision 1.18 2003/02/04 18:15:56 harald
  148. * Version 3 released
  149. *
  150. * Revision 1.17 2003/01/14 16:56:39 harald
  151. * Racing condition on signaled events fixed.
  152. * All posting routines return the number of woken up threads.
  153. *
  154. * Revision 1.16 2002/06/26 17:29:44 harald
  155. * First pre-release with 2.4 stack
  156. *
  157. * \endverbatim
  158. */
  159. #include <cfg/os.h>
  160. #include <compiler.h>
  161. #include <sys/atom.h>
  162. #include <sys/heap.h>
  163. #include <sys/timer.h>
  164. #include <sys/thread.h>
  165. #include <sys/event.h>
  166. #include <sys/nutdebug.h>
  167. #ifdef NUTDEBUG
  168. #include <sys/osdebug.h>
  169. #include <stdio.h>
  170. #endif
  171. #ifdef NUTTRACER
  172. #include <sys/tracer.h>
  173. #endif
  174. /*!
  175. * \addtogroup xgEvent
  176. */
  177. /*@{*/
  178. /*!
  179. * \brief Timer callback in case of event timeout.
  180. *
  181. * Applications should not call this function. It is provided as a global
  182. * to enable debugging code inspecting the callbacks in the timer list.
  183. *
  184. * \param timer Handle of the elapsed timeout timer.
  185. * \param arg Handle of an event queue.
  186. *
  187. */
  188. void NutEventTimeout(HANDLE timer, void *arg)
  189. {
  190. NUTTHREADINFO *tqp;
  191. NUTTHREADINFO *volatile *tqpp = arg;
  192. NUTASSERT(tqpp != NULL);
  193. /* Get the queue's root atomically. */
  194. NutEnterCritical();
  195. tqp = *tqpp;
  196. NutExitCritical();
  197. /*
  198. * A signaled queue is an empty queue. So our
  199. * thread already left this queue.
  200. */
  201. if (tqp != SIGNALED) {
  202. /*
  203. * Walk down the linked list and identify
  204. * the thread by the timer id it is waiting
  205. * for.
  206. */
  207. while (tqp) {
  208. if (tqp->td_timer == timer) {
  209. /* Found the thread. Remove it from the event queue. */
  210. NutEnterCritical();
  211. *tqpp = tqp->td_qnxt;
  212. if (tqp->td_qpec) {
  213. if (tqp->td_qnxt) {
  214. tqp->td_qnxt->td_qpec = tqp->td_qpec;
  215. }
  216. else {
  217. *tqpp = SIGNALED;
  218. }
  219. tqp->td_qpec = 0;
  220. }
  221. NutExitCritical();
  222. /* Add it to the queue of threads, which are ready to run. */
  223. tqp->td_state = TDS_READY;
  224. NutThreadAddPriQueue(tqp, (NUTTHREADINFO **) & runQueue);
  225. /* Signal the timer entry in the thread's info structure.
  226. This will tell the waiting thread, that it has been
  227. woken up by a timeout. */
  228. tqp->td_timer = SIGNALED;
  229. break;
  230. }
  231. tqpp = &tqp->td_qnxt;
  232. tqp = tqp->td_qnxt;
  233. }
  234. }
  235. }
  236. /*!
  237. * \brief Wait for an event in a specified queue.
  238. *
  239. * Give up the CPU until another thread or an interrupt routine posts an
  240. * event to this queue or until a time-out occurs, whichever comes first.
  241. *
  242. * If previously an event had been posted to this queue without any
  243. * thread waiting, then the thread will not wait for a new event,
  244. * but may still pass CPU control, if another thread with equal or
  245. * higher priority is ready to run.
  246. *
  247. * \param qhp Identifies the queue to wait on.
  248. * \param ms Maximum wait time in milliseconds. To disable timeout,
  249. * set this parameter to NUT_WAIT_INFINITE.
  250. *
  251. * \return 0 if event received, -1 on timeout.
  252. *
  253. * \note Timeout is limited to the granularity of the system timer.
  254. */
  255. int NutEventWait(volatile HANDLE * qhp, uint32_t ms)
  256. {
  257. NUTTHREADINFO *tdp;
  258. NUTASSERT(qhp != NULL);
  259. /* Get the queue's root atomically. */
  260. NutEnterCritical();
  261. tdp = *qhp;
  262. NutExitCritical();
  263. /*
  264. * Check for posts on a previously empty queue.
  265. */
  266. if (tdp == SIGNALED) {
  267. /* Clear the singaled state. */
  268. NutEnterCritical();
  269. *qhp = 0;
  270. NutExitCritical();
  271. /*
  272. * Even if already signaled, switch to any other thread, which
  273. * is ready to run and has the same or higher priority.
  274. */
  275. NutThreadYield();
  276. return 0;
  277. }
  278. /*
  279. * Remove the current thread from the list of running threads
  280. * and add it to the specified queue.
  281. */
  282. NutThreadRemoveQueue(runningThread, &runQueue);
  283. NutThreadAddPriQueue(runningThread, (NUTTHREADINFO **) qhp);
  284. /* Update our thread's state (sleeping + timer) */
  285. runningThread->td_state = TDS_SLEEP;
  286. if (ms) {
  287. runningThread->td_timer = NutTimerStart(ms, NutEventTimeout, (void *) qhp, TM_ONESHOT);
  288. }
  289. else {
  290. runningThread->td_timer = 0;
  291. }
  292. /*
  293. * Switch to the next thread, which is ready to run.
  294. */
  295. #ifdef NUTTRACER
  296. TRACE_ADD_ITEM(TRACE_TAG_THREAD_WAIT,(int)runningThread);
  297. #endif
  298. NutThreadResume();
  299. /* If our timer handle is signaled, we were woken up by a timeout. */
  300. if (runningThread->td_timer == SIGNALED) {
  301. runningThread->td_timer = 0;
  302. return -1;
  303. }
  304. return 0;
  305. }
  306. /*!
  307. * \brief Wait for a new event in a specified queue.
  308. *
  309. * Give up the CPU until another thread or an interrupt routine posts
  310. * an event to this queue or until a time-out occurs, whichever comes
  311. * first.
  312. *
  313. * This call is similar to NutEventWait(), but will ignore the SIGNALED
  314. * state of the queue. This way, previously posted events to an empty
  315. * queue are not considered.
  316. *
  317. * \param qhp Identifies the queue to wait on.
  318. * \param ms Maximum wait time in milliseconds. To disable timeout,
  319. * set this parameter to NUT_WAIT_INFINITE.
  320. *
  321. * \return 0 if event received, -1 on timeout.
  322. *
  323. * \note Timeout is limited to the granularity of the system timer.
  324. */
  325. int NutEventWaitNext(volatile HANDLE * qhp, uint32_t ms)
  326. {
  327. NUTASSERT(qhp != NULL);
  328. /*
  329. * Check for posts on a previously empty queue.
  330. */
  331. NutEnterCritical();
  332. if (*qhp == SIGNALED)
  333. *qhp = 0;
  334. NutExitCritical();
  335. return NutEventWait(qhp, ms);
  336. }
  337. /*!
  338. * \brief Asynchronously post an event to a specified queue.
  339. *
  340. * Wake up the thread with the highest priority waiting on the
  341. * specified queue. But even if the priority of the woken thread is
  342. * higher than the current thread's priority, the current one
  343. * continues running.
  344. *
  345. * If no thread is waiting, then the queue will be set to the SIGNALED
  346. * state.
  347. *
  348. * \note Interrupts must not call this function but use NutEventPostFromIrq()
  349. * to post events to specific queues.
  350. *
  351. * \param qhp Identifies the queue an event is posted to.
  352. *
  353. * \return The number of threads woken up, either 0 or 1.
  354. *
  355. */
  356. int NutEventPostAsync(volatile HANDLE * qhp)
  357. {
  358. NUTTHREADINFO *td;
  359. NUTASSERT(qhp != NULL);
  360. NutEnterCritical();
  361. td = *qhp;
  362. NutExitCritical();
  363. /* Ignore signaled queues. */
  364. if (td != SIGNALED) {
  365. /* A thread is waiting. */
  366. if (td) {
  367. /* Remove the thread from the wait queue. */
  368. NutEnterCritical();
  369. *qhp = td->td_qnxt;
  370. if (td->td_qpec) {
  371. if (td->td_qnxt) {
  372. td->td_qnxt->td_qpec = td->td_qpec;
  373. }
  374. else {
  375. *qhp = SIGNALED;
  376. }
  377. td->td_qpec = 0;
  378. }
  379. NutExitCritical();
  380. /* Stop any running timeout timer. */
  381. if (td->td_timer) {
  382. NutTimerStop(td->td_timer);
  383. td->td_timer = 0;
  384. }
  385. /* The thread is ready to run. */
  386. td->td_state = TDS_READY;
  387. NutThreadAddPriQueue(td, (NUTTHREADINFO **) & runQueue);
  388. return 1;
  389. }
  390. /* No thread is waiting. Mark the queue signaled. */
  391. else {
  392. NutEnterCritical();
  393. *qhp = SIGNALED;
  394. NutExitCritical();
  395. }
  396. }
  397. return 0;
  398. }
  399. /*!
  400. * \brief Post an event to a specified queue.
  401. *
  402. * Wake up the thread with the highest priority waiting on this queue.
  403. * If the priority of the waiting thread is higher or equal than the
  404. * current thread's priority, then the current thread is stopped and
  405. * CPU control is passed to the waiting thread.
  406. *
  407. * If no thread is waiting, the queue will be set to the signaled
  408. * state.
  409. *
  410. * \note Interrupts must not call this function but use NutEventPostFromIrq()
  411. * to post events to specific queues.
  412. *
  413. * \param qhp Identifies the queue an event is posted to.
  414. *
  415. * \return The number of threads woken up, either 0 or 1.
  416. *
  417. */
  418. int NutEventPost(volatile HANDLE * qhp)
  419. {
  420. int rc;
  421. rc = NutEventPostAsync(qhp);
  422. /*
  423. * If any thread with higher or equal priority is
  424. * ready to run, switch the context.
  425. */
  426. NutThreadYield();
  427. return rc;
  428. }
  429. /*!
  430. * \brief Asynchronously broadcast an event to a specified queue.
  431. *
  432. * Wake up all threads waiting on this queue. But even if the
  433. * priority of any woken thread is higher than the current thread's
  434. * priority, the current one continues running.
  435. *
  436. * In opposite to NutEventPostAsync(), the queue will be cleared in
  437. * any case, even if it is in signaled state. Applications may use this
  438. * call to make sure, that a queue is cleared before initiating some
  439. * event triggering action.
  440. *
  441. * \param qhp Identifies the queue an event is broadcasted to.
  442. *
  443. * \return The number of threads woken up.
  444. *
  445. */
  446. int NutEventBroadcastAsync(volatile HANDLE * qhp)
  447. {
  448. int rc = 0;
  449. NUTTHREADINFO *tdp;
  450. NUTASSERT(qhp != NULL);
  451. /* Get the queue's root atomically. */
  452. NutEnterCritical();
  453. tdp = *qhp;
  454. NutExitCritical();
  455. if (tdp == SIGNALED) {
  456. NutEnterCritical();
  457. *qhp = 0;
  458. NutExitCritical();
  459. }
  460. else if (tdp) {
  461. do {
  462. rc += NutEventPostAsync(qhp);
  463. /* Get the queue's updated root atomically. */
  464. NutEnterCritical();
  465. tdp = *qhp;
  466. NutExitCritical();
  467. } while (tdp && tdp != SIGNALED);
  468. }
  469. return rc;
  470. }
  471. /*!
  472. * \brief Broadcast an event to a specified queue.
  473. *
  474. * Wake up all threads waiting on this queue. If the priority of any
  475. * waiting thread is higher or equal than the current thread's
  476. * priority, then the current thread is stopped and CPU control is
  477. * passed to the woken up thread with the highest priority.
  478. *
  479. * In opposite to NutEventPost(), the queue will be cleared in any
  480. * case, even if it is in signaled state. Applications may use this
  481. * call to make sure, that a queue is cleared before initiating
  482. * some event triggering action.
  483. *
  484. * \param qhp Identifies the queue an event is broadcasted to.
  485. *
  486. * \return The number of threads woken up.
  487. *
  488. */
  489. int NutEventBroadcast(volatile HANDLE * qhp)
  490. {
  491. int rc = NutEventBroadcastAsync(qhp);
  492. NutThreadYield();
  493. return rc;
  494. }
  495. /*@}*/