Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Initial signal support
  • Loading branch information
palaviv committed Aug 2, 2019
commit 1b17926e913140f946c3b102edad10532f33a933
1 change: 1 addition & 0 deletions vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ impl Frame {
/// Execute a single instruction.
#[allow(clippy::cognitive_complexity)]
fn execute_instruction(&self, vm: &VirtualMachine) -> FrameResult {
vm.check_signals();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this contraption makes sense, it inserts signals nicely at a defined point of time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do you think this is a good point?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think so, we would have to wait for builtin functions to finish, but then again, we do not face threading issues.

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"))]
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
37 changes: 37 additions & 0 deletions vm/src/stdlib/signal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::obj::objfunction::PyFunctionRef;
use crate::obj::objint::PyIntRef;
use crate::pyobject::PyObjectRef;
use crate::pyobject::PyResult;
use crate::vm::{VirtualMachine, TRIGGERS};

use std::sync::atomic::Ordering;

use num_traits::cast::ToPrimitive;

use nix::sys::signal;

extern "C" fn run_signal(signum: i32) {
unsafe {
TRIGGERS[signum as usize].store(true, Ordering::Relaxed);
}
}

fn signal(signalnum: PyIntRef, handler: PyFunctionRef, vm: &VirtualMachine) -> PyResult<()> {
vm.signal_handlers.borrow_mut().insert(
signalnum.as_bigint().to_i32().unwrap(),
handler.into_object(),
);
let handler = nix::sys::signal::SigHandler::Handler(run_signal);
let sig_action =
signal::SigAction::new(handler, signal::SaFlags::empty(), signal::SigSet::empty());
unsafe { signal::sigaction(signal::SIGINT, &sig_action) }.unwrap();
Ok(())
}

pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;

py_module!(vm, "_signal", {
"signal" => ctx.new_rustfunc(signal),
})
}
35 changes: 35 additions & 0 deletions vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, MutexGuard};

use crate::builtins;
Expand Down Expand Up @@ -42,6 +43,23 @@ use num_bigint::BigInt;
#[cfg(feature = "rustpython-compiler")]
use rustpython_compiler::{compile, error::CompileError};

// Signal triggers
// TODO: 64
pub const NSIG: usize = 10;

pub static mut TRIGGERS: [AtomicBool; NSIG] = [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe you could use Default::default() here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Did not work. I will create a procedural macro in the future to solve this duplication.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Having this variable as a static global would prevent multiple VirtualMachine instances. We can solve this, by moving the triggers struct into a RefCell. Place this RefCell into the signals.rs file. In the signals.rs file, create a global cell, with a trigger list. Each vm then can register a trigger list to.

Okay, my explanation is poor. Lets see how this evolves further :).

AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this be a boolean flag, or a queue of events? Each event triggers a callback, so if we have multiple events happening in a short time, the boolean flag was already true, so it was not handled. I propose to create a queue of events per signal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This will be different from the CPython implementation. Maybe we should leave this for as future improvement?

AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
];

// use objects::objects;

// Objects are live when they are on stack, or referenced by a name (for now)
Expand All @@ -62,6 +80,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 +179,7 @@ impl VirtualMachine {
trace_func,
use_tracing: RefCell::new(false),
settings,
signal_handlers: RefCell::new(HashMap::new()),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You could use Default::default() here instead of RefCell::new(HashMap::new())

};

builtins::make_module(&vm, builtins.clone());
Expand Down Expand Up @@ -1143,6 +1163,21 @@ impl VirtualMachine {
pub fn current_exception(&self) -> Option<PyObjectRef> {
self.exceptions.borrow().last().cloned()
}

pub fn check_signals(&self) {
for (signum, handler) in self.signal_handlers.borrow().iter() {
if *signum as usize >= NSIG {
panic!("Signum bigger then NSIG");
}
let triggerd;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can just be let triggerd = unsafe { ... };

unsafe {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please move this unsafe code into the signal module. This function should just loop over the list of triggered events. Also, you probably want to guard this for loop with some sort of lock. Another solution would be to use the std::sync::mpsc fifo, which is thread safe. This might be handy, so that the signal handlers still can add events, while this loop still is processing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is not problem that the an event will be added while I process the list. I will just process it in the next run.

triggerd = TRIGGERS[*signum as usize].swap(false, Ordering::Relaxed);
}
if triggerd {
self.invoke(handler.clone(), vec![]).expect("Test");
}
}
}
}

impl Default for VirtualMachine {
Expand Down