Message Queues: Easy Communication#
Basic Usage#
Producer/consumer queue
Fixed maximum number of fixed-size data items (⟶
N*sizeof(Elem)total)Much like we used the
std::list<>(together with a spinlock) in Interrupt HandlingOperations
get()andput()
Wait conditions on either side
Consumer must wait if no items in queue
Producer waits if queue full
⟶ Not safe in interrupts
And usage in interrupt context?
Use Non-blocking versions:
get_noblock()andput_noblock()
#include <why-thread.h>
#include <why-messagequeue.h>
#include <why-time.h>
#include <print>
int main()
{
Why::init();
Why::MessageQueue<int> queue(7); // <-- room for 7 elements
Why::Thread producer(
[&queue](){
for (int i=0;;i++) {
Why::sleep_milli(500);
queue.put(i); // <-- here we block!
}
}
);
// main thread consumes at highest priority
while (true) {
int elem = queue.get();
std::println("received {}", elem);
}
return 0;
}
Communicating From An ISR Into Thread Context#
Common pattern: in the ISR, quickly transfer work into an easier - schedulable - context
Producer is an ISR: cannot wait ⟶
put_noblock()Consumer is a thread: can wait ⟶
get()Main: not a busy loop anymore
OS does powermanagement when nothing else happens
#include <why-irq.h>
#include <why-messagequeue.h>
#include <why-time.h>
#include <why-sensor.h>
#include <print>
#include <list>
#include <cstdlib>
struct sensor_data
{
Why::TimeSpec timestamp;
uint64_t value;
};
Why::MessageQueue<sensor_data> the_queue(7);
int main()
{
Why::init();
Why::RandomSensor sensor(0, 100);
auto isr = [&sensor](int irqnum){
if (! the_queue.put_noblock({Why::now_monotonic(), sensor.get_value()}))
std::println(stderr, "OIDA! ZAH AUN!!");
};
Why::IRQ::connect(2, isr);
while (true) {
auto [timestamp, value] = the_queue.get(); // <-- wait
std::println("timestamp={}, value={}", timestamp.to_seconds(), value);
}
return 0;
}
$ while true; do echo irq 7; done | \
why-shell code/why-mq-sensor-messagequeue