use std::sync::Arc;
use std::sync::atomic::Ordering;
const DEFAULT_MAX_LISTENERS: usize = 50;
pub fn create_abort_controller(max_listeners: usize) -> AbortController {
AbortController::new(max_listeners)
}
pub fn create_abort_controller_default() -> AbortController {
create_abort_controller(DEFAULT_MAX_LISTENERS)
}
pub struct AbortController {
signal: Arc<AbortSignal>,
}
impl AbortController {
pub fn new(max_listeners: usize) -> Self {
Self {
signal: Arc::new(AbortSignal::new(max_listeners)),
}
}
pub fn signal(&self) -> &Arc<AbortSignal> {
&self.signal
}
pub fn abort(&self, reason: Option<Arc<dyn std::any::Any + Send + Sync>>) {
self.signal.abort(reason);
}
pub fn is_aborted(&self) -> bool {
self.signal.is_aborted()
}
}
impl Default for AbortController {
fn default() -> Self {
Self::new(DEFAULT_MAX_LISTENERS)
}
}
impl Clone for AbortController {
fn clone(&self) -> Self {
Self {
signal: Arc::clone(&self.signal),
}
}
}
pub struct AbortSignal {
aborted: std::sync::atomic::AtomicBool,
reason: std::sync::Mutex<Option<Arc<dyn std::any::Any + Send + Sync>>>,
listeners: std::sync::Mutex<Vec<AbortCallback>>,
max_listeners: usize,
}
pub type AbortCallback = Box<dyn Fn(Option<&dyn std::any::Any>) + Send + Sync>;
impl AbortSignal {
pub fn new(max_listeners: usize) -> Self {
Self {
aborted: std::sync::atomic::AtomicBool::new(false),
reason: std::sync::Mutex::new(None),
listeners: std::sync::Mutex::new(Vec::new()),
max_listeners,
}
}
pub fn is_aborted(&self) -> bool {
self.aborted.load(Ordering::SeqCst)
}
pub fn abort_flag(&self) -> &std::sync::atomic::AtomicBool {
&self.aborted
}
pub fn reason(&self) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
self.reason.lock().ok().and_then(|guard| guard.clone())
}
pub fn abort(&self, reason: Option<Arc<dyn std::any::Any + Send + Sync>>) {
if self.aborted.swap(true, Ordering::SeqCst) {
return; }
*self.reason.lock().unwrap() = reason.clone();
let reason_ref = reason.as_deref().map(|a| a as &dyn std::any::Any);
for listener in self.listeners.lock().unwrap().iter() {
listener(reason_ref);
}
}
pub fn add_event_listener(&self, callback: AbortCallback) -> usize {
let mut listeners = self.listeners.lock().unwrap();
if listeners.len() >= self.max_listeners {
log::warn!(
"Max listeners ({}) exceeded for AbortSignal",
self.max_listeners
);
}
listeners.push(callback);
listeners.len()
}
#[allow(dead_code)]
pub fn remove_event_listener(&self, _callback: &AbortCallback) {
}
#[allow(dead_code)]
pub fn listener_count(&self) -> usize {
self.listeners.lock().unwrap().len()
}
}
impl Default for AbortSignal {
fn default() -> Self {
Self::new(DEFAULT_MAX_LISTENERS)
}
}
impl Clone for AbortSignal {
fn clone(&self) -> Self {
Self {
aborted: std::sync::atomic::AtomicBool::new(self.aborted.load(Ordering::SeqCst)),
reason: std::sync::Mutex::new(self.reason.lock().ok().and_then(|g| g.clone())),
listeners: std::sync::Mutex::new(Vec::new()), max_listeners: self.max_listeners,
}
}
}
impl std::fmt::Debug for AbortSignal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AbortSignal")
.field("aborted", &self.aborted.load(Ordering::SeqCst))
.field("max_listeners", &self.max_listeners)
.finish()
}
}
#[allow(dead_code)]
pub fn create_child_abort_controller(
parent: &AbortController,
max_listeners: Option<usize>,
) -> AbortController {
let max_listeners = max_listeners.unwrap_or(DEFAULT_MAX_LISTENERS);
let child = AbortController::new(max_listeners);
if parent.is_aborted() {
child.abort(parent.signal.reason());
return child;
}
let child_signal = Arc::clone(&child.signal);
let parent_signal = Arc::clone(parent.signal());
let reason = parent_signal.reason();
parent_signal.add_event_listener(Box::new(move |_reason| {
child_signal.abort(reason.clone());
}));
child
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_abort_controller() {
let controller = create_abort_controller(50);
assert!(!controller.is_aborted());
}
#[test]
fn test_abort_controller_abort() {
let controller = create_abort_controller(50);
controller.abort(None);
assert!(controller.is_aborted());
}
#[test]
fn test_abort_with_reason() {
let controller = create_abort_controller(50);
let reason = Arc::new("test reason".to_string()) as Arc<dyn std::any::Any + Send + Sync>;
controller.abort(Some(reason));
assert!(controller.is_aborted());
let stored_reason = controller.signal().reason();
assert!(stored_reason.is_some());
}
#[test]
fn test_abort_listener() {
let controller = create_abort_controller(50);
let called = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let called_clone = called.clone();
controller
.signal()
.add_event_listener(Box::new(move |_reason| {
called.store(true, std::sync::atomic::Ordering::SeqCst);
}));
controller.abort(None);
assert!(called_clone.load(std::sync::atomic::Ordering::SeqCst));
}
#[test]
fn test_child_abort_controller() {
let parent = create_abort_controller(50);
let child = create_child_abort_controller(&parent, None);
assert!(!parent.is_aborted());
assert!(!child.is_aborted());
parent.abort(None);
assert!(parent.is_aborted());
assert!(child.is_aborted());
}
#[test]
fn test_child_already_aborted_parent() {
let parent = create_abort_controller(50);
parent.abort(None);
let child = create_child_abort_controller(&parent, None);
assert!(child.is_aborted());
}
#[test]
fn test_abort_flag_reflects_state() {
let controller = create_abort_controller(50);
let flag = controller.signal().abort_flag();
assert!(!flag.load(Ordering::SeqCst));
controller.abort(None);
assert!(flag.load(Ordering::SeqCst));
}
#[test]
fn test_abort_flag_survives_guard() {
let abort_ctrl = create_abort_controller(50);
let flag = abort_ctrl.signal().abort_flag();
assert!(!flag.load(Ordering::SeqCst));
abort_ctrl.abort(None);
assert!(flag.load(Ordering::SeqCst));
}
}