use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub struct AbortSignal {
inner: Arc<Mutex<AbortSignalInner>>,
}
#[derive(Debug)]
struct AbortSignalInner {
aborted: bool,
reason: Option<String>,
}
impl AbortSignal {
pub fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(AbortSignalInner {
aborted: false,
reason: None,
})),
}
}
pub fn abort(reason: Option<String>) -> Self {
Self {
inner: Arc::new(Mutex::new(AbortSignalInner {
aborted: true,
reason,
})),
}
}
pub fn aborted(&self) -> bool {
self.inner.lock().unwrap().aborted
}
pub fn reason(&self) -> Option<String> {
self.inner.lock().unwrap().reason.clone()
}
pub(crate) fn do_abort(&self, reason: Option<String>) {
let mut inner = self.inner.lock().unwrap();
if !inner.aborted {
inner.aborted = true;
inner.reason = reason;
}
}
}
impl Default for AbortSignal {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct AbortController {
signal: AbortSignal,
}
impl AbortController {
pub fn new() -> Self {
Self {
signal: AbortSignal::new(),
}
}
pub fn signal(&self) -> &AbortSignal {
&self.signal
}
pub fn abort(&self) {
self.signal.do_abort(Some("AbortError".to_string()));
}
}
impl Default for AbortController {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_abort_signal_new() {
let signal = AbortSignal::new();
assert!(!signal.aborted());
assert!(signal.reason().is_none());
}
#[test]
fn test_abort_signal_abort() {
let signal = AbortSignal::abort(Some("Test reason".to_string()));
assert!(signal.aborted());
assert_eq!(signal.reason().unwrap(), "Test reason");
}
#[test]
fn test_abort_signal_abort_no_reason() {
let signal = AbortSignal::abort(None);
assert!(signal.aborted());
assert!(signal.reason().is_none());
}
#[test]
fn test_abort_signal_default() {
let signal = AbortSignal::default();
assert!(!signal.aborted());
assert!(signal.reason().is_none());
}
#[test]
fn test_abort_controller_new() {
let controller = AbortController::new();
assert!(!controller.signal().aborted());
}
#[test]
fn test_abort_controller_abort() {
let controller = AbortController::new();
assert!(!controller.signal().aborted());
controller.abort();
assert!(controller.signal().aborted());
assert_eq!(controller.signal().reason().unwrap(), "AbortError");
}
#[test]
fn test_abort_controller_default() {
let controller = AbortController::default();
assert!(!controller.signal().aborted());
}
#[test]
fn test_abort_signal_do_abort() {
let signal = AbortSignal::new();
assert!(!signal.aborted());
signal.do_abort(Some("Manual abort".to_string()));
assert!(signal.aborted());
assert_eq!(signal.reason().unwrap(), "Manual abort");
signal.do_abort(Some("Second abort".to_string()));
assert_eq!(signal.reason().unwrap(), "Manual abort");
}
#[test]
fn test_abort_signal_clone() {
let signal = AbortSignal::new();
let cloned = signal.clone();
assert!(!signal.aborted());
assert!(!cloned.aborted());
signal.do_abort(Some("Test".to_string()));
assert!(signal.aborted());
assert!(cloned.aborted());
assert_eq!(signal.reason().unwrap(), "Test");
assert_eq!(cloned.reason().unwrap(), "Test");
}
#[test]
fn test_signal_sharing_between_threads() {
use std::thread;
use std::time::Duration;
let controller = AbortController::new();
let signal = controller.signal().clone();
let handle = thread::spawn(move || {
thread::sleep(Duration::from_millis(10));
controller.abort();
});
handle.join().unwrap();
assert!(signal.aborted());
}
#[test]
fn test_multiple_signal_clones() {
let controller = AbortController::new();
let signal1 = controller.signal().clone();
let signal2 = controller.signal().clone();
let signal3 = controller.signal().clone();
assert!(!signal1.aborted());
assert!(!signal2.aborted());
assert!(!signal3.aborted());
controller.abort();
assert!(signal1.aborted());
assert!(signal2.aborted());
assert!(signal3.aborted());
assert_eq!(signal1.reason().unwrap(), "AbortError");
assert_eq!(signal2.reason().unwrap(), "AbortError");
assert_eq!(signal3.reason().unwrap(), "AbortError");
}
}