Skip to main content

tower_http/on_early_drop/
guard.rs

1//! Drop guard that fires a callback when dropped unless marked completed.
2
3use crate::on_early_drop::traits::OnDropCallback;
4
5/// Runs a callback on drop unless [`completed`](Self::completed) is called
6/// first.
7///
8/// # Examples
9///
10/// ```
11/// use tower_http::on_early_drop::OnEarlyDropGuard;
12/// use std::sync::atomic::{AtomicUsize, Ordering};
13/// use std::sync::Arc;
14///
15/// let count = Arc::new(AtomicUsize::new(0));
16/// let count_for_guard = count.clone();
17/// {
18///     let _guard = OnEarlyDropGuard::new(move || {
19///         count_for_guard.fetch_add(1, Ordering::Relaxed);
20///     });
21/// }
22/// assert_eq!(count.load(Ordering::Relaxed), 1);
23/// ```
24///
25/// Marking the guard completed suppresses the callback:
26///
27/// ```
28/// use tower_http::on_early_drop::OnEarlyDropGuard;
29/// use std::sync::atomic::{AtomicUsize, Ordering};
30/// use std::sync::Arc;
31///
32/// let count = Arc::new(AtomicUsize::new(0));
33/// let count_for_guard = count.clone();
34/// {
35///     let mut guard = OnEarlyDropGuard::new(move || {
36///         count_for_guard.fetch_add(1, Ordering::Relaxed);
37///     });
38///     guard.completed();
39/// }
40/// assert_eq!(count.load(Ordering::Relaxed), 0);
41/// ```
42///
43/// [`OnEarlyDropLayer`]: super::OnEarlyDropLayer
44#[derive(Debug)]
45pub struct OnEarlyDropGuard<Callback: OnDropCallback> {
46    callback: Option<Callback>,
47}
48
49impl<Callback: OnDropCallback> OnEarlyDropGuard<Callback> {
50    /// Create a guard that will fire `callback` on drop.
51    pub fn new(callback: Callback) -> Self {
52        Self {
53            callback: Some(callback),
54        }
55    }
56
57    /// Mark the guard completed and drop the callback without firing it.
58    ///
59    /// Any resources captured by the callback are released immediately
60    /// rather than at guard drop time.
61    pub fn completed(&mut self) {
62        self.callback = None;
63    }
64}
65
66impl<Callback: OnDropCallback> Drop for OnEarlyDropGuard<Callback> {
67    fn drop(&mut self) {
68        // Panicking in Drop aborts the process if we are already unwinding,
69        // so avoid `expect` here.
70        if let Some(callback) = self.callback.take() {
71            callback.on_drop();
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use std::sync::atomic::{AtomicBool, Ordering};
80    use std::sync::Arc;
81
82    #[test]
83    fn fires_on_drop() {
84        let fired = Arc::new(AtomicBool::new(false));
85        let fired_for_guard = fired.clone();
86        {
87            let _guard = OnEarlyDropGuard::new(move || {
88                fired_for_guard.store(true, Ordering::Relaxed);
89            });
90        }
91        assert!(fired.load(Ordering::Relaxed));
92    }
93
94    #[test]
95    fn suppresses_when_completed() {
96        let fired = Arc::new(AtomicBool::new(false));
97        let fired_for_guard = fired.clone();
98        {
99            let mut guard = OnEarlyDropGuard::new(move || {
100                fired_for_guard.store(true, Ordering::Relaxed);
101            });
102            guard.completed();
103        }
104        assert!(!fired.load(Ordering::Relaxed));
105    }
106
107    #[test]
108    fn accepts_custom_on_drop_callback_impl() {
109        // Verify a named struct implementing OnDropCallback works through
110        // the guard, not just closures via the blanket impl.
111        struct Counter(Arc<AtomicBool>);
112        impl super::super::traits::OnDropCallback for Counter {
113            fn on_drop(self) {
114                self.0.store(true, Ordering::Relaxed);
115            }
116        }
117
118        let fired = Arc::new(AtomicBool::new(false));
119        {
120            let _guard = OnEarlyDropGuard::new(Counter(fired.clone()));
121        }
122        assert!(fired.load(Ordering::Relaxed));
123    }
124
125    // The guard must be Send and Sync whenever its callback is.
126    #[allow(dead_code)]
127    fn static_property_guard_is_send_sync() {
128        fn assert_send<T: Send>(_: &T) {}
129        fn assert_sync<T: Sync>(_: &T) {}
130
131        let guard = OnEarlyDropGuard::new(|| {});
132        assert_send(&guard);
133        assert_sync(&guard);
134    }
135}