Repository navigation
Show stdin traceback lines; omit StopIteration None args #8778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
35dd0f7
80990bf
9578f01
2df95f9
04e6f7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1733,6 +1733,124 @@ for _ in range(40): | |
| worker.join().expect("worker panicked"); | ||
| } | ||
|
|
||
| /// `STOP_BIT` is process-wide and `start_the_world` in any interpreter | ||
| /// clears it. A worker must still park from its own `stop_requested`. | ||
| #[cfg(all(feature = "threading", feature = "rustpython-compiler"))] | ||
| #[test] | ||
| fn stop_the_world_parks_when_global_stop_bit_is_cleared() { | ||
| use crate::compiler::Mode; | ||
| use alloc::sync::Arc; | ||
| use core::{ | ||
| sync::atomic::{AtomicBool, AtomicU64, Ordering}, | ||
| time::Duration, | ||
| }; | ||
|
|
||
| let main = Interpreter::without_stdlib(Default::default()); | ||
| let sub = main.create_subinterpreter(); | ||
| let sub_state = sub.enter(|vm| vm.state.clone()); | ||
|
|
||
| let progress = Arc::new(AtomicU64::new(0)); | ||
| let stop = Arc::new(AtomicBool::new(false)); | ||
| let flip = Arc::new(AtomicBool::new(true)); | ||
|
|
||
| let progress_worker = Arc::clone(&progress); | ||
| let stop_worker = Arc::clone(&stop); | ||
| let worker = sub.enter(|vm| { | ||
| let thread_vm = vm.new_thread(); | ||
| std::thread::spawn(move || { | ||
| thread_vm.run(|vm| { | ||
| let source = "x = 1 + 1\n"; | ||
| let code = vm | ||
| .compile(source, Mode::Exec, "<spin>") | ||
| .map_err(|err| err.into_pyexception(vm, Some(source))) | ||
| .unwrap(); | ||
| while !stop_worker.load(Ordering::Acquire) { | ||
| let scope = vm.new_scope_with_builtins(); | ||
| vm.run_code_obj(code.clone(), scope).unwrap(); | ||
| progress_worker.fetch_add(1, Ordering::Release); | ||
| } | ||
| }); | ||
| }) | ||
| }); | ||
|
|
||
| let deadline = std::time::Instant::now() + Duration::from_secs(10); | ||
| while progress.load(Ordering::Acquire) == 0 { | ||
| assert!( | ||
| std::time::Instant::now() < deadline, | ||
| "worker never started making progress" | ||
| ); | ||
| std::thread::yield_now(); | ||
| } | ||
|
|
||
| let flip_flag = Arc::clone(&flip); | ||
| let flicker_cycles = Arc::new(AtomicU64::new(0)); | ||
| let flicker_cycles_thread = Arc::clone(&flicker_cycles); | ||
| let flicker = std::thread::spawn(move || { | ||
| while flip_flag.load(Ordering::Acquire) { | ||
| crate::signal::set_stop_bit(); | ||
| crate::signal::clear_stop_bit(); | ||
| flicker_cycles_thread.fetch_add(1, Ordering::Release); | ||
|
Comment on lines
+1788
to
+1792
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '1720,1870p' crates/vm/src/vm/interpreter.rs
sed -n '3480,3530p' crates/vm/src/vm/mod.rs
rg -n "stop_requested_for_current_thread|set_stop_bit|clear_stop_bit|stop_the_world_parks_when_global" crates/vm/src/vmRepository: RustPython/RustPython Length of output: 8850 🏁 Script executed: #!/bin/bash
printf '%s\n' '--- changed hunk and file status ---'
git diff -- crates/vm/src/vm/interpreter.rs crates/vm/src/vm/mod.rs crates/vm/src/vm/thread.rs crates/vm/src/signal.rs
printf '%s\n' '--- thread stop-request implementation ---'
sed -n '760,930p' crates/vm/src/vm/thread.rs
printf '%s\n' '--- stop-the-world implementation ---'
sed -n '320,410p' crates/vm/src/vm/mod.rs
printf '%s\n' '--- STOP_BIT implementation and breaker ---'
rg -n -A20 -B12 'STOP_BIT|eval_breaker_pending|set_stop_bit|clear_stop_bit' crates/vm/srcRepository: RustPython/RustPython Length of output: 41045 🏁 Script executed: #!/bin/bash
printf '%s\n' '--- parent eval_breaker_tripped ---'
git show HEAD^:crates/vm/src/vm/mod.rs | sed -n '3485,3525p'
printf '%s\n' '--- current stop-the-world entry and wait ---'
sed -n '399,575p' crates/vm/src/vm/mod.rs
printf '%s\n' '--- parent test region ---'
git show HEAD^:crates/vm/src/vm/interpreter.rs | sed -n '1720,1845p'
printf '%s\n' '--- current thread registration/request fields ---'
rg -n -A25 -B15 'CURRENT_STOP_REQUESTED|CURRENT_THREAD_SLOT|stop_requested:|ThreadSlot' crates/vm/src/vm/thread.rsRepository: RustPython/RustPython Length of output: 42331 Make the regression condition deterministic. The flicker thread can set Add a focused test that sets the current thread's 🤖 Prompt for AI Agents |
||
| } | ||
| }); | ||
|
|
||
| while flicker_cycles.load(Ordering::Acquire) == 0 { | ||
| assert!( | ||
| std::time::Instant::now() < deadline, | ||
| "STOP_BIT flicker never ran" | ||
| ); | ||
| std::thread::yield_now(); | ||
| } | ||
|
|
||
| // Stop from a thread of its own so a hang fails the test instead of | ||
| // stalling the suite. Keep flickering until after the stop completes. | ||
| let (tx, rx) = std::sync::mpsc::channel(); | ||
| let stopper = std::thread::spawn(move || { | ||
| sub_state.stop_the_world.stop_the_world(&sub_state); | ||
| let stopped = tx.send(()); | ||
| (sub_state, stopped) | ||
| }); | ||
|
|
||
| let stopped = rx.recv_timeout(Duration::from_secs(10)); | ||
|
|
||
| let parked_at = progress.load(Ordering::Acquire); | ||
| std::thread::sleep(Duration::from_millis(50)); | ||
| let parked_after = progress.load(Ordering::Acquire); | ||
|
|
||
| // Release before joining: if the stop hung, stopping the worker lets | ||
| // its slot go away so the stopper can finish. | ||
| flip.store(false, Ordering::Release); | ||
| if stopped.is_err() { | ||
| stop.store(true, Ordering::Release); | ||
| } | ||
| flicker.join().expect("stop-bit flicker panicked"); | ||
| let (stop_state, sent) = stopper.join().expect("stopper panicked"); | ||
| if stopped.is_ok() { | ||
| stop_state.stop_the_world.start_the_world(&stop_state); | ||
| } | ||
| sent.expect("send"); | ||
| assert!( | ||
| stopped.is_ok(), | ||
| "stop-the-world hung while STOP_BIT was flickering" | ||
| ); | ||
| assert_eq!( | ||
| parked_after, parked_at, | ||
| "subinterpreter thread kept running after STOP_BIT was cleared" | ||
| ); | ||
|
|
||
| let resumed_from = progress.load(Ordering::Acquire); | ||
| let resume_deadline = std::time::Instant::now() + Duration::from_secs(10); | ||
| while progress.load(Ordering::Acquire) == resumed_from { | ||
| assert!( | ||
| std::time::Instant::now() < resume_deadline, | ||
| "worker never resumed after start_the_world" | ||
| ); | ||
| std::thread::yield_now(); | ||
| } | ||
|
|
||
| stop.store(true, Ordering::Release); | ||
| worker.join().expect("worker panicked"); | ||
| } | ||
|
|
||
| /// Entering a subinterpreter from inside the parent's `enter` must attach | ||
| /// the subinterpreter's thread slot (and detach the parent's). Otherwise the | ||
| /// thread runs the sub's bytecode with a DETACHED slot, and a collector | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🏁 Script executed:
Repository: RustPython/RustPython
Length of output: 4538
🏁 Script executed:
Repository: RustPython/RustPython
Length of output: 11417
🏁 Script executed:
sed -n '3088,3125p' crates/stdlib/src/socket.rsRepository: RustPython/RustPython
Length of output: 1262
Preserve the
sendalltotal timeout.sendallcomputes a remaining duration from its absolute deadline before callingsock_op_timeout_err. Ifsock_snapshot()is delayed, this code starts a newDeadlinewith that stale duration. The final write can then complete after thesendalldeadline, and the caller returns success without rechecking its outer deadline.Keep the caller-provided absolute deadline, or recheck the outer deadline before returning success.
🤖 Prompt for AI Agents