script/dom/workers/
abstractworkerglobalscope.rs1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use crossbeam_channel::{Receiver, select};
8use devtools_traits::DevtoolScriptControlMsg;
9use rustc_hash::FxHashSet;
10use script_bindings::reflector::DomObject;
11use servo_base::generic_channel::RoutedReceiver;
12use servo_constellation_traits::WorkerAnimationFrameTick;
13
14use crate::dom::bindings::conversions::DerivedFrom;
15use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset;
16use crate::dom::globalscope::GlobalScope;
17use crate::dom::worker::TrustedWorkerAddress;
18use crate::dom::workerglobalscope::WorkerGlobalScope;
19use crate::realms::enter_auto_realm;
20use crate::tasks::task_queue::{QueuedTaskConversion, TaskQueue};
21
22pub(crate) trait WorkerEventLoopMethods {
23 type WorkerMsg: QueuedTaskConversion + Send;
24 type ControlMsg;
25 type Event;
26 fn task_queue(&self) -> &TaskQueue<Self::WorkerMsg>;
27 fn handle_event(&self, event: Self::Event, cx: &mut js::context::JSContext) -> bool;
28 fn handle_worker_post_event(
29 &self,
30 worker: &TrustedWorkerAddress,
31 ) -> Option<AutoWorkerReset<'_>>;
32 fn from_control_msg(msg: Self::ControlMsg) -> Self::Event;
33 fn from_worker_msg(msg: Self::WorkerMsg) -> Self::Event;
34 fn from_devtools_msg(msg: DevtoolScriptControlMsg) -> Self::Event;
35 fn from_timer_msg() -> Self::Event;
36 fn from_animation_frame_tick_msg(_msg: WorkerAnimationFrameTick) -> Option<Self::Event> {
37 None
38 }
39 fn animation_frame_tick_receiver(&self) -> Option<&RoutedReceiver<WorkerAnimationFrameTick>> {
40 None
41 }
42 fn control_receiver(&self) -> &Receiver<Self::ControlMsg>;
43}
44
45pub(crate) fn run_worker_event_loop<T, WorkerMsg, Event>(
47 worker_scope: &T,
48 worker: Option<&TrustedWorkerAddress>,
49 cx: &mut js::context::JSContext,
50) where
51 WorkerMsg: QueuedTaskConversion + Send,
52 T: WorkerEventLoopMethods<WorkerMsg = WorkerMsg, Event = Event>
53 + DerivedFrom<WorkerGlobalScope>
54 + DerivedFrom<GlobalScope>
55 + DomObject,
56{
57 let scope = worker_scope.upcast::<WorkerGlobalScope>();
58 let task_queue = worker_scope.task_queue();
59
60 let devtools_never = crossbeam_channel::never();
61 let devtools_receiver = scope.devtools_receiver().unwrap_or(&devtools_never);
62 let animation_frame_tick_never = crossbeam_channel::never();
63 let animation_frame_tick_receiver = worker_scope
64 .animation_frame_tick_receiver()
65 .unwrap_or(&animation_frame_tick_never);
66
67 let event = select! {
68 recv(worker_scope.control_receiver()) -> msg => match msg {
69 Ok(msg) => Some(T::from_control_msg(msg)),
70 Err(_) => None,
71 },
72 recv(task_queue.select()) -> msg => match msg {
73 Ok(msg) => {
74 task_queue.take_tasks(msg, &FxHashSet::default());
75 task_queue.recv().ok().map(T::from_worker_msg)
76 },
77 Err(_) => None,
78 },
79 recv(devtools_receiver) -> msg => match msg {
80 Ok(msg) => msg.ok().map(T::from_devtools_msg),
81 Err(_) => None,
82 },
83 recv(animation_frame_tick_receiver) -> msg => match msg {
84 Ok(Ok(msg)) => T::from_animation_frame_tick_msg(msg),
85 Ok(Err(_)) | Err(_) => None,
86 },
87 recv(scope.timer_scheduler().wait_channel()) -> _ => Some(T::from_timer_msg()),
88 };
89
90 let Some(event) = event else {
94 scope.close();
95 return;
96 };
97
98 scope.timer_scheduler().dispatch_completed_timers();
99
100 let mut sequential = vec![event];
101
102 while !scope.is_closing() {
108 match task_queue.take_tasks_and_recv(&FxHashSet::default()) {
111 Err(_) => match devtools_receiver.try_recv() {
112 Ok(message) => sequential.push(T::from_devtools_msg(message.unwrap())),
113 Err(_) => break,
114 },
115 Ok(ev) => sequential.push(T::from_worker_msg(ev)),
116 }
117 }
118
119 for event in sequential {
121 let mut realm = enter_auto_realm(cx, worker_scope);
122 let cx = &mut realm.current_realm();
123 let _ar = match worker {
124 Some(worker) => worker_scope.handle_worker_post_event(worker),
125 None => None,
126 };
127 if !worker_scope.handle_event(event, cx) {
128 return;
130 }
131 scope.perform_a_microtask_checkpoint(cx);
133 }
134 worker_scope
135 .upcast::<GlobalScope>()
136 .perform_a_dom_garbage_collection_checkpoint();
137}