Merge pull request #1189 from palaviv/signal

signal support
This commit is contained in:
Windel Bouwman
2019-08-04 20:29:47 +02:00
committed by GitHub
7 changed files with 244 additions and 0 deletions

22
Cargo.lock generated
View File

@@ -33,6 +33,25 @@ dependencies = [
"scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arr_macro"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arr_macro_impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arr_macro_impl"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayvec"
version = "0.4.11"
@@ -1011,6 +1030,7 @@ dependencies = [
name = "rustpython-vm"
version = "0.1.0"
dependencies = [
"arr_macro 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1856,6 +1876,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36b7aa1ccb7d7ea3f437cf025a2ab1c47cc6c1bc9fc84918ff449def12f5e282"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392"
"checksum arr_macro 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d262b83f2f573121554ad6e764cd444303df85d86e5fcebc81903ddcf8dd3a97"
"checksum arr_macro_impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8decbe97ffec939e44228d91e5d0829ceb1616c6ed0984c09df164b1e7ebaafc"
"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
"checksum ascii-canvas 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b385d69402821a1c254533a011a312531cbcc0e3e24f19bbb4747a5a2daf37e2"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"

View File

@@ -0,0 +1,44 @@
import signal
import time
import sys
from testutils import assert_raises
assert_raises(TypeError, lambda: signal.signal(signal.SIGINT, 2))
signals = []
def handler(signum, frame):
signals.append(signum)
signal.signal(signal.SIGILL, signal.SIG_IGN);
assert signal.getsignal(signal.SIGILL) is signal.SIG_IGN
old_signal = signal.signal(signal.SIGILL, signal.SIG_DFL)
assert old_signal is signal.SIG_IGN
assert signal.getsignal(signal.SIGILL) is signal.SIG_DFL
# unix
if "win" not in sys.platform:
signal.signal(signal.SIGALRM, handler)
assert signal.getsignal(signal.SIGALRM) is handler
signal.alarm(1)
time.sleep(2.0)
assert signals == [signal.SIGALRM]
signal.signal(signal.SIGALRM, signal.SIG_IGN)
signal.alarm(1)
time.sleep(2.0)
assert signals == [signal.SIGALRM]
signal.signal(signal.SIGALRM, handler)
signal.alarm(1)
time.sleep(2.0)
assert signals == [signal.SIGALRM, signal.SIGALRM]

View File

@@ -63,6 +63,7 @@ bitflags = "1.1"
libc = "0.2"
nix = "0.14.1"
wtf8 = "0.0.3"
arr_macro = "0.1.2"
flame = { version = "0.2", optional = true }
flamer = { version = "0.3", optional = true }

View File

@@ -23,6 +23,9 @@ use crate::vm::VirtualMachine;
use indexmap::IndexMap;
use itertools::Itertools;
#[cfg(not(target_arch = "wasm32"))]
use crate::stdlib::signal::check_signals;
#[derive(Clone, Debug)]
struct Block {
/// The type of block.
@@ -163,6 +166,10 @@ impl Frame {
/// Execute a single instruction.
#[allow(clippy::cognitive_complexity)]
fn execute_instruction(&self, vm: &VirtualMachine) -> FrameResult {
#[cfg(not(target_arch = "wasm32"))]
{
check_signals(vm);
}
let instruction = self.fetch_instruction();
flame_guard!(format!("Frame::execute_instruction({:?})", instruction));

View File

@@ -38,6 +38,8 @@ pub mod io;
mod os;
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
mod pwd;
#[cfg(not(target_arch = "wasm32"))]
pub mod signal;
use crate::pyobject::PyObjectRef;
@@ -92,6 +94,7 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
modules.insert("_io".to_string(), Box::new(io::make_module));
modules.insert("_os".to_string(), Box::new(os::make_module));
modules.insert("socket".to_string(), Box::new(socket::make_module));
modules.insert("signal".to_string(), Box::new(signal::make_module));
}
// Unix-only

165
vm/src/stdlib/signal.rs Normal file
View File

@@ -0,0 +1,165 @@
use crate::obj::objint::PyIntRef;
use crate::pyobject::{IdProtocol, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
use std::sync::atomic::{AtomicBool, Ordering};
use num_traits::cast::ToPrimitive;
use arr_macro::arr;
#[cfg(unix)]
use nix::unistd::alarm as sig_alarm;
use libc;
#[cfg(not(windows))]
use libc::{SIG_DFL, SIG_ERR, SIG_IGN};
#[cfg(windows)]
const SIG_DFL: libc::sighandler_t = 0;
#[cfg(windows)]
const SIG_IGN: libc::sighandler_t = 1;
#[cfg(windows)]
const SIG_ERR: libc::sighandler_t = !0;
const NSIG: usize = 64;
// We cannot use the NSIG const in the arr macro. This will fail compilation if NSIG is different.
static mut TRIGGERS: [AtomicBool; NSIG] = arr![AtomicBool::new(false); 64];
extern "C" fn run_signal(signum: i32) {
unsafe {
TRIGGERS[signum as usize].store(true, Ordering::Relaxed);
}
}
fn signal(
signalnum: PyIntRef,
handler: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<Option<PyObjectRef>> {
if !vm.isinstance(&handler, &vm.ctx.function_type())?
&& !vm.isinstance(&handler, &vm.ctx.bound_method_type())?
&& !vm.isinstance(&handler, &vm.ctx.builtin_function_or_method_type())?
{
return Err(vm.new_type_error("Hanlder must be callable".to_string()));
}
let signal_module = vm.import("signal", &vm.ctx.new_tuple(vec![]), 0)?;
let sig_dfl = vm.get_attribute(signal_module.clone(), "SIG_DFL")?;
let sig_ign = vm.get_attribute(signal_module, "SIG_IGN")?;
let signalnum = signalnum.as_bigint().to_i32().unwrap();
check_signals(vm);
let sig_handler = if handler.is(&sig_dfl) {
SIG_DFL
} else if handler.is(&sig_ign) {
SIG_IGN
} else {
run_signal as libc::sighandler_t
};
let old = unsafe { libc::signal(signalnum, sig_handler) };
if old == SIG_ERR {
return Err(vm.new_os_error("Failed to set signal".to_string()));
}
let old_handler = vm.signal_handlers.borrow_mut().insert(signalnum, handler);
Ok(old_handler)
}
fn getsignal(signalnum: PyIntRef, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
let signalnum = signalnum.as_bigint().to_i32().unwrap();
Ok(vm.signal_handlers.borrow_mut().get(&signalnum).cloned())
}
#[cfg(unix)]
fn alarm(time: PyIntRef, _vm: &VirtualMachine) -> u32 {
let time = time.as_bigint().to_u32().unwrap();
let prev_time = if time == 0 {
sig_alarm::cancel()
} else {
sig_alarm::set(time)
};
prev_time.unwrap_or(0)
}
#[allow(clippy::needless_range_loop)]
pub fn check_signals(vm: &VirtualMachine) {
for signum in 1..NSIG {
let triggerd = unsafe { TRIGGERS[signum].swap(false, Ordering::Relaxed) };
if triggerd {
let handler = vm
.signal_handlers
.borrow()
.get(&(signum as i32))
.expect("Handler should be set")
.clone();
vm.invoke(handler, vec![vm.new_int(signum), vm.get_none()])
.expect("Test");
}
}
}
fn stub_func(_vm: &VirtualMachine) -> PyResult {
panic!("Do not use directly");
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let sig_dfl = ctx.new_rustfunc(stub_func);
let sig_ign = ctx.new_rustfunc(stub_func);
let module = py_module!(vm, "signal", {
"signal" => ctx.new_rustfunc(signal),
"getsignal" => ctx.new_rustfunc(getsignal),
"SIG_DFL" => sig_dfl,
"SIG_IGN" => sig_ign,
"SIGABRT" => ctx.new_int(libc::SIGABRT as u8),
"SIGFPE" => ctx.new_int(libc::SIGFPE as u8),
"SIGILL" => ctx.new_int(libc::SIGILL as u8),
"SIGINT" => ctx.new_int(libc::SIGINT as u8),
"SIGSEGV" => ctx.new_int(libc::SIGSEGV as u8),
"SIGTERM" => ctx.new_int(libc::SIGTERM as u8),
});
extend_module_platform_specific(vm, module)
}
#[cfg(unix)]
fn extend_module_platform_specific(vm: &VirtualMachine, module: PyObjectRef) -> PyObjectRef {
let ctx = &vm.ctx;
extend_module!(vm, module, {
"alarm" => ctx.new_rustfunc(alarm),
"SIGHUP" => ctx.new_int(libc::SIGHUP as u8),
"SIGQUIT" => ctx.new_int(libc::SIGQUIT as u8),
"SIGTRAP" => ctx.new_int(libc::SIGTRAP as u8),
"SIGBUS" => ctx.new_int(libc::SIGBUS as u8),
"SIGKILL" => ctx.new_int(libc::SIGKILL as u8),
"SIGUSR1" => ctx.new_int(libc::SIGUSR1 as u8),
"SIGUSR2" => ctx.new_int(libc::SIGUSR2 as u8),
"SIGPIPE" => ctx.new_int(libc::SIGPIPE as u8),
"SIGALRM" => ctx.new_int(libc::SIGALRM as u8),
"SIGSTKFLT" => ctx.new_int(libc::SIGSTKFLT as u8),
"SIGCHLD" => ctx.new_int(libc::SIGCHLD as u8),
"SIGCONT" => ctx.new_int(libc::SIGCONT as u8),
"SIGSTOP" => ctx.new_int(libc::SIGSTOP as u8),
"SIGTSTP" => ctx.new_int(libc::SIGTSTP as u8),
"SIGTTIN" => ctx.new_int(libc::SIGTTIN as u8),
"SIGTTOU" => ctx.new_int(libc::SIGTTOU as u8),
"SIGURG" => ctx.new_int(libc::SIGURG as u8),
"SIGXCPU" => ctx.new_int(libc::SIGXCPU as u8),
"SIGXFSZ" => ctx.new_int(libc::SIGXFSZ as u8),
"SIGVTALRM" => ctx.new_int(libc::SIGVTALRM as u8),
"SIGPROF" => ctx.new_int(libc::SIGPROF as u8),
"SIGWINCH" => ctx.new_int(libc::SIGWINCH as u8),
"SIGIO" => ctx.new_int(libc::SIGIO as u8),
"SIGPWR" => ctx.new_int(libc::SIGPWR as u8),
"SIGSYS" => ctx.new_int(libc::SIGSYS as u8),
});
module
}
#[cfg(not(unix))]
fn extend_module_platform_specific(_vm: &VirtualMachine, module: PyObjectRef) -> PyObjectRef {
module
}

View File

@@ -62,6 +62,7 @@ pub struct VirtualMachine {
pub trace_func: RefCell<PyObjectRef>,
pub use_tracing: RefCell<bool>,
pub settings: PySettings,
pub signal_handlers: RefCell<HashMap<i32, PyObjectRef>>,
}
/// Struct containing all kind of settings for the python vm.
@@ -160,6 +161,7 @@ impl VirtualMachine {
trace_func,
use_tracing: RefCell::new(false),
settings,
signal_handlers: Default::default(),
};
builtins::make_module(&vm, builtins.clone());