threaded virtual machine

This commit is contained in:
Jeong Yunwon
2022-04-23 06:22:09 +09:00
parent 7f5d683af4
commit f959ab9159
2 changed files with 74 additions and 71 deletions

View File

@@ -41,7 +41,7 @@ mod vm_ops;
pub use interpreter::Interpreter;
pub use setting::PySettings;
pub use thread::PyThread;
pub use thread::ThreadedVirtualMachine;
// Objects are live when they are on stack, or referenced by a name (for now)
@@ -276,71 +276,6 @@ impl VirtualMachine {
self.signal_rx = Some(signal_rx);
}
/// Start a new thread with access to the same interpreter.
///
/// # Note
///
/// If you return a `PyObjectRef` (or a type that contains one) from `F`, and don't `join()`
/// on the thread, there is a possibility that that thread will panic as `PyObjectRef`'s `Drop`
/// implementation tries to run the `__del__` destructor of a python object but finds that it's
/// not in the context of any vm.
#[cfg(feature = "threading")]
pub fn start_thread<F, R>(&self, f: F) -> std::thread::JoinHandle<R>
where
F: FnOnce(&VirtualMachine) -> R,
F: Send + 'static,
R: Send + 'static,
{
let thread = self.new_thread();
std::thread::spawn(|| thread.run(f))
}
/// Create a new VM thread that can be passed to a function like [`std::thread::spawn`]
/// to use the same interpreter on a different thread. Note that if you just want to
/// use this with `thread::spawn`, you can use
/// [`vm.start_thread()`](`VirtualMachine::start_thread`) as a convenience.
///
/// # Usage
///
/// ```
/// # rustpython_vm::Interpreter::default().enter(|vm| {
/// use std::thread::Builder;
/// let handle = Builder::new()
/// .name("my thread :)".into())
/// .spawn(vm.new_thread().make_spawn_func(|vm| vm.ctx.none()))
/// .expect("couldn't spawn thread");
/// let returned_obj = handle.join().expect("thread panicked");
/// assert!(vm.is_none(&returned_obj));
/// # })
/// ```
///
/// Note: this function is safe, but running the returned PyThread in the same
/// thread context (i.e. with the same thread-local storage) doesn't have any
/// specific guaranteed behavior.
#[cfg(feature = "threading")]
pub fn new_thread(&self) -> PyThread {
let thread_vm = VirtualMachine {
builtins: self.builtins.clone(),
sys_module: self.sys_module.clone(),
ctx: self.ctx.clone(),
frames: RefCell::new(vec![]),
wasm_id: self.wasm_id.clone(),
exceptions: RefCell::default(),
import_func: self.import_func.clone(),
profile_func: RefCell::new(self.ctx.none()),
trace_func: RefCell::new(self.ctx.none()),
use_tracing: Cell::new(false),
recursion_limit: self.recursion_limit.clone(),
signal_handlers: None,
signal_rx: None,
repr_guards: RefCell::default(),
state: self.state.clone(),
initialized: self.initialized,
recursion_depth: Cell::new(0),
};
PyThread { thread_vm }
}
pub fn run_code_obj(&self, code: PyRef<PyCode>, scope: Scope) -> PyResult {
let frame = Frame::new(code, scope, self.builtins.dict(), &[], self).into_ref(self);
self.run_frame_full(frame)

View File

@@ -39,14 +39,14 @@ where
})
}
#[must_use = "PyThread does nothing unless you move it to another thread and call .run()"]
#[must_use = "ThreadedVirtualMachine does nothing unless you move it to another thread and call .run()"]
#[cfg(feature = "threading")]
pub struct PyThread {
pub(super) thread_vm: VirtualMachine,
pub struct ThreadedVirtualMachine {
pub(super) vm: VirtualMachine,
}
#[cfg(feature = "threading")]
impl PyThread {
impl ThreadedVirtualMachine {
/// Create a `FnOnce()` that can easily be passed to a function like [`std::thread::Builder::spawn`]
///
/// # Note
@@ -74,7 +74,75 @@ impl PyThread {
where
F: FnOnce(&VirtualMachine) -> R,
{
let vm = &self.thread_vm;
let vm = &self.vm;
enter_vm(vm, || f(vm))
}
}
impl VirtualMachine {
/// Start a new thread with access to the same interpreter.
///
/// # Note
///
/// If you return a `PyObjectRef` (or a type that contains one) from `F`, and don't `join()`
/// on the thread, there is a possibility that that thread will panic as `PyObjectRef`'s `Drop`
/// implementation tries to run the `__del__` destructor of a python object but finds that it's
/// not in the context of any vm.
#[cfg(feature = "threading")]
pub fn start_thread<F, R>(&self, f: F) -> std::thread::JoinHandle<R>
where
F: FnOnce(&VirtualMachine) -> R,
F: Send + 'static,
R: Send + 'static,
{
let thread = self.new_thread();
std::thread::spawn(|| thread.run(f))
}
/// Create a new VM thread that can be passed to a function like [`std::thread::spawn`]
/// to use the same interpreter on a different thread. Note that if you just want to
/// use this with `thread::spawn`, you can use
/// [`vm.start_thread()`](`VirtualMachine::start_thread`) as a convenience.
///
/// # Usage
///
/// ```
/// # rustpython_vm::Interpreter::default().enter(|vm| {
/// use std::thread::Builder;
/// let handle = Builder::new()
/// .name("my thread :)".into())
/// .spawn(vm.new_thread().make_spawn_func(|vm| vm.ctx.none()))
/// .expect("couldn't spawn thread");
/// let returned_obj = handle.join().expect("thread panicked");
/// assert!(vm.is_none(&returned_obj));
/// # })
/// ```
///
/// Note: this function is safe, but running the returned ThreadedVirtualMachine in the same
/// thread context (i.e. with the same thread-local storage) doesn't have any
/// specific guaranteed behavior.
#[cfg(feature = "threading")]
pub fn new_thread(&self) -> ThreadedVirtualMachine {
use std::cell::Cell;
let vm = VirtualMachine {
builtins: self.builtins.clone(),
sys_module: self.sys_module.clone(),
ctx: self.ctx.clone(),
frames: RefCell::new(vec![]),
wasm_id: self.wasm_id.clone(),
exceptions: RefCell::default(),
import_func: self.import_func.clone(),
profile_func: RefCell::new(self.ctx.none()),
trace_func: RefCell::new(self.ctx.none()),
use_tracing: Cell::new(false),
recursion_limit: self.recursion_limit.clone(),
signal_handlers: None,
signal_rx: None,
repr_guards: RefCell::default(),
state: self.state.clone(),
initialized: self.initialized,
recursion_depth: Cell::new(0),
};
ThreadedVirtualMachine { vm }
}
}