Ask some question for Fiber scheduler traceing log

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 sets fiber_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.

1 Like