use super::subscribers::Subscribers;
use crate::{
context::{RcDerefMut, SharedCell},
scheduler::{Scheduler, Task, TaskHandle, TaskState},
subscription::Subscription,
};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SubscriptionState {
Pending,
Ready(usize),
Cancelled,
}
pub struct SubjectSubscription<P, Sch, Cell> {
pub(crate) observers: P,
pub(crate) state: Cell,
pub(crate) scheduler: Sch,
}
impl<P, Sch, Cell> SubjectSubscription<P, Sch, Cell> {
pub(crate) fn new(observers: P, state: Cell, scheduler: Sch) -> Self {
Self { observers, state, scheduler }
}
}
pub struct RemoveState<P> {
observers: P,
id: usize,
}
impl<P, Sch, Cell> SubjectSubscription<P, Sch, Cell> {
pub fn unsubscribe_with_handle<O>(self) -> TaskHandle
where
P: RcDerefMut<Target = Subscribers<O>>,
Sch: Scheduler<Task<RemoveState<P>>>,
Cell: SharedCell<SubscriptionState>,
{
let current = loop {
let current = self.state.get();
if current == SubscriptionState::Cancelled {
return TaskHandle::finished();
}
if self
.state
.compare_exchange(current, SubscriptionState::Cancelled)
.is_ok()
{
break current;
}
};
let SubscriptionState::Ready(id) = current else {
return TaskHandle::finished();
};
if let Some(mut guard) = self.observers.try_rc_deref_mut() {
let observer = guard.remove(id);
drop(guard);
drop(observer);
return TaskHandle::finished();
}
self.scheduler.schedule(
Task::new(RemoveState { observers: self.observers, id }, |state| {
let observer = { state.observers.rc_deref_mut().remove(state.id) };
drop(observer);
TaskState::Finished
}),
None,
)
}
}
impl<P, O, Sch, Cell> Subscription for SubjectSubscription<P, Sch, Cell>
where
P: RcDerefMut<Target = Subscribers<O>>,
Sch: Scheduler<Task<RemoveState<P>>>,
Cell: SharedCell<SubscriptionState>,
{
fn unsubscribe(self) { self.unsubscribe_with_handle(); }
fn is_closed(&self) -> bool { self.state.get() == SubscriptionState::Cancelled }
}