diff --git a/docs/ch.txt b/docs/ch.txt index 0ae4eb61b..74436d6b5 100644 --- a/docs/ch.txt +++ b/docs/ch.txt @@ -192,7 +192,28 @@ * * @section thread_states Threads States * The image shows how threads can change their state in ChibiOS/RT.
- * @image html states.png + * @dot + digraph example { + /*rankdir="LR";*/ + node [shape=circle, fontname=Helvetica, fontsize=8, fixedsize="true", width="0.75", height="0.75"]; + start [label="Start", style="bold"]; + run [label="Running"]; + ready [label="Ready"]; + suspend [label="Suspended"]; + sleep [label="Sleeping"]; + stop [label="Stop", style="bold"]; + start -> suspend [label="chThdInit()", fontname=Helvetica, fontsize=8, constraint=false]; + start -> run [label="chThdCreate()", fontname=Helvetica, fontsize=8]; + start -> ready [label="chThdCreate()", fontname=Helvetica, fontsize=8]; + run -> ready [dir="both", label="Reschedulation", fontname=Helvetica, fontsize=8]; + suspend -> run [label="chThdResume()", fontname=Helvetica, fontsize=8]; + suspend -> ready [label="chThdResume()", fontname=Helvetica, fontsize=8]; + run -> sleep [label="chSchGoSleepS()", fontname=Helvetica, fontsize=8]; + sleep -> run [label="chSchWakepS()", fontname=Helvetica, fontsize=8]; + sleep -> ready [label="chSchWakepS()", fontname=Helvetica, fontsize=8]; + run -> stop [label="chThdExit()", fontname=Helvetica, fontsize=8]; + } + * @enddot * * @section priority Priority Levels * Priorities in ChibiOS/RT are a contiguous numerical range but the initial @@ -246,6 +267,7 @@ * - @subpage article_atomic * - @subpage article_saveram * - @subpage article_interrupts + * - @subpage article_timing */ /** @} */ diff --git a/docs/src/atomic.dox b/docs/src/atomic.dox index 22601d8d9..144d6bca0 100644 --- a/docs/src/atomic.dox +++ b/docs/src/atomic.dox @@ -12,17 +12,13 @@ chSemSignalI(&sem1); chSemSignalI(&sem2); - if (tp != NULL) { - chThdResumeI(tp); - tp = NULL; - } chSchRescheduleS(); chSysUnlock(); * @endcode - * The above example performs a signal operation on two semaphores, optionally - * resumes a thread, and performs a final reschedulation. The three operations - * are performed atomically.
+ * The above example performs a signal operation on two semaphores and + * performs a final reschedulation. The two operations are performed + * atomically.
* An hypotetical @p chSemSignalSignalWait() operation could be implemented as * follow: * @code diff --git a/docs/src/timing.dox b/docs/src/timing.dox new file mode 100644 index 000000000..e38fcb74c --- /dev/null +++ b/docs/src/timing.dox @@ -0,0 +1,42 @@ +/** + * @page article_timing Reliable timings using Threads + * @{ + * One common task is to have threads do something at regular, scheduled, + * intervals. + * An obvious solution is to write something like this: + * @code +msg_t my_thread(void *param) { + + while (TRUE) { + do_something(); + chThdSleepMilliseconds(1000); /* Fixed interval */ + } +} + * @endcode + * This example works well assuming that @p do_something() execution time is + * well below the system tick period and that @p my_thread() is not preempted + * by other threads inserting long intervals.
+ * If the above conditions are not satisfied you may have @p do_something() + * executed at irregular intevals, as example:

+ * T0...T0+1000...T0+2002...T0+3002...T0+4005...etc.

+ * Also note that the error increases over time and this kind of behavior can + * lead anomalies really hard to debug. + *

A better solution

+ * It is possible to rewrite the above code using absolute deadlines rather + * than fixed intervals: + * @code +msg_t my_thread(void *param) { + + systick_t time = chSysGetTime(); /* T0 */ + while (TRUE) { + time += MS2ST(1000); /* Next deadline */ + do_something(); + chThdSleepUntil(time); + } +} + * @endcode + * Using this code @p do_something() will always be executed at an absolute + * deadline time and the error will not accumulate over time regardless of + * the execution time and delays inserted by other threads. + */ +/** @} */ diff --git a/src/chschd.c b/src/chschd.c index e568ac64a..cc89682a8 100644 --- a/src/chschd.c +++ b/src/chschd.c @@ -102,11 +102,13 @@ static void wakeup(void *p) { #ifdef CH_USE_SEMAPHORES case PRWTSEM: chSemFastSignalI(tp->p_wtsemp); - /* Falls into, intentional.*/ + /* Falls into, intentional. */ #endif case PRWTCOND: - chSchReadyI(dequeue(tp))->p_rdymsg = RDY_TIMEOUT; - break; + case PRWTMTX: + /* States requiring dequeuing. */ + dequeue(tp); + /* Falls into, intentional. */ default: chSchReadyI(tp)->p_rdymsg = RDY_TIMEOUT; } diff --git a/src/chthreads.c b/src/chthreads.c index 355687423..28d969e46 100644 --- a/src/chthreads.c +++ b/src/chthreads.c @@ -214,26 +214,6 @@ void chThdSetPriority(tprio_t newprio) { chSysUnlock(); } -/** - * @brief Suspends the invoking thread. - * - * @param tpp pointer to a @p Thread pointer, the @p Thread pointer is set - * to point to the suspended process before it enters the - * @p PRSUSPENDED state. The variable pointed by this parameter - * must be set to @p NULL on entry. - * @note The resume operation is meant to be executed into an interrupt or timer - * handler. The handler is also responsible to clear the variable pointed - * by @p tpp after invoking @p chThdResumeI(). - */ -void chThdSuspend(Thread **tpp) { - - chSysLock(); - chDbgAssert(*tpp == NULL, "chthreads.c, chThdSuspend()"); - *tpp = currp; - chSchGoSleepS(PRSUSPENDED); - chSysUnlock(); -} - /** * @brief Resumes a suspended thread. * diff --git a/src/include/threads.h b/src/include/threads.h index 68f7f9bb9..5ca7dd0ce 100644 --- a/src/include/threads.h +++ b/src/include/threads.h @@ -183,7 +183,6 @@ extern "C" { #endif void chThdSetPriority(tprio_t newprio); Thread *chThdResume(Thread *tp); - void chThdSuspend(Thread **tpp); void chThdTerminate(Thread *tp); void chThdSleep(systime_t time); void chThdSleepUntil(systime_t time); diff --git a/test/testsem.c b/test/testsem.c index c9361a0bb..1ec0a7a13 100644 --- a/test/testsem.c +++ b/test/testsem.c @@ -94,7 +94,6 @@ static void sem2_execute(void) { test_assert(isempty(&sem1.s_queue), "queue not empty"); test_assert(&sem1.s_cnt != 0, "counter not zero"); } - test_assert(chSemGetCounter(&sem1) == 0, "non zero counter"); test_assert_sequence("ABCDE"); test_assert_time_window(target_time, target_time + ALLOWED_DELAY); }