rquickjs_core/runtime/
base.rs1use super::{
4 opaque::Opaque, raw::RawRuntime, InterruptHandler, MemoryUsage, PromiseHook, RejectionTracker,
5};
6use crate::allocator::Allocator;
7#[cfg(feature = "loader")]
8use crate::loader::{Loader, Resolver};
9use crate::{qjs, result::JobException, Context, Mut, Ref, Result, Weak};
10use alloc::{ffi::CString, vec::Vec};
11use core::{ptr::NonNull, result::Result as StdResult};
12#[cfg(feature = "parallel")]
13use std::sync::mpsc::{self, Sender};
14
15#[derive(Clone)]
19pub struct WeakRuntime {
20 inner: Weak<Mut<RawRuntime>>,
21 #[cfg(feature = "parallel")]
22 pending_free: Sender<NonNull<qjs::JSContext>>,
23}
24
25impl WeakRuntime {
26 pub fn try_ref(&self) -> Option<Runtime> {
27 self.inner.upgrade().map(|inner| Runtime {
28 inner,
29 #[cfg(feature = "parallel")]
30 pending_free: self.pending_free.clone(),
31 })
32 }
33}
34
35#[derive(Clone)]
37pub struct Runtime {
38 pub(crate) inner: Ref<Mut<RawRuntime>>,
39 #[cfg(feature = "parallel")]
40 pub(crate) pending_free: Sender<NonNull<qjs::JSContext>>,
41}
42
43impl Runtime {
44 pub fn new() -> Result<Self> {
51 let opaque = Opaque::new();
52 #[cfg(feature = "parallel")]
53 let (pending_free, pending_free_recv) = mpsc::channel();
54 let rt = unsafe {
55 RawRuntime::new(
56 opaque,
57 #[cfg(feature = "parallel")]
58 pending_free_recv,
59 )?
60 };
61 Ok(Self {
62 inner: Ref::new(Mut::new(rt)),
63 #[cfg(feature = "parallel")]
64 pending_free,
65 })
66 }
67
68 pub fn new_with_alloc<A>(allocator: A) -> Result<Self>
72 where
73 A: Allocator + 'static,
74 {
75 let opaque = Opaque::new();
76 #[cfg(feature = "parallel")]
77 let (pending_free, pending_free_recv) = mpsc::channel();
78 let rt = unsafe {
79 RawRuntime::new_with_allocator(
80 opaque,
81 allocator,
82 #[cfg(feature = "parallel")]
83 pending_free_recv,
84 )?
85 };
86 Ok(Self {
87 inner: Ref::new(Mut::new(rt)),
88 #[cfg(feature = "parallel")]
89 pending_free,
90 })
91 }
92
93 pub fn weak(&self) -> WeakRuntime {
95 WeakRuntime {
96 inner: Ref::downgrade(&self.inner),
97 #[cfg(feature = "parallel")]
98 pending_free: self.pending_free.clone(),
99 }
100 }
101
102 #[inline]
104 pub fn set_promise_hook(&self, tracker: Option<PromiseHook>) {
105 unsafe {
106 self.inner.lock().set_promise_hook(tracker);
107 }
108 }
109
110 #[inline]
112 pub fn set_host_promise_rejection_tracker(&self, tracker: Option<RejectionTracker>) {
113 unsafe {
114 self.inner
115 .lock()
116 .set_host_promise_rejection_tracker(tracker);
117 }
118 }
119
120 #[inline]
124 pub fn set_interrupt_handler(&self, handler: Option<InterruptHandler>) {
125 unsafe {
126 self.inner.lock().set_interrupt_handler(handler);
127 }
128 }
129
130 #[cfg(feature = "loader")]
132 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "loader")))]
133 pub fn set_loader<R, L>(&self, resolver: R, loader: L)
134 where
135 R: Resolver + 'static,
136 L: Loader + 'static,
137 {
138 unsafe {
139 self.inner.lock().set_loader(resolver, loader);
140 }
141 }
142
143 pub fn set_info<S: Into<Vec<u8>>>(&self, info: S) -> Result<()> {
145 let string = CString::new(info)?;
146 unsafe {
147 self.inner.lock().set_info(string);
148 }
149 Ok(())
150 }
151
152 pub fn set_memory_limit(&self, limit: usize) {
159 unsafe {
160 self.inner.lock().set_memory_limit(limit);
161 }
162 }
163
164 pub fn set_max_stack_size(&self, limit: usize) {
168 unsafe {
169 self.inner.lock().set_max_stack_size(limit);
170 }
171 }
172
173 pub fn set_gc_threshold(&self, threshold: usize) {
175 unsafe {
176 self.inner.lock().set_gc_threshold(threshold);
177 }
178 }
179
180 pub fn set_dump_flags(&self, flags: u64) {
182 unsafe {
183 self.inner.lock().set_dump_flags(flags);
184 }
185 }
186
187 pub fn run_gc(&self) {
194 unsafe {
195 self.inner.lock().run_gc();
196 }
197 }
198
199 pub fn memory_usage(&self) -> MemoryUsage {
201 unsafe { self.inner.lock().memory_usage() }
202 }
203
204 #[inline]
208 pub fn is_job_pending(&self) -> bool {
209 self.inner.lock().is_job_pending()
210 }
211
212 #[inline]
216 pub fn execute_pending_job(&self) -> StdResult<bool, JobException> {
217 let mut lock = self.inner.lock();
218 lock.update_stack_top();
219 lock.execute_pending_job().map_err(|e| {
220 let ptr = NonNull::new(e).expect("QuickJS returned null ptr for job error");
221 unsafe { qjs::JS_DupContext(ptr.as_ptr()) };
224 JobException(unsafe { Context::from_raw(ptr, self.clone()) })
225 })
226 }
227}
228
229#[cfg(feature = "parallel")]
232unsafe impl Send for Runtime {}
233#[cfg(feature = "parallel")]
234unsafe impl Send for WeakRuntime {}
235
236#[cfg(feature = "parallel")]
240unsafe impl Sync for Runtime {}
241#[cfg(feature = "parallel")]
242unsafe impl Sync for WeakRuntime {}
243
244#[cfg(test)]
245mod test {
246 use super::*;
247 #[test]
248 fn base_runtime() {
249 let rt = Runtime::new().unwrap();
250 rt.set_info("test runtime").unwrap();
251 rt.set_memory_limit(0xFFFF);
252 rt.set_gc_threshold(0xFF);
253 rt.run_gc();
254 }
255
256 #[test]
257 fn set_max_stack_size_large_values() {
258 let rt = Runtime::new().unwrap();
259 rt.set_max_stack_size(usize::MAX);
260 let ctx = crate::Context::full(&rt).unwrap();
261 ctx.with(|ctx| {
262 ctx.eval::<i32, _>("1 + 1").unwrap();
263 });
264 rt.set_max_stack_size(isize::MAX as usize);
265 ctx.with(|ctx| {
266 ctx.eval::<i32, _>("1 + 1").unwrap();
267 });
268 rt.set_max_stack_size(0);
269 ctx.with(|ctx| {
270 ctx.eval::<i32, _>("1 + 1").unwrap();
271 });
272 rt.set_max_stack_size(256 * 1024);
273 ctx.with(|ctx| {
274 ctx.eval::<i32, _>("1 + 1").unwrap();
275 });
276 }
277
278 #[test]
279 fn context_dropped_while_lock_held() {
280 let rt = Runtime::new().unwrap();
281 let ctx1 = crate::Context::full(&rt).unwrap();
282 let ctx2 = crate::Context::full(&rt).unwrap();
283
284 ctx1.with(|_| {
285 drop(ctx2);
286 });
287 }
288
289 #[test]
290 #[cfg(feature = "parallel")]
291 fn context_parked_by_other_thread_is_still_freed() {
292 use std::sync::{Arc, Barrier};
293 use std::{thread, time::Duration};
294
295 let rt = Runtime::new().unwrap();
296 let ctx1 = crate::Context::full(&rt).unwrap();
297 let ctx2 = crate::Context::full(&rt).unwrap();
298
299 let barrier = Arc::new(Barrier::new(2));
300 let barrier_holder = barrier.clone();
301 let holder = thread::spawn(move || {
302 ctx1.with(|_| {
303 barrier_holder.wait();
304 thread::sleep(Duration::from_millis(100));
305 });
306 });
307
308 barrier.wait();
309 drop(ctx2);
313
314 holder.join().unwrap();
315 drop(rt);
316 }
317}