I spent time implementing fibers completely in user-space. My intepretation of the the System V calling convention is that the callee is only required to save 7 registers and the stack, so the context switch is much lighter weight than switching threads. Then I use an object pool to avoid dynamic allocation when I rapidly create/remove fibers.
Now I can do:
Code: Select all
int main() {
Scheduler::Defer([]() {
std::cout << "world" << std::endl;
});
std::cout << "Hello ";
// While this fiber sleeps, here's our chance to run the deferred function above.
Sleep(100);
std::cout << "!" << std::endl;
return 0;
}
There are two functions:
Code: Select all
Scheduler::FinishAnyPendingWork();
Code: Select all
Scheduler::HandOverControl();
Here's my PS/2 keyboard and mouse driver, which shows how I set up some handlers, then hand over control:
Code: Select all
int main() {
// These are services that handle incoming RPCs.
mouse_driver = std::make_unique<PS2MouseDriver>();
keyboard_driver = std::make_unique<PS2KeyboardDriver>();
InitializePS2Controller();
// Calls InterruptHandler() when IRQ1 or IRQ12 occurs.
RegisterInterruptHandler(1, InterruptHandler);
RegisterInterruptHandler(12, InterruptHandler);
// Switches us over to be an event loop that sleeps unless we're dispatching interrupts and RPCs.
HandOverControl();
return 0;
}