OS Primitives: Spinlocks#

Producer/Consumer Interrupts#

  • One interrupt (say a sensor) produces some data

  • Another (say device-buffer-empty) consumes

  • ⟶ Parallelism

  • ⟶ No good!

#include <why-thread.h>
#include <why-irq.h>
#include <why-irqengine.h>
#include <why-logger.h>

#include <string>
#include <list>
#include <thread>
#include <print>

using namespace std::chrono_literals;


std::list<std::string> the_list;

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

    auto sensor_isr = [](int){
        the_list.push_back("some data");
    };
    Why::IRQ::connect(2, sensor_isr);

    auto device_buffer_empty = [](int){
        if (! the_list.empty())
            the_list.pop_front();
    };

    Why::IRQ::connect(3, device_buffer_empty);

    size_t last_num = 0;
    while (true) {
        if (last_num != the_list.size()) {
            last_num = the_list.size();
            std::println("#elems: {}", the_list.size());
        }
        std::this_thread::sleep_for(0.5s);
    }
    return 0;
}

IRQ engine internals

In why-shell#
> pid
833252
> irqsig
60
In another terminal#
$ kill -s 60 -q 2 833252

Or, for short

In why-shell#
> irq 2
> #elems: 1
irq 3
> #elems: 0

Problem: Interrupt Parallelism#

  • Interrupt nesting

  • Awkward in total

  • IRQ2 and IRQ3 are considered parallel

Bombard it:

$ while true; do echo irq 2; echo irq 3; done|why-shell ./why-spinlocks-do-much-in-isr
double free or corruption (fasttop)
child died: signal 6, core dumped: 128

Solution: Spinlock#

jjj check this

  • On a multiprocessor: spin until lock can be had

    • In schedulable context, disable interrupts on the local CPU

  • 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

Bombard it:

$ while true; do echo irq 2; echo irq 3; done|why-shell ./why-spinlocks-do-much-in-isr-spinlock
...
#include <why-spinlock.h>                              // <-- include it
#include <why-thread.h>
#include <why-irq.h>
#include <why-irqengine.h>
#include <why-logger.h>

#include <string>
#include <list>
#include <thread>
#include <print>
#include <unistd.h>

using namespace std::chrono_literals;


std::list<std::string> the_list;
Why::Spinlock the_lock;                                // <-- instantiate one

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

    auto sensor_isr = [](int){
        the_lock.lock();                               // <-- use it
        the_list.push_back("some data");
        the_lock.unlock();                             // <--
    };
    Why::IRQ::connect(2, sensor_isr);

    auto device_buffer_empty = [](int){
        the_lock.lock();                               // <--
        if (! the_list.empty())
            the_list.pop_front();
        the_lock.unlock();                             // <--
    };

    Why::IRQ::connect(3, device_buffer_empty);

    size_t last_num = 0;
    while (true) {
        if (last_num != the_list.size()) {
            last_num = the_list.size();
            std::println("#elems: {}", the_list.size());
        }
        std::this_thread::sleep_for(0.5s);
    }
    return 0;
}