script/
task_manager.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use core::cell::RefCell;
6use core::sync::atomic::Ordering;
7use std::cell::Ref;
8use std::collections::HashMap;
9
10use base::id::PipelineId;
11use strum::VariantArray;
12
13use crate::messaging::ScriptEventLoopSender;
14use crate::task::TaskCanceller;
15use crate::task_source::{TaskSource, TaskSourceName};
16
17#[derive(JSTraceable, MallocSizeOf)]
18enum TaskCancellers {
19    /// A shared canceller that is used for workers, which can create multiple TaskManagers, but all
20    /// of them need to have the same canceller flag for all task sources.
21    Shared(TaskCanceller),
22    /// For `Window` each `TaskSource` has its own canceller.
23    OnePerTaskSource(RefCell<HashMap<TaskSourceName, TaskCanceller>>),
24}
25
26impl TaskCancellers {
27    fn get(&self, name: TaskSourceName) -> TaskCanceller {
28        match self {
29            Self::Shared(canceller) => canceller.clone(),
30            Self::OnePerTaskSource(map) => map.borrow_mut().entry(name).or_default().clone(),
31        }
32    }
33
34    fn cancel_all_tasks_and_ignore_future_tasks(&self) {
35        match self {
36            Self::Shared(canceller) => canceller.cancelled.store(true, Ordering::SeqCst),
37            Self::OnePerTaskSource(..) => {
38                // We must create the canceller if they aren't created because we want future
39                // tasks to be ignored completely.
40                for task_source_name in TaskSourceName::VARIANTS.iter() {
41                    self.get(*task_source_name)
42                        .cancelled
43                        .store(true, Ordering::SeqCst)
44                }
45            },
46        }
47    }
48
49    fn cancel_pending_tasks_for_source(&self, task_source_name: TaskSourceName) {
50        let Self::OnePerTaskSource(map) = self else {
51            unreachable!(
52                "It isn't possible to cancel pending tasks for Worker \
53                 TaskManager's without ignoring future tasks."
54            )
55        };
56
57        // Remove the canceller from the map so that the next time a task like this is
58        // queued, it has a fresh, uncancelled canceller.
59        if let Some(canceller) = map.borrow_mut().remove(&task_source_name) {
60            // Cancel any tasks that use the current canceller.
61            canceller.cancelled.store(true, Ordering::SeqCst);
62        }
63    }
64}
65
66macro_rules! task_source_functions {
67    ($self:ident, $task_source:ident, $task_source_name:ident) => {
68        pub(crate) fn $task_source(&$self) -> TaskSource<'_> {
69            TaskSource {
70                task_manager: $self,
71                name: TaskSourceName::$task_source_name,
72            }
73        }
74    };
75}
76
77#[derive(JSTraceable, MallocSizeOf)]
78pub(crate) struct TaskManager {
79    sender: RefCell<Option<ScriptEventLoopSender>>,
80    #[no_trace]
81    pipeline_id: PipelineId,
82    cancellers: TaskCancellers,
83}
84
85impl TaskManager {
86    pub(crate) fn new(
87        sender: Option<ScriptEventLoopSender>,
88        pipeline_id: PipelineId,
89        shared_canceller: Option<TaskCanceller>,
90    ) -> Self {
91        let cancellers = match shared_canceller {
92            Some(shared_canceller) => TaskCancellers::Shared(shared_canceller),
93            None => TaskCancellers::OnePerTaskSource(Default::default()),
94        };
95        let sender = RefCell::new(sender);
96
97        TaskManager {
98            sender,
99            pipeline_id,
100            cancellers,
101        }
102    }
103
104    pub(crate) fn pipeline_id(&self) -> PipelineId {
105        self.pipeline_id
106    }
107
108    pub(crate) fn sender(&self) -> Ref<'_, Option<ScriptEventLoopSender>> {
109        self.sender.borrow()
110    }
111
112    pub(crate) fn canceller(&self, name: TaskSourceName) -> TaskCanceller {
113        self.cancellers.get(name)
114    }
115
116    /// Update the sender for this [`TaskSource`]. This is used by dedicated workers, which only have a
117    /// sender while handling messages (as their sender prevents the main thread Worker object from being
118    /// garbage collected).
119    pub(crate) fn set_sender(&self, sender: Option<ScriptEventLoopSender>) {
120        *self.sender.borrow_mut() = sender;
121    }
122
123    /// Cancel all queued but unexecuted tasks and ignore all subsequently queued tasks.
124    pub(crate) fn cancel_all_tasks_and_ignore_future_tasks(&self) {
125        self.cancellers.cancel_all_tasks_and_ignore_future_tasks();
126    }
127
128    /// Cancel all queued but unexecuted tasks for the given task source, but subsequently queued
129    /// tasks will not be ignored.
130    pub(crate) fn cancel_pending_tasks_for_source(&self, task_source_name: TaskSourceName) {
131        self.cancellers
132            .cancel_pending_tasks_for_source(task_source_name);
133    }
134
135    task_source_functions!(self, bitmap_task_source, Bitmap);
136    task_source_functions!(self, canvas_blob_task_source, Canvas);
137    task_source_functions!(self, clipboard_task_source, Clipboard);
138    task_source_functions!(self, database_access_task_source, DatabaseAccess);
139    task_source_functions!(self, dom_manipulation_task_source, DOMManipulation);
140    task_source_functions!(self, file_reading_task_source, FileReading);
141    task_source_functions!(self, font_loading_task_source, FontLoading);
142    task_source_functions!(self, gamepad_task_source, Gamepad);
143    task_source_functions!(self, media_element_task_source, MediaElement);
144    task_source_functions!(self, networking_task_source, Networking);
145    task_source_functions!(self, performance_timeline_task_source, PerformanceTimeline);
146    task_source_functions!(self, port_message_queue, PortMessage);
147    task_source_functions!(self, remote_event_task_source, RemoteEvent);
148    task_source_functions!(self, timer_task_source, Timer);
149    task_source_functions!(self, user_interaction_task_source, UserInteraction);
150    task_source_functions!(self, websocket_task_source, WebSocket);
151    task_source_functions!(
152        self,
153        intersection_observer_task_source,
154        IntersectionObserver
155    );
156    task_source_functions!(self, webgpu_task_source, WebGPU);
157}