Skip to main content

v8/
platform.rs

1use crate::Isolate;
2use crate::isolate::RealIsolate;
3use crate::support::int;
4
5use crate::support::Opaque;
6use crate::support::Shared;
7use crate::support::SharedPtrBase;
8use crate::support::SharedRef;
9use crate::support::UniquePtr;
10use crate::support::UniqueRef;
11use crate::support::long;
12
13unsafe extern "C" {
14  fn v8__Platform__NewDefaultPlatform(
15    thread_pool_size: int,
16    idle_task_support: bool,
17  ) -> *mut Platform;
18  fn v8__Platform__NewUnprotectedDefaultPlatform(
19    thread_pool_size: int,
20    idle_task_support: bool,
21  ) -> *mut Platform;
22  fn v8__Platform__NewSingleThreadedDefaultPlatform(
23    idle_task_support: bool,
24  ) -> *mut Platform;
25  fn v8__Platform__NewCustomPlatform(
26    thread_pool_size: int,
27    idle_task_support: bool,
28    unprotected: bool,
29    context: *mut std::ffi::c_void,
30  ) -> *mut Platform;
31  fn v8__Platform__DELETE(this: *mut Platform);
32
33  fn v8__Platform__PumpMessageLoop(
34    platform: *mut Platform,
35    isolate: *mut RealIsolate,
36    wait_for_work: bool,
37  ) -> bool;
38
39  fn v8__Platform__RunIdleTasks(
40    platform: *mut Platform,
41    isolate: *mut RealIsolate,
42    idle_time_in_seconds: f64,
43  );
44
45  fn v8__Platform__NotifyIsolateShutdown(
46    platform: *mut Platform,
47    isolate: *mut RealIsolate,
48  );
49
50  fn std__shared_ptr__v8__Platform__CONVERT__std__unique_ptr(
51    unique_ptr: UniquePtr<Platform>,
52  ) -> SharedPtrBase<Platform>;
53  fn std__shared_ptr__v8__Platform__get(
54    ptr: *const SharedPtrBase<Platform>,
55  ) -> *mut Platform;
56  fn std__shared_ptr__v8__Platform__COPY(
57    ptr: *const SharedPtrBase<Platform>,
58  ) -> SharedPtrBase<Platform>;
59  fn std__shared_ptr__v8__Platform__reset(ptr: *mut SharedPtrBase<Platform>);
60  fn std__shared_ptr__v8__Platform__use_count(
61    ptr: *const SharedPtrBase<Platform>,
62  ) -> long;
63}
64
65#[repr(C)]
66#[derive(Debug)]
67pub struct Platform(Opaque);
68
69/// Trait for customizing platform behavior, following the same pattern as
70/// [`V8InspectorClientImpl`](crate::inspector::V8InspectorClientImpl).
71///
72/// Implement this trait to receive callbacks for overridden C++ virtual
73/// methods on the `DefaultPlatform` and its per-isolate `TaskRunner`.
74///
75/// The C++ `CustomPlatform` wraps each isolate's `TaskRunner` so that
76/// every `PostTask` / `PostDelayedTask` / etc. call is forwarded to the
77/// default implementation *and* notifies Rust through the corresponding
78/// trait method.
79///
80/// All methods have default no-op implementations; override only what
81/// you need.
82///
83/// Implementations must be `Send + Sync` as callbacks may fire from any
84/// thread.
85#[allow(unused_variables)]
86pub trait PlatformImpl: Send + Sync {
87  // ---- TaskRunner virtual methods ----
88
89  /// Called when `TaskRunner::PostTask` is invoked for the given isolate.
90  ///
91  /// The task itself has already been forwarded to the default platform's
92  /// queue and will be executed by `PumpMessageLoop`. This callback is a
93  /// notification that a new task is available.
94  ///
95  /// May be called from ANY thread (V8 background threads, etc.).
96  fn post_task(&self, isolate_ptr: *mut std::ffi::c_void) {}
97
98  /// Called when `TaskRunner::PostNonNestableTask` is invoked.
99  ///
100  /// Same semantics as [`post_task`](Self::post_task).
101  fn post_non_nestable_task(&self, isolate_ptr: *mut std::ffi::c_void) {}
102
103  /// Called when `TaskRunner::PostDelayedTask` is invoked.
104  ///
105  /// The task has been forwarded to the default runner's delayed queue.
106  /// `delay_in_seconds` is the delay before the task should execute.
107  /// Embedders should schedule a wake-up after this delay.
108  ///
109  /// May be called from ANY thread.
110  fn post_delayed_task(
111    &self,
112    isolate_ptr: *mut std::ffi::c_void,
113    delay_in_seconds: f64,
114  ) {
115  }
116
117  /// Called when `TaskRunner::PostNonNestableDelayedTask` is invoked.
118  ///
119  /// Same semantics as [`post_delayed_task`](Self::post_delayed_task).
120  fn post_non_nestable_delayed_task(
121    &self,
122    isolate_ptr: *mut std::ffi::c_void,
123    delay_in_seconds: f64,
124  ) {
125  }
126
127  /// Called when `TaskRunner::PostIdleTask` is invoked.
128  ///
129  /// Same semantics as [`post_task`](Self::post_task).
130  fn post_idle_task(&self, isolate_ptr: *mut std::ffi::c_void) {}
131}
132
133// FFI callbacks called from C++ CustomPlatform/CustomTaskRunner.
134// `context` is a raw pointer to a `Box<dyn PlatformImpl>`.
135
136#[unsafe(no_mangle)]
137unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__PostTask(
138  context: *mut std::ffi::c_void,
139  isolate: *mut std::ffi::c_void,
140) {
141  let imp = unsafe { &*(context as *const Box<dyn PlatformImpl>) };
142  imp.post_task(isolate);
143}
144
145#[unsafe(no_mangle)]
146unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__PostNonNestableTask(
147  context: *mut std::ffi::c_void,
148  isolate: *mut std::ffi::c_void,
149) {
150  let imp = unsafe { &*(context as *const Box<dyn PlatformImpl>) };
151  imp.post_non_nestable_task(isolate);
152}
153
154#[unsafe(no_mangle)]
155unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__PostDelayedTask(
156  context: *mut std::ffi::c_void,
157  isolate: *mut std::ffi::c_void,
158  delay_in_seconds: f64,
159) {
160  let imp = unsafe { &*(context as *const Box<dyn PlatformImpl>) };
161  imp.post_delayed_task(isolate, delay_in_seconds);
162}
163
164#[unsafe(no_mangle)]
165unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__PostNonNestableDelayedTask(
166  context: *mut std::ffi::c_void,
167  isolate: *mut std::ffi::c_void,
168  delay_in_seconds: f64,
169) {
170  let imp = unsafe { &*(context as *const Box<dyn PlatformImpl>) };
171  imp.post_non_nestable_delayed_task(isolate, delay_in_seconds);
172}
173
174#[unsafe(no_mangle)]
175unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__PostIdleTask(
176  context: *mut std::ffi::c_void,
177  isolate: *mut std::ffi::c_void,
178) {
179  let imp = unsafe { &*(context as *const Box<dyn PlatformImpl>) };
180  imp.post_idle_task(isolate);
181}
182
183#[unsafe(no_mangle)]
184unsafe extern "C" fn v8__Platform__CustomPlatform__BASE__DROP(
185  context: *mut std::ffi::c_void,
186) {
187  unsafe {
188    let _ = Box::from_raw(context as *mut Box<dyn PlatformImpl>);
189  }
190}
191
192/// Returns a new instance of the default v8::Platform implementation.
193///
194/// |thread_pool_size| is the number of worker threads to allocate for
195/// background jobs. If a value of zero is passed, a suitable default
196/// based on the current number of processors online will be chosen.
197/// If |idle_task_support| is enabled then the platform will accept idle
198/// tasks (IdleTasksEnabled will return true) and will rely on the embedder
199/// calling v8::platform::RunIdleTasks to process the idle tasks.
200///
201/// The default platform for v8 may include restrictions and caveats on thread
202/// creation and initialization. This platform should only be used in cases
203/// where v8 can be reliably initialized on the application's main thread, or
204/// the parent thread to all threads in the system that will use v8.
205///
206/// One example of a restriction is the use of Memory Protection Keys (pkeys) on
207/// modern Linux systems using modern Intel/AMD processors. This particular
208/// technology requires that all threads using v8 are created as descendent
209/// threads of the thread that called `v8::Initialize`.
210#[inline(always)]
211pub fn new_default_platform(
212  thread_pool_size: u32,
213  idle_task_support: bool,
214) -> UniqueRef<Platform> {
215  Platform::new(thread_pool_size, idle_task_support)
216}
217
218/// Creates a platform that is identical to the default platform, but does not
219/// enforce thread-isolated allocations. This may reduce security in some cases,
220/// so this method should be used with caution in cases where the threading
221/// guarantees of `new_default_platform` cannot be upheld (generally for tests).
222#[inline(always)]
223pub fn new_unprotected_default_platform(
224  thread_pool_size: u32,
225  idle_task_support: bool,
226) -> UniqueRef<Platform> {
227  Platform::new_unprotected(thread_pool_size, idle_task_support)
228}
229
230/// The same as new_default_platform() but disables the worker thread pool.
231/// It must be used with the --single-threaded V8 flag.
232///
233/// If |idle_task_support| is enabled then the platform will accept idle
234/// tasks (IdleTasksEnabled will return true) and will rely on the embedder
235/// calling v8::platform::RunIdleTasks to process the idle tasks.
236#[inline(always)]
237pub fn new_single_threaded_default_platform(
238  idle_task_support: bool,
239) -> UniqueRef<Platform> {
240  Platform::new_single_threaded(idle_task_support)
241}
242
243/// Creates a custom platform backed by `DefaultPlatform` that delegates
244/// virtual method overrides to the provided [`PlatformImpl`] trait object.
245///
246/// This follows the same pattern as
247/// [`V8InspectorClient::new`](crate::inspector::V8InspectorClient::new).
248///
249/// When `unprotected` is true, thread-isolated allocations are disabled
250/// (same as `new_unprotected_default_platform`). This is required when
251/// isolates may be created on threads other than the one that called
252/// `V8::initialize`.
253#[inline(always)]
254pub fn new_custom_platform(
255  thread_pool_size: u32,
256  idle_task_support: bool,
257  unprotected: bool,
258  platform_impl: impl PlatformImpl + 'static,
259) -> UniqueRef<Platform> {
260  Platform::new_custom(
261    thread_pool_size,
262    idle_task_support,
263    unprotected,
264    platform_impl,
265  )
266}
267
268impl Platform {
269  /// Returns a new instance of the default v8::Platform implementation.
270  ///
271  /// |thread_pool_size| is the number of worker threads to allocate for
272  /// background jobs. If a value of zero is passed, a suitable default
273  /// based on the current number of processors online will be chosen.
274  /// If |idle_task_support| is enabled then the platform will accept idle
275  /// tasks (IdleTasksEnabled will return true) and will rely on the embedder
276  /// calling v8::platform::RunIdleTasks to process the idle tasks.
277  ///
278  /// The default platform for v8 may include restrictions and caveats on thread
279  /// creation and initialization. This platform should only be used in cases
280  /// where v8 can be reliably initialized on the application's main thread, or
281  /// the parent thread to all threads in the system that will use v8.
282  ///
283  /// One example of a restriction is the use of Memory Protection Keys (pkeys)
284  /// on modern Linux systems using modern Intel/AMD processors. This particular
285  /// technology requires that all threads using v8 are created as descendent
286  /// threads of the thread that called `v8::Initialize`.
287  #[inline(always)]
288  pub fn new(
289    thread_pool_size: u32,
290    idle_task_support: bool,
291  ) -> UniqueRef<Self> {
292    unsafe {
293      UniqueRef::from_raw(v8__Platform__NewDefaultPlatform(
294        thread_pool_size.min(16) as i32,
295        idle_task_support,
296      ))
297    }
298  }
299
300  /// Creates a platform that is identical to the default platform, but does not
301  /// enforce thread-isolated allocations. This may reduce security in some
302  /// cases, so this method should be used with caution in cases where the
303  /// threading guarantees of `new_default_platform` cannot be upheld (generally
304  /// for tests).
305  #[inline(always)]
306  pub fn new_unprotected(
307    thread_pool_size: u32,
308    idle_task_support: bool,
309  ) -> UniqueRef<Self> {
310    unsafe {
311      UniqueRef::from_raw(v8__Platform__NewUnprotectedDefaultPlatform(
312        thread_pool_size.min(16) as i32,
313        idle_task_support,
314      ))
315    }
316  }
317
318  /// The same as new() but disables the worker thread pool.
319  /// It must be used with the --single-threaded V8 flag.
320  ///
321  /// If |idle_task_support| is enabled then the platform will accept idle
322  /// tasks (IdleTasksEnabled will return true) and will rely on the embedder
323  /// calling v8::platform::RunIdleTasks to process the idle tasks.
324  #[inline(always)]
325  pub fn new_single_threaded(idle_task_support: bool) -> UniqueRef<Self> {
326    unsafe {
327      UniqueRef::from_raw(v8__Platform__NewSingleThreadedDefaultPlatform(
328        idle_task_support,
329      ))
330    }
331  }
332
333  /// Creates a custom platform backed by `DefaultPlatform` that delegates
334  /// virtual method overrides to the provided [`PlatformImpl`] trait object.
335  ///
336  /// The trait object is owned by the platform and will be dropped when the
337  /// platform is destroyed.
338  #[inline(always)]
339  pub fn new_custom(
340    thread_pool_size: u32,
341    idle_task_support: bool,
342    unprotected: bool,
343    platform_impl: impl PlatformImpl + 'static,
344  ) -> UniqueRef<Self> {
345    // Double-box: inner Box<dyn> is a fat pointer, outer Box gives us a
346    // thin pointer we can pass through C++ void*.
347    let boxed: Box<dyn PlatformImpl> = Box::new(platform_impl);
348    let context = Box::into_raw(Box::new(boxed)) as *mut std::ffi::c_void;
349    // thread_pool_size clamping (0 → hardware_concurrency, max 16) is
350    // handled on the C++ side in v8__Platform__NewCustomPlatform.
351    unsafe {
352      UniqueRef::from_raw(v8__Platform__NewCustomPlatform(
353        thread_pool_size as i32,
354        idle_task_support,
355        unprotected,
356        context,
357      ))
358    }
359  }
360}
361
362impl Platform {
363  /// Pumps the message loop for the given isolate.
364  ///
365  /// The caller has to make sure that this is called from the right thread.
366  /// Returns true if a task was executed, and false otherwise. If the call to
367  /// PumpMessageLoop is nested within another call to PumpMessageLoop, only
368  /// nestable tasks may run. Otherwise, any task may run. Unless requested through
369  /// the |wait_for_work| parameter, this call does not block if no task is pending.
370  #[inline(always)]
371  pub fn pump_message_loop(
372    platform: &SharedRef<Self>,
373    isolate: &Isolate,
374    wait_for_work: bool,
375  ) -> bool {
376    unsafe {
377      v8__Platform__PumpMessageLoop(
378        &**platform as *const Self as *mut _,
379        isolate.as_real_ptr(),
380        wait_for_work,
381      )
382    }
383  }
384
385  /// Runs pending idle tasks for at most |idle_time_in_seconds| seconds.
386  ///
387  /// The caller has to make sure that this is called from the right thread.
388  /// This call does not block if no task is pending.
389  #[inline(always)]
390  pub fn run_idle_tasks(
391    platform: &SharedRef<Self>,
392    isolate: &Isolate,
393    idle_time_in_seconds: f64,
394  ) {
395    unsafe {
396      v8__Platform__RunIdleTasks(
397        &**platform as *const Self as *mut _,
398        isolate.as_real_ptr(),
399        idle_time_in_seconds,
400      );
401    }
402  }
403
404  /// Notifies the given platform about the Isolate getting deleted soon. Has to
405  /// be called for all Isolates which are deleted - unless we're shutting down
406  /// the platform.
407  ///
408  /// The |platform| has to be created using |NewDefaultPlatform|.
409  #[inline(always)]
410  pub(crate) unsafe fn notify_isolate_shutdown(
411    platform: &SharedRef<Self>,
412    isolate: &Isolate,
413  ) {
414    unsafe {
415      v8__Platform__NotifyIsolateShutdown(
416        &**platform as *const Self as *mut _,
417        isolate.as_real_ptr(),
418      );
419    }
420  }
421}
422
423impl Shared for Platform {
424  fn from_unique_ptr(unique_ptr: UniquePtr<Self>) -> SharedPtrBase<Self> {
425    unsafe {
426      std__shared_ptr__v8__Platform__CONVERT__std__unique_ptr(unique_ptr)
427    }
428  }
429  fn get(ptr: &SharedPtrBase<Self>) -> *const Self {
430    unsafe { std__shared_ptr__v8__Platform__get(ptr) }
431  }
432  fn clone(ptr: &SharedPtrBase<Self>) -> SharedPtrBase<Self> {
433    unsafe { std__shared_ptr__v8__Platform__COPY(ptr) }
434  }
435  fn reset(ptr: &mut SharedPtrBase<Self>) {
436    unsafe { std__shared_ptr__v8__Platform__reset(ptr) }
437  }
438  fn use_count(ptr: &SharedPtrBase<Self>) -> long {
439    unsafe { std__shared_ptr__v8__Platform__use_count(ptr) }
440  }
441}
442
443impl Drop for Platform {
444  fn drop(&mut self) {
445    unsafe { v8__Platform__DELETE(self) };
446  }
447}