ChibiOS/RT Architecture - Reference Manual - Guides |
Waking up a thread after an hardware event is one of the most common tasks that an RTOS must be able to perform efficiently. In ChibiOS/RT there are several mechanisms that can be used, often each mechanism is best suited in a specific scenario.
A common situation is to have to synchronously wake up a specific thread. This can be accomplished without the use of any specific synchronization primitive, it uses the very efficient low level scheduler APIs, note that you can also optionally send a simple message from the IRQ handler to the thread.
static Thread *tp = NULL; void mythread(void *p) { while (TRUE) { msg_t msg; // Waiting for the IRQ to happen. chSysLock(); tp = chThdSelf(); chSchGoSleepS(PRSUSPENDED); msg = chThdSelf()->p_rdymsg; // Retrieving the message, optional chSysUnlock(); // Perform processing here. } } CH_IRQ_HANDLER(myIRQ) { CH_IRQ_PROLOGUE(); // Wakes up the thread. chSysLockFromIsr(); if (tp != NULL) { tp->p_rdymsg = (msg_t)55; // Sending the message, optional chSchReadyI(tp); tp = NULL; } chSysUnlockFromIsr(). CH_IRQ_EPILOGUE(); }
Lets assume you have a queue of waiting threads, you want to wake up the threads one by one in FIFO order, if there are no waiting threads then nothing happens.
This can be accomplished using a Semaphore
object initialized to zero:
CH_IRQ_HANDLER(myIRQ) { CH_IRQ_PROLOGUE(); // If there is at least one waiting thread then signal it. chSysLockFromIsr(); if (chSemGetCounterI(&mysem) < 0) chSemSignalI(&mysem); chSysUnlockFromIsr(). CH_IRQ_EPILOGUE(); }
In this scenario you want to synchronously wake up all the waiting threads, if there are no waiting threads then nothing happens.
This can be accomplished using a Semaphore
object initialized to zero:
CH_IRQ_HANDLER(myIRQ) { CH_IRQ_PROLOGUE(); // Wakes up all the threads waiting on the semaphore. chSysLockFromIsr(); chSemResetI(&mysem); chSysUnlockFromIsr(). CH_IRQ_EPILOGUE(); }
If you have to asynchronously wake up a specific thread then a simple event flags can be used.
static Thread *tp; void mythread(void *p) { tp = chThdSelf(); while (TRUE) { // Checks if an IRQ happened else wait. chEvtWaitAny((eventmask_t)1); // Perform processing here. } } CH_IRQ_HANDLER(myIRQ) { CH_IRQ_PROLOGUE(); // Wakes up the thread. chSysLockFromIsr(); chEvtSignalI(tp, (eventmask_t)1); chSysUnlockFromIsr(). CH_IRQ_EPILOGUE(); }
By using event sources it is possible to asynchronously wake up one or more listener threads. The mechanism requires a single initialized EventSource
object, all the threads registered as listeners on the event source will be broadcasted.
CH_IRQ_HANDLER(myIRQ) { CH_IRQ_PROLOGUE(); // Pends an event flag on all the listening threads. chSysLockFromIsr(); chEvtBroadcastI(&my_event_source); chSysUnlockFromIsr(). CH_IRQ_EPILOGUE(); }