rustpython_vm/vm/
thread.rs1use crate::{AsObject, PyObject, PyObjectRef, VirtualMachine};
2use itertools::Itertools;
3use std::{
4 cell::{Cell, RefCell},
5 ptr::NonNull,
6 thread_local,
7};
8
9thread_local! {
10 pub(super) static VM_STACK: RefCell<Vec<NonNull<VirtualMachine>>> = Vec::with_capacity(1).into();
11 static VM_CURRENT: RefCell<*const VirtualMachine> = std::ptr::null::<VirtualMachine>().into();
12
13 pub(crate) static COROUTINE_ORIGIN_TRACKING_DEPTH: Cell<u32> = const { Cell::new(0) };
14 pub(crate) static ASYNC_GEN_FINALIZER: RefCell<Option<PyObjectRef>> = const { RefCell::new(None) };
15 pub(crate) static ASYNC_GEN_FIRSTITER: RefCell<Option<PyObjectRef>> = const { RefCell::new(None) };
16}
17
18pub fn with_current_vm<R>(f: impl FnOnce(&VirtualMachine) -> R) -> R {
19 VM_CURRENT.with(|x| unsafe {
20 f(x.clone()
21 .into_inner()
22 .as_ref()
23 .expect("call with_current_vm() but VM_CURRENT is null"))
24 })
25}
26
27pub fn enter_vm<R>(vm: &VirtualMachine, f: impl FnOnce() -> R) -> R {
28 VM_STACK.with(|vms| {
29 vms.borrow_mut().push(vm.into());
30 let prev = VM_CURRENT.with(|current| current.replace(vm));
31 let ret = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
32 vms.borrow_mut().pop();
33 VM_CURRENT.with(|current| current.replace(prev));
34 ret.unwrap_or_else(|e| std::panic::resume_unwind(e))
35 })
36}
37
38pub fn with_vm<F, R>(obj: &PyObject, f: F) -> Option<R>
39where
40 F: Fn(&VirtualMachine) -> R,
41{
42 let vm_owns_obj = |intp: NonNull<VirtualMachine>| {
43 let vm = unsafe { intp.as_ref() };
45 obj.fast_isinstance(vm.ctx.types.object_type)
46 };
47 VM_STACK.with(|vms| {
48 let intp = match vms.borrow().iter().copied().exactly_one() {
49 Ok(x) => {
50 debug_assert!(vm_owns_obj(x));
51 x
52 }
53 Err(mut others) => others.find(|x| vm_owns_obj(*x))?,
54 };
55 let vm = unsafe { intp.as_ref() };
58 let prev = VM_CURRENT.with(|current| current.replace(vm));
59 let ret = f(vm);
60 VM_CURRENT.with(|current| current.replace(prev));
61 Some(ret)
62 })
63}
64
65#[must_use = "ThreadedVirtualMachine does nothing unless you move it to another thread and call .run()"]
66#[cfg(feature = "threading")]
67pub struct ThreadedVirtualMachine {
68 pub(super) vm: VirtualMachine,
69}
70
71#[cfg(feature = "threading")]
72impl ThreadedVirtualMachine {
73 pub fn make_spawn_func<F, R>(self, f: F) -> impl FnOnce() -> R
82 where
83 F: FnOnce(&VirtualMachine) -> R,
84 {
85 move || self.run(f)
86 }
87
88 pub fn run<F, R>(&self, f: F) -> R
97 where
98 F: FnOnce(&VirtualMachine) -> R,
99 {
100 let vm = &self.vm;
101 enter_vm(vm, || f(vm))
102 }
103}
104
105impl VirtualMachine {
106 #[cfg(feature = "threading")]
115 pub fn start_thread<F, R>(&self, f: F) -> std::thread::JoinHandle<R>
116 where
117 F: FnOnce(&VirtualMachine) -> R,
118 F: Send + 'static,
119 R: Send + 'static,
120 {
121 let func = self.new_thread().make_spawn_func(f);
122 std::thread::spawn(func)
123 }
124
125 #[cfg(feature = "threading")]
148 pub fn new_thread(&self) -> ThreadedVirtualMachine {
149 let vm = VirtualMachine {
150 builtins: self.builtins.clone(),
151 sys_module: self.sys_module.clone(),
152 ctx: self.ctx.clone(),
153 frames: RefCell::new(vec![]),
154 wasm_id: self.wasm_id.clone(),
155 exceptions: RefCell::default(),
156 import_func: self.import_func.clone(),
157 profile_func: RefCell::new(self.ctx.none()),
158 trace_func: RefCell::new(self.ctx.none()),
159 use_tracing: Cell::new(false),
160 recursion_limit: self.recursion_limit.clone(),
161 signal_handlers: None,
162 signal_rx: None,
163 repr_guards: RefCell::default(),
164 state: self.state.clone(),
165 initialized: self.initialized,
166 recursion_depth: Cell::new(0),
167 };
168 ThreadedVirtualMachine { vm }
169 }
170}