ChibiOS/RT Logo ChibiOS/RT

Architecture - Reference Manual - Guides

How to wake up a thread from an interrupt handler

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.

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

Synchronously waking up one of the waiting threads

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

Synchronously 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();
  chSemResetI(&mysem);
  chSysUnlockFromIsr().

  CH_IRQ_EPILOGUE();
}

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

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

Generated on Sun Nov 28 2010 14:09:56 for ChibiOS/RT by doxygen 1.7.1