aboutsummaryrefslogtreecommitdiffstats
path: root/components/shared/msg
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2023-10-05 19:47:39 +0200
committerMartin Robinson <mrobinson@igalia.com>2023-11-03 15:38:18 +0000
commitf4d3af296c05260dfbb3deea4f8fa400cb6887d3 (patch)
tree169db2cc68e01a755b30500dd525f1a2ec2da861 /components/shared/msg
parent863529d9622c68f0a9535225237eb5e5c5b8c757 (diff)
downloadservo-f4d3af296c05260dfbb3deea4f8fa400cb6887d3.tar.gz
servo-f4d3af296c05260dfbb3deea4f8fa400cb6887d3.zip
Move `*_traits` and other shared types to `shared`
This is the start of the organization of types that are in their own crates in order to break dependency cycles between other crates. The idea here is that putting these packages into their own directory is the first step toward cleaning them up. They have grown organically and it is difficult to explain to new folks where to put new shared types. Many of these crates contain more than traits or don't contain traits at all. Notably, `script_traits` isn't touched because it is vendored from Gecko. Eventually this will move to `third_party`.
Diffstat (limited to 'components/shared/msg')
-rw-r--r--components/shared/msg/Cargo.toml23
-rw-r--r--components/shared/msg/constellation_msg.rs738
-rw-r--r--components/shared/msg/lib.rs7
3 files changed, 768 insertions, 0 deletions
diff --git a/components/shared/msg/Cargo.toml b/components/shared/msg/Cargo.toml
new file mode 100644
index 00000000000..d64880b5c6f
--- /dev/null
+++ b/components/shared/msg/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "msg"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+license = "MPL-2.0"
+edition = "2018"
+publish = false
+
+[lib]
+name = "msg"
+path = "lib.rs"
+test = false
+doctest = false
+
+[dependencies]
+ipc-channel = { workspace = true }
+lazy_static = { workspace = true }
+malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of_derive = { workspace = true }
+parking_lot = { workspace = true }
+serde = { workspace = true }
+size_of_test = { path = "../../size_of_test" }
+webrender_api = { workspace = true }
diff --git a/components/shared/msg/constellation_msg.rs b/components/shared/msg/constellation_msg.rs
new file mode 100644
index 00000000000..335fffe5f62
--- /dev/null
+++ b/components/shared/msg/constellation_msg.rs
@@ -0,0 +1,738 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! The high-level interface from script to constellation. Using this abstract interface helps
+//! reduce coupling between these two components.
+
+use std::cell::Cell;
+use std::num::NonZeroU32;
+use std::sync::Arc;
+use std::time::Duration;
+use std::{fmt, mem};
+
+use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
+use lazy_static::lazy_static;
+use malloc_size_of::malloc_size_of_is_0;
+use malloc_size_of_derive::MallocSizeOf;
+use parking_lot::Mutex;
+use serde::{Deserialize, Serialize};
+use size_of_test::size_of_test;
+use webrender_api::{ExternalScrollId, PipelineId as WebRenderPipelineId};
+
+macro_rules! namespace_id_method {
+ ($func_name:ident, $func_return_data_type:ident, $self:ident, $index_name:ident) => {
+ fn $func_name(&mut $self) -> $func_return_data_type {
+ $func_return_data_type {
+ namespace_id: $self.id,
+ index: $index_name($self.next_index()),
+ }
+ }
+ };
+}
+
+macro_rules! namespace_id {
+ ($id_name:ident, $index_name:ident) => {
+ #[derive(
+ Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
+ )]
+ pub struct $index_name(pub NonZeroU32);
+ malloc_size_of_is_0!($index_name);
+
+ #[derive(
+ Clone,
+ Copy,
+ Debug,
+ Deserialize,
+ Eq,
+ Hash,
+ MallocSizeOf,
+ Ord,
+ PartialEq,
+ PartialOrd,
+ Serialize,
+ )]
+ pub struct $id_name {
+ pub namespace_id: PipelineNamespaceId,
+ pub index: $index_name,
+ }
+ };
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum TraversalDirection {
+ Forward(usize),
+ Back(usize),
+}
+
+#[derive(Debug, Deserialize, Serialize)]
+/// Request a pipeline-namespace id from the constellation.
+pub struct PipelineNamespaceRequest(pub IpcSender<PipelineNamespaceId>);
+
+/// A per-process installer of pipeline-namespaces.
+pub struct PipelineNamespaceInstaller {
+ request_sender: Option<IpcSender<PipelineNamespaceRequest>>,
+ namespace_sender: IpcSender<PipelineNamespaceId>,
+ namespace_receiver: IpcReceiver<PipelineNamespaceId>,
+}
+
+impl PipelineNamespaceInstaller {
+ pub fn new() -> Self {
+ let (namespace_sender, namespace_receiver) =
+ ipc::channel().expect("PipelineNamespaceInstaller ipc channel failure");
+ PipelineNamespaceInstaller {
+ request_sender: None,
+ namespace_sender: namespace_sender,
+ namespace_receiver: namespace_receiver,
+ }
+ }
+
+ /// Provide a request sender to send requests to the constellation.
+ pub fn set_sender(&mut self, sender: IpcSender<PipelineNamespaceRequest>) {
+ self.request_sender = Some(sender);
+ }
+
+ /// Install a namespace, requesting a new Id from the constellation.
+ pub fn install_namespace(&self) {
+ match self.request_sender.as_ref() {
+ Some(sender) => {
+ let _ = sender.send(PipelineNamespaceRequest(self.namespace_sender.clone()));
+ let namespace_id = self
+ .namespace_receiver
+ .recv()
+ .expect("The constellation to make a pipeline namespace id available");
+ PipelineNamespace::install(namespace_id);
+ },
+ None => unreachable!("PipelineNamespaceInstaller should have a request_sender setup"),
+ }
+ }
+}
+
+lazy_static! {
+ /// A per-process unique pipeline-namespace-installer.
+ /// Accessible via PipelineNamespace.
+ ///
+ /// Use PipelineNamespace::set_installer_sender to initiate with a sender to the constellation,
+ /// when a new process has been created.
+ ///
+ /// Use PipelineNamespace::fetch_install to install a unique pipeline-namespace from the calling thread.
+ static ref PIPELINE_NAMESPACE_INSTALLER: Arc<Mutex<PipelineNamespaceInstaller>> =
+ Arc::new(Mutex::new(PipelineNamespaceInstaller::new()));
+}
+
+/// Each pipeline ID needs to be unique. However, it also needs to be possible to
+/// generate the pipeline ID from an iframe element (this simplifies a lot of other
+/// code that makes use of pipeline IDs).
+///
+/// To achieve this, each pipeline index belongs to a particular namespace. There is
+/// a namespace for the constellation thread, and also one for every script thread.
+///
+/// A namespace can be installed for any other thread in a process
+/// where an pipeline-installer has been initialized.
+///
+/// This allows pipeline IDs to be generated by any of those threads without conflicting
+/// with pipeline IDs created by other script threads or the constellation. The
+/// constellation is the only code that is responsible for creating new *namespaces*.
+/// This ensures that namespaces are always unique, even when using multi-process mode.
+///
+/// It may help conceptually to think of the namespace ID as an identifier for the
+/// thread that created this pipeline ID - however this is really an implementation
+/// detail so shouldn't be relied upon in code logic. It's best to think of the
+/// pipeline ID as a simple unique identifier that doesn't convey any more information.
+#[derive(Clone, Copy)]
+pub struct PipelineNamespace {
+ id: PipelineNamespaceId,
+ index: u32,
+}
+
+impl PipelineNamespace {
+ /// Install a namespace for a given Id.
+ pub fn install(namespace_id: PipelineNamespaceId) {
+ PIPELINE_NAMESPACE.with(|tls| {
+ assert!(tls.get().is_none());
+ tls.set(Some(PipelineNamespace {
+ id: namespace_id,
+ index: 0,
+ }));
+ });
+ }
+
+ /// Setup the pipeline-namespace-installer, by providing it with a sender to the constellation.
+ /// Idempotent in single-process mode.
+ pub fn set_installer_sender(sender: IpcSender<PipelineNamespaceRequest>) {
+ PIPELINE_NAMESPACE_INSTALLER.lock().set_sender(sender);
+ }
+
+ /// Install a namespace in the current thread, without requiring having a namespace Id ready.
+ /// Panics if called more than once per thread.
+ pub fn auto_install() {
+ // Note that holding the lock for the duration of the call is irrelevant to performance,
+ // since a thread would have to block on the ipc-response from the constellation,
+ // with the constellation already acting as a global lock on namespace ids,
+ // and only being able to handle one request at a time.
+ //
+ // Hence, any other thread attempting to concurrently install a namespace
+ // would have to wait for the current call to finish, regardless of the lock held here.
+ PIPELINE_NAMESPACE_INSTALLER.lock().install_namespace();
+ }
+
+ fn next_index(&mut self) -> NonZeroU32 {
+ self.index += 1;
+ NonZeroU32::new(self.index).expect("pipeline id index wrapped!")
+ }
+
+ namespace_id_method! {next_pipeline_id, PipelineId, self, PipelineIndex}
+ namespace_id_method! {next_browsing_context_id, BrowsingContextId, self, BrowsingContextIndex}
+ namespace_id_method! {next_history_state_id, HistoryStateId, self, HistoryStateIndex}
+ namespace_id_method! {next_message_port_id, MessagePortId, self, MessagePortIndex}
+ namespace_id_method! {next_message_port_router_id, MessagePortRouterId, self, MessagePortRouterIndex}
+ namespace_id_method! {next_broadcast_channel_router_id, BroadcastChannelRouterId, self, BroadcastChannelRouterIndex}
+ namespace_id_method! {next_service_worker_id, ServiceWorkerId, self, ServiceWorkerIndex}
+ namespace_id_method! {next_service_worker_registration_id, ServiceWorkerRegistrationId,
+ self, ServiceWorkerRegistrationIndex}
+ namespace_id_method! {next_blob_id, BlobId, self, BlobIndex}
+}
+
+thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = Cell::new(None));
+
+#[derive(
+ Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
+)]
+pub struct PipelineNamespaceId(pub u32);
+
+namespace_id! {PipelineId, PipelineIndex}
+
+size_of_test!(PipelineId, 8);
+size_of_test!(Option<PipelineId>, 8);
+
+impl PipelineId {
+ pub fn new() -> PipelineId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let new_pipeline_id = namespace.next_pipeline_id();
+ tls.set(Some(namespace));
+ new_pipeline_id
+ })
+ }
+
+ pub fn to_webrender(&self) -> WebRenderPipelineId {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let PipelineIndex(index) = self.index;
+ WebRenderPipelineId(namespace_id, index.get())
+ }
+
+ #[allow(unsafe_code)]
+ pub fn from_webrender(pipeline: WebRenderPipelineId) -> PipelineId {
+ let WebRenderPipelineId(namespace_id, index) = pipeline;
+ unsafe {
+ PipelineId {
+ namespace_id: PipelineNamespaceId(namespace_id),
+ index: PipelineIndex(NonZeroU32::new_unchecked(index)),
+ }
+ }
+ }
+
+ pub fn root_scroll_id(&self) -> webrender_api::ExternalScrollId {
+ ExternalScrollId(0, self.to_webrender())
+ }
+}
+
+impl fmt::Display for PipelineId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let PipelineIndex(index) = self.index;
+ write!(fmt, "({},{})", namespace_id, index.get())
+ }
+}
+
+namespace_id! {BrowsingContextId, BrowsingContextIndex}
+
+size_of_test!(BrowsingContextId, 8);
+size_of_test!(Option<BrowsingContextId>, 8);
+
+impl BrowsingContextId {
+ pub fn new() -> BrowsingContextId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let new_browsing_context_id = namespace.next_browsing_context_id();
+ tls.set(Some(namespace));
+ new_browsing_context_id
+ })
+ }
+}
+
+impl fmt::Display for BrowsingContextId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let BrowsingContextIndex(index) = self.index;
+ write!(fmt, "({},{})", namespace_id, index.get())
+ }
+}
+
+#[derive(Clone, Default, Eq, Hash, PartialEq)]
+pub struct BrowsingContextGroupId(pub u32);
+
+thread_local!(pub static TOP_LEVEL_BROWSING_CONTEXT_ID: Cell<Option<TopLevelBrowsingContextId>> = Cell::new(None));
+
+#[derive(
+ Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
+)]
+pub struct TopLevelBrowsingContextId(pub BrowsingContextId);
+
+size_of_test!(TopLevelBrowsingContextId, 8);
+size_of_test!(Option<TopLevelBrowsingContextId>, 8);
+
+impl TopLevelBrowsingContextId {
+ pub fn new() -> TopLevelBrowsingContextId {
+ TopLevelBrowsingContextId(BrowsingContextId::new())
+ }
+ /// Each script and layout thread should have the top-level browsing context id installed,
+ /// since it is used by crash reporting.
+ pub fn install(id: TopLevelBrowsingContextId) {
+ TOP_LEVEL_BROWSING_CONTEXT_ID.with(|tls| tls.set(Some(id)))
+ }
+
+ pub fn installed() -> Option<TopLevelBrowsingContextId> {
+ TOP_LEVEL_BROWSING_CONTEXT_ID.with(|tls| tls.get())
+ }
+}
+
+impl fmt::Display for TopLevelBrowsingContextId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(fmt)
+ }
+}
+
+impl From<TopLevelBrowsingContextId> for BrowsingContextId {
+ fn from(id: TopLevelBrowsingContextId) -> BrowsingContextId {
+ id.0
+ }
+}
+
+impl PartialEq<TopLevelBrowsingContextId> for BrowsingContextId {
+ fn eq(&self, rhs: &TopLevelBrowsingContextId) -> bool {
+ self.eq(&rhs.0)
+ }
+}
+
+impl PartialEq<BrowsingContextId> for TopLevelBrowsingContextId {
+ fn eq(&self, rhs: &BrowsingContextId) -> bool {
+ self.0.eq(rhs)
+ }
+}
+
+namespace_id! {MessagePortId, MessagePortIndex}
+
+impl MessagePortId {
+ pub fn new() -> MessagePortId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_message_port_id = namespace.next_message_port_id();
+ tls.set(Some(namespace));
+ next_message_port_id
+ })
+ }
+}
+
+impl fmt::Display for MessagePortId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let MessagePortIndex(index) = self.index;
+ write!(fmt, "({},{})", namespace_id, index.get())
+ }
+}
+
+namespace_id! {MessagePortRouterId, MessagePortRouterIndex}
+
+impl MessagePortRouterId {
+ pub fn new() -> MessagePortRouterId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_message_port_router_id = namespace.next_message_port_router_id();
+ tls.set(Some(namespace));
+ next_message_port_router_id
+ })
+ }
+}
+
+impl fmt::Display for MessagePortRouterId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let MessagePortRouterIndex(index) = self.index;
+ write!(fmt, "({},{})", namespace_id, index.get())
+ }
+}
+
+namespace_id! {BroadcastChannelRouterId, BroadcastChannelRouterIndex}
+
+impl BroadcastChannelRouterId {
+ pub fn new() -> BroadcastChannelRouterId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_broadcast_channel_router_id = namespace.next_broadcast_channel_router_id();
+ tls.set(Some(namespace));
+ next_broadcast_channel_router_id
+ })
+ }
+}
+
+impl fmt::Display for BroadcastChannelRouterId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let BroadcastChannelRouterIndex(index) = self.index;
+ write!(
+ fmt,
+ "(BroadcastChannelRouterId{},{})",
+ namespace_id,
+ index.get()
+ )
+ }
+}
+
+namespace_id! {ServiceWorkerId, ServiceWorkerIndex}
+
+impl ServiceWorkerId {
+ pub fn new() -> ServiceWorkerId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_service_worker_id = namespace.next_service_worker_id();
+ tls.set(Some(namespace));
+ next_service_worker_id
+ })
+ }
+}
+
+impl fmt::Display for ServiceWorkerId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let ServiceWorkerIndex(index) = self.index;
+ write!(fmt, "(ServiceWorkerId{},{})", namespace_id, index.get())
+ }
+}
+
+namespace_id! {ServiceWorkerRegistrationId, ServiceWorkerRegistrationIndex}
+
+impl ServiceWorkerRegistrationId {
+ pub fn new() -> ServiceWorkerRegistrationId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_service_worker_registration_id =
+ namespace.next_service_worker_registration_id();
+ tls.set(Some(namespace));
+ next_service_worker_registration_id
+ })
+ }
+}
+
+impl fmt::Display for ServiceWorkerRegistrationId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let ServiceWorkerRegistrationIndex(index) = self.index;
+ write!(
+ fmt,
+ "(ServiceWorkerRegistrationId{},{})",
+ namespace_id,
+ index.get()
+ )
+ }
+}
+
+namespace_id! {BlobId, BlobIndex}
+
+impl BlobId {
+ pub fn new() -> BlobId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_blob_id = namespace.next_blob_id();
+ tls.set(Some(namespace));
+ next_blob_id
+ })
+ }
+}
+
+impl fmt::Display for BlobId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let BlobIndex(index) = self.index;
+ write!(fmt, "({},{})", namespace_id, index.get())
+ }
+}
+
+namespace_id! {HistoryStateId, HistoryStateIndex}
+
+impl HistoryStateId {
+ pub fn new() -> HistoryStateId {
+ PIPELINE_NAMESPACE.with(|tls| {
+ let mut namespace = tls.get().expect("No namespace set for this thread!");
+ let next_history_state_id = namespace.next_history_state_id();
+ tls.set(Some(namespace));
+ next_history_state_id
+ })
+ }
+}
+
+impl fmt::Display for HistoryStateId {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let PipelineNamespaceId(namespace_id) = self.namespace_id;
+ let HistoryStateIndex(index) = self.index;
+ write!(fmt, "({},{})", namespace_id, index.get())
+ }
+}
+
+// We provide ids just for unit testing.
+pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234);
+#[allow(unsafe_code)]
+pub const TEST_PIPELINE_INDEX: PipelineIndex =
+ unsafe { PipelineIndex(NonZeroU32::new_unchecked(5678)) };
+pub const TEST_PIPELINE_ID: PipelineId = PipelineId {
+ namespace_id: TEST_NAMESPACE,
+ index: TEST_PIPELINE_INDEX,
+};
+#[allow(unsafe_code)]
+pub const TEST_BROWSING_CONTEXT_INDEX: BrowsingContextIndex =
+ unsafe { BrowsingContextIndex(NonZeroU32::new_unchecked(8765)) };
+pub const TEST_BROWSING_CONTEXT_ID: BrowsingContextId = BrowsingContextId {
+ namespace_id: TEST_NAMESPACE,
+ index: TEST_BROWSING_CONTEXT_INDEX,
+};
+
+// Used to specify the kind of input method editor appropriate to edit a field.
+// This is a subset of htmlinputelement::InputType because some variants of InputType
+// don't make sense in this context.
+#[derive(Debug, Deserialize, Serialize)]
+pub enum InputMethodType {
+ Color,
+ Date,
+ DatetimeLocal,
+ Email,
+ Month,
+ Number,
+ Password,
+ Search,
+ Tel,
+ Text,
+ Time,
+ Url,
+ Week,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+/// The equivalent of script_layout_interface::message::Msg
+pub enum LayoutHangAnnotation {
+ AddStylesheet,
+ RemoveStylesheet,
+ SetQuirksMode,
+ Reflow,
+ GetRPC,
+ CollectReports,
+ PrepareToExit,
+ ExitNow,
+ GetCurrentEpoch,
+ GetWebFontLoadState,
+ CreateLayoutThread,
+ SetFinalUrl,
+ SetScrollStates,
+ UpdateScrollStateFromScript,
+ RegisterPaint,
+ SetNavigationStart,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+/// The equivalent of script::script_runtime::ScriptEventCategory
+pub enum ScriptHangAnnotation {
+ AttachLayout,
+ ConstellationMsg,
+ DevtoolsMsg,
+ DocumentEvent,
+ DomEvent,
+ FileRead,
+ FormPlannedNavigation,
+ ImageCacheMsg,
+ InputEvent,
+ HistoryEvent,
+ NetworkEvent,
+ Resize,
+ ScriptEvent,
+ SetScrollState,
+ SetViewport,
+ StylesheetLoad,
+ TimerEvent,
+ UpdateReplacedElement,
+ WebSocketEvent,
+ WorkerEvent,
+ WorkletEvent,
+ ServiceWorkerEvent,
+ EnterFullscreen,
+ ExitFullscreen,
+ WebVREvent,
+ PerformanceTimelineTask,
+ PortMessage,
+ WebGPUMsg,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
+pub enum HangAnnotation {
+ Layout(LayoutHangAnnotation),
+ Script(ScriptHangAnnotation),
+}
+
+/// Hang-alerts are sent by the monitor to the constellation.
+#[derive(Deserialize, Serialize)]
+pub enum HangMonitorAlert {
+ /// A component hang has been detected.
+ Hang(HangAlert),
+ /// Report a completed sampled profile.
+ Profile(Vec<u8>),
+}
+
+impl fmt::Debug for HangMonitorAlert {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ HangMonitorAlert::Hang(..) => write!(fmt, "Hang"),
+ HangMonitorAlert::Profile(..) => write!(fmt, "Profile"),
+ }
+ }
+}
+
+/// Hang-alerts are sent by the monitor to the constellation.
+#[derive(Deserialize, Serialize)]
+pub enum HangAlert {
+ /// Report a transient hang.
+ Transient(MonitoredComponentId, HangAnnotation),
+ /// Report a permanent hang.
+ Permanent(MonitoredComponentId, HangAnnotation, Option<HangProfile>),
+}
+
+impl fmt::Debug for HangAlert {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let (annotation, profile) = match self {
+ HangAlert::Transient(component_id, annotation) => {
+ write!(
+ fmt,
+ "\n The following component is experiencing a transient hang: \n {:?}",
+ component_id
+ )?;
+ (annotation.clone(), None)
+ },
+ HangAlert::Permanent(component_id, annotation, profile) => {
+ write!(
+ fmt,
+ "\n The following component is experiencing a permanent hang: \n {:?}",
+ component_id
+ )?;
+ (annotation.clone(), profile.clone())
+ },
+ };
+
+ write!(fmt, "\n Annotation for the hang:\n{:?}", annotation)?;
+ if let Some(profile) = profile {
+ write!(fmt, "\n {:?}", profile)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+pub struct HangProfileSymbol {
+ pub name: Option<String>,
+ pub filename: Option<String>,
+ pub lineno: Option<u32>,
+}
+
+#[derive(Clone, Deserialize, Serialize)]
+/// Info related to the activity of an hanging component.
+pub struct HangProfile {
+ pub backtrace: Vec<HangProfileSymbol>,
+}
+
+impl fmt::Debug for HangProfile {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let hex_width = mem::size_of::<usize>() * 2 + 2;
+
+ write!(fmt, "HangProfile backtrace:")?;
+
+ if self.backtrace.is_empty() {
+ write!(fmt, "backtrace failed to resolve")?;
+ return Ok(());
+ }
+
+ for symbol in self.backtrace.iter() {
+ write!(fmt, "\n {:1$}", "", hex_width)?;
+
+ if let Some(ref name) = symbol.name {
+ write!(fmt, " - {}", name)?;
+ } else {
+ write!(fmt, " - <unknown>")?;
+ }
+
+ if let (Some(ref file), Some(ref line)) = (symbol.filename.as_ref(), symbol.lineno) {
+ write!(fmt, "\n {:3$}at {}:{}", "", file, line, hex_width)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub enum MonitoredComponentType {
+ Layout,
+ Script,
+}
+
+#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct MonitoredComponentId(pub PipelineId, pub MonitoredComponentType);
+
+/// A handle to register components for hang monitoring,
+/// and to receive a means to communicate with the underlying hang monitor worker.
+pub trait BackgroundHangMonitorRegister: BackgroundHangMonitorClone + Send {
+ /// Register a component for hang monitoring:
+ /// to be called from within the thread to be monitored for hangs.
+ fn register_component(
+ &self,
+ component: MonitoredComponentId,
+ transient_hang_timeout: Duration,
+ permanent_hang_timeout: Duration,
+ exit_signal: Option<Box<dyn BackgroundHangMonitorExitSignal>>,
+ ) -> Box<dyn BackgroundHangMonitor>;
+}
+
+impl Clone for Box<dyn BackgroundHangMonitorRegister> {
+ fn clone(&self) -> Box<dyn BackgroundHangMonitorRegister> {
+ self.clone_box()
+ }
+}
+
+pub trait BackgroundHangMonitorClone {
+ fn clone_box(&self) -> Box<dyn BackgroundHangMonitorRegister>;
+}
+
+/// Proxy methods to communicate with the background hang monitor
+pub trait BackgroundHangMonitor {
+ /// Notify the start of handling an event.
+ fn notify_activity(&self, annotation: HangAnnotation);
+ /// Notify the start of waiting for a new event to come in.
+ fn notify_wait(&self);
+ /// Unregister the component from monitor.
+ fn unregister(&self);
+}
+
+/// A means for the BHM to signal a monitored component to exit.
+/// Useful when the component is hanging, and cannot be notified via the usual way.
+/// The component should implement this in a way allowing for the signal to be received when hanging,
+/// if at all.
+pub trait BackgroundHangMonitorExitSignal: Send {
+ /// Called by the BHM, to notify the monitored component to exit.
+ fn signal_to_exit(&self);
+}
+
+/// Messages to control the sampling profiler.
+#[derive(Deserialize, Serialize)]
+pub enum BackgroundHangMonitorControlMsg {
+ /// Enable the sampler, with a given sampling rate and max total sampling duration.
+ EnableSampler(Duration, Duration),
+ DisableSampler,
+ /// Exit, and propagate the signal to monitored components.
+ Exit(IpcSender<()>),
+}
diff --git a/components/shared/msg/lib.rs b/components/shared/msg/lib.rs
new file mode 100644
index 00000000000..b2ce3a52c2c
--- /dev/null
+++ b/components/shared/msg/lib.rs
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#![deny(unsafe_code)]
+
+pub mod constellation_msg;