Spinlocks#

Deferring Work To Main Loop#

../../../../../../_images/sensor-list.svg
  • Interrupt still produces samples

  • Consumption in main loop

  • Communication from producer (ISR) to consumer (main loop): doubly linked list

Code: Deferring Work To Main Loop (Naively)#

#include <why-irq.h>
#include <why-time.h>
#include <why-sensor.h>
#include <print>
#include <list>

using sensor_data = std::pair<Why::TimeSpec/*timestamp*/, uint64_t/*value*/>;
std::list<sensor_data> the_data;

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

    Why::RandomSensor sensor(0, 100);
    auto isr = [&sensor](int irqnum){
        the_data.emplace_back(                         // <-- produce
            Why::now_monotonic(), sensor.get_value());
    };

    Why::IRQ::connect(7, isr);

    while (true) {
        if (the_data.size()) {                         // <-- consume
            auto [timestamp, value] = the_data.front();
            the_data.pop_front();
            std::println("timestamp={}, value={}", timestamp.to_seconds(), value);
        }
        else
            /*power management?*/;
    }
    return 0;
}
$ why-shell code/why-interrupt-sensor-enqueue-list
> irq 7
timestamp=199147898365133, value=100

Problem: Critical Section#

  • A std::list is not at all safe

  • Non-atomic manipulation of multiple pointers

  • Main loop dequeues samples

  • Possibly interleaved by ISR which enqueues a sample

Bombard it, and see a crash at some point:

$ while true; do echo irq 7; done | \
    why-shell code/why-interupt-sensor-enqueue-list
...
/usr/include/c++/15/bits/stl_list.h:1674: std::__cxx11::list<_Tp, _Allocator>::reference std::__cxx11::list<_Tp, _Allocator>::back() [with _Tp = std::pair<long unsigned int, long unsigned int>; _Alloc = std::allocator<std::pair<long unsigned int, long unsigned int> >; reference = std::pair<long unsigned int, long unsigned int>&]: Assertion '!this->empty()' failed.
child died: signal 6, core dumped: 128

Note

Try as root, to get realtime behavior and hard preemption rather than soft timeslices.

Spinlock: Interrupt Mutual Exclusion#

Attention

Although spinlocks are easy to use and wrap around one’s own data structures, it is probably wiser to rely on OS capabilities and use message queues (or comparable devices) and scheduling.

  • Need something that tests and sets a flags atomically

  • Safe to use in ISR (should be used, actually)

  • Also usable between threads (see later)

  • On a multiprocessor: spin until lock can be had

    • In schedulable context, disable interrupts on the local CPU

    • Actual implementation/definition is much more complicated

    • ⟶ Do not try to write one yourself

  • On a single processor:

    • In schedulable context: disable interrupts, be happy with the lock

    • In interrupt context: nothing to do - interrupts not disabled, that’s enough

#include <why-irq.h>
#include <why-spinlock.h>
#include <why-time.h>
#include <why-sensor.h>
#include <print>
#include <list>

using sensor_data = std::pair<Why::TimeSpec/*timestamp*/, uint64_t/*value*/>;
std::list<sensor_data> the_data;
Why::Spinlock the_lock;                                // <-- instantiate

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

    Why::RandomSensor sensor(0, 100);
    auto isr = [&sensor](int irqnum){
        the_lock.lock();                               // <-- use
        the_data.emplace_back(
            Why::now_monotonic(), sensor.get_value());
        the_lock.unlock();                             // <-- use
    };

    Why::IRQ::connect(7, isr);

    while (true) {
        if (the_data.size()) {
            the_lock.lock();                           // <-- use
            auto [timestamp, value] = the_data.front();
            the_data.pop_front();
            the_lock.unlock();                         // <-- use
            std::println("timestamp={}, value={}", timestamp.to_seconds(), value);
        }
        else
            /*power management?*/;
    }
    return 0;
}

Bombard it like earlier, all well

$ while true; do echo irq 7; done | \
    why-shell code/why-interrupt-sensor-enqueue-list-spinlock
...