Skip to content

signal support #1189

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

Merged
merged 27 commits into from
Aug 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1b17926
Initial signal support
palaviv Jul 28, 2019
5b670f8
Simplify signal code
palaviv Jul 29, 2019
f540d31
Move static triggers array to signal.rs
palaviv Jul 29, 2019
ca23c43
Return previous handler from signal
palaviv Jul 29, 2019
57cdae1
Add signal.getsignal
palaviv Jul 29, 2019
a7d96b7
Use rust signal module directly
palaviv Jul 29, 2019
e8001d7
Add signal.alarm
palaviv Jul 29, 2019
c1e0799
Add test for signal
palaviv Jul 29, 2019
7061f1c
Add signal.{SIG_IGN, SIG_DFL}
palaviv Jul 31, 2019
a1af6b4
Iterate over triggers in check_sginals
palaviv Jul 31, 2019
56b555b
Add signal numbers
palaviv Jul 31, 2019
785b5d8
Improve signal test
palaviv Jul 31, 2019
61bf076
User arr_macro to create triggers array
palaviv Jul 31, 2019
9470d75
Compile nix parts only on unix
palaviv Jul 31, 2019
48da527
Test signal only on unix
palaviv Jul 31, 2019
f3b4b28
SIGINT not defined on windows
palaviv Aug 2, 2019
7cd5e89
Get SIG_IGN
palaviv Aug 2, 2019
52d204c
Fix clippy warnings
palaviv Aug 2, 2019
60b5d9d
Add empty check_signals on WASM
palaviv Aug 2, 2019
25b9f35
Fix more clippy warnings
palaviv Aug 2, 2019
3e07d61
Add vm to WASM check_sginals
palaviv Aug 2, 2019
075f2c9
Use libc directly to set signal
palaviv Aug 3, 2019
23cba40
Get signal numbers from libc
palaviv Aug 3, 2019
f24c6da
Add some windows test to signal
palaviv Aug 3, 2019
f600868
Define SIG_* on windows
palaviv Aug 3, 2019
c5486a6
Remove stub check_signals in WASM
palaviv Aug 3, 2019
5e5d46d
Remove os_set_signal
palaviv Aug 3, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions tests/snippets/stdlib_signal.py
Original file line number Diff line number Diff line change
@@ -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]



1 change: 1 addition & 0 deletions vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
7 changes: 7 additions & 0 deletions vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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));
Expand Down
3 changes: 3 additions & 0 deletions vm/src/stdlib/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
165 changes: 165 additions & 0 deletions vm/src/stdlib/signal.rs
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@windelbouwman @coolreader18 any suggestion on how should I do this? This don't actually works...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore this. I had a typo...

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()])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should happen when a signal handler throws an exception? Do you want to tackle this now, or implement it later?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to do it later

.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
}
2 changes: 2 additions & 0 deletions vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -160,6 +161,7 @@ impl VirtualMachine {
trace_func,
use_tracing: RefCell::new(false),
settings,
signal_handlers: Default::default(),
};

builtins::make_module(&vm, builtins.clone());
Expand Down