OSLIB Binary Semaphores

In OSLIB binary semaphores are built on underlying counting semaphores using very efficient inline code. The counter of binary semaphores is not allowed to count beyond one thus the semaphore has only two possible states:

  • Taken, when its counter has a value of zero or lower than zero. A negative number represent the number of threads queued on the binary semaphore.
  • Not Taken, when its counter has a value of one.

State Diagram

Binary semaphores are regulated by the following state diagram:

 Binary Semaphore States

Extensions

OSLIB implements an extended version of the binary semaphores, there are several enhancements over the usual definition:

  • Reset Operation. In addition to the classic Wait and Signal operations a new Reset operation has been added. This operation is able to reset a semaphore state to either “taken” or “not taken”, all waiting threads are dequeued, if any.
  • Timeouts. The Wait operation has an optional timeout parameter, a queued thread is able to be dequeued if a Signal or Reset is not performed within the specified time interval.
  • Message. The Wait operation returns a message code indicating the way the thread has been signaled:
    • MSG_OK. The thread has taken the resource normally.
    • MSG_RESET. The thread was queued and a Reset operation has been performed on the semaphore.
    • MSG_TIMEOUT. The thread was queued and a timeout occurred.

Binary Semaphores API

binary_semaphore_t Type of a binary semaphore object.
BSEMAPHORE_DECL() Semaphore static initializer.
chBSemObjectInit() Initializes a semaphore object of type binary_semaphore_t.
chBSemWait() Performs a Wait operation on the semaphore.
chBSemWaitS() Performs a Wait operation on the semaphore (S-Class variant).
chBSemWaitTimeout() Performs a Wait operation on the semaphore with timeout specification.
chBSemWaitTimeoutS() Performs a Wait operation on the semaphore with timeout specification (S-Class variant).
chBSemSignal() Performs a Signal operation on the semaphore.
chBSemSignalI() Performs a Signal operation on the semaphore (I-Class variant).
chBSemReset() Performs a Reset operation on the semaphore.
chBSemResetI() Performs a Reset operation on the semaphore (I-Class variant).
chBSemGetStateI() Return true if the semaphore is taken (I-Class variant).

Examples

Thread Serving an IRQ.

/*
 * Synchronization semaphore.
 */
binary_semaphore_t uart_bsem;
 
/*
 * ISR serving the UART RX FIFO interrupt.
 */
CH_IRQ_HANDLER(UART_RX_IRQ) {
 
  CH_IRQ_PROLOGUE();
 
  /* If there is data available in the UART RX FIFO.*/
  if ((UART->SR & UART_SR_DATA_AVAILABLE) != 0) {
 
    /* Entering I-Locked state and signaling the semaphore.*/
    chSysLockFromISR();
    chBSemSignalI(&uart_bsem);
    chSysUnlockFromISR();
 
    /* Resetting interrupt source.*/
    UART->SR &= ~UART_SR_DATA_AVAILABLE;
  }
 
  CH_IRQ_EPILOGUE();
}
 
/*
 * IRQ serving thread, communication timeouts are handled.
 */
static THD_WORKING_AREA(waUartReceiveThread, 128);
static THD_FUNCTION(UartReceiveThread, arg) {
 
  while (true) {
    /* Waiting for an interrupt. If the interrupt has already occurred
       then the thread will not stop into chBSemWaitTimeout() because
       the binary semaphore would be in the "not taken" state.
       A 500mS timeout is programmed.*/
    msg_t msg = chBSemWaitTimeout(&uart_bsem, TIME_MS2ST(500));
 
    /* If a communication timeout occurred then some special handling
       is required.*/
    if (msg == MSG_TIMEOUT) {
      handle_comm_timeout();
      continue;
    }
 
    /* Empties the UART RX FIFO and processes all data before attempting
       taking the semaphore again.*/
    while ((UART->SR & UART_SR_RXFIFO_CNT) > 0) {
       process_data(UART->DR);
    }
  }
}
 
/*
 * Initialization.
 */
void main(void) {
 
  /*
   * System initializations.
   * - Kernel initialization, the main() function becomes a thread and the
   *   RTOS is active.
   */
  chSysInit();
 
  /* Initializing the UART semaphore in the "taken" state then starting
     the communication.*/
  chBSemObjectInit(&uart_bsem, true);
  uart_init();
 
  /* Starting the UART thread.*/
  chThdCreateStatic(waUartReceiveThread, sizeof(waUartReceiveThread),
                    NORMALPRIO + 1, UartReceiveThread, NULL);
 
  /* Continuing.*/
  ...;
}

Note that a counter semaphore should not used like in this example because the counter would count up after each interrupt and it could overflow if not decremented fast enough by the thread. The binary semaphore is safe because there is only a single “not taken” state, multiple signaling has no effect.