Here’s a simplified and readable English translation of your text:
I wanted to understand how rescheduling works in Crystal, so here’s my investigation.
First, I started with fiber.cr.
From that, I found that Crystal::Scheduler
is responsible for managing Fiber execution. Next, I asked ChatGPT to look at scheduler.cr and explain it.
It turns out that Fiber.swapcontext
handles the switch from the current Fiber to another by changing the execution context. But where is this swapcontext
implemented? It’s here.
fiber
├── context
│ ├── aarch64.cr
│ ├── arm.cr
│ ├── i386.cr
│ ├── interpreted.cr
│ ├── wasm32.cr
│ ├── x86_64-microsoft.cr
│ └── x86_64-sysv.cr
├── context.cr
└── stack_pool.cr
These files are specific to different system architectures. For Linux, the relevant file is x86_64-sysv.cr
, which follows the System V ABI. I asked ChatGPT for further clarification and learned that assembly code is used here to directly manipulate registers and the stack.
Two functions are important: makecontext
and swapcontext
.
makecontext
setsfiber_main
as the entry point for the Fiber’s first execution.swapcontext
switches execution to a new Fiber by updating the stack pointer (though, to be honest, the finer details are still a bit over my head).
Next, I asked ChatGPT about stack_pool.cr.
This revealed that each Fiber is assigned 8 MiB of stack memory, with logic to handle memory allocation and deallocation efficiently.
With these surface-level insights, I circled back to my initial question: How does the scheduler assign the next task to a Fiber?
Here’s the relevant reschedule
method:
protected def reschedule : Nil
loop do
if runnable = @lock.sync { @runnables.shift? }
resume(runnable) unless runnable == @thread.current_fiber
break
else
Crystal.trace :sched, "event_loop" do
@event_loop.run(blocking: true)
end
end
end
end
This method loops through available Fibers. If none are available, it calls @event_loop.run(blocking: true)
to wait for the next event.
@event_loop
refers to EventLoop, which is implemented here.
On Linux, Crystal uses Crystal::LibEvent::EventLoop.new
, which is a binding for libevent.
It seems that LibEvent2.event_base_loop
waits for the right moment to resume processing.
Most likely, libevent2 uses Linux system calls such as epoll
or select
to efficiently monitor events.
This translation aims to keep the friendly and curious tone of your original text while making it concise and easy to follow.