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