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 Handling

    • Operations get() and put()

  • 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() and put_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