Semaphores: At The Basis Of Waiting#

Facts#

../../../../../../_images/sem.jpg
  • Resource count

  • Operations give and take

    • Dijkstra: v (verhogen) and p (proberen)

    • up and down

    • signal and wait

  • take waits until resources given ⟶ not safe in interrupt context

  • Binary semaphore: max count is 1

  • Often used as a lock/mutex ⟶ the locker thread (successful taker) unlocks (gives)

  • Can be used for signalling

Facts (Zephyr Specific)#

  • Semaphores integrate with polling

  • If given and there are waiters, then is is taken by the waiter with highest priority

  • If there more than one candidate, that with the longest wait time is chosen

  • Giving never blocks ⟶ usable in interrupt context

  • Giving beyond count is ignored (Zephyr specific, others might render that undefined behavior)

    Caution though; read https://docs.zephyrproject.org/latest/doxygen/html/group__semaphore__apis.html

Basic Usage#

#include <why.h>
#include <why-semaphore.h>
#include <why-irq.h>

#include <print>

int main()
{
    Why::init();

    Why::Semaphore sem(0);

    Why::IRQ::connect(7, [&sem](int){ sem.give(); });

    std::println("waiting for semaphore to be given");
    sem.take();
    std::println("semaphore taken");

    return 0;
}

Implementing Blocking Operations#

#include "custom-device.h"
#include <why.h>
#include <print>

int main()
{
    Why::init();

    CustomDevice device(/*irqnum*/7);

    std::println("sending");
    device.send_away_data("blah");                    // <-- blocks until IRQ
    std::println("sent");

    return 0;
}
#pragma once

#include <why-semaphore.h>
#include <why-irq.h>
#include <why-internal-error.h>

#include <expected>
#include <format>
#include <cstring>


class CustomDevice
{
public:
    CustomDevice(int irqnum)
    : _irqnum(irqnum),
      _done_signal(0)
    {}

    void send_away_data(const std::string& data)
    {
        /*omitted*/;                                   // <-- copy data to hw buffer

        Why::IRQ::connect(_irqnum,                     // <-- activate interrupt
                          [this](int){ 
                              _done_signal.give();     // <-- ... when called: wake up caller
                          });

        _done_signal.take();                           // <-- wait until woken

        Why::IRQ::disconnect(_irqnum);                 // <-- deactivate interrupt again
    }

private:
    int _irqnum;
    Why::Semaphore _done_signal;
};