Skip to content

Conversation

@ko1
Copy link
Contributor

@ko1 ko1 commented Dec 12, 2025

When a thread invokes the blocking operation such as I/O, the interpreter currently tries to pass the GVL to another thread immediately. However, if the blocking operation completes in a sufficiently short amount of time, the cost of context switching, especially on multi-core machines outweigh the benefit.

This patch introduces delayed GVL passing for blocking operations.

Scenario1: Short blocking operation

(1) thread_sched_to_waiting_common() does not immediately
schedule another ready thread, but instead set the waiting flag.
(2) When the blocking operation finishes,
thread_sched_to_running_common() is called without a context switching
and sched->running is not changed.
Clear the waiting flag and keep going.

Scenario2: Long blocking operation

(1) Same as above.
(2) The timer thread checks the running threads to provide
timeslice every 10ms and finds the thread (th) performing
blocking operation.
(3) The timer thread switches the running thread from th to another
ready thread.
(4) When the blocking operation (on th) finishes,
thread_sched_to_running_common() is called and sched->running
should be another thread (or be NULL) GVL.

fix https://bugs.ruby-lang.org/issues/21685 (and the idea is from discussions)

@launchable-app

This comment has been minimized.

@ko1 ko1 force-pushed the delay_gvl_handoff branch from 247df9e to 8acc492 Compare December 12, 2025 16:07
When a thread invokes the blocking operation such as I/O,
the interpreter currently tries to pass the GVL to another
thread immediately. However, if the blocking operation completes
in a sufficiently short amount of time, the cost of context switching,
especially on multi-core machines outweigh the benefit.

This patch introduces delayed GVL passing for blocking operations.

Scenario1: Short blocking operation

(1) `thread_sched_to_waiting_common()` does not immediately
    schedule another ready thread, but instead set the `waiting` flag.
(2) When the blocking operation finishes,
    `thread_sched_to_running_common()` is called without a context switching
    and `sched->running` is not changed.
    Clear the `waiting` flag and keep going.

Scenario2: Long blocking operation

(1) Same as above.
(2) The timer thread checks the running threads to provide
    timeslice every 10ms and finds the thread (th) performing
    blocking operation.
(3) The timer thread switches the running thread from th to another
    ready thread.
(4) When the blocking operation (on th) finishes,
    `thread_sched_to_running_common()` is called and `sched->running`
    should be another thread (or be NULL) GVL.

fix https://bugs.ruby-lang.org/issues/21685 (and the idea is from discussions)
@ko1 ko1 force-pushed the delay_gvl_handoff branch from 8acc492 to 54f7099 Compare December 12, 2025 16:18
@ko1 ko1 force-pushed the delay_gvl_handoff branch from 54f7099 to 59815c1 Compare December 12, 2025 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant