Semaphores: At The Basis Of Waiting#
Facts#
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
countis 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;
};