diff --git a/components/script/dom/abstractworker.rs b/components/script/dom/abstractworker.rs index c67f444ea92..3803089b0b3 100644 --- a/components/script/dom/abstractworker.rs +++ b/components/script/dom/abstractworker.rs @@ -7,7 +7,7 @@ use servo_url::ImmutableOrigin; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; -use crate::script_runtime::CommonScriptMsg; +use crate::messaging::CommonScriptMsg; /// Messages used to control the worker event loops pub enum WorkerScriptMsg { diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs index 966dacaffd1..383df363ebf 100644 --- a/components/script/dom/abstractworkerglobalscope.rs +++ b/components/script/dom/abstractworkerglobalscope.rs @@ -2,88 +2,19 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crossbeam_channel::{select, Receiver, Sender}; +use crossbeam_channel::{select, Receiver}; use devtools_traits::DevtoolScriptControlMsg; -use crate::dom::abstractworker::WorkerScriptMsg; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::reflector::DomObject; -use crate::dom::dedicatedworkerglobalscope::{AutoWorkerReset, DedicatedWorkerScriptMsg}; +use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset; use crate::dom::globalscope::GlobalScope; use crate::dom::worker::TrustedWorkerAddress; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::realms::enter_realm; -use crate::script_runtime::{CanGc, CommonScriptMsg, ScriptChan, ScriptPort}; +use crate::script_runtime::CanGc; use crate::task_queue::{QueuedTaskConversion, TaskQueue}; -/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with -/// common event loop messages. While this SendableWorkerScriptChan is alive, the associated -/// Worker object will remain alive. -#[derive(Clone, JSTraceable)] -pub struct SendableWorkerScriptChan { - #[no_trace] - pub sender: Sender, - pub worker: TrustedWorkerAddress, -} - -impl ScriptChan for SendableWorkerScriptChan { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - let msg = DedicatedWorkerScriptMsg::CommonWorker( - self.worker.clone(), - WorkerScriptMsg::Common(msg), - ); - self.sender.send(msg).map_err(|_| ()) - } - - fn as_boxed(&self) -> Box { - Box::new(SendableWorkerScriptChan { - sender: self.sender.clone(), - worker: self.worker.clone(), - }) - } -} - -/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with -/// worker event loop messages. While this SendableWorkerScriptChan is alive, the associated -/// Worker object will remain alive. -#[derive(Clone, JSTraceable)] -pub struct WorkerThreadWorkerChan { - #[no_trace] - pub sender: Sender, - pub worker: TrustedWorkerAddress, -} - -impl ScriptChan for WorkerThreadWorkerChan { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - let msg = DedicatedWorkerScriptMsg::CommonWorker( - self.worker.clone(), - WorkerScriptMsg::Common(msg), - ); - self.sender.send(msg).map_err(|_| ()) - } - - fn as_boxed(&self) -> Box { - Box::new(WorkerThreadWorkerChan { - sender: self.sender.clone(), - worker: self.worker.clone(), - }) - } -} - -impl ScriptPort for Receiver { - fn recv(&self) -> Result { - let common_msg = match self.recv() { - Ok(DedicatedWorkerScriptMsg::CommonWorker(_worker, common_msg)) => common_msg, - Err(_) => return Err(()), - Ok(DedicatedWorkerScriptMsg::WakeUp) => panic!("unexpected worker event message!"), - }; - match common_msg { - WorkerScriptMsg::Common(script_msg) => Ok(script_msg), - WorkerScriptMsg::DOMMessage { .. } => panic!("unexpected worker event message!"), - } - } -} - pub trait WorkerEventLoopMethods { type WorkerMsg: QueuedTaskConversion + Send; type ControlMsg; diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs index 4b43f518e2f..d0a2ea9dffe 100644 --- a/components/script/dom/bindings/refcounted.rs +++ b/components/script/dom/bindings/refcounted.rs @@ -53,7 +53,10 @@ mod dummy { pub use self::dummy::LIVE_REFERENCES; /// A pointer to a Rust DOM object that needs to be destroyed. -struct TrustedReference(*const libc::c_void); +#[derive(MallocSizeOf)] +struct TrustedReference( + #[ignore_malloc_size_of = "This is a shared reference."] *const libc::c_void, +); unsafe impl Send for TrustedReference {} impl TrustedReference { @@ -158,10 +161,13 @@ impl TrustedPromise { /// DOM object is guaranteed to live at least as long as the last outstanding /// `Trusted` instance. #[crown::unrooted_must_root_lint::allow_unrooted_interior] +#[derive(MallocSizeOf)] pub struct Trusted { /// A pointer to the Rust DOM object of type T, but void to allow /// sending `Trusted` between threads, regardless of T's sendability. + #[conditional_malloc_size_of] refcount: Arc, + #[ignore_malloc_size_of = "These are shared by all `Trusted` types."] owner_thread: *const LiveDOMReferences, phantom: PhantomData, } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index d7c0848987b..0132a688085 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -37,6 +37,7 @@ use std::hash::{BuildHasher, Hash}; use std::mem; use std::ops::{Deref, DerefMut}; +use crossbeam_channel::Sender; use indexmap::IndexMap; /// A trait to allow tracing (only) DOM objects. pub use js::gc::Traceable as JSTraceable; @@ -107,6 +108,10 @@ unsafe impl CustomTraceable for OnceCell { } } +unsafe impl CustomTraceable for Sender { + unsafe fn trace(&self, _: *mut JSTracer) {} +} + /// Wrapper type for nop traceble /// /// SAFETY: Inner type must not impl JSTraceable diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index d7ce8c0bd37..6981b806d8f 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -27,9 +27,7 @@ use style::thread_state::{self, ThreadState}; use crate::devtools; use crate::dom::abstractworker::{SimpleWorkerErrorHandler, WorkerScriptMsg}; -use crate::dom::abstractworkerglobalscope::{ - run_worker_event_loop, SendableWorkerScriptChan, WorkerEventLoopMethods, WorkerThreadWorkerChan, -}; +use crate::dom::abstractworkerglobalscope::{run_worker_event_loop, WorkerEventLoopMethods}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; @@ -41,7 +39,7 @@ use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::structuredclone; -use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox}; use crate::dom::errorevent::ErrorEvent; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventtarget::EventTarget; @@ -52,12 +50,10 @@ use crate::dom::webgpu::identityhub::IdentityHub; use crate::dom::worker::{TrustedWorkerAddress, Worker}; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::fetch::load_whole_resource; +use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender}; use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent; -use crate::script_runtime::{ - CanGc, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan, ScriptPort, - ThreadSafeJSContext, -}; +use crate::script_runtime::{CanGc, JSContext as SafeJSContext, Runtime, ThreadSafeJSContext}; use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; use crate::task_source::{SendableTaskSource, TaskSourceName}; @@ -182,14 +178,12 @@ pub struct DedicatedWorkerGlobalScope { workerglobalscope: WorkerGlobalScope, #[ignore_malloc_size_of = "Defined in std"] task_queue: TaskQueue, - #[ignore_malloc_size_of = "Defined in std"] - #[no_trace] own_sender: Sender, #[ignore_malloc_size_of = "Trusted has unclear ownership like Dom"] worker: DomRefCell>, #[ignore_malloc_size_of = "Can't measure trait objects"] /// Sender to the parent thread. - parent_sender: Box, + parent_event_loop_sender: ScriptEventLoopSender, #[ignore_malloc_size_of = "Arc"] #[no_trace] image_cache: Arc, @@ -250,7 +244,7 @@ impl DedicatedWorkerGlobalScope { worker_url: ServoUrl, from_devtools_receiver: Receiver, runtime: Runtime, - parent_sender: Box, + parent_event_loop_sender: ScriptEventLoopSender, own_sender: Sender, receiver: Receiver, closing: Arc, @@ -273,7 +267,7 @@ impl DedicatedWorkerGlobalScope { ), task_queue: TaskQueue::new(receiver, own_sender.clone()), own_sender, - parent_sender, + parent_event_loop_sender, worker: DomRefCell::new(None), image_cache, browsing_context, @@ -282,14 +276,14 @@ impl DedicatedWorkerGlobalScope { } #[allow(unsafe_code, clippy::too_many_arguments)] - pub fn new( + pub(crate) fn new( init: WorkerGlobalScopeInit, worker_name: DOMString, worker_type: WorkerType, worker_url: ServoUrl, from_devtools_receiver: Receiver, runtime: Runtime, - parent_sender: Box, + parent_event_loop_sender: ScriptEventLoopSender, own_sender: Sender, receiver: Receiver, closing: Arc, @@ -306,7 +300,7 @@ impl DedicatedWorkerGlobalScope { worker_url, from_devtools_receiver, runtime, - parent_sender, + parent_event_loop_sender, own_sender, receiver, closing, @@ -321,12 +315,12 @@ impl DedicatedWorkerGlobalScope { /// #[allow(unsafe_code, clippy::too_many_arguments)] - pub fn run_worker_scope( + pub(crate) fn run_worker_scope( mut init: WorkerGlobalScopeInit, worker_url: ServoUrl, from_devtools_receiver: IpcReceiver, worker: TrustedWorkerAddress, - parent_sender: Box, + parent_event_loop_sender: ScriptEventLoopSender, own_sender: Sender, receiver: Receiver, worker_load_origin: WorkerScriptLoadOrigin, @@ -379,10 +373,10 @@ impl DedicatedWorkerGlobalScope { let runtime = unsafe { let task_source = SendableTaskSource { - sender: Box::new(WorkerThreadWorkerChan { + sender: ScriptEventLoopSender::DedicatedWorker { sender: own_sender.clone(), - worker: worker.clone(), - }), + main_thread_worker: worker.clone(), + }, pipeline_id, name: TaskSourceName::Networking, canceller: Default::default(), @@ -417,7 +411,7 @@ impl DedicatedWorkerGlobalScope { worker_url, devtools_mpsc_port, runtime, - parent_sender.as_boxed(), + parent_event_loop_sender.clone(), own_sender, receiver, closing, @@ -442,7 +436,7 @@ impl DedicatedWorkerGlobalScope { ) { Err(_) => { println!("error loading script {}", serialized_worker_url); - parent_sender + parent_event_loop_sender .send(CommonScriptMsg::Task( WorkerEvent, Box::new(SimpleWorkerErrorHandler::new(worker)), @@ -491,7 +485,7 @@ impl DedicatedWorkerGlobalScope { } }, reporter_name, - parent_sender, + parent_event_loop_sender, CommonScriptMsg::CollectReports, ); @@ -526,21 +520,23 @@ impl DedicatedWorkerGlobalScope { self.image_cache.clone() } - pub(crate) fn event_loop_sender(&self) -> Option> { - let worker = self.worker.borrow().clone()?; - Some(Box::new(WorkerThreadWorkerChan { + pub(crate) fn event_loop_sender(&self) -> Option { + Some(ScriptEventLoopSender::DedicatedWorker { sender: self.own_sender.clone(), - worker, - })) + main_thread_worker: self.worker.borrow().clone()?, + }) } - pub fn new_script_pair(&self) -> (Box, Box) { - let (tx, rx) = unbounded(); - let chan = Box::new(SendableWorkerScriptChan { - sender: tx, - worker: self.worker.borrow().as_ref().unwrap().clone(), - }); - (chan, Box::new(rx)) + pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) { + let (sender, receiver) = unbounded(); + let main_thread_worker = self.worker.borrow().as_ref().unwrap().clone(); + ( + ScriptEventLoopSender::DedicatedWorker { + sender, + main_thread_worker, + }, + ScriptEventLoopReceiver::DedicatedWorker(receiver), + ) } fn handle_script_event(&self, msg: WorkerScriptMsg, can_gc: CanGc) { @@ -626,7 +622,7 @@ impl DedicatedWorkerGlobalScope { global.report_an_error(error_info, HandleValue::null(), CanGc::note()); } })); - self.parent_sender + self.parent_event_loop_sender .send(CommonScriptMsg::Task( WorkerEvent, task, @@ -650,7 +646,7 @@ impl DedicatedWorkerGlobalScope { let task = Box::new(task!(post_worker_message: move || { Worker::handle_message(worker, data, CanGc::note()); })); - self.parent_sender + self.parent_event_loop_sender .send(CommonScriptMsg::Task( WorkerEvent, task, diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 907f8b955aa..a8a5bef65b5 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -184,10 +184,10 @@ use crate::dom::windowproxy::WindowProxy; use crate::dom::xpathevaluator::XPathEvaluator; use crate::fetch::FetchCanceller; use crate::iframe_collection::IFrameCollection; -use crate::messaging::MainThreadScriptMsg; +use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg}; use crate::network_listener::{NetworkListener, PreInvoke}; use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; -use crate::script_runtime::{CanGc, CommonScriptMsg, ScriptThreadEventCategory}; +use crate::script_runtime::{CanGc, ScriptThreadEventCategory}; use crate::script_thread::{with_script_thread, ScriptThread}; use crate::stylesheet_set::StylesheetSetRef; use crate::task::TaskBox; diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 00d033a2ae9..60e768f3dc8 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -128,13 +128,12 @@ use crate::dom::webgpu::identityhub::IdentityHub; use crate::dom::window::Window; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::workletglobalscope::WorkletGlobalScope; +use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender}; use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask}; use crate::network_listener::{NetworkListener, PreInvoke}; use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; use crate::script_module::{DynamicModuleList, ModuleScript, ModuleTree, ScriptFetchOptions}; -use crate::script_runtime::{ - CanGc, CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort, ThreadSafeJSContext, -}; +use crate::script_runtime::{CanGc, JSContext as SafeJSContext, ThreadSafeJSContext}; use crate::script_thread::{with_script_thread, ScriptThread}; use crate::security_manager::CSPViolationReporter; use crate::task_manager::TaskManager; @@ -2480,7 +2479,7 @@ impl GlobalScope { /// A sender to the event loop of this global scope. This either sends to the Worker event loop /// or the ScriptThread event loop in the case of a `Window`. This can be `None` for dedicated /// workers that are not currently handling a message. - pub(crate) fn event_loop_sender(&self) -> Option> { + pub(crate) fn event_loop_sender(&self) -> Option { if let Some(window) = self.downcast::() { Some(window.event_loop_sender()) } else if let Some(dedicated) = self.downcast::() { @@ -2825,7 +2824,7 @@ impl GlobalScope { /// Create a new sender/receiver pair that can be used to implement an on-demand /// event loop. Used for implementing web APIs that require blocking semantics /// without resorting to nested event loops. - pub fn new_script_pair(&self) -> (Box, Box) { + pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) { if let Some(window) = self.downcast::() { return window.new_script_pair(); } diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index a7e699fee08..b547dd51b57 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -33,6 +33,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::structuredclone; +use crate::dom::bindings::trace::CustomTraceable; use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; @@ -44,10 +45,9 @@ use crate::dom::webgpu::identityhub::IdentityHub; use crate::dom::worker::TrustedWorkerAddress; use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::fetch::load_whole_resource; +use crate::messaging::{CommonScriptMsg, ScriptEventLoopSender}; use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; -use crate::script_runtime::{ - CanGc, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan, ThreadSafeJSContext, -}; +use crate::script_runtime::{CanGc, JSContext as SafeJSContext, Runtime, ThreadSafeJSContext}; use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; use crate::task_source::TaskSourceName; @@ -128,28 +128,6 @@ pub enum MixedMessage { Timer, } -#[derive(Clone, JSTraceable)] -pub struct ServiceWorkerChan { - #[no_trace] - pub sender: Sender, -} - -impl ScriptChan for ServiceWorkerChan { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.sender - .send(ServiceWorkerScriptMsg::CommonWorker( - WorkerScriptMsg::Common(msg), - )) - .map_err(|_| ()) - } - - fn as_boxed(&self) -> Box { - Box::new(ServiceWorkerChan { - sender: self.sender.clone(), - }) - } -} - #[dom_struct] pub struct ServiceWorkerGlobalScope { workerglobalscope: WorkerGlobalScope, @@ -158,8 +136,6 @@ pub struct ServiceWorkerGlobalScope { #[no_trace] task_queue: TaskQueue, - #[ignore_malloc_size_of = "Defined in std"] - #[no_trace] own_sender: Sender, /// A port on which a single "time-out" message can be received, @@ -485,10 +461,8 @@ impl ServiceWorkerGlobalScope { } } - pub(crate) fn event_loop_sender(&self) -> Box { - Box::new(ServiceWorkerChan { - sender: self.own_sender.clone(), - }) + pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender { + ScriptEventLoopSender::ServiceWorker(self.own_sender.clone()) } fn dispatch_activate(&self, can_gc: CanGc) { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 5147180eaf1..6c9d1f1f0c8 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -112,7 +112,7 @@ use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::structuredclone; -use crate::dom::bindings::trace::{JSTraceable, RootedTraceableBox}; +use crate::dom::bindings::trace::{CustomTraceable, JSTraceable, RootedTraceableBox}; use crate::dom::bindings::utils::GlobalStaticData; use crate::dom::bindings::weakref::DOMTracker; use crate::dom::bluetooth::BluetoothExtraPermissionData; @@ -149,11 +149,11 @@ use crate::dom::worklet::Worklet; use crate::dom::workletglobalscope::WorkletGlobalScopeType; use crate::layout_image::fetch_image_for_layout; use crate::messaging::{ - ImageCacheMsg, MainThreadScriptChan, MainThreadScriptMsg, SendableMainThreadScriptChan, + ImageCacheMsg, MainThreadScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender, }; use crate::microtask::MicrotaskQueue; use crate::realms::{enter_realm, InRealm}; -use crate::script_runtime::{CanGc, JSContext, Runtime, ScriptChan, ScriptPort}; +use crate::script_runtime::{CanGc, JSContext, Runtime}; use crate::script_thread::ScriptThread; use crate::timers::{IsInterval, TimerCallback}; use crate::unminify::unminified_path; @@ -202,8 +202,7 @@ impl LayoutBlocker { #[dom_struct] pub struct Window { globalscope: GlobalScope, - #[ignore_malloc_size_of = "trait objects are hard"] - script_chan: MainThreadScriptChan, + script_chan: Sender, #[no_trace] #[ignore_malloc_size_of = "TODO: Add MallocSizeOf support to layout"] layout: RefCell>, @@ -216,7 +215,6 @@ pub struct Window { #[ignore_malloc_size_of = "Arc"] #[no_trace] image_cache: Arc, - #[ignore_malloc_size_of = "channels are hard"] #[no_trace] image_cache_chan: Sender, window_proxy: MutNullableDom, @@ -452,16 +450,23 @@ impl Window { } pub(crate) fn main_thread_script_chan(&self) -> &Sender { - &self.script_chan.0 + &self.script_chan } pub fn parent_info(&self) -> Option { self.parent_info } - pub fn new_script_pair(&self) -> (Box, Box) { - let (tx, rx) = unbounded(); - (Box::new(SendableMainThreadScriptChan(tx)), Box::new(rx)) + pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) { + let (sender, receiver) = unbounded(); + ( + ScriptEventLoopSender::MainThread(sender), + ScriptEventLoopReceiver::MainThread(receiver), + ) + } + + pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender { + ScriptEventLoopSender::MainThread(self.script_chan.clone()) } pub fn image_cache(&self) -> Arc { @@ -2677,7 +2682,7 @@ impl Window { #[allow(clippy::too_many_arguments)] pub(crate) fn new( runtime: Rc, - script_chan: MainThreadScriptChan, + script_chan: Sender, layout: Box, font_context: Arc, image_cache_chan: Sender, @@ -2803,10 +2808,6 @@ impl Window { unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) } } - pub(crate) fn event_loop_sender(&self) -> Box { - Box::new(self.script_chan.clone()) - } - pub(crate) fn pipeline_id(&self) -> PipelineId { self.as_global_scope().pipeline_id() } diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index b2400341809..0370161582a 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -28,7 +28,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::USVString; use crate::dom::bindings::structuredclone; -use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox}; use crate::dom::dedicatedworkerglobalscope::{ DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg, }; @@ -47,8 +47,6 @@ pub type TrustedWorkerAddress = Trusted; #[dom_struct] pub struct Worker { eventtarget: EventTarget, - #[ignore_malloc_size_of = "Defined in std"] - #[no_trace] /// Sender to the Receiver associated with the DedicatedWorkerGlobalScope /// this Worker created. sender: Sender, diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 8e12f182924..d7b353e0644 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -56,8 +56,9 @@ use crate::dom::window::{base64_atob, base64_btoa}; use crate::dom::workerlocation::WorkerLocation; use crate::dom::workernavigator::WorkerNavigator; use crate::fetch; +use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender}; use crate::realms::{enter_realm, InRealm}; -use crate::script_runtime::{CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptPort}; +use crate::script_runtime::{CanGc, JSContext, Runtime}; use crate::task::TaskCanceller; use crate::timers::{IsInterval, TimerCallback}; @@ -491,7 +492,7 @@ impl WorkerGlobalScope { } } - pub fn new_script_pair(&self) -> (Box, Box) { + pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) { let dedicated = self.downcast::(); if let Some(dedicated) = dedicated { dedicated.new_script_pair() diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index b8947fe24ac..1d47c646dd5 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -49,9 +49,9 @@ use crate::dom::workletglobalscope::{ WorkletGlobalScope, WorkletGlobalScopeInit, WorkletGlobalScopeType, WorkletTask, }; use crate::fetch::load_whole_resource; -use crate::messaging::MainThreadScriptMsg; +use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg}; use crate::realms::InRealm; -use crate::script_runtime::{CanGc, CommonScriptMsg, Runtime, ScriptThreadEventCategory}; +use crate::script_runtime::{CanGc, Runtime, ScriptThreadEventCategory}; use crate::script_thread::ScriptThread; use crate::task::TaskBox; use crate::task_source::TaskSourceName; @@ -768,7 +768,6 @@ impl WorkletThread { #[derive(Clone, JSTraceable, MallocSizeOf)] pub struct WorkletExecutor { worklet_id: WorkletId, - #[ignore_malloc_size_of = "channels are hard"] #[no_trace] primary_sender: Sender, } diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index 7a25f361172..c6c6361ba25 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -21,6 +21,7 @@ use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::trace::CustomTraceable; use crate::dom::globalscope::GlobalScope; use crate::dom::paintworkletglobalscope::{PaintWorkletGlobalScope, PaintWorkletTask}; use crate::dom::testworkletglobalscope::{TestWorkletGlobalScope, TestWorkletTask}; @@ -40,8 +41,6 @@ pub struct WorkletGlobalScope { #[no_trace] base_url: ServoUrl, /// Sender back to the script thread - #[ignore_malloc_size_of = "channels are hard"] - #[no_trace] to_script_thread_sender: Sender, /// Worklet task executor executor: WorkletExecutor, diff --git a/components/script/messaging.rs b/components/script/messaging.rs index af768222fd6..7dd1db6b3ba 100644 --- a/components/script/messaging.rs +++ b/components/script/messaging.rs @@ -2,17 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use core::fmt; use std::cell::RefCell; use std::option::Option; use std::result::Result; use base::id::PipelineId; use bluetooth_traits::BluetoothRequest; -use crossbeam_channel::{select, Receiver, Sender}; +use crossbeam_channel::{select, Receiver, SendError, Sender}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg}; use ipc_channel::ipc::IpcSender; use net_traits::image_cache::PendingImageResponse; -use profile_traits::mem::{self as profile_mem, OpaqueSender}; +use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan}; use profile_traits::time::{self as profile_time}; use script_traits::{ConstellationControlMsg, LayoutMsg, Painter, ScriptMsg}; use servo_atoms::Atom; @@ -20,9 +21,13 @@ use timers::TimerScheduler; #[cfg(feature = "webgpu")] use webgpu::WebGPUMsg; -use crate::dom::serviceworker::TrustedServiceWorkerAddress; +use crate::dom::abstractworker::WorkerScriptMsg; +use crate::dom::bindings::trace::CustomTraceable; +use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerScriptMsg; +use crate::dom::serviceworkerglobalscope::ServiceWorkerScriptMsg; use crate::dom::worker::TrustedWorkerAddress; -use crate::script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; +use crate::script_runtime::ScriptThreadEventCategory; +use crate::task::TaskBox; use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; use crate::task_source::TaskSourceName; @@ -124,6 +129,106 @@ pub(crate) enum MainThreadScriptMsg { WakeUp, } +/// Common messages used to control the event loops in both the script and the worker +pub enum CommonScriptMsg { + /// Requests that the script thread measure its memory usage. The results are sent back via the + /// supplied channel. + CollectReports(ReportsChan), + /// Generic message that encapsulates event handling. + Task( + ScriptThreadEventCategory, + Box, + Option, + TaskSourceName, + ), +} + +impl fmt::Debug for CommonScriptMsg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CommonScriptMsg::CollectReports(_) => write!(f, "CollectReports(...)"), + CommonScriptMsg::Task(ref category, ref task, _, _) => { + f.debug_tuple("Task").field(category).field(task).finish() + }, + } + } +} + +/// A wrapper around various types of `Sender`s that send messages back to the event loop +/// of a script context event loop. This will either target the main `ScriptThread` event +/// loop or that of a worker. +#[derive(Clone, JSTraceable, MallocSizeOf)] +pub(crate) enum ScriptEventLoopSender { + /// A sender that sends to the main `ScriptThread` event loop. + MainThread(Sender), + /// A sender that sends to a `ServiceWorker` event loop. + ServiceWorker(Sender), + /// A sender that sends to a dedicated worker (such as a generic Web Worker) event loop. + /// Note that this sender keeps the main thread Worker DOM object alive as long as it or + /// or any message it sends is not dropped. + DedicatedWorker { + sender: Sender, + main_thread_worker: TrustedWorkerAddress, + }, +} + +impl ScriptEventLoopSender { + /// Send a message to the event loop, which might be a main thread event loop or a worker event loop. + pub(crate) fn send(&self, message: CommonScriptMsg) -> Result<(), SendError<()>> { + match self { + Self::MainThread(sender) => sender + .send(MainThreadScriptMsg::Common(message)) + .map_err(|_| SendError(())), + Self::ServiceWorker(sender) => sender + .send(ServiceWorkerScriptMsg::CommonWorker( + WorkerScriptMsg::Common(message), + )) + .map_err(|_| SendError(())), + Self::DedicatedWorker { + sender, + main_thread_worker, + } => { + let common_message = WorkerScriptMsg::Common(message); + sender + .send(DedicatedWorkerScriptMsg::CommonWorker( + main_thread_worker.clone(), + common_message, + )) + .map_err(|_| SendError(())) + }, + } + } +} + +/// A wrapper around various types of `Receiver`s that receive event loop messages. Used for +/// synchronous DOM APIs that need to abstract over multiple kinds of event loops (worker/main +/// thread) with different Receiver interfaces. +pub(crate) enum ScriptEventLoopReceiver { + /// A receiver that receives messages to the main `ScriptThread` event loop. + MainThread(Receiver), + /// A receiver that receives messages to dedicated workers (such as a generic Web Worker) event loop. + DedicatedWorker(Receiver), +} + +impl ScriptEventLoopReceiver { + pub(crate) fn recv(&self) -> Result { + match self { + Self::MainThread(receiver) => match receiver.recv() { + Ok(MainThreadScriptMsg::Common(script_msg)) => Ok(script_msg), + Ok(_) => panic!("unexpected main thread event message!"), + Err(_) => Err(()), + }, + Self::DedicatedWorker(receiver) => match receiver.recv() { + Ok(DedicatedWorkerScriptMsg::CommonWorker(_, WorkerScriptMsg::Common(message))) => { + Ok(message) + }, + Ok(_) => panic!("unexpected worker event message!"), + Err(_) => Err(()), + }, + } + } +} + impl QueuedTaskConversion for MainThreadScriptMsg { fn task_source_name(&self) -> Option<&TaskSourceName> { let script_msg = match self { @@ -182,83 +287,9 @@ impl QueuedTaskConversion for MainThreadScriptMsg { } } -impl OpaqueSender for Box { - fn send(&self, msg: CommonScriptMsg) { - ScriptChan::send(&**self, msg).unwrap(); - } -} - -impl ScriptPort for Receiver { - fn recv(&self) -> Result { - self.recv().map_err(|_| ()) - } -} - -impl ScriptPort for Receiver { - fn recv(&self) -> Result { - match self.recv() { - Ok(MainThreadScriptMsg::Common(script_msg)) => Ok(script_msg), - Ok(_) => panic!("unexpected main thread event message!"), - Err(_) => Err(()), - } - } -} - -impl ScriptPort for Receiver<(TrustedWorkerAddress, CommonScriptMsg)> { - fn recv(&self) -> Result { - self.recv().map(|(_, msg)| msg).map_err(|_| ()) - } -} - -impl ScriptPort for Receiver<(TrustedWorkerAddress, MainThreadScriptMsg)> { - fn recv(&self) -> Result { - match self.recv().map(|(_, msg)| msg) { - Ok(MainThreadScriptMsg::Common(script_msg)) => Ok(script_msg), - Ok(_) => panic!("unexpected main thread event message!"), - Err(_) => Err(()), - } - } -} - -impl ScriptPort for Receiver<(TrustedServiceWorkerAddress, CommonScriptMsg)> { - fn recv(&self) -> Result { - self.recv().map(|(_, msg)| msg).map_err(|_| ()) - } -} - -/// Encapsulates internal communication of shared messages within the script thread. -#[derive(Clone, JSTraceable)] -pub(crate) struct SendableMainThreadScriptChan(#[no_trace] pub Sender); - -impl ScriptChan for SendableMainThreadScriptChan { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.0.send(msg).map_err(|_| ()) - } - - fn as_boxed(&self) -> Box { - Box::new(SendableMainThreadScriptChan((self.0).clone())) - } -} - -/// Encapsulates internal communication of main thread messages within the script thread. -#[derive(Clone, JSTraceable)] -pub(crate) struct MainThreadScriptChan(#[no_trace] pub Sender); - -impl ScriptChan for MainThreadScriptChan { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.0 - .send(MainThreadScriptMsg::Common(msg)) - .map_err(|_| ()) - } - - fn as_boxed(&self) -> Box { - Box::new(MainThreadScriptChan((self.0).clone())) - } -} - -impl OpaqueSender for Sender { - fn send(&self, msg: CommonScriptMsg) { - self.send(MainThreadScriptMsg::Common(msg)).unwrap() +impl OpaqueSender for ScriptEventLoopSender { + fn send(&self, message: CommonScriptMsg) { + self.send(message).unwrap() } } @@ -266,7 +297,7 @@ impl OpaqueSender for Sender { pub(crate) struct ScriptThreadSenders { /// A channel to hand out to script thread-based entities that need to be able to enqueue /// events in the event queue. - pub self_sender: MainThreadScriptChan, + pub self_sender: Sender, /// A handle to the bluetooth thread. #[no_trace] diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 4563f879eef..28241117744 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -16,10 +16,9 @@ use std::os::raw::c_void; use std::rc::Rc; use std::sync::Mutex; use std::time::{Duration, Instant}; -use std::{fmt, os, ptr, thread}; +use std::{os, ptr, thread}; use background_hang_monitor_api::ScriptHangAnnotation; -use base::id::PipelineId; use content_security_policy::{CheckResult, PolicyDisposition}; use js::conversions::jsstr_to_string; use js::glue::{ @@ -50,7 +49,7 @@ use js::rust::{ }; use malloc_size_of::MallocSizeOfOps; use malloc_size_of_derive::MallocSizeOf; -use profile_traits::mem::{Report, ReportKind, ReportsChan}; +use profile_traits::mem::{Report, ReportKind}; use profile_traits::path; use profile_traits::time::ProfilerCategory; use servo_config::{opts, pref}; @@ -70,7 +69,6 @@ use crate::dom::bindings::refcounted::{ }; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::trace_roots; -use crate::dom::bindings::trace::JSTraceable; use crate::dom::bindings::utils::DOM_CALLBACKS; use crate::dom::bindings::{principals, settings_stack}; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; @@ -84,8 +82,7 @@ use crate::realms::{AlreadyInRealm, InRealm}; use crate::script_module::EnsureModuleHooksInitialized; use crate::script_thread::trace_thread; use crate::security_manager::CSPViolationReporter; -use crate::task::TaskBox; -use crate::task_source::{SendableTaskSource, TaskSourceName}; +use crate::task_source::SendableTaskSource; static JOB_QUEUE_TRAPS: JobQueueTraps = JobQueueTraps { getIncumbentGlobal: Some(get_incumbent_global), @@ -98,40 +95,6 @@ static SECURITY_CALLBACKS: JSSecurityCallbacks = JSSecurityCallbacks { subsumes: Some(principals::subsumes), }; -/// Common messages used to control the event loops in both the script and the worker -pub enum CommonScriptMsg { - /// Requests that the script thread measure its memory usage. The results are sent back via the - /// supplied channel. - CollectReports(ReportsChan), - /// Generic message that encapsulates event handling. - Task( - ScriptThreadEventCategory, - Box, - Option, - TaskSourceName, - ), -} - -impl fmt::Debug for CommonScriptMsg { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - CommonScriptMsg::CollectReports(_) => write!(f, "CollectReports(...)"), - CommonScriptMsg::Task(ref category, ref task, _, _) => { - f.debug_tuple("Task").field(category).field(task).finish() - }, - } - } -} - -/// A cloneable interface for communicating with an event loop. -pub trait ScriptChan: JSTraceable + Send { - /// Send a message to the associated event loop. - #[allow(clippy::result_unit_err)] - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()>; - /// Return a cloned version of this sender in a [`Box`]. - fn as_boxed(&self) -> Box; -} - #[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, MallocSizeOf, PartialEq)] pub enum ScriptThreadEventCategory { AttachLayout, @@ -253,14 +216,6 @@ impl From for ScriptHangAnnotation { } } -/// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM -/// APIs that need to abstract over multiple kinds of event loops (worker/main thread) with -/// different Receiver interfaces. -pub trait ScriptPort { - #[allow(clippy::result_unit_err)] - fn recv(&self) -> Result; -} - #[allow(unsafe_code)] unsafe extern "C" fn get_incumbent_global(_: *const c_void, _: *mut RawJSContext) -> *mut JSObject { let mut result = ptr::null_mut(); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 5b0c201764a..6587c604d8b 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -142,15 +142,14 @@ use crate::dom::worklet::WorkletThreadPool; use crate::dom::workletglobalscope::WorkletGlobalScopeInit; use crate::fetch::FetchCanceller; use crate::messaging::{ - MainThreadScriptChan, MainThreadScriptMsg, MixedMessage, ScriptThreadReceivers, - ScriptThreadSenders, + CommonScriptMsg, MainThreadScriptMsg, MixedMessage, ScriptEventLoopSender, + ScriptThreadReceivers, ScriptThreadSenders, }; use crate::microtask::{Microtask, MicrotaskQueue}; use crate::realms::enter_realm; use crate::script_module::ScriptFetchOptions; use crate::script_runtime::{ - CanGc, CommonScriptMsg, JSContext, Runtime, ScriptChan, ScriptThreadEventCategory, - ThreadSafeJSContext, + CanGc, JSContext, Runtime, ScriptThreadEventCategory, ThreadSafeJSContext, }; use crate::task_queue::TaskQueue; use crate::task_source::{SendableTaskSource, TaskSourceName}; @@ -531,7 +530,7 @@ impl ScriptThreadFactory for ScriptThread { .send(()); }, reporter_name, - script_thread.senders.self_sender.0.clone(), + ScriptEventLoopSender::MainThread(script_thread.senders.self_sender.clone()), CommonScriptMsg::CollectReports, ); @@ -795,7 +794,7 @@ impl ScriptThread { .borrow_mut() .get_or_insert_with(|| { let init = WorkletGlobalScopeInit { - to_script_thread_sender: script_thread.senders.self_sender.0.clone(), + to_script_thread_sender: script_thread.senders.self_sender.clone(), resource_threads: script_thread.resource_threads.clone(), mem_profiler_chan: script_thread.senders.memory_profiler_sender.clone(), time_profiler_chan: script_thread.senders.time_profiler_sender.clone(), @@ -900,9 +899,8 @@ impl ScriptThread { opts.output_file.is_some() || opts.exit_after_load || opts.webdriver_port.is_some(); let (self_sender, self_receiver) = unbounded(); - let self_sender = MainThreadScriptChan(self_sender.clone()); let runtime = Runtime::new(Some(SendableTaskSource { - sender: self_sender.as_boxed(), + sender: ScriptEventLoopSender::MainThread(self_sender.clone()), pipeline_id: state.id, name: TaskSourceName::Networking, canceller: Default::default(), @@ -927,7 +925,7 @@ impl ScriptThread { .unwrap_or_else(crossbeam_channel::never); let (image_cache_sender, image_cache_receiver) = unbounded(); - let task_queue = TaskQueue::new(self_receiver, self_sender.0.clone()); + let task_queue = TaskQueue::new(self_receiver, self_sender.clone()); let closing = Arc::new(AtomicBool::new(false)); let background_hang_monitor_exit_signal = BHMExitSignal { diff --git a/components/script/task_manager.rs b/components/script/task_manager.rs index f150053f6af..3a5498b9a5f 100644 --- a/components/script/task_manager.rs +++ b/components/script/task_manager.rs @@ -9,7 +9,7 @@ use std::collections::HashMap; use base::id::PipelineId; -use crate::script_runtime::ScriptChan; +use crate::messaging::ScriptEventLoopSender; use crate::task::TaskCanceller; use crate::task_source::{TaskSource, TaskSourceName}; @@ -75,8 +75,7 @@ macro_rules! task_source_functions { #[derive(JSTraceable, MallocSizeOf)] pub(crate) struct TaskManager { - #[ignore_malloc_size_of = "We need to push the measurement of this down into the ScriptChan trait"] - sender: RefCell>>, + sender: RefCell>, #[no_trace] pipeline_id: PipelineId, cancellers: TaskCancellers, @@ -84,7 +83,7 @@ pub(crate) struct TaskManager { impl TaskManager { pub(crate) fn new( - sender: Option>, + sender: Option, pipeline_id: PipelineId, shared_canceller: Option, ) -> Self { @@ -105,7 +104,7 @@ impl TaskManager { self.pipeline_id } - pub(crate) fn sender(&self) -> Ref>> { + pub(crate) fn sender(&self) -> Ref> { self.sender.borrow() } @@ -116,7 +115,7 @@ impl TaskManager { /// Update the sender for this [`TaskSource`]. This is used by dedicated workers, which only have a /// sender while handling messages (as their sender prevents the main thread Worker object from being /// garbage collected). - pub(crate) fn set_sender(&self, sender: Option>) { + pub(crate) fn set_sender(&self, sender: Option) { *self.sender.borrow_mut() = sender; } diff --git a/components/script/task_source.rs b/components/script/task_source.rs index ffde2691523..0b9bf41e3be 100644 --- a/components/script/task_source.rs +++ b/components/script/task_source.rs @@ -11,7 +11,8 @@ use servo_atoms::Atom; use crate::dom::bindings::refcounted::Trusted; use crate::dom::event::{EventBubbles, EventCancelable, EventTask, SimpleEventTask}; use crate::dom::eventtarget::EventTarget; -use crate::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory}; +use crate::messaging::{CommonScriptMsg, ScriptEventLoopSender}; +use crate::script_runtime::ScriptThreadEventCategory; use crate::task::{TaskCanceller, TaskOnce}; use crate::task_manager::TaskManager; @@ -141,7 +142,7 @@ impl TaskSource<'_> { let sender = sender .as_ref() .expect("Tried to enqueue task for DedicatedWorker while not handling a message.") - .as_boxed(); + .clone(); SendableTaskSource { sender, pipeline_id: self.task_manager.pipeline_id(), @@ -159,8 +160,7 @@ impl<'task_manager> From> for SendableTaskSource { #[derive(JSTraceable, MallocSizeOf)] pub(crate) struct SendableTaskSource { - #[ignore_malloc_size_of = "Need to push MallocSizeOf down into the ScriptChan trait implementations"] - pub sender: Box, + pub sender: ScriptEventLoopSender, #[no_trace] pub pipeline_id: PipelineId, pub name: TaskSourceName, @@ -197,7 +197,7 @@ impl SendableTaskSource { impl Clone for SendableTaskSource { fn clone(&self) -> Self { Self { - sender: self.sender.as_boxed(), + sender: self.sender.clone(), pipeline_id: self.pipeline_id, name: self.name, canceller: self.canceller.clone(),