GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Fix _at_fork_reinit to write INIT directly instead of calling unlock() by youknowone · Pull Request #7312 · RustPython/RustPython Conversation
|
📝 WalkthroughWalkthroughAdds Unix-only fork-safety primitives that reset mutex state in the child after fork: a low-level routine to zero a PyMutex, an import-lock reinitialization path invoked post-fork, and changes to thread lock reinit handlers to atomically reset mutexes instead of unlocking. Changes
Sequence Diagram(s)sequenceDiagram
participant Parent as Parent Process
participant OS as Operating System
participant Child as Child Process
participant ImpLock as Import Lock
participant ThreadLocks as Thread Locks
Parent->>OS: fork()
OS->>Child: child process created
Child->>Child: py_os_after_fork_child()
Child->>ImpLock: reinit_imp_lock_after_fork()
ImpLock->>ImpLock: inspect owner (dead parent thread?)
ImpLock->>ImpLock: reinit_mutex_after_fork() (zero raw mutex)
Child->>ThreadLocks: run _at_fork_reinit handlers
ThreadLocks->>ThreadLocks: atomic swap RawMutex::INIT
Child->>Child: resume execution
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/vm/src/stdlib/imp.rs`:
- Around line 47-64: The unsafe reinit_after_fork() inspects and zeroes IMP_LOCK
via layout-dependent casts to AtomicUsize/bytes; replace this with the safer
pattern used in RLock::after_fork_child: set IMP_LOCK to RawRMutex::INIT
atomically (e.g., via AtomicCell::swap) instead of casting/ write_bytes, or else
use the public RawRMutex methods (is_locked()/is_owned_by_current_thread()) to
decide whether to reset via the public API; update reinit_after_fork to avoid
direct field access of RawRMutex and rely on AtomicCell::swap(RawRMutex::INIT)
or public methods to reset state.
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
crates/common/src/lock.rscrates/vm/src/stdlib/imp.rscrates/vm/src/stdlib/posix.rscrates/vm/src/stdlib/thread.rs
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| pub(crate) unsafe fn reinit_after_fork() { | ||
| use core::sync::atomic::{AtomicUsize, Ordering}; | ||
|
|
||
| unsafe { | ||
| // RawReentrantMutex layout: owner: AtomicUsize at offset 0 | ||
| let owner_ptr = &IMP_LOCK as *const RawRMutex as *const AtomicUsize; | ||
| let owner = (*owner_ptr).load(Ordering::Relaxed); | ||
|
|
||
| if owner != 0 { | ||
| let current = rustpython_common::lock::current_thread_id().get(); | ||
| if owner != current { | ||
| // Held by a dead thread — reset to unlocked | ||
| let ptr = &IMP_LOCK as *const RawRMutex as *mut u8; | ||
| core::ptr::write_bytes(ptr, 0, core::mem::size_of::<RawRMutex>()); | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
🏁 Script executed:
cd /tmp && find . -name "Cargo.lock" -o -name "Cargo.toml" | head -5
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# First, let's examine the actual code in the file cat -n crates/vm/src/stdlib/imp.rs | head -75
Repository: RustPython/RustPython
Length of output: 2898
🏁 Script executed:
# Check lock_api dependency version grep -A5 "lock_api" Cargo.lock | head -20
Repository: RustPython/RustPython
Length of output: 457
🏁 Script executed:
# Check the RawRMutex API available in lock_api python3 <<'EOF' import subprocess import re # Get lock_api version from Cargo.lock result = subprocess.run(['grep', '-A2', 'name = "lock_api"', 'Cargo.lock'], capture_output=True, text=True) lock_api_info = result.stdout # Extract version version_match = re.search(r'version = "([^"]+)"', lock_api_info) if version_match: version = version_match.group(1) print(f"lock_api version found: {version}") else: print("Could not find lock_api version") EOF
Repository: RustPython/RustPython
Length of output: 95
🏁 Script executed:
# Check if RawReentrantMutex has layout specification rg -A5 "struct RawReentrantMutex" crates/
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Search for how IMP_LOCK is used elsewhere in the codebase rg "IMP_LOCK" crates/vm/src/stdlib/
Repository: RustPython/RustPython
Length of output: 698
🏁 Script executed:
# Check lock_api imports and usage patterns rg "use.*lock_api" crates/vm/src/
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check where RawRMutex is defined rg "type RawRMutex|struct RawRMutex" crates/ --type rust
Repository: RustPython/RustPython
Length of output: 162
🏁 Script executed:
# Check the thread module to see RawRMutex definition cat -n crates/vm/src/stdlib/thread.rs | head -100
Repository: RustPython/RustPython
Length of output: 4361
🏁 Script executed:
# Look for RawRMutex import in stdlib rg -B5 -A5 "RawRMutex" crates/vm/src/stdlib/thread.rs | head -40
Repository: RustPython/RustPython
Length of output: 1279
🏁 Script executed:
# Check lock_api crate documentation and source for RawReentrantMutex repr
web_search
lock_api 0.4.14 RawReentrantMutex struct layout reprRepository: RustPython/RustPython
Length of output: 162
🏁 Script executed:
# Check if the proposed fix's is_owned_by_current_thread method exists rg "is_owned_by_current_thread|owned_by" crates/
Repository: RustPython/RustPython
Length of output: 229
🌐 Web query:
lock_api 0.4.14 RawReentrantMutex documentation site:docs.rs
💡 Result:
lock_api 0.4.14 docs.rs page for RawReentrantMutex:
Crate landing page (same version on docs.rs):
Sources: [1] [2]
🏁 Script executed:
# Check if is_owned_by_current_thread exists on RawRMutex in the actual code rg -B2 -A10 "fn is_owned_by_current_thread" crates/
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Search for the parking_lot RawReentrantMutex trait and methods python3 <<'EOF' import subprocess import json # Check what methods are available on RawRMutex in the current codebase result = subprocess.run( ['rg', '-A30', 'pub type RawRMutex', 'crates/vm/src/stdlib/thread.rs'], capture_output=True, text=True ) print("RawRMutex type definition context:") print(result.stdout) # Now check all calls to IMP_LOCK methods result2 = subprocess.run( ['rg', 'IMP_LOCK\\.', 'crates/vm/src/stdlib/imp.rs'], capture_output=True, text=True ) print("\nAll IMP_LOCK method calls:") print(result2.stdout) EOF
Repository: RustPython/RustPython
Length of output: 1236
🏁 Script executed:
# Check parking_lot documentation for RawReentrantMutex methods available python3 <<'EOF' # Try to find the actual parking_lot source or check if methods exist import subprocess # Search for method signatures that might give us hints about available API result = subprocess.run( ['rg', '-B5', '-A15', 'impl.*RawReentrantMutex', 'crates/', '--type', 'rust'], capture_output=True, text=True, timeout=10 ) if result.stdout: print("RawReentrantMutex implementations found:") print(result.stdout[:2000]) else: print("No local implementations found - using parking_lot from deps") # Check if the code actually compiles - look for any compilation notes result2 = subprocess.run( ['grep', '-r', 'is_owned_by_current_thread', 'crates/vm/src/stdlib/'], capture_output=True, text=True ) print("\nis_owned_by_current_thread usage:") print(result2.stdout) EOF
Repository: RustPython/RustPython
Length of output: 326
🏁 Script executed:
# Examine the actual implementation that calls is_owned_by_current_thread rg -B5 -A5 "is_owned_by_current_thread" crates/vm/src/stdlib/thread.rs
Repository: RustPython/RustPython
Length of output: 479
🏁 Script executed:
# Check if there's a public method to reset/reinit a RawReentrantMutex python3 <<'EOF' import subprocess # Let's look at what methods are actually available on RawReentrantMutex # by checking parking_lot or lock_api documentation patterns # First, check all uses of write_bytes in the codebase to understand intent result = subprocess.run( ['rg', 'write_bytes|write\(ptr', 'crates/', '--type', 'rust', '-B2', '-A2'], capture_output=True, text=True ) print("All write_bytes/write(ptr) patterns:") print(result.stdout[:2000] if result.stdout else "None found") # Check if there's any method to check owned_by on RawReentrantMutex result2 = subprocess.run( ['rg', 'owner|owned', 'crates/vm/src/stdlib/thread.rs', '-B1', '-A1'], capture_output=True, text=True ) print("\n\nOwner references in thread.rs:") print(result2.stdout) EOF
Repository: RustPython/RustPython
Length of output: 4359
🏁 Script executed:
# Check the actual lock_api source by inspecting what's available in docs python3 <<'EOF' import subprocess import re # The key question: Does RawReentrantMutex have a public contract around resetting after fork? # Let's check the parking_lot version in use result = subprocess.run( ['grep', '-A5', 'parking_lot', 'Cargo.lock'], capture_output=True, text=True ) print("parking_lot version info:") print(result.stdout[:1500]) # Now check if there's any existing fork-handling code that resets mutexes result2 = subprocess.run( ['rg', 'fork|reinit', 'crates/', '--type', 'rust', '-l'], capture_output=True, text=True ) print("\n\nFiles mentioning fork/reinit:") print(result2.stdout) EOF
Repository: RustPython/RustPython
Length of output: 1440
🏁 Script executed:
# Check the actual semantics: What is RawReentrantMutex::INIT and can we use it? python3 <<'EOF' import subprocess # Search for how RawRMutex::INIT is defined or used result = subprocess.run( ['rg', 'RawRMutex::INIT|RawReentrantMutex::INIT', 'crates/', '--type', 'rust', '-B2', '-A2'], capture_output=True, text=True ) print("RawRMutex::INIT usage patterns:") print(result.stdout) EOF
Repository: RustPython/RustPython
Length of output: 1204
🏁 Script executed:
# Critical check: Does the current unsafe code actually match the assumption? # Let's see if there's any test or documentation about this layout assumption python3 <<'EOF' import subprocess # Look for any comments or tests about reentrant mutex layout result = subprocess.run( ['rg', '-i', 'atomicusize.*offset|owner.*offset|layout', 'crates/', '--type', 'rust'], capture_output=True, text=True ) print("Layout documentation or comments:") print(result.stdout if result.stdout else "None found") # Check parking_lot Cargo.lock entry more carefully result2 = subprocess.run( ['grep', '-B2', '-A20', 'name = "parking_lot"$', 'Cargo.lock'], capture_output=True, text=True ) print("\n\nparking_lot full entry:") print(result2.stdout) EOF
Repository: RustPython/RustPython
Length of output: 10353
🏁 Script executed:
# Check the RLock::after_fork implementation more closely rg -B5 -A20 "fn after_fork" crates/vm/src/stdlib/thread.rs
Repository: RustPython/RustPython
Length of output: 1242
🏁 Script executed:
# Verify what methods RawReentrantMutex actually exposes in lock_api 0.4.14 python3 <<'EOF' import subprocess # Check if there's a method to check ownership in the current code usage result = subprocess.run( ['rg', '-B3', '-A3', 'is_locked|is_owned', 'crates/vm/src/stdlib/imp.rs'], capture_output=True, text=True ) print("Usage of is_locked/is_owned in imp.rs:") print(result.stdout) EOF
Repository: RustPython/RustPython
Length of output: 524
🏁 Script executed:
# Final verification: Check if write() can be used on a static, and what the correct pattern is python3 <<'EOF' import subprocess # Check the AtomicCell usage pattern in RLock result = subprocess.run( ['rg', '-B5', '-A10', 'AtomicCell.*RawRMutex', 'crates/vm/src/stdlib/thread.rs'], capture_output=True, text=True ) print("AtomicCell pattern for RawRMutex reset:") print(result.stdout) # Also check if there's a public method on RawReentrantMutex to "unlock all" result2 = subprocess.run( ['rg', 'lock|unlock', 'crates/vm/src/stdlib/imp.rs', '-B1', '-A1'], capture_output=True, text=True ) print("\n\nLock/unlock operations in imp.rs:") print(result2.stdout) EOF
Repository: RustPython/RustPython
Length of output: 2121
Address unsafe memory access that depends on undocumented RawReentrantMutex layout.
Lines 52–60 cast IMP_LOCK to AtomicUsize assuming the owner field is at offset 0, then zero-fill the entire struct with write_bytes(). Both assumptions rely on private lock_api internals and will break if the struct layout changes.
A safer approach is already used elsewhere in the codebase (RLock::after_fork_child in thread.rs): use AtomicCell::swap() with RawRMutex::INIT instead of unsafe layout assumptions. Alternatively, use the public API methods (is_locked() and is_owned_by_current_thread()) to safely check state without inspecting internal fields.
The current implementation violates Rust best practices for memory safety and maintainability.
Verify each finding against the current code and only fix it if needed.
In `@crates/vm/src/stdlib/imp.rs` around lines 47 - 64, The unsafe
reinit_after_fork() inspects and zeroes IMP_LOCK via layout-dependent casts to
AtomicUsize/bytes; replace this with the safer pattern used in
RLock::after_fork_child: set IMP_LOCK to RawRMutex::INIT atomically (e.g., via
AtomicCell::swap) instead of casting/ write_bytes, or else use the public
RawRMutex methods (is_locked()/is_owned_by_current_thread()) to decide whether
to reset via the public API; update reinit_after_fork to avoid direct field
access of RawRMutex and rely on AtomicCell::swap(RawRMutex::INIT) or public
methods to reset state.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
unlock() goes through unlock_slow() which accesses parking_lot's global hash table to unpark waiters. After fork(), this hash table contains stale entries from dead parent threads, making unlock_slow() unsafe. Writing INIT directly bypasses parking_lot internals entirely.
The import lock is a ReentrantMutex that was never reinit'd after fork(). If a parent thread held it during fork, the child would deadlock on any import. Only reset if the owner is a dead thread; if the surviving thread held it, normal unlock still works.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
crates/vm/src/stdlib/imp.rs (1)
47-55:⚠️ Potential issue | 🟠 Major
transmute(&IMP_LOCK)toAtomicCell<RawRMutex>is still representation-dependent unsafe.This cast assumes undocumented compatibility between
RawRMutexandAtomicCell<RawRMutex>. If either crate changes layout/aliasing semantics, this can become UB in a critical fork-reinit path.Is it sound Rust to transmute `&T` into `&crossbeam_utils::atomic::AtomicCell<T>` and call `swap`? Does `AtomicCell<T>` guarantee representation compatibility with `T` across versions?As per coding guidelines "Follow Rust best practices for error handling and memory management".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/stdlib/imp.rs` around lines 47 - 55, The current unsafe cast from &IMP_LOCK to &crossbeam_utils::atomic::AtomicCell<RawRMutex> in reinit_after_fork is representation-dependent and unsound; instead, make the lock representation explicitly an AtomicCell so you don't transmute: change the static IMP_LOCK (or introduce a new static wrapper) to be a crossbeam_utils::atomic::AtomicCell<RawRMutex> and then call AtomicCell::swap directly (e.g., use IMP_LOCK.swap(RawRMutex::INIT) inside reinit_after_fork), or if changing the static type is not possible, create a properly typed AtomicCell static alias that is initialized from the same initial value and use that—avoid transmute of &IMP_LOCK to AtomicCell entirely and refer to IMP_LOCK, reinit_after_fork, RawRMutex, and AtomicCell::swap in the fix.
crates/vm/src/stdlib/imp.rs (1)
60-63: Add a short# Safetydoc for the crate-visible unsafe wrapper.Document caller obligations directly on
reinit_imp_lock_after_fork()to keep the fork contract explicit at the call boundary.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/stdlib/imp.rs` around lines 60 - 63, Add a short `# Safety` doc on the crate-visible unsafe wrapper `reinit_imp_lock_after_fork()` that states the caller obligations: that the function is unsafe because it must only be invoked in the post-fork child when no other threads are running (or otherwise guarantee single-threadedness), that it must not be called from contexts that invoke non-async-signal-safe operations, and that it simply forwards to `lock::reinit_after_fork()` to reinitialize internal locks; keep it concise and place it immediately above the `pub(crate) unsafe fn reinit_imp_lock_after_fork()` declaration.
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/common/src/lock.rs`:
- Around line 72-79: reinit_mutex_after_fork currently performs raw byte writes
assuming the raw mutex is at offset 0 and all-zero equals initial state; update
it to call the unsafe Mutex::raw() accessor on the PyMutex to get a &RawMutex
and reinitialize by writing RawMutex::INIT into that memory (use
core::ptr::write to overwrite the raw lock with RawMutex::INIT) so you no longer
depend on layout or zero bytes; target the function reinit_mutex_after_fork and
the PyMutex type and use RawMutex::INIT (or the corresponding raw type's INIT)
when performing the write.
---
Duplicate comments:
In `@crates/vm/src/stdlib/imp.rs`:
- Around line 47-55: The current unsafe cast from &IMP_LOCK to
&crossbeam_utils::atomic::AtomicCell<RawRMutex> in reinit_after_fork is
representation-dependent and unsound; instead, make the lock representation
explicitly an AtomicCell so you don't transmute: change the static IMP_LOCK (or
introduce a new static wrapper) to be a
crossbeam_utils::atomic::AtomicCell<RawRMutex> and then call AtomicCell::swap
directly (e.g., use IMP_LOCK.swap(RawRMutex::INIT) inside reinit_after_fork), or
if changing the static type is not possible, create a properly typed AtomicCell
static alias that is initialized from the same initial value and use that—avoid
transmute of &IMP_LOCK to AtomicCell entirely and refer to IMP_LOCK,
reinit_after_fork, RawRMutex, and AtomicCell::swap in the fix.
---
Nitpick comments:
In `@crates/vm/src/stdlib/imp.rs`:
- Around line 60-63: Add a short `# Safety` doc on the crate-visible unsafe
wrapper `reinit_imp_lock_after_fork()` that states the caller obligations: that
the function is unsafe because it must only be invoked in the post-fork child
when no other threads are running (or otherwise guarantee single-threadedness),
that it must not be called from contexts that invoke non-async-signal-safe
operations, and that it simply forwards to `lock::reinit_after_fork()` to
reinitialize internal locks; keep it concise and place it immediately above the
`pub(crate) unsafe fn reinit_imp_lock_after_fork()` declaration.
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
crates/common/src/lock.rscrates/vm/src/stdlib/imp.rscrates/vm/src/stdlib/posix.rscrates/vm/src/stdlib/thread.rs
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| pub unsafe fn reinit_mutex_after_fork<T: ?Sized>(mutex: &PyMutex<T>) { | ||
| // lock_api::Mutex<R, T> layout: raw R at offset 0, then UnsafeCell<T>. | ||
| // Zeroing R resets to unlocked for both parking_lot::RawMutex (AtomicU8) | ||
| // and RawCellMutex (Cell<bool>). | ||
| unsafe { | ||
| let ptr = mutex as *const PyMutex<T> as *mut u8; | ||
| core::ptr::write_bytes(ptr, 0, core::mem::size_of::<RawMutex>()); | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
🏁 Script executed:
cd crates/common && pwd && cat Cargo.toml | grep -A 5 "\[dependencies\]" | head -20
Repository: RustPython/RustPython
Length of output: 255
🏁 Script executed:
cat -n crates/common/src/lock.rs | sed -n '60,85p'
Repository: RustPython/RustPython
Length of output: 1093
🏁 Script executed:
rg "type PyMutex|struct PyMutex|type RawMutex" crates/common/src/lock.rs -B 2 -A 3Repository: RustPython/RustPython
Length of output: 421
🏁 Script executed:
rg "RawMutex\|RawCellMutex\|parking_lot\|lock_api" crates/common/src/lock.rs | head -20
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
cat crates/common/Cargo.toml
Repository: RustPython/RustPython
Length of output: 1443
🏁 Script executed:
rg "type RawMutex\|type RawCellMutex\|enum RawMutex" crates/common/src/lock.rs -B 2 -A 3Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
rg "thread_mutex|RawMutex\|RawCellMutex" crates/common/src/lock.rs | head -30
Repository: RustPython/RustPython
Length of output: 107
🏁 Script executed:
cat -n crates/common/src/lock.rs | head -60Repository: RustPython/RustPython
Length of output: 3139
🏁 Script executed:
cat crates/common/src/lock/cell_lock.rs
Repository: RustPython/RustPython
Length of output: 5019
🏁 Script executed:
rg "impl.*Mutex|fn raw\(" crates/common/src/lock.rs -B 2 -A 3Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
web_search
lock_api 0.4 Mutex raw() method API documentationRepository: RustPython/RustPython
Length of output: 250
🏁 Script executed:
python3 << 'EOF' import subprocess import json # Check if lock_api 0.4 has raw() method by looking at what methods are available # We'll search crates.io or offline docs if available result = subprocess.run( ["rg", "lock_api", "Cargo.lock", "-A 5"], capture_output=True, text=True, cwd="/home/jailuser/git" ) print("=== Cargo.lock lock_api info ===") print(result.stdout[:1000] if result.stdout else "Not found in Cargo.lock") EOF
Repository: RustPython/RustPython
Length of output: 121
🏁 Script executed:
web_search lock_api 0.4 Mutex documentation raw method
Repository: RustPython/RustPython
Length of output: 162
🏁 Script executed:
rg "raw\(\)" crates/ --type rs | head -20
Repository: RustPython/RustPython
Length of output: 91
🏁 Script executed:
python3 << 'EOF' # Check lock_api docs online for 0.4.x import subprocess result = subprocess.run( ["curl", "-s", "https://docs.rs/lock_api/0.4/lock_api/struct.Mutex.html"], capture_output=True, text=True, timeout=5 ) # Search for "raw()" in the HTML if "raw()" in result.stdout: print("Found raw() method") # Extract relevant section import re matches = re.findall(r'raw\(\)[^<]*<[^>]*>[^<]*', result.stdout) for match in matches[:3]: print(match) else: print("No raw() method found in docs") # Show what methods are listed if "pub fn" in result.stdout: methods = re.findall(r'pub fn (\w+)', result.stdout) print("Available methods:", methods[:10]) EOF
Repository: RustPython/RustPython
Length of output: 94
🏁 Script executed:
# Check if there's documentation or examples in the codebase showing how to access raw lock rg "Mutex.*raw\|\.raw\(\)" crates/ --type rs -B 2 -A 2
Repository: RustPython/RustPython
Length of output: 91
🏁 Script executed:
# Search for actual usage patterns of PyMutex in fork context rg "fork\|reinit_mutex" crates/ --type rs -B 3 -A 3
Repository: RustPython/RustPython
Length of output: 91
🌐 Web query:
lock_api 0.4 Mutex raw() method API documentation
💡 Result:
In lock_api 0.4.x, Mutex exposes this method:
pub unsafe fn raw(&self) -> &RR) wrapped by lock_api::Mutex<R, T>. (docs.rs)unsafe: Because having access to R can let you unlock the mutex while a MutexGuard still exists, violating Mutex’s safety invariants. (docs.rs)use lock_api::RawMutex; in scope to call trait methods on the returned raw mutex. (docs.rs)Citations:
🏁 Script executed:
rg "raw\(\)" crates/ | grep -E "\.rs:" | head -20
Repository: RustPython/RustPython
Length of output: 1770
🏁 Script executed:
rg "fork|reinit_mutex" crates/ | head -30
Repository: RustPython/RustPython
Length of output: 2864
🏁 Script executed:
# Search lock_api source or documentation for the Mutex type signature rg "pub struct Mutex|impl.*Mutex" crates/ | head -20
Repository: RustPython/RustPython
Length of output: 2400
🏁 Script executed:
# Look at what imports are available from lock_api rg "from lock_api|use lock_api" crates/common/src/lock.rs
Repository: RustPython/RustPython
Length of output: 80
🏁 Script executed:
# Check usage of reinit_mutex_after_fork rg "reinit_mutex_after_fork" crates/ -B 2 -A 2
Repository: RustPython/RustPython
Length of output: 488
🏁 Script executed:
# Check if reinit_after_fork exists in lock module rg "pub.*fn reinit_after_fork\|reinit_mutex_after_fork\|reinit_imp_lock" crates/common/src/lock.rs -B 3 -A 5
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check RawMutex trait definition and INIT constant usage rg "trait RawMutex|const INIT" crates/common/src/lock.rs crates/common/src/lock/cell_lock.rs | head -20
Repository: RustPython/RustPython
Length of output: 265
Use Mutex::raw() and RawMutex::INIT instead of layout-dependent byte writes when reinitializing PyMutex after fork.
The current implementation relies on two undocumented assumptions: (1) lock_api::Mutex places the raw lock at offset 0, and (2) all-zero bytes equal RawMutex::INIT. This is fragile across lock_api/parking_lot version changes.
lock_api 0.4.x provides pub unsafe fn raw(&self) -> &R to access the raw mutex and both parking_lot::RawMutex and RawCellMutex define const INIT. Use these instead:
pub unsafe fn reinit_mutex_after_fork<T: ?Sized>(mutex: &PyMutex<T>) {
unsafe {
- let ptr = mutex as *const PyMutex<T> as *mut u8;
- core::ptr::write_bytes(ptr, 0, core::mem::size_of::<RawMutex>());
+ let raw = mutex.raw() as *const RawMutex as *mut RawMutex;
+ core::ptr::write(raw, RawMutex::INIT);
}
}Verify each finding against the current code and only fix it if needed.
In `@crates/common/src/lock.rs` around lines 72 - 79, reinit_mutex_after_fork
currently performs raw byte writes assuming the raw mutex is at offset 0 and
all-zero equals initial state; update it to call the unsafe Mutex::raw()
accessor on the PyMutex to get a &RawMutex and reinitialize by writing
RawMutex::INIT into that memory (use core::ptr::write to overwrite the raw lock
with RawMutex::INIT) so you no longer depend on layout or zero bytes; target the
function reinit_mutex_after_fork and the PyMutex type and use RawMutex::INIT (or
the corresponding raw type's INIT) when performing the write.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
…() (RustPython#7312) * Fix _at_fork_reinit to write INIT directly instead of calling unlock() unlock() goes through unlock_slow() which accesses parking_lot's global hash table to unpark waiters. After fork(), this hash table contains stale entries from dead parent threads, making unlock_slow() unsafe. Writing INIT directly bypasses parking_lot internals entirely. * Add import lock (IMP_LOCK) reinit after fork The import lock is a ReentrantMutex that was never reinit'd after fork(). If a parent thread held it during fork, the child would deadlock on any import. Only reset if the owner is a dead thread; if the surviving thread held it, normal unlock still works.
…() (#7312) * Fix _at_fork_reinit to write INIT directly instead of calling unlock() unlock() goes through unlock_slow() which accesses parking_lot's global hash table to unpark waiters. After fork(), this hash table contains stale entries from dead parent threads, making unlock_slow() unsafe. Writing INIT directly bypasses parking_lot internals entirely. * Add import lock (IMP_LOCK) reinit after fork The import lock is a ReentrantMutex that was never reinit'd after fork(). If a parent thread held it during fork, the child would deadlock on any import. Only reset if the owner is a dead thread; if the surviving thread held it, normal unlock still works.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.