The love of fibers
Posted: Thu Feb 11, 2021 1:31 pm
How are others going about threads, fibers, parallelism in their OS's?
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:
The reason for implementing fibers is that anytime user code blocks (waiting on an RPC or kernel message), we can continue to keep the processor busy, without the overhead of thread switching (system calling, and storing + loading all registers). Any incoming RPC or message is handled as a fiber.
There are two functions:
FinishAnyPendingWork() handles any queued fibers and polls the kernel for incoming messages and returns when there's nothing more to do. The intention for this is that you could use it in game loop.
HandOverControl() never returns and sleeps the kernel thread until there is something to do. The intention for this is to turn the program into an event loop (such as a UI application waiting for clicks, or a server waiting for RPCs.)
Here's my PS/2 keyboard and mouse driver, which shows how I set up some handlers, then hand over control:
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;
}