use crate::compat::HashMap;
use crate::compat::{Mutex, RwLock};
use alloc::sync::Arc;
use core::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Priority {
High,
#[default]
Normal,
Low,
}
impl Priority {
fn rank(&self) -> u8 {
match self {
Priority::High => 0,
Priority::Normal => 1,
Priority::Low => 2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ConnectionHandle(pub u64);
static NEXT_HANDLE: AtomicU64 = AtomicU64::new(1);
type SlotFn<T> = Box<dyn FnMut(Arc<T>) + Send + Sync + 'static>;
struct SlotEntry<T: Clone + Send + 'static> {
callback: Option<SlotFn<T>>,
once: bool,
blocked: bool,
priority: Priority,
}
struct SignalInner<T: Clone + Send + 'static> {
slots: RwLock<HashMap<ConnectionHandle, SlotEntry<T>>>,
}
impl<T: Clone + Send + 'static> SignalInner<T> {
fn disconnect(&self, handle: ConnectionHandle) -> bool {
self.slots.write().expect("signal lock poisoned").remove(&handle).is_some()
}
fn block(&self, handle: ConnectionHandle) -> bool {
if let Some(entry) = self.slots.write().expect("signal lock poisoned").get_mut(&handle) {
entry.blocked = true;
true
} else {
false
}
}
fn unblock(&self, handle: ConnectionHandle) -> bool {
if let Some(entry) = self.slots.write().expect("signal lock poisoned").get_mut(&handle) {
entry.blocked = false;
true
} else {
false
}
}
fn is_blocked(&self, handle: ConnectionHandle) -> Option<bool> {
self.slots.read().expect("signal lock poisoned").get(&handle).map(|entry| entry.blocked)
}
fn set_priority(&self, handle: ConnectionHandle, priority: Priority) -> bool {
if let Some(entry) = self.slots.write().expect("signal lock poisoned").get_mut(&handle) {
entry.priority = priority;
true
} else {
false
}
}
}
#[derive(Default)]
pub struct ConnectionScope {
disconnectors: Mutex<Vec<Box<dyn FnOnce() + Send + 'static>>>,
}
impl ConnectionScope {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&self) {
let mut disconnectors = self.disconnectors.lock().unwrap_or_else(|e| e.into_inner());
while let Some(disconnector) = disconnectors.pop() {
disconnector();
}
}
pub fn disconnect_count(&self) -> usize {
self.disconnectors.lock().unwrap_or_else(|e| e.into_inner()).len()
}
fn track(&self, disconnector: Box<dyn FnOnce() + Send + 'static>) {
self.disconnectors.lock().unwrap_or_else(|e| e.into_inner()).push(disconnector);
}
}
impl Drop for ConnectionScope {
fn drop(&mut self) {
let mut disconnectors = self.disconnectors.lock().unwrap_or_else(|e| e.into_inner());
while let Some(disconnector) = disconnectors.pop() {
disconnector();
}
}
}
#[derive(Clone)]
pub struct Signal<T: Clone + Send + 'static> {
inner: Arc<SignalInner<T>>,
}
impl<T: Clone + Send + 'static> Signal<T> {
pub fn new() -> Self {
Self { inner: Arc::new(SignalInner { slots: RwLock::new(HashMap::new()) }) }
}
pub fn connect<F>(&self, slot: F) -> ConnectionHandle
where
F: FnMut(Arc<T>) + Send + Sync + 'static,
{
self.connect_with_priority(slot, Priority::Normal)
}
pub fn connect_with_priority<F>(&self, slot: F, priority: Priority) -> ConnectionHandle
where
F: FnMut(Arc<T>) + Send + Sync + 'static,
{
let handle = ConnectionHandle(NEXT_HANDLE.fetch_add(1, Ordering::Relaxed));
self.inner.slots.write().expect("signal lock poisoned").insert(
handle,
SlotEntry { callback: Some(Box::new(slot)), once: false, blocked: false, priority },
);
handle
}
pub fn connect_once<F>(&self, slot: F) -> ConnectionHandle
where
F: FnMut(Arc<T>) + Send + Sync + 'static,
{
let handle = ConnectionHandle(NEXT_HANDLE.fetch_add(1, Ordering::Relaxed));
self.inner.slots.write().expect("signal lock poisoned").insert(
handle,
SlotEntry {
callback: Some(Box::new(slot)),
once: true,
blocked: false,
priority: Priority::Normal,
},
);
handle
}
pub fn connect_scoped<F>(&self, owner: &ConnectionScope, slot: F) -> ConnectionHandle
where
F: FnMut(Arc<T>) + Send + Sync + 'static,
{
let handle = self.connect(slot);
self.track_owner(owner, handle);
handle
}
pub fn connect_once_scoped<F>(&self, owner: &ConnectionScope, slot: F) -> ConnectionHandle
where
F: FnMut(Arc<T>) + Send + Sync + 'static,
{
let handle = self.connect_once(slot);
self.track_owner(owner, handle);
handle
}
pub fn disconnect(&self, handle: ConnectionHandle) -> bool {
self.inner.disconnect(handle)
}
pub fn disconnect_all(&self) {
self.inner.slots.write().expect("signal lock poisoned").clear();
}
pub fn block(&self, handle: ConnectionHandle) -> bool {
self.inner.block(handle)
}
pub fn unblock(&self, handle: ConnectionHandle) -> bool {
self.inner.unblock(handle)
}
pub fn is_blocked(&self, handle: ConnectionHandle) -> Option<bool> {
self.inner.is_blocked(handle)
}
pub fn is_connected(&self, handle: ConnectionHandle) -> bool {
self.inner.slots.read().expect("signal lock poisoned").contains_key(&handle)
}
pub fn set_priority(&self, handle: ConnectionHandle, priority: Priority) -> bool {
self.inner.set_priority(handle, priority)
}
pub fn emit(&self, value: T) {
let arc_value = Arc::new(value);
let snapshot: Vec<(ConnectionHandle, Priority)> = {
let slots = self.inner.slots.read().expect("signal lock poisoned");
slots.iter().map(|(h, e)| (*h, e.priority)).collect()
};
let mut snapshot = snapshot;
snapshot.sort_by_key(|a| a.1.rank());
for (handle, _priority) in snapshot {
let taken = {
let mut slots = self.inner.slots.write().expect("signal lock poisoned");
if let Some(entry) = slots.get_mut(&handle) {
if entry.blocked {
None
} else {
entry.callback.take()
}
} else {
None
}
};
if let Some(mut callback) = taken {
callback(arc_value.clone());
let mut slots = self.inner.slots.write().expect("signal lock poisoned");
if let Some(entry) = slots.get_mut(&handle) {
if entry.once {
slots.remove(&handle);
} else {
entry.callback = Some(callback);
}
}
}
}
}
pub fn slot_count(&self) -> usize {
self.inner.slots.read().expect("signal lock poisoned").len()
}
fn track_owner(&self, owner: &ConnectionScope, handle: ConnectionHandle) {
let weak = Arc::downgrade(&self.inner);
owner.track(Box::new(move || {
if let Some(inner) = weak.upgrade() {
let _ = inner.disconnect(handle);
}
}));
}
}
impl<T: Clone + Send + 'static> Default for Signal<T> {
fn default() -> Self {
Self::new()
}
}