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