Skip to content

sync: prevent RWMutex deadlocks with queued readers - #5697

Draft
yohimik wants to merge 1 commit into
tinygo-org:devfrom
yohimik:upstream-pr/sync-rwmutex
Draft

yohimik wants to merge 1 commit into
tinygo-org:devfrom
yohimik:upstream-pr/sync-rwmutex

Conversation

@yohimik

@yohimik yohimik commented Sep 17, 2026

Copy link
Copy Markdown

Addresses #5692. A writer can remain blocked when readers queue behind it. A released reader can also lose its wakeup. Separate the reader counts and transfer the lock with counting semaphores.

The two regression tests cover both failures. They reproduced the bug on official v0.42.0 on 2 September 2026. Concurrent process work in #5699 needs this fix.

Replaces #5630, closed on 17 September 2026 with a request for an issue first. Based on dev at 93940cb6. Patches are unchanged after rebase. Formatting and whitespace checks pass. New runtime tests and CI are pending.

RWMutex counts the readers that hold the lock and the readers that queue
behind a waiting writer in one number, and both sides wait on a predicate over
that number. Two interleavings stop the program permanently.

A writer waits until the count shows no readers at all. A reader that arrives
during that wait joins the same count, so the last holder of the lock no
longer sees the condition that wakes the writer.

A reader that Unlock releases reads the count again instead of an acquire. A
writer that arrives in between changes the base of the count, so the reader
goes back to sleep after its wakeup is spent, while that writer waits for it.

Use the split that the standard library uses. A writer records how many
readers it finds and waits only for those, so later readers cannot starve it.
Counting semaphores hand the lock over, so a released waiter holds the lock
and does not test a value again that a third party can change back.
task.Semaphore cannot do this, because one Post does nothing when there are
several waiters, so the file gets a small futex semaphore that can.

Ordinary code reaches this. syscall.ForkLock is an RWMutex, os.Pipe read-locks
it and os.StartProcess write-locks it, so a program that starts processes and
makes pipes at the same time can stop.

The two new tests fail on the current code and pass with this change.
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