runtime: add fast path for non-blocking single-state select#5500
runtime: add fast path for non-blocking single-state select#5500rdon-key wants to merge 1 commit into
Conversation
|
Additional stability test: a #4975-style workload ran successfully with this PR applied. Setup: pico, -scheduler=cores, SSD1306 OLED over I2C at 400kHz, SDA=GP12, SCL=GP13, address 0x3c, 128x32 display. After adjusting the reproducer for the current ssd1306.NewI2C API (*ssd1306.Device), it completed two 30-minute runs without freezin |
|
I found that the underlying cause is the waiter wake-up ordering in the common channel path. I submitted #5513 to fix it. A blocking multi-state select reproducer, which cannot use the fast path in this PR, froze in 3/3 runs on dev. With #5513 applied, it completed all five three-minute runs. This indicates that the underlying freeze is not limited to non-blocking single-state selects. However, the fast path introduced by this PR is still a useful optimization. The Go compiler applies the same optimization to a select with one channel operation and a default case, lowering it to I am therefore keeping this PR open but changing its purpose from a correctness fix to an optimization. |
Fixes #4974.
This PR adds a fast path in
chanSelectfor non-blocking single-state selects.For example:
This form can lock at most one channel, so it does not need the global
chanSelectLock, which is used to avoid deadlocks when multiple channels may be locked in different orders.This fast path does not use
chanSelectLock, and also avoidslockAllStates/unlockAllStates. Instead, when parallelism is enabled, it directly locks and unlocks the target channel.lockAllStates/unlockAllStatesusechannel.selectLocked, and that state is protected bychanSelectLock. Therefore, simply skippingchanSelectLockwhile still usinglockAllStates/unlockAllStatesis not safe. The fast path avoids both.Background
#4974 reports a freeze on RP2040 with
-scheduler=coreswhen using channels, goroutines, and timers together.While investigating this issue, I found that a repeated single-channel select send with a default case can reproduce a freeze on RP2040/RP2350 with
-scheduler=cores.The minimized reproducer repeatedly sends from a timer goroutine using this form:
A receiver goroutine continuously receives from the same channel.
At the time of freeze, the last report showed:
So the issue did not appear to be caused by entering the default branch. It looked related to the successful send path through
chanSelectand the receiver wakeup path.Reproducer
I used
a03_nonblocking_select_send_large_buffer_receiver.goas a minimized reproducer.Details
The important part is this single-state default select send:
This form uses
runtime.chanSelectand reproduced the freeze on RP2040/RP2350 with-scheduler=cores.At the time of freeze, the report showed:
This suggests that the channel values were not being lost. Instead, progress appeared to stop around the successful
chanSelectsend path and receiver wakeup.Control case
I also used
a06_two_state_select_send_full_case.goas a multi-state select control.Details
The important part is:
otherChis pre-filled, so the second send case should normally not be selected.This test is intended to check that the existing multi-state select path is not broken. With this PR, multi-state selects still use the existing
chanSelectLockpath.Investigation
I first tried skipping only
chanSelectLockfor single-state selects. However, while still usinglockAllStates/unlockAllStates, the #4974 reproducer hit this panic:This happened because
lockAllStates/unlockAllStatesusechannel.selectLocked, and that state is protected bychanSelectLock.Therefore, this PR adds a dedicated fast path for non-blocking single-state selects. This path avoids both
chanSelectLockandlockAllStates/unlockAllStates, while still directly locking the channel when parallelism is enabled.Validation
RP2040 /
-scheduler=coresa03single-state default select reproducer: 3/3 completeda06multi-state select control: 3/3 completedRP2040 /
-scheduler=tasksa03single-state default select reproducer: 1/1 completeda06multi-state select control: 1/1 completedRP2350 /
-scheduler=coresa03single-state default select reproducer: 3/3 completeda06multi-state select control: 3/3 completedRP2350 /
-scheduler=tasksa03single-state default select reproducer: 1/1 completeda06multi-state select control: 1/1 completedBefore this change
a03reproducer repeatedly froze on RP2040.a03reproducer also reproduced the freeze on RP2350.Notes
This change only adds a fast path for non-blocking single-state selects.
Multi-state selects continue to use the existing
chanSelectLockpath, so the existing protection against deadlocks from locking multiple channels in different orders is preserved.The fast path still directly locks and unlocks the target channel when parallelism is enabled, so channel-level protection is preserved.