Skip to content

Commit bcdf58e

Browse files
committed
address review: keep sendall deadline; isolate STW test
Pass sendall's absolute Deadline through sock_op_timeout_err. Add a test that trips eval_breaker_tripped with STOP_BIT clear. Assisted-by: Grok:4.6
1 parent ea4e51d commit bcdf58e

3 files changed

Lines changed: 44 additions & 20 deletions

File tree

‎crates/stdlib/src/socket.rs‎

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -970,30 +970,23 @@ mod _socket {
970970
where
971971
F: FnMut() -> io::Result<R>,
972972
{
973-
let timeout = self.get_timeout().ok();
974-
self.sock_op_timeout_err(vm, wait_kind, timeout, f)
973+
let deadline = self.get_timeout().ok().map(Deadline::new);
974+
self.sock_op_timeout_err(vm, wait_kind, deadline, f)
975975
}
976976

977977
fn sock_op_timeout_err<F, R>(
978978
&self,
979979
vm: &VirtualMachine,
980980
wait_kind: SockWaitKind,
981-
timeout: Option<Duration>,
981+
deadline: Option<Deadline>,
982982
mut f: F,
983983
) -> Result<R, IoOrPyException>
984984
where
985985
F: FnMut() -> io::Result<R>,
986986
{
987-
// Start the deadline after the first snapshot, so lock/scheduling
988-
// delay is not subtracted from a short timeout before poll.
989-
let mut deadline = None;
990-
991987
loop {
992-
if timeout.is_some() || matches!(wait_kind, SockWaitKind::Connect) {
988+
if deadline.is_some() || matches!(wait_kind, SockWaitKind::Connect) {
993989
let sock = self.sock_snapshot()?;
994-
if deadline.is_none() {
995-
deadline = timeout.map(Deadline::new);
996-
}
997990
sock_wait_deadline(&sock, wait_kind, deadline.as_ref(), vm)?;
998991
}
999992

@@ -1006,7 +999,7 @@ mod _socket {
1006999
Err(e) => break e,
10071000
}
10081001
};
1009-
if timeout.is_some() && err.kind() == io::ErrorKind::WouldBlock {
1002+
if deadline.is_some() && err.kind() == io::ErrorKind::WouldBlock {
10101003
continue;
10111004
}
10121005
return Err(err.into());
@@ -1732,8 +1725,7 @@ mod _socket {
17321725
let mut buf_offset = 0;
17331726
// now we have like 3 layers of interrupt loop :)
17341727
while buf_offset < buf.len() {
1735-
let interval = deadline.as_ref().map(|d| d.time_until()).transpose()?;
1736-
self.sock_op_timeout_err(vm, SockWaitKind::Write, interval, || {
1728+
self.sock_op_timeout_err(vm, SockWaitKind::Write, deadline, || {
17371729
let subbuf = &buf[buf_offset..];
17381730
buf_offset += self.sock_snapshot()?.send_with_flags(subbuf, flags)?;
17391731
Ok(())
@@ -3043,6 +3035,7 @@ mod _socket {
30433035
vm.new_os_subtype_error(timeout(vm), None, msg)
30443036
}
30453037

3038+
#[derive(Copy, Clone)]
30463039
pub(crate) struct Deadline {
30473040
deadline: Instant,
30483041
}
@@ -3056,12 +3049,6 @@ mod _socket {
30563049
fn instant(&self) -> Instant {
30573050
self.deadline
30583051
}
3059-
fn time_until(&self) -> Result<Duration, IoOrPyException> {
3060-
self.deadline
3061-
.checked_duration_since(Instant::now())
3062-
// past the deadline already
3063-
.ok_or(IoOrPyException::Timeout)
3064-
}
30653052
}
30663053

30673054
static DEFAULT_TIMEOUT: AtomicCell<f64> = AtomicCell::new(-1.0);

‎crates/vm/src/vm/interpreter.rs‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,6 +1851,30 @@ for _ in range(40):
18511851
worker.join().expect("worker panicked");
18521852
}
18531853

1854+
/// Per-thread `stop_requested` must trip the eval breaker even when the
1855+
/// process-wide `STOP_BIT` is clear.
1856+
#[cfg(feature = "threading")]
1857+
#[test]
1858+
fn eval_breaker_tripped_when_stop_requested_and_stop_bit_clear() {
1859+
let interp = Interpreter::without_stdlib(Default::default());
1860+
interp.enter(|vm| {
1861+
crate::signal::clear_stop_bit();
1862+
assert!(
1863+
crate::vm::thread::set_stop_requested_for_current_thread(true),
1864+
"current thread has no stop_requested flag"
1865+
);
1866+
crate::signal::clear_stop_bit();
1867+
let pending = crate::signal::eval_breaker_pending();
1868+
let tripped = vm.eval_breaker_tripped();
1869+
crate::vm::thread::set_stop_requested_for_current_thread(false);
1870+
assert!(
1871+
!pending,
1872+
"STOP_BIT or another process-wide breaker bit was set"
1873+
);
1874+
assert!(tripped);
1875+
});
1876+
}
1877+
18541878
/// Entering a subinterpreter from inside the parent's `enter` must attach
18551879
/// the subinterpreter's thread slot (and detach the parent's). Otherwise the
18561880
/// thread runs the sub's bytecode with a DETACHED slot, and a collector

‎crates/vm/src/vm/thread.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,19 @@ pub fn stop_requested_for_current_thread() -> bool {
892892
})
893893
}
894894

895+
#[cfg(all(test, feature = "threading"))]
896+
pub(crate) fn set_stop_requested_for_current_thread(value: bool) -> bool {
897+
CURRENT_STOP_REQUESTED.with(|cached| {
898+
let flag = cached.get();
899+
if flag.is_null() {
900+
return false;
901+
}
902+
// SAFETY: same lifetime as `stop_requested_for_current_thread`.
903+
unsafe { &*flag }.store(value, Ordering::Release);
904+
true
905+
})
906+
}
907+
895908
/// Whether the QSBR subsystem asked this thread to pass a checkpoint.
896909
/// A missed or racing read of this flag is harmless: the pending
897910
/// retirement is still processed at the next checkpoint or by the GC

0 commit comments

Comments
 (0)