Interrupt Handling#
IRQs And ISRs#
Interrupt service routine (ISR): callback
void(int irqnum)Attached to an Interrupt request (IRQ)
irqnumCalled 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 sequentiallyOther 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;
}