Skip to content

Commit ecb9b4c

Browse files
committed
fix
1 parent d3ba6b4 commit ecb9b4c

File tree

2 files changed

+28
-11
lines changed

2 files changed

+28
-11
lines changed

crates/vm/src/stdlib/posix.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,8 +677,13 @@ pub mod module {
677677

678678
/// Warn if forking from a multi-threaded process
679679
fn warn_if_multi_threaded(name: &str, vm: &VirtualMachine) {
680-
// Check threading._active and threading._limbo to count threads
681-
let threading = match vm.import("threading", 0) {
680+
// Only check threading if it was already imported
681+
// Avoid vm.import() which can execute arbitrary Python code in the fork path
682+
let threading = match vm
683+
.sys_module
684+
.get_attr("modules", vm)
685+
.and_then(|m| m.get_item("threading", vm))
686+
{
682687
Ok(m) => m,
683688
Err(_) => return,
684689
};

crates/vm/src/stdlib/thread.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,13 @@ pub(crate) mod _thread {
485485
};
486486

487487
match handle_to_join {
488-
Some((inner, done_event)) => {
488+
Some((_inner, done_event)) => {
489489
// Wait for this thread to finish (infinite timeout)
490+
// Only check done flag to avoid lock ordering issues
491+
// (done_event lock vs inner lock)
490492
let (lock, cvar) = &*done_event;
491493
let mut done = lock.lock();
492-
while !*done && inner.lock().state != ThreadHandleState::Done {
494+
while !*done {
493495
cvar.wait(&mut done);
494496
}
495497
}
@@ -534,11 +536,14 @@ pub(crate) mod _thread {
534536

535537
// Store the main thread ident (initialized at VM startup)
536538
static MAIN_THREAD_IDENT: AtomicCell<u64> = AtomicCell::new(0);
539+
static MAIN_THREAD_INIT: std::sync::Once = std::sync::Once::new();
537540

538541
/// Initialize the main thread ident. Should be called once at interpreter startup.
539542
pub fn init_main_thread_ident() {
540-
let ident = get_ident();
541-
MAIN_THREAD_IDENT.store(ident);
543+
MAIN_THREAD_INIT.call_once(|| {
544+
let ident = get_ident();
545+
MAIN_THREAD_IDENT.store(ident);
546+
});
542547
}
543548

544549
/// ExceptHookArgs - simple class to hold exception hook arguments
@@ -687,9 +692,11 @@ pub(crate) mod _thread {
687692

688693
impl Drop for LocalGuard {
689694
fn drop(&mut self) {
690-
// eprintln!("[DEBUG] LocalGuard::drop called for thread {:?}", self.thread_id);
691695
if let Some(local_data) = self.local.upgrade() {
692-
local_data.data.lock().remove(&self.thread_id);
696+
// Remove from map while holding the lock, but drop the value
697+
// outside the lock to prevent deadlock if __del__ accesses _local
698+
let removed = local_data.data.lock().remove(&self.thread_id);
699+
drop(removed);
693700
}
694701
}
695702
}
@@ -961,14 +968,15 @@ pub(crate) mod _thread {
961968
}
962969

963970
// Wait for thread completion using Condvar (supports timeout)
971+
// Loop to handle spurious wakeups
964972
let (lock, cvar) = &*self.done_event;
965973
let mut done = lock.lock();
966974

967-
if !*done {
975+
while !*done {
968976
if let Some(timeout) = timeout_duration {
969977
let result = cvar.wait_for(&mut done, timeout);
970-
if result.timed_out() {
971-
// Timeout occurred, return without joining
978+
if result.timed_out() && !*done {
979+
// Timeout occurred and done is still false
972980
return Ok(());
973981
}
974982
} else {
@@ -1100,6 +1108,10 @@ pub(crate) mod _thread {
11001108
unsafe { lock.mu.unlock() };
11011109
}
11021110
}
1111+
1112+
// Clean up thread-local data while VM context is still active
1113+
cleanup_thread_local_data();
1114+
11031115
vm_state.thread_count.fetch_sub(1);
11041116
}
11051117

0 commit comments

Comments
 (0)