diff --git a/examples/hello_world.rs b/examples/hello_world.rs index a201e572..2eebe5ee 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -2,7 +2,7 @@ use rusty_v8 as v8; fn main() { // Initialize V8. - let platform = v8::new_default_platform().unwrap(); + let platform = v8::new_default_platform(0, false).make_shared(); v8::V8::initialize_platform(platform); v8::V8::initialize(); diff --git a/examples/process.rs b/examples/process.rs index 35ee9ce0..7ae53f4c 100644 --- a/examples/process.rs +++ b/examples/process.rs @@ -19,7 +19,7 @@ fn log_callback( fn main() { // Initialize V8. - let platform = v8::new_default_platform().unwrap(); + let platform = v8::new_default_platform(0, false).make_shared(); v8::V8::initialize_platform(platform); v8::V8::initialize(); diff --git a/examples/shell.rs b/examples/shell.rs index 296de638..02987d13 100644 --- a/examples/shell.rs +++ b/examples/shell.rs @@ -2,7 +2,7 @@ use rusty_v8 as v8; fn main() { // Initialize V8. - let platform = v8::new_default_platform().unwrap(); + let platform = v8::new_default_platform(0, false).make_shared(); v8::V8::initialize_platform(platform); v8::V8::initialize(); @@ -79,11 +79,15 @@ fn run_main( let script: &str = &args[i + 1]; skip_next = true; - // TODO: pump event loop (not implemented on rusty_v8?) - // while v8::Platform::pump_message_loop(&platform, isolate) { - // // do nothing - // } execute_string(scope, script, "unnamed", false, true); + + while v8::Platform::pump_message_loop( + &v8::V8::get_current_platform(), + scope, + false, + ) { + // do nothing + } } arg => { if arg.starts_with("--") { @@ -94,6 +98,14 @@ fn run_main( // Use all other arguments as names of files to load and run. let script = std::fs::read_to_string(arg).expect("failed to read file"); execute_string(scope, &script, arg, false, true); + + while v8::Platform::pump_message_loop( + &v8::V8::get_current_platform(), + scope, + false, + ) { + // do nothing + } } } } diff --git a/src/V8.rs b/src/V8.rs index b66950d7..2802157d 100644 --- a/src/V8.rs +++ b/src/V8.rs @@ -7,7 +7,7 @@ use std::sync::Mutex; use std::vec::Vec; use crate::platform::Platform; -use crate::support::UniqueRef; +use crate::support::SharedRef; use crate::support::UnitType; extern "C" { @@ -61,24 +61,26 @@ where } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug)] enum GlobalState { Uninitialized, - PlatformInitialized, - Initialized, - Disposed, + PlatformInitialized(SharedRef), + Initialized(SharedRef), + Disposed(SharedRef), PlatformShutdown, } use GlobalState::*; lazy_static! { - static ref GLOBAL_STATE: Mutex = - Mutex::new(GlobalState::Uninitialized); + static ref GLOBAL_STATE: Mutex = Mutex::new(Uninitialized); } pub fn assert_initialized() { let global_state_guard = GLOBAL_STATE.lock().unwrap(); - assert_eq!(*global_state_guard, Initialized); + match *global_state_guard { + Initialized(_) => {} + _ => panic!("Invalid global state"), + }; } /// Pass the command line arguments to v8. @@ -173,25 +175,41 @@ pub fn get_version() -> &'static str { c_str.to_str().unwrap() } -// TODO: V8::InitializePlatform does not actually take a UniquePtr but rather -// a raw pointer. This means that the Platform object is not released when -// V8::ShutdownPlatform is called. /// Sets the v8::Platform to use. This should be invoked before V8 is /// initialized. -pub fn initialize_platform(platform: UniqueRef) { +pub fn initialize_platform(platform: SharedRef) { let mut global_state_guard = GLOBAL_STATE.lock().unwrap(); - assert_eq!(*global_state_guard, Uninitialized); - unsafe { v8__V8__InitializePlatform(platform.into_raw()) }; - *global_state_guard = PlatformInitialized; + *global_state_guard = match *global_state_guard { + Uninitialized => PlatformInitialized(platform.clone()), + _ => panic!("Invalid global state"), + }; + + { + unsafe { + v8__V8__InitializePlatform(&*platform as *const Platform as *mut _) + }; + } } /// Initializes V8. This function needs to be called before the first Isolate /// is created. It always returns true. pub fn initialize() { let mut global_state_guard = GLOBAL_STATE.lock().unwrap(); - assert_eq!(*global_state_guard, PlatformInitialized); - unsafe { v8__V8__Initialize() }; - *global_state_guard = Initialized; + *global_state_guard = match *global_state_guard { + PlatformInitialized(ref platform) => Initialized(platform.clone()), + _ => panic!("Invalid global state"), + }; + unsafe { v8__V8__Initialize() } +} + +/// Sets the v8::Platform to use. This should be invoked before V8 is +/// initialized. +pub fn get_current_platform() -> SharedRef { + let global_state_guard = GLOBAL_STATE.lock().unwrap(); + match *global_state_guard { + Initialized(ref platform) => platform.clone(), + _ => panic!("Invalid global state"), + } } /// Releases any resources used by v8 and stops any utility threads @@ -208,9 +226,11 @@ pub fn initialize() { /// to a crash. pub unsafe fn dispose() -> bool { let mut global_state_guard = GLOBAL_STATE.lock().unwrap(); - assert_eq!(*global_state_guard, Initialized); + *global_state_guard = match *global_state_guard { + Initialized(ref platform) => Disposed(platform.clone()), + _ => panic!("Invalid global state"), + }; assert!(v8__V8__Dispose()); - *global_state_guard = Disposed; true } @@ -218,7 +238,10 @@ pub unsafe fn dispose() -> bool { /// V8 was disposed. pub fn shutdown_platform() { let mut global_state_guard = GLOBAL_STATE.lock().unwrap(); - assert_eq!(*global_state_guard, Disposed); + // First shutdown platform, then drop platform unsafe { v8__V8__ShutdownPlatform() }; - *global_state_guard = PlatformShutdown; + *global_state_guard = match *global_state_guard { + Disposed(_) => PlatformShutdown, + _ => panic!("Invalid global state"), + }; } diff --git a/src/binding.cc b/src/binding.cc index d250a7e1..433cca7c 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -2028,18 +2028,64 @@ v8::StartupData v8__SnapshotCreator__CreateBlob( return self->CreateBlob(function_code_handling); } -v8::Platform* v8__platform__NewDefaultPlatform() { - // TODO(bnoordhuis) Support optional arguments. - return v8::platform::NewDefaultPlatform().release(); +v8::Platform* v8__Platform__NewDefaultPlatform(int thread_pool_size, + bool idle_task_support) { + return v8::platform::NewDefaultPlatform( + thread_pool_size, + idle_task_support ? v8::platform::IdleTaskSupport::kEnabled + : v8::platform::IdleTaskSupport::kDisabled, + v8::platform::InProcessStackDumping::kDisabled, nullptr) + .release(); } -v8::Platform* v8__platform__NewSingleThreadedDefaultPlatform() { - // TODO(bnoordhuis) Support optional arguments. - return v8::platform::NewSingleThreadedDefaultPlatform().release(); +v8::Platform* v8__Platform__NewSingleThreadedDefaultPlatform( + bool idle_task_support) { + return v8::platform::NewSingleThreadedDefaultPlatform( + idle_task_support ? v8::platform::IdleTaskSupport::kEnabled + : v8::platform::IdleTaskSupport::kDisabled, + v8::platform::InProcessStackDumping::kDisabled, nullptr) + .release(); +} + +bool v8__Platform__PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate, + bool wait_for_work) { + return v8::platform::PumpMessageLoop( + platform, isolate, + wait_for_work ? v8::platform::MessageLoopBehavior::kWaitForWork + : v8::platform::MessageLoopBehavior::kDoNotWait); +} + +void v8__Platform__RunIdleTasks(v8::Platform* platform, v8::Isolate* isolate, + double idle_time_in_seconds) { + v8::platform::RunIdleTasks(platform, isolate, idle_time_in_seconds); } void v8__Platform__DELETE(v8::Platform* self) { delete self; } +two_pointers_t std__shared_ptr__v8__Platform__CONVERT__std__unique_ptr( + v8::Platform* unique_ptr) { + return make_pod(std::shared_ptr(unique_ptr)); +} + +v8::Platform* std__shared_ptr__v8__Platform__get( + const std::shared_ptr& ptr) { + return ptr.get(); +} + +two_pointers_t std__shared_ptr__v8__Platform__COPY( + const std::shared_ptr& ptr) { + return make_pod(ptr); +} + +void std__shared_ptr__v8__Platform__reset(std::shared_ptr* ptr) { + ptr->reset(); +} + +long std__shared_ptr__v8__Platform__use_count( + const std::shared_ptr& ptr) { + return ptr.use_count(); +} + void v8_inspector__V8Inspector__Channel__BASE__sendResponse( v8_inspector::V8Inspector::Channel* self, int callId, v8_inspector::StringBuffer* message); diff --git a/src/lib.rs b/src/lib.rs index 8aee411e..38a1c609 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! ```rust //! use rusty_v8 as v8; //! -//! let platform = v8::new_default_platform().unwrap(); +//! let platform = v8::new_default_platform(0, false).make_shared(); //! v8::V8::initialize_platform(platform); //! v8::V8::initialize(); //! diff --git a/src/platform.rs b/src/platform.rs index f2a0ca79..d9967255 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -1,31 +1,181 @@ +use crate::support::int; +use crate::Isolate; + +use crate::support::long; use crate::support::Opaque; +use crate::support::Shared; +use crate::support::SharedPtrBase; +use crate::support::SharedRef; use crate::support::UniquePtr; +use crate::support::UniqueRef; extern "C" { - fn v8__platform__NewDefaultPlatform() -> *mut Platform; - fn v8__platform__NewSingleThreadedDefaultPlatform() -> *mut Platform; + fn v8__Platform__NewDefaultPlatform( + thread_pool_size: int, + idle_task_support: bool, + ) -> *mut Platform; + fn v8__Platform__NewSingleThreadedDefaultPlatform( + idle_task_support: bool, + ) -> *mut Platform; fn v8__Platform__DELETE(this: *mut Platform); -} -/// Returns a new instance of the default v8::Platform implementation. -pub fn new_default_platform() -> UniquePtr { - unsafe { UniquePtr::from_raw(v8__platform__NewDefaultPlatform()) } -} + fn v8__Platform__PumpMessageLoop( + platform: *mut Platform, + isolate: *mut Isolate, + wait_for_work: bool, + ) -> bool; -/// The same as new_default_platform() but disables the worker thread pool. -/// It must be used with the --single-threaded V8 flag. -pub fn new_single_threaded_default_platform() -> UniquePtr { - unsafe { - UniquePtr::from_raw(v8__platform__NewSingleThreadedDefaultPlatform()) - } + fn v8__Platform__RunIdleTasks( + platform: *mut Platform, + isolate: *mut Isolate, + idle_time_in_seconds: f64, + ); + + fn std__shared_ptr__v8__Platform__CONVERT__std__unique_ptr( + unique_ptr: UniquePtr, + ) -> SharedPtrBase; + fn std__shared_ptr__v8__Platform__get( + ptr: *const SharedPtrBase, + ) -> *mut Platform; + fn std__shared_ptr__v8__Platform__COPY( + ptr: *const SharedPtrBase, + ) -> SharedPtrBase; + fn std__shared_ptr__v8__Platform__reset(ptr: *mut SharedPtrBase); + fn std__shared_ptr__v8__Platform__use_count( + ptr: *const SharedPtrBase, + ) -> long; } #[repr(C)] #[derive(Debug)] pub struct Platform(Opaque); -impl Drop for Platform { - fn drop(&mut self) { - unsafe { v8__Platform__DELETE(self) } +/// Returns a new instance of the default v8::Platform implementation. +/// +/// |thread_pool_size| is the number of worker threads to allocate for +/// background jobs. If a value of zero is passed, a suitable default +/// based on the current number of processors online will be chosen. +/// If |idle_task_support| is enabled then the platform will accept idle +/// tasks (IdleTasksEnabled will return true) and will rely on the embedder +/// calling v8::platform::RunIdleTasks to process the idle tasks. +pub fn new_default_platform( + thread_pool_size: u32, + idle_task_support: bool, +) -> UniqueRef { + Platform::new(thread_pool_size, idle_task_support) +} + +/// The same as new_default_platform() but disables the worker thread pool. +/// It must be used with the --single-threaded V8 flag. +/// +/// If |idle_task_support| is enabled then the platform will accept idle +/// tasks (IdleTasksEnabled will return true) and will rely on the embedder +/// calling v8::platform::RunIdleTasks to process the idle tasks. +pub fn new_single_threaded_default_platform( + idle_task_support: bool, +) -> UniqueRef { + Platform::new_single_threaded(idle_task_support) +} + +impl Platform { + /// Returns a new instance of the default v8::Platform implementation. + /// + /// |thread_pool_size| is the number of worker threads to allocate for + /// background jobs. If a value of zero is passed, a suitable default + /// based on the current number of processors online will be chosen. + /// If |idle_task_support| is enabled then the platform will accept idle + /// tasks (IdleTasksEnabled will return true) and will rely on the embedder + /// calling v8::platform::RunIdleTasks to process the idle tasks. + pub fn new( + thread_pool_size: u32, + idle_task_support: bool, + ) -> UniqueRef { + unsafe { + UniqueRef::from_raw(v8__Platform__NewDefaultPlatform( + thread_pool_size.min(16) as i32, + idle_task_support, + )) + } + } + + /// The same as new() but disables the worker thread pool. + /// It must be used with the --single-threaded V8 flag. + /// + /// If |idle_task_support| is enabled then the platform will accept idle + /// tasks (IdleTasksEnabled will return true) and will rely on the embedder + /// calling v8::platform::RunIdleTasks to process the idle tasks. + pub fn new_single_threaded(idle_task_support: bool) -> UniqueRef { + unsafe { + UniqueRef::from_raw(v8__Platform__NewSingleThreadedDefaultPlatform( + idle_task_support, + )) + } + } +} + +impl Platform { + /// Pumps the message loop for the given isolate. + /// + /// The caller has to make sure that this is called from the right thread. + /// Returns true if a task was executed, and false otherwise. If the call to + /// PumpMessageLoop is nested within another call to PumpMessageLoop, only + /// nestable tasks may run. Otherwise, any task may run. Unless requested through + /// the |wait_for_work| parameter, this call does not block if no task is pending. + pub fn pump_message_loop( + platform: &SharedRef, + isolate: &mut Isolate, + wait_for_work: bool, + ) -> bool { + unsafe { + v8__Platform__PumpMessageLoop( + &**platform as *const Self as *mut _, + isolate, + wait_for_work, + ) + } + } + + /// Runs pending idle tasks for at most |idle_time_in_seconds| seconds. + /// + /// The caller has to make sure that this is called from the right thread. + /// This call does not block if no task is pending. + pub fn run_idle_tasks( + platform: &SharedRef, + isolate: &mut Isolate, + idle_time_in_seconds: f64, + ) { + unsafe { + v8__Platform__RunIdleTasks( + &**platform as *const Self as *mut _, + isolate, + idle_time_in_seconds, + ) + } + } +} + +impl Shared for Platform { + fn from_unique_ptr(unique_ptr: UniquePtr) -> SharedPtrBase { + unsafe { + std__shared_ptr__v8__Platform__CONVERT__std__unique_ptr(unique_ptr) + } + } + fn get(ptr: &SharedPtrBase) -> *const Self { + unsafe { std__shared_ptr__v8__Platform__get(ptr) } + } + fn clone(ptr: &SharedPtrBase) -> SharedPtrBase { + unsafe { std__shared_ptr__v8__Platform__COPY(ptr) } + } + fn reset(ptr: &mut SharedPtrBase) { + unsafe { std__shared_ptr__v8__Platform__reset(ptr) } + } + fn use_count(ptr: &SharedPtrBase) -> long { + unsafe { std__shared_ptr__v8__Platform__use_count(ptr) } + } +} + +impl Drop for Platform { + fn drop(&mut self) { + unsafe { v8__Platform__DELETE(self) }; } } diff --git a/src/scope.rs b/src/scope.rs index 535817c7..a7c1f693 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1727,7 +1727,7 @@ mod tests { fn initialize_v8() { static INIT: Once = Once::new(); INIT.call_once(|| { - V8::initialize_platform(new_default_platform().unwrap()); + V8::initialize_platform(new_default_platform(0, false).make_shared()); V8::initialize(); }); } diff --git a/tests/slots.rs b/tests/slots.rs index 968b067e..db00e4be 100644 --- a/tests/slots.rs +++ b/tests/slots.rs @@ -28,7 +28,9 @@ impl CoreIsolate { fn new(drop_count: Rc) -> CoreIsolate { static START: Once = Once::new(); START.call_once(|| { - v8::V8::initialize_platform(v8::new_default_platform().unwrap()); + v8::V8::initialize_platform( + v8::new_default_platform(0, false).make_shared(), + ); v8::V8::initialize(); }); let mut isolate = v8::Isolate::new(Default::default()); diff --git a/tests/test_api.rs b/tests/test_api.rs index 29ee1147..5a16dbbc 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -31,7 +31,9 @@ fn setup() -> SetupGuard { )) .is_ok()); v8::V8::set_flags_from_string("--expose_gc --harmony-import-assertions"); - v8::V8::initialize_platform(v8::new_default_platform().unwrap()); + v8::V8::initialize_platform( + v8::new_default_platform(0, false).make_shared(), + ); v8::V8::initialize(); }); SetupGuard {} diff --git a/tests/test_api_entropy_source.rs b/tests/test_api_entropy_source.rs index ca2ff0dd..7b69e5f8 100644 --- a/tests/test_api_entropy_source.rs +++ b/tests/test_api_entropy_source.rs @@ -19,7 +19,7 @@ fn set_entropy_source() { true }); - v8::V8::initialize_platform(v8::new_default_platform().unwrap()); + v8::V8::initialize_platform(v8::new_default_platform(0, false).make_shared()); v8::V8::initialize(); // Assumes that every isolate creates a PRNG from scratch, which is currently true. diff --git a/tests/test_api_flags.rs b/tests/test_api_flags.rs index f9da88f0..ed1dab3f 100644 --- a/tests/test_api_flags.rs +++ b/tests/test_api_flags.rs @@ -5,7 +5,7 @@ use rusty_v8 as v8; #[test] fn set_flags_from_string() { v8::V8::set_flags_from_string("--use_strict"); - v8::V8::initialize_platform(v8::new_default_platform().unwrap()); + v8::V8::initialize_platform(v8::new_default_platform(0, false).make_shared()); v8::V8::initialize(); let isolate = &mut v8::Isolate::new(Default::default()); let scope = &mut v8::HandleScope::new(isolate); diff --git a/tests/test_platform_atomics_pump_message_loop.rs b/tests/test_platform_atomics_pump_message_loop.rs new file mode 100644 index 00000000..4de42cef --- /dev/null +++ b/tests/test_platform_atomics_pump_message_loop.rs @@ -0,0 +1,53 @@ +use rusty_v8 as v8; + +#[test] +fn atomics_pump_message_loop() { + v8::V8::set_flags_from_string("--harmony-top-level-await --allow-natives-syntax --harmony-sharedarraybuffer"); + v8::V8::initialize_platform(v8::new_default_platform(0, false).make_shared()); + v8::V8::initialize(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + let source = r#" + function assertEquals(a, b) { + if (a === b) return; + throw a + " does not equal " + b; + } + + const sab = new SharedArrayBuffer(16); + const i32a = new Int32Array(sab); + + let resolved = false; + (function() { + const result = Atomics.waitAsync(i32a, 0, 0); + result.value.then( + (value) => { assertEquals("ok", value); resolved = true; }, + () => { assertUnreachable(); + }); + })(); + + const notify_return_value = Atomics.notify(i32a, 0, 1); + assertEquals(1, notify_return_value); + assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0)); + assertEquals(1, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0)); + "#; + let source = v8::String::new(scope, source).unwrap(); + let script = v8::Script::compile(scope, source, None).unwrap(); + script.run(scope).unwrap(); + + while v8::Platform::pump_message_loop( + &v8::V8::get_current_platform(), + scope, + false, + ) { + // do nothing + } + + let source2 = r#" + assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0)); + "#; + let source2 = v8::String::new(scope, source2).unwrap(); + let script2 = v8::Script::compile(scope, source2, None).unwrap(); + script2.run(scope).unwrap(); +} diff --git a/tests/test_single_threaded_default_platform.rs b/tests/test_single_threaded_default_platform.rs index ed1fac0a..518a5b99 100644 --- a/tests/test_single_threaded_default_platform.rs +++ b/tests/test_single_threaded_default_platform.rs @@ -4,7 +4,7 @@ use rusty_v8 as v8; fn single_threaded_default_platform() { v8::V8::set_flags_from_string("--single_threaded"); v8::V8::initialize_platform( - v8::new_single_threaded_default_platform().unwrap(), + v8::new_single_threaded_default_platform(false).make_shared(), ); v8::V8::initialize();