Skip to content

Commit ee74713

Browse files
authored
signal timer (#6535)
* fix waitpid * signal timer
1 parent 527111b commit ee74713

File tree

3 files changed

+127
-15
lines changed

3 files changed

+127
-15
lines changed

Lib/test/test_signal.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,6 @@ def test_strsignal(self):
127127
self.assertIn("Terminated", signal.strsignal(signal.SIGTERM))
128128
self.assertIn("Hangup", signal.strsignal(signal.SIGHUP))
129129

130-
# TODO: RUSTPYTHON
131-
@unittest.expectedFailure
132130
# Issue 3864, unknown if this affects earlier versions of freebsd also
133131
def test_interprocess_signal(self):
134132
dirname = os.path.dirname(__file__)
@@ -820,8 +818,6 @@ def sig_prof(self, *args):
820818
self.hndl_called = True
821819
signal.setitimer(signal.ITIMER_PROF, 0)
822820

823-
# TODO: RUSTPYTHON
824-
@unittest.expectedFailure
825821
def test_itimer_exc(self):
826822
# XXX I'm assuming -1 is an invalid itimer, but maybe some platform
827823
# defines it ?
@@ -831,16 +827,12 @@ def test_itimer_exc(self):
831827
self.assertRaises(signal.ItimerError,
832828
signal.setitimer, signal.ITIMER_REAL, -1)
833829

834-
# TODO: RUSTPYTHON
835-
@unittest.expectedFailure
836830
def test_itimer_real(self):
837831
self.itimer = signal.ITIMER_REAL
838832
signal.setitimer(self.itimer, 1.0)
839833
signal.pause()
840834
self.assertEqual(self.hndl_called, True)
841835

842-
# TODO: RUSTPYTHON
843-
@unittest.expectedFailure
844836
# Issue 3864, unknown if this affects earlier versions of freebsd also
845837
@unittest.skipIf(sys.platform in ('netbsd5',),
846838
'itimer not reliable (does not mix well with threading) on some BSDs.')
@@ -861,8 +853,6 @@ def test_itimer_virtual(self):
861853
# and the handler should have been called
862854
self.assertEqual(self.hndl_called, True)
863855

864-
# TODO: RUSTPYTHON
865-
@unittest.expectedFailure
866856
def test_itimer_prof(self):
867857
self.itimer = signal.ITIMER_PROF
868858
signal.signal(signal.SIGPROF, self.sig_prof)
@@ -880,8 +870,6 @@ def test_itimer_prof(self):
880870
# and the handler should have been called
881871
self.assertEqual(self.hndl_called, True)
882872

883-
# TODO: RUSTPYTHON
884-
@unittest.expectedFailure
885873
def test_setitimer_tiny(self):
886874
# bpo-30807: C setitimer() takes a microsecond-resolution interval.
887875
# Check that float -> timeval conversion doesn't round

crates/vm/src/stdlib/posix.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,9 +1738,17 @@ pub mod module {
17381738
#[pyfunction]
17391739
fn waitpid(pid: libc::pid_t, opt: i32, vm: &VirtualMachine) -> PyResult<(libc::pid_t, i32)> {
17401740
let mut status = 0;
1741-
let pid = unsafe { libc::waitpid(pid, &mut status, opt) };
1742-
let pid = nix::Error::result(pid).map_err(|err| err.into_pyexception(vm))?;
1743-
Ok((pid, status))
1741+
loop {
1742+
let res = unsafe { libc::waitpid(pid, &mut status, opt) };
1743+
if res == -1 {
1744+
if nix::Error::last_raw() == libc::EINTR {
1745+
vm.check_signals()?;
1746+
continue;
1747+
}
1748+
return Err(nix::Error::last().into_pyexception(vm));
1749+
}
1750+
return Ok((res, status));
1751+
}
17441752
}
17451753

17461754
#[pyfunction]

crates/vm/src/stdlib/signal.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ pub(crate) mod _signal {
1919
convert::{IntoPyException, TryFromBorrowedObject},
2020
};
2121
use crate::{PyObjectRef, PyResult, VirtualMachine, signal};
22+
#[cfg(unix)]
23+
use crate::{
24+
builtins::PyTypeRef,
25+
function::{ArgIntoFloat, OptionalArg},
26+
};
2227
use std::sync::atomic::{self, Ordering};
2328

2429
#[cfg(any(unix, windows))]
@@ -89,6 +94,18 @@ pub(crate) mod _signal {
8994
fn siginterrupt(sig: i32, flag: i32) -> i32;
9095
}
9196

97+
#[cfg(any(target_os = "linux", target_os = "android"))]
98+
mod ffi {
99+
unsafe extern "C" {
100+
pub fn getitimer(which: libc::c_int, curr_value: *mut libc::itimerval) -> libc::c_int;
101+
pub fn setitimer(
102+
which: libc::c_int,
103+
new_value: *const libc::itimerval,
104+
old_value: *mut libc::itimerval,
105+
) -> libc::c_int;
106+
}
107+
}
108+
92109
#[pyattr]
93110
use crate::signal::NSIG;
94111

@@ -114,6 +131,31 @@ pub(crate) mod _signal {
114131
#[pyattr]
115132
use libc::{SIGPWR, SIGSTKFLT};
116133

134+
// Interval timer constants
135+
#[cfg(all(unix, not(target_os = "android")))]
136+
#[pyattr]
137+
use libc::{ITIMER_PROF, ITIMER_REAL, ITIMER_VIRTUAL};
138+
139+
#[cfg(target_os = "android")]
140+
#[pyattr]
141+
const ITIMER_REAL: libc::c_int = 0;
142+
#[cfg(target_os = "android")]
143+
#[pyattr]
144+
const ITIMER_VIRTUAL: libc::c_int = 1;
145+
#[cfg(target_os = "android")]
146+
#[pyattr]
147+
const ITIMER_PROF: libc::c_int = 2;
148+
149+
#[cfg(unix)]
150+
#[pyattr(name = "ItimerError", once)]
151+
fn itimer_error(vm: &VirtualMachine) -> PyTypeRef {
152+
vm.ctx.new_exception_type(
153+
"signal",
154+
"ItimerError",
155+
Some(vec![vm.ctx.exceptions.os_error.to_owned()]),
156+
)
157+
}
158+
117159
#[cfg(any(unix, windows))]
118160
pub(super) fn init_signal_handlers(
119161
module: &Py<crate::builtins::PyModule>,
@@ -216,6 +258,80 @@ pub(crate) mod _signal {
216258
prev_time.unwrap_or(0)
217259
}
218260

261+
#[cfg(unix)]
262+
#[pyfunction]
263+
fn pause(vm: &VirtualMachine) -> PyResult<()> {
264+
unsafe { libc::pause() };
265+
signal::check_signals(vm)?;
266+
Ok(())
267+
}
268+
269+
#[cfg(unix)]
270+
fn timeval_to_double(tv: &libc::timeval) -> f64 {
271+
tv.tv_sec as f64 + (tv.tv_usec as f64 / 1_000_000.0)
272+
}
273+
274+
#[cfg(unix)]
275+
fn double_to_timeval(val: f64) -> libc::timeval {
276+
libc::timeval {
277+
tv_sec: val.trunc() as _,
278+
tv_usec: ((val.fract()) * 1_000_000.0) as _,
279+
}
280+
}
281+
282+
#[cfg(unix)]
283+
fn itimerval_to_tuple(it: &libc::itimerval) -> (f64, f64) {
284+
(
285+
timeval_to_double(&it.it_value),
286+
timeval_to_double(&it.it_interval),
287+
)
288+
}
289+
290+
#[cfg(unix)]
291+
#[pyfunction]
292+
fn setitimer(
293+
which: i32,
294+
seconds: ArgIntoFloat,
295+
interval: OptionalArg<ArgIntoFloat>,
296+
vm: &VirtualMachine,
297+
) -> PyResult<(f64, f64)> {
298+
let seconds: f64 = seconds.into();
299+
let interval: f64 = interval.map(|v| v.into()).unwrap_or(0.0);
300+
let new = libc::itimerval {
301+
it_value: double_to_timeval(seconds),
302+
it_interval: double_to_timeval(interval),
303+
};
304+
let mut old = std::mem::MaybeUninit::<libc::itimerval>::uninit();
305+
#[cfg(any(target_os = "linux", target_os = "android"))]
306+
let ret = unsafe { ffi::setitimer(which, &new, old.as_mut_ptr()) };
307+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
308+
let ret = unsafe { libc::setitimer(which, &new, old.as_mut_ptr()) };
309+
if ret != 0 {
310+
let err = std::io::Error::last_os_error();
311+
let itimer_error = itimer_error(vm);
312+
return Err(vm.new_exception_msg(itimer_error, err.to_string()));
313+
}
314+
let old = unsafe { old.assume_init() };
315+
Ok(itimerval_to_tuple(&old))
316+
}
317+
318+
#[cfg(unix)]
319+
#[pyfunction]
320+
fn getitimer(which: i32, vm: &VirtualMachine) -> PyResult<(f64, f64)> {
321+
let mut old = std::mem::MaybeUninit::<libc::itimerval>::uninit();
322+
#[cfg(any(target_os = "linux", target_os = "android"))]
323+
let ret = unsafe { ffi::getitimer(which, old.as_mut_ptr()) };
324+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
325+
let ret = unsafe { libc::getitimer(which, old.as_mut_ptr()) };
326+
if ret != 0 {
327+
let err = std::io::Error::last_os_error();
328+
let itimer_error = itimer_error(vm);
329+
return Err(vm.new_exception_msg(itimer_error, err.to_string()));
330+
}
331+
let old = unsafe { old.assume_init() };
332+
Ok(itimerval_to_tuple(&old))
333+
}
334+
219335
#[pyfunction]
220336
fn default_int_handler(
221337
_signum: PyObjectRef,

0 commit comments

Comments
 (0)