blob: 52a508a801fb2bf54e5c5381931387750c3ecdbc [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::model::component::{ComponentInstance, WeakComponentInstance},
anyhow::format_err,
async_trait::async_trait,
cm_rust::ComponentDecl,
cm_types::Name,
errors::ModelError,
fidl_fuchsia_component as fcomponent, fidl_fuchsia_diagnostics_types as fdiagnostics,
fidl_fuchsia_io as fio, fuchsia_zircon as zx,
futures::{channel::oneshot, lock::Mutex},
moniker::{ExtendedMoniker, Moniker},
sandbox::{Receiver, Sender},
std::{
collections::HashMap,
fmt,
sync::{Arc, Mutex as StdMutex, Weak},
},
tracing::warn,
};
/// Defines the `EventType` enum as well as its implementation.
/// |description| is the description of the event that will be a doc comment on that event type.
/// |name| is the name of the event on CamelCase format, capitalized.
/// |string_name| is the name of the event on snake_case format, not capitalized.
macro_rules! events {
([$($(#[$description:meta])* ($name:ident, $string_name:ident),)*]) => {
pub trait HasEventType {
fn event_type(&self) -> EventType;
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum EventType {
$(
$(#[$description])*
$name,
)*
}
impl From<EventType> for Name {
fn from(event_type: EventType) -> Name {
match event_type {
$(
EventType::$name => stringify!($string_name).parse().unwrap(),
)*
}
}
}
/// Transfers any move-only state out of self into a new event that is otherwise
/// a clone.
#[async_trait]
pub trait TransferEvent {
async fn transfer(&self) -> Self;
}
impl fmt::Display for EventType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", match self {
$(
EventType::$name => stringify!($string_name),
)*
}
.to_string())
}
}
impl TryFrom<String> for EventType {
type Error = anyhow::Error;
fn try_from(string: String) -> Result<EventType, Self::Error> {
match string.as_str() {
$(
stringify!($string_name) => Ok(EventType::$name),
)*
other => Err(format_err!("invalid string for event type: {:?}", other))
}
}
}
impl EventType {
/// Returns all available event types.
pub fn values() -> Vec<EventType> {
vec![
$(EventType::$name,)*
]
}
}
impl HasEventType for EventPayload {
fn event_type(&self) -> EventType {
match self {
$(
EventPayload::$name { .. } => EventType::$name,
)*
}
}
}
};
}
macro_rules! external_events {
($($name:ident),*) => {
impl From<fcomponent::EventType> for EventType {
fn from(fidl_event_type: fcomponent::EventType) -> Self {
match fidl_event_type {
$(
fcomponent::EventType::$name => EventType::$name,
)*
}
}
}
impl TryInto<fcomponent::EventType> for EventType {
type Error = anyhow::Error;
fn try_into(self) -> Result<fcomponent::EventType, anyhow::Error> {
match self {
$(
EventType::$name => Ok(fcomponent::EventType::$name),
)*
}
}
}
}
}
// Keep the event types listed below in alphabetical order!
events!([
/// After a CapabilityProvider has been selected, the CapabilityRequested event is dispatched
/// with the ServerEnd of the channel for the capability.
(CapabilityRequested, capability_requested),
/// A component instance was discovered.
(Discovered, discovered),
/// Destruction of an instance has begun. The instance may/may not be stopped by this point.
/// The instance still exists in the parent's realm but will soon be removed.
(Destroyed, destroyed),
/// An instance's declaration was resolved successfully for the first time.
(Resolved, resolved),
/// An instance is about to be started.
(Started, started),
/// An instance was stopped successfully.
/// This event must occur before Destroyed.
(Stopped, stopped),
/// Similar to the Started event, except the payload will carry an eventpair
/// that the subscriber could use to defer the launch of the component.
(DebugStarted, debug_started),
/// A component instance was unresolved.
(Unresolved, unresolved),
]);
external_events!(
CapabilityRequested,
Discovered,
Destroyed,
Resolved,
Started,
Stopped,
DebugStarted,
Unresolved
);
/// The component manager calls out to objects that implement the `Hook` trait on registered
/// component manager events. Hooks block the flow of a task, and can mutate, decorate and replace
/// capabilities. This permits `Hook` to serve as a point of extensibility for the component
/// manager.
/// IMPORTANT: Hooks must not block on completion of an Action since Hooks are often called while
/// executing an Action. Waiting on an Action in a Hook could cause a deadlock.
/// IMPORTANT: Hooks should avoid causing event dispatch because we do not guarantee serialization
/// between Hooks. Therefore the order a receiver see events in may be unexpected.
#[async_trait]
pub trait Hook: Send + Sync {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError>;
}
/// An object registers a hook into a component manager event via a `HooksRegistration` object.
/// A single object may register for multiple events through a vector of `EventType`. `Hooks`
/// does not retain the callback. The hook is lazily removed when the callback object loses
/// strong references.
#[derive(Clone)]
pub struct HooksRegistration {
events: Vec<EventType>,
callback: Weak<dyn Hook>,
}
impl HooksRegistration {
pub fn new(
_name: &'static str,
events: Vec<EventType>,
callback: Weak<dyn Hook>,
) -> HooksRegistration {
Self { events, callback }
}
}
/// A [`CapabilityReceiver`] lets a `CapabilityRequested` event subscriber take the
/// opportunity to monitor requests for the corresponding capability.
#[derive(Clone)]
pub struct CapabilityReceiver {
inner: Arc<StdMutex<Option<Receiver>>>,
}
impl CapabilityReceiver {
/// Creates a [`CapabilityReceiver`] that receives connection requests sent via the
/// [`Sender`] capability.
pub fn new() -> (Self, Sender) {
let (receiver, sender) = Receiver::new();
let inner = Arc::new(StdMutex::new(Some(receiver)));
(Self { inner }, sender)
}
/// Take the opportunity to monitor requests.
pub fn take(&self) -> Option<Receiver> {
self.inner.lock().unwrap().take()
}
/// Did someone call `take` on this capability receiver.
pub fn is_taken(&self) -> bool {
self.inner.lock().unwrap().is_none()
}
}
#[async_trait]
impl TransferEvent for CapabilityReceiver {
async fn transfer(&self) -> Self {
let receiver = self.take();
let inner = Arc::new(StdMutex::new(receiver));
Self { inner }
}
}
#[derive(Clone)]
pub enum EventPayload {
// Keep the events listed below in alphabetical order!
CapabilityRequested {
source_moniker: Moniker,
name: String,
receiver: CapabilityReceiver,
},
Discovered,
Destroyed,
Resolved {
component: WeakComponentInstance,
decl: ComponentDecl,
},
Unresolved,
Started {
runtime: RuntimeInfo,
component_decl: ComponentDecl,
},
Stopped {
status: zx::Status,
stop_time: zx::Time,
execution_duration: zx::Duration,
requested_escrow: bool,
},
DebugStarted {
runtime_dir: Option<fio::DirectoryProxy>,
break_on_start: Arc<zx::EventPair>,
},
}
/// Information about a component's runtime provided to `Started`.
#[derive(Clone)]
pub struct RuntimeInfo {
pub diagnostics_receiver:
Arc<Mutex<Option<oneshot::Receiver<fdiagnostics::ComponentDiagnostics>>>>,
pub start_time: zx::Time,
}
impl RuntimeInfo {
pub fn new(
timestamp: zx::Time,
diagnostics_receiver: oneshot::Receiver<fdiagnostics::ComponentDiagnostics>,
) -> Self {
let diagnostics_receiver = Arc::new(Mutex::new(Some(diagnostics_receiver)));
Self { diagnostics_receiver, start_time: timestamp }
}
}
impl fmt::Debug for EventPayload {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut formatter = fmt.debug_struct("EventPayload");
formatter.field("type", &self.event_type());
match self {
EventPayload::CapabilityRequested { name, .. } => {
formatter.field("name", &name).finish()
}
EventPayload::Started { component_decl, .. } => {
formatter.field("component_decl", &component_decl).finish()
}
EventPayload::Resolved { component: _, decl, .. } => {
formatter.field("decl", decl).finish()
}
EventPayload::Stopped { status, .. } => formatter.field("status", status).finish(),
EventPayload::Unresolved
| EventPayload::Discovered
| EventPayload::Destroyed
| EventPayload::DebugStarted { .. } => formatter.finish(),
}
}
}
#[derive(Clone, Debug)]
pub struct Event {
/// Moniker of component that this event applies to
pub target_moniker: ExtendedMoniker,
/// Component url of the component that this event applies to
pub component_url: String,
/// Payload of the event
pub payload: EventPayload,
/// Time when this event was created
pub timestamp: zx::Time,
}
impl Event {
pub fn new(component: &Arc<ComponentInstance>, payload: EventPayload) -> Self {
let timestamp = zx::Time::get_monotonic();
Self::new_internal(
component.moniker.clone().into(),
component.component_url.clone(),
timestamp,
payload,
)
}
pub fn new_builtin(payload: EventPayload) -> Self {
let timestamp = zx::Time::get_monotonic();
Self::new_internal(
ExtendedMoniker::ComponentManager,
"bin/component_manager".to_string(),
timestamp,
payload,
)
}
pub fn new_with_timestamp(
component: &Arc<ComponentInstance>,
payload: EventPayload,
timestamp: zx::Time,
) -> Self {
Self::new_internal(
component.moniker.clone().into(),
component.component_url.clone(),
timestamp,
payload,
)
}
#[cfg(test)]
pub fn new_for_test(
target_moniker: Moniker,
component_url: impl Into<String>,
payload: EventPayload,
) -> Self {
let timestamp = zx::Time::get_monotonic();
Self::new_internal(
ExtendedMoniker::ComponentInstance(target_moniker),
component_url.into(),
timestamp,
payload,
)
}
fn new_internal(
target_moniker: ExtendedMoniker,
component_url: String,
timestamp: zx::Time,
payload: EventPayload,
) -> Self {
Self { target_moniker, component_url, timestamp, payload }
}
}
#[async_trait]
impl TransferEvent for EventPayload {
async fn transfer(&self) -> Self {
match self {
EventPayload::CapabilityRequested { source_moniker, name, receiver } => {
EventPayload::CapabilityRequested {
source_moniker: source_moniker.clone(),
name: name.to_string(),
receiver: receiver.transfer().await,
}
}
result => result.clone(),
}
}
}
impl HasEventType for Event {
fn event_type(&self) -> EventType {
self.payload.event_type()
}
}
#[async_trait]
impl TransferEvent for Event {
async fn transfer(&self) -> Self {
Self {
target_moniker: self.target_moniker.clone(),
component_url: self.component_url.clone(),
payload: self.payload.transfer().await,
timestamp: self.timestamp,
}
}
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let payload = match &self.payload {
EventPayload::CapabilityRequested { source_moniker, name, .. } => {
format!("requested '{}' from '{}'", name.to_string(), source_moniker)
}
EventPayload::Stopped { status, .. } => {
format!("with status: {}", status.to_string())
}
EventPayload::Discovered { .. }
| EventPayload::Destroyed { .. }
| EventPayload::Resolved { .. }
| EventPayload::DebugStarted { .. }
| EventPayload::Started { .. }
| EventPayload::Unresolved => "".to_string(),
};
write!(f, "[{}] '{}' {}", self.event_type().to_string(), self.target_moniker, payload)
}
}
/// This is a collection of hooks to component manager events.
pub struct Hooks {
hooks_map: Mutex<HashMap<EventType, Vec<Weak<dyn Hook>>>>,
}
impl Hooks {
pub fn new() -> Self {
Self { hooks_map: Mutex::new(HashMap::new()) }
}
/// For every hook in `hooks`, add it to the list of hooks that are executed when `dispatch`
/// is called for `hook.event`.
pub async fn install(&self, hooks: Vec<HooksRegistration>) {
let mut hooks_map = self.hooks_map.lock().await;
for hook in hooks {
for event in hook.events {
let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
existing_hooks.push(hook.callback.clone());
}
}
}
/// Same as `install`, but adds the hook to the front of the queue.
///
/// This is test-only because in general it shouldn't matter what order hooks are executed
/// in. This is useful for tests that need guarantees about hook execution order.
#[cfg(test)]
pub async fn install_front(&self, hooks: Vec<HooksRegistration>) {
let mut hooks_map = self.hooks_map.lock().await;
for hook in hooks {
for event in hook.events {
let existing_hooks = hooks_map.entry(event).or_insert(vec![]);
existing_hooks.insert(0, hook.callback.clone());
}
}
}
pub async fn dispatch(&self, event: &Event) {
let strong_hooks = {
let mut hooks_map = self.hooks_map.lock().await;
if let Some(hooks) = hooks_map.get_mut(&event.event_type()) {
// We must upgrade our weak references to hooks to strong ones before we can
// call out to them.
let mut strong_hooks = vec![];
hooks.retain(|hook| {
if let Some(hook) = hook.upgrade() {
strong_hooks.push(hook);
true
} else {
false
}
});
strong_hooks
} else {
vec![]
}
};
for hook in strong_hooks {
if let Err(err) = hook.on(event).await {
warn!(%err, %event, "Hook produced error for event");
}
}
}
}
#[cfg(test)]
mod tests {
use {super::*, moniker::MonikerBase};
// This test verifies that the payload of the CapabilityRequested event will be transferred.
#[fuchsia::test]
async fn capability_requested_transfer() {
let (receiver, _sender) = CapabilityReceiver::new();
let event = Event::new_for_test(
Moniker::root(),
"fuchsia-pkg://root",
EventPayload::CapabilityRequested {
source_moniker: Moniker::root(),
name: "foo".to_string(),
receiver,
},
);
// Verify the transferred event carries the capability.
let transferred_event = event.transfer().await;
match transferred_event.payload {
EventPayload::CapabilityRequested { receiver, .. } => {
assert!(!receiver.is_taken());
}
_ => panic!("Event type unexpected"),
}
// Verify that the original event no longer carries the capability.
match &event.payload {
EventPayload::CapabilityRequested { receiver, .. } => {
assert!(receiver.is_taken());
}
_ => panic!("Event type unexpected"),
}
// Transferring the original event again should give an empty capability provider.
let second_transferred_event = event.transfer().await;
match &second_transferred_event.payload {
EventPayload::CapabilityRequested { receiver, .. } => {
assert!(receiver.is_taken());
}
_ => panic!("Event type unexpected"),
}
}
}