devices/usb/xhci/
ring_buffer_stop_cb.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::sync::Arc;
6use std::sync::Mutex;
7
8use base::error;
9
10use crate::utils::FailHandle;
11
12/// RingBufferStopCallback wraps a callback. The callback will be invoked when last instance of
13/// RingBufferStopCallback and its clones is dropped.
14///
15/// The callback might not be invoked in certain cases. Don't depend this for safety.
16#[derive(Clone)]
17pub struct RingBufferStopCallback {
18    _inner: Arc<Mutex<RingBufferStopCallbackInner>>,
19}
20
21impl RingBufferStopCallback {
22    /// Create new callback from closure.
23    pub fn new<C: 'static + FnMut() + Send>(cb: C) -> RingBufferStopCallback {
24        RingBufferStopCallback {
25            _inner: Arc::new(Mutex::new(RingBufferStopCallbackInner {
26                callback: Box::new(cb),
27            })),
28        }
29    }
30}
31
32struct RingBufferStopCallbackInner {
33    callback: Box<dyn FnMut() + Send>,
34}
35
36impl Drop for RingBufferStopCallbackInner {
37    fn drop(&mut self) {
38        (self.callback)();
39    }
40}
41
42/// Helper function to wrap up a closure with fail handle. The fail handle will be triggered if the
43/// closure returns an error.
44pub fn fallible_closure<E: std::fmt::Display, C: FnMut() -> Result<(), E> + 'static + Send>(
45    fail_handle: Arc<dyn FailHandle>,
46    mut callback: C,
47) -> impl FnMut() + 'static + Send {
48    move || match callback() {
49        Ok(()) => {}
50        Err(e) => {
51            error!("callback failed {}", e);
52            fail_handle.fail();
53        }
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use std::sync::Arc;
60    use std::sync::Mutex;
61
62    use super::*;
63
64    fn task(_: RingBufferStopCallback) {}
65
66    #[test]
67    fn simple_raii_callback() {
68        let a = Arc::new(Mutex::new(0));
69        let ac = a.clone();
70        let cb = RingBufferStopCallback::new(move || {
71            *ac.lock().unwrap() = 1;
72        });
73        task(cb.clone());
74        task(cb.clone());
75        task(cb);
76        assert_eq!(*a.lock().unwrap(), 1);
77    }
78}