Interrupt Handling#

IRQs And ISRs#

../../../../../../_images/sensor-irq.svg
  • Interrupt service routine (ISR): callback void(int irqnum)

  • Attached to an Interrupt request (IRQ) irqnum

  • Called asynchronously ⟶ runs at the most inopportune time

  • IRQ can safely be considered parallel (though varies per architecture)

  • Main loop: power saving, ideally

Serving Interrupts#

  • In WhyOS: attach a void(int irqum) functional to a number (the IRQ number)

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

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

    Why::RandomSensor sensor(0, 100);
    auto isr = [&sensor](int irqnum){
        auto timestamp = Why::now_monotonic();
        auto value = sensor.get_value();
        std::println("irq #{}: timestamp={}, value={}", irqnum, timestamp.to_seconds(), value);
    };

    Why::IRQ::connect(7, isr);                         // <-- activate ISR

    Why::pause();                                      // <-- main loop, power-saving
    return 0;
}
$ why-shell code/why-interrupt-sensor-irq
> irq 7
irq #7: timestamp=196188342592226, value=97
>

ISR Must Be Quick#

  • Interrupts are blocked during the run of an ISR (arch dependent though)

  • What if an ISR takes too long? (std::println() takes ages)

  • ⟶ Latency issues

  • Potentially missing critical events

  • Solution: defer work to main loop where interrupts are re-enabled

Demo: ISR That Takes Long#

  • WhyOS: interrupts of the same kind (same irqnum) are executed sequentially

  • Other OS/arch combinations might behave differently

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

int main()
{
    Why::init();
    Why::IRQ::connect(
        7, 
        [](int irqnum){
            std::println("seeing irq {}, burn CPU ...", irqnum);
            Why::busywait_milli(5000);
            std::println("... continuing");
        }
    );
    Why::pause();
    return 0;
}
$ why-shell ./why-interrupt-isr-too-long
> irq 7
seeing irq 7, nap time ...
> irq 7
... 5 seconds silence ...
> ... wakeup
seeing irq 7, nap time ...

Spoiler: Using Higher Level APIs In ISRs#

  • Access to filesystem and mass storage requires waiting (e.g. for “DMA done” interrupt) to be able to continue

  • ⟶ Hard to accomplish wait-free, only interrupt driven

  • WhyOS’s answer to doing complicated things in ISRs: NO!

#include <why-irq.h>
#include <why-file.h>
#include <why-time.h>

int main()
{
    Why::init();
    
    // good in schedulable context
    auto file = Why::File::open("/etc/passwd", Why::File::ReadOnly);

    Why::IRQ::connect(
        7, 
        [&file](int irqnum){
            uint8_t bytes[16];
            // no good in interrupt context
            auto nread = file->read(bytes, sizeof(bytes));
        }
    );
    Why::pause();
    return 0;
}