馃悰 fix(read-write): refuse cross-thread write release - #761
Merged
gaborbernat merged 1 commit intoOct 1, 2026
Merged
Conversation
feiiiiii5
pushed a commit
to feiiiiii5/filelock
that referenced
this pull request
Sep 30, 2026
gaborbernat
force-pushed
the
fix/read-write-release-thread-pin
branch
from
October 1, 2026 05:01
050d3f1 to
5e5725b
Compare
ReadWriteLock and SoftReadWriteLock pin a write lock to the thread that acquired it, but release() did not check the caller, so another thread could drop the holder's lock and let a second writer in. Check ownership under the lock the release takes, so a release waiting behind an acquisition sees the new owner. force=True and close() stay the cross-thread exits, SoftReadWriteLock lets its own heartbeat thread release for on_compromise, and the async wrappers use force=True since their executor may release on another worker.
gaborbernat
force-pushed
the
fix/read-write-release-thread-pin
branch
from
October 1, 2026 05:48
5e5725b to
3c4f3af
Compare
gaborbernat
enabled auto-merge (squash)
October 1, 2026 05:55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ReadWriteLockandSoftReadWriteLockpin a write lock to the thread that acquired it, butrelease()did not check the caller. Another thread could drop the holder's lock and let a second writer in while the holder still believed it held it.ReadWriteLockruns its connection withcheck_same_thread=False, so SQLite raised nothing either. 馃悰release()from a thread that does not own the write lock now raisesRuntimeErrorin both classes. The ownership check and the release run under one lock, so a release queued behind an in-flight acquisition checks the new owner instead of the empty state it saw before the acquisition finished.release(force=True)andclose()stay the deliberate cross-thread exits.SoftReadWriteLockalso lets the hold's own heartbeat thread release, sinceon_compromiseruns there.That queued
ReadWriteLockrelease used to succeed and now raises too. The async wrappers track ownership per task since #746, but their executor can run a release on a different worker than the acquire. That is the normal case forAsyncSoftReadWriteLockon the default executor, and the how-to shows the same multi-worker setup forAsyncReadWriteLock, so both wrappers release the backend withforce=True.