blob: 60371b0d4ab3b03fc0287cc37e2bb82f26ea1506 [file] [log] [blame]
// Copyright 2020 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 fuchsia_async as fasync;
use futures::lock::Mutex;
use std::sync::Arc;
/// Closure definition for an action that can be triggered by ActionFuse.
pub type TriggeredAction = Box<dyn FnOnce() + Send + Sync + 'static>;
/// The reference-counted handle to an ActionFuse. When all references go out of
/// scope, the action will be triggered (if not defused).
pub type ActionFuseHandle = Arc<Mutex<ActionFuse>>;
/// ActionFuseBuilder allows creation of ActionFuses. Note that all parameters
/// are completely optional to the builder. A fuse with no action or chained
/// fuse is valid.
pub(crate) struct ActionFuseBuilder {
actions: Vec<TriggeredAction>,
chained_fuses: Vec<ActionFuseHandle>,
}
impl ActionFuseBuilder {
pub(crate) fn new() -> Self {
ActionFuseBuilder { actions: vec![], chained_fuses: vec![] }
}
/// Chains supplied fuse to the built fuse. Multiple fuses can be added
/// and all will be triggered upon this fuse being dropped.
#[cfg(test)]
pub(crate) fn chain_fuse(mut self, fuse: ActionFuseHandle) -> Self {
self.chained_fuses.push(fuse);
self
}
/// Shortcut for chaining multiple fuses at once.
pub(crate) fn chain_fuses(mut self, fuses: Vec<ActionFuseHandle>) -> Self {
for fuse in fuses {
self.chained_fuses.push(fuse);
}
self
}
/// Adds an action to be executed once dropped.
pub(crate) fn add_action(mut self, action: TriggeredAction) -> Self {
self.actions.push(action);
self
}
/// Generates fuse based on parameters.
pub(crate) fn build(self) -> ActionFuseHandle {
ActionFuse::create(self.actions, self.chained_fuses)
}
}
/// ActionFuse is a wrapper around a triggered action (a closure with no
/// arguments and no return value). This action is invoked once the fuse goes
/// out of scope (via the Drop trait). An ActionFuse can be defused, preventing
/// the action from automatically invoking when going out of scope.
pub struct ActionFuse {
/// An optional action that will be invoked when the ActionFuse goes out of
/// scope.
actions: Vec<TriggeredAction>,
/// A list of optional fuses that follows this fuse. It will be defused in
/// tandem and dropped at the same time.
chained_fuses: Vec<ActionFuseHandle>,
}
impl ActionFuse {
/// Returns an ActionFuse reference with the given TriggerAction.
pub(super) fn create(
actions: Vec<TriggeredAction>,
chained_fuses: Vec<ActionFuseHandle>,
) -> ActionFuseHandle {
Arc::new(Mutex::new(ActionFuse { actions, chained_fuses }))
}
/// Suppresses the action from automatically executing.
pub(crate) fn defuse(handle: ActionFuseHandle) {
fasync::Task::spawn(async move {
let mut fuse = handle.lock().await;
fuse.actions.clear();
for chained_fuse in &fuse.chained_fuses {
ActionFuse::defuse(chained_fuse.clone());
}
})
.detach();
}
}
impl Drop for ActionFuse {
fn drop(&mut self) {
while let Some(action) = self.actions.pop() {
(action)();
}
}
}