Skip to main content

archivist_lib/
component_lifecycle.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl::endpoints::RequestStream;
6use fidl_fuchsia_process_lifecycle::{LifecycleRequest, LifecycleRequestStream};
7use fuchsia_async as fasync;
8use fuchsia_runtime::{HandleInfo, HandleType, take_startup_handle};
9use futures::{Future, StreamExt};
10use log::{debug, warn};
11
12/// Takes the startup handle for LIFECYCLE and returns a stream listening for Lifecycle FIDL
13/// requests on it.
14pub fn take_lifecycle_request_stream() -> LifecycleRequestStream {
15    let lifecycle_handle_info = HandleInfo::new(HandleType::Lifecycle, 0);
16    let lifecycle_handle = take_startup_handle(lifecycle_handle_info)
17        .expect("must have been provided a lifecycle channel in procargs");
18    let async_chan = fasync::Channel::from_channel(lifecycle_handle.into());
19    LifecycleRequestStream::from_channel(async_chan)
20}
21
22/// Serves the Lifecycle protocol from the component runtime used for controlled shutdown of the
23/// archivist. When Stop is requrested executes the given callback.
24pub async fn on_stop_request<F, Fut>(mut request_stream: LifecycleRequestStream, cb: F)
25where
26    F: FnOnce() -> Fut,
27    Fut: Future<Output = ()>,
28{
29    match request_stream.next().await {
30        None => {
31            warn!("Lifecycle closed");
32        }
33        Some(Err(err)) => {
34            warn!(err:?; "Lifecycle error");
35        }
36        Some(Ok(LifecycleRequest::Stop { .. })) => {
37            debug!("Initiating shutdown.");
38            cb().await
39        }
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use fidl::endpoints::{Proxy, create_proxy_and_stream};
47    use fidl_fuchsia_process_lifecycle::LifecycleMarker;
48    use std::sync::Arc;
49    use std::sync::atomic::{AtomicBool, Ordering};
50
51    #[fuchsia::test]
52    async fn on_stop_request_invokes_callback() {
53        let (proxy, stream) = create_proxy_and_stream::<LifecycleMarker>();
54        let called = Arc::new(AtomicBool::new(false));
55        let called_clone = Arc::clone(&called);
56
57        let stop_fut = on_stop_request(stream, move || async move {
58            called_clone.store(true, Ordering::Relaxed);
59        });
60
61        proxy.stop().unwrap();
62        stop_fut.await;
63
64        assert!(called.load(Ordering::Relaxed));
65    }
66
67    #[fuchsia::test]
68    async fn on_stop_request_channel_closed() {
69        let (proxy, stream) = create_proxy_and_stream::<LifecycleMarker>();
70        let called = Arc::new(AtomicBool::new(false));
71        let called_clone = Arc::clone(&called);
72
73        let stop_fut = on_stop_request(stream, move || async move {
74            called_clone.store(true, Ordering::Relaxed);
75        });
76
77        drop(proxy);
78        stop_fut.await;
79
80        assert!(!called.load(Ordering::Relaxed));
81    }
82
83    #[fuchsia::test]
84    async fn on_stop_request_stream_error() {
85        let (proxy, stream) = create_proxy_and_stream::<LifecycleMarker>();
86        let called = Arc::new(AtomicBool::new(false));
87        let called_clone = Arc::clone(&called);
88
89        // Send invalid FIDL message
90        proxy.as_channel().write(&[0xff; 16], &mut []).expect("write invalid bytes");
91
92        on_stop_request(stream, move || async move {
93            called_clone.store(true, Ordering::Relaxed);
94        })
95        .await;
96
97        assert!(!called.load(Ordering::Relaxed));
98    }
99}