How to wake up a Thread from an ISR

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.

Synchronous wakeup

A synchronous wakeup operations operates only on threads that are already waiting for the ISR event in the moment the event happens. The event is no way buffered if there are no waiting threads.

Waking up a specific thread

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 very efficient low level APIs, note that you can also optionally send a simple message from the IRQ handler to the thread.

static thread_reference_t trp = NULL;
 
THD_FUNCTION(mythread, arg) {
 
  while (true) {
    msg_t msg;
 
    /* Waiting for the IRQ to happen.*/
    chSysLock();
    msg = chThdSuspendS(&trp);
    chSysUnlock();
 
    /* Perform processing here.*/
    ...
  }
}
 
CH_IRQ_HANDLER(myIRQ) {
  CH_IRQ_PROLOGUE();
 
  /* Wakes up the thread.*/
  chSysLockFromISR();
  chThdResumeI(&trp, (msg_t)0x1337);  /* Resuming the thread with message.*/
  chSysUnlockFromISR().
 
  CH_IRQ_EPILOGUE();
}
Waking up a single queued thread

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 threads_queue_t object:

_THREADS_QUEUE_DECL(tq);
 
CH_IRQ_HANDLER(myIRQ) {
  CH_IRQ_PROLOGUE();
 
  /* If there is at least one waiting thread then signal it.*/
  chSysLockFromISR();
  chThdDequeueNextI(&tq, (msg_t)0x1337); /* Waking up one with message.*/
  chSysUnlockFromISR().
 
  CH_IRQ_EPILOGUE();
}
Waking up all the waiting threads

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();
  chThdDequeueAllI(&tq, (msg_t)0x1337);  /* Waking up all with message.*/
  chSysUnlockFromISR().
 
  CH_IRQ_EPILOGUE();
}

The thread would be like this:

THD_FUNCTION(mythread, arg) {
 
  while (true) {
    chSysLock();
    msg_t msg = chThdEnqueueTimeoutS(&tq, TIME_INFINITE);
    chSysUnlock();
 
    /* Perform processing here.*/
    ...
  }
}

Asynchronous wakeup

An asynchronous wakeup operation can wake up waiting threads like synchronous operations but if the target threads are not immediately ready to serve the event then the event is buffered and the threads will serve it as soon they check for events.

Waking up a specific thread

If you have to asynchronously wake up a specific thread then a simple event flags can be used.

static thread_t *tp;
 
THD_FUNCTION(mythread, arg) {
 
  tp = chThdGetSelfX();
  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();
}
Waking up one or more threads

By using event sources it is possible to asynchronously wake up one or more listener threads. The mechanism requires a single initialized event_source_t object, all the threads registered as listeners on the event source will be signaled.

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();
}

More articles and guides are available on the technical wiki.

learn more

Need Tutorials?

Try the video tutorials and guides on Play Embedded.

learn more

Need Support?

The forums is the best place, registration required.

learn more