#![forbid(unsafe_code)]
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug, Default)]
pub struct TunnelStats {
pub forwards_served: std::sync::atomic::AtomicU64,
pub capacity_waits: std::sync::atomic::AtomicU64,
pub effective_port: std::sync::atomic::AtomicU32,
pub stopped_by_signal: AtomicBool,
pub stopped_by_accept_error: AtomicBool,
}
impl TunnelStats {
#[must_use]
pub fn close_reason(&self) -> crate::json_wire::TunnelCloseReason {
use crate::json_wire::TunnelCloseReason;
if self.stopped_by_accept_error.load(Ordering::Acquire) {
TunnelCloseReason::AcceptError
} else if self.stopped_by_signal.load(Ordering::Acquire) {
TunnelCloseReason::Signal
} else {
TunnelCloseReason::Deadline
}
}
}
#[cfg(test)]
mod tests {
use super::TunnelStats;
use std::sync::atomic::Ordering;
#[test]
fn close_reason_is_derived_from_the_flags_the_loop_managed_to_set() {
let stats = TunnelStats::default();
assert_eq!(
stats.close_reason(),
crate::json_wire::TunnelCloseReason::Deadline,
"neither flag set means the loop was still serving when it was cancelled"
);
let signalled = TunnelStats::default();
signalled.stopped_by_signal.store(true, Ordering::Release);
assert_eq!(
signalled.close_reason(),
crate::json_wire::TunnelCloseReason::Signal
);
let failed = TunnelStats::default();
failed
.stopped_by_accept_error
.store(true, Ordering::Release);
assert_eq!(
failed.close_reason(),
crate::json_wire::TunnelCloseReason::AcceptError
);
let both = TunnelStats::default();
both.stopped_by_signal.store(true, Ordering::Release);
both.stopped_by_accept_error.store(true, Ordering::Release);
assert_eq!(
both.close_reason(),
crate::json_wire::TunnelCloseReason::AcceptError
);
}
}