1use crate::delegate_disposal;
2use crate::disposable::{Disposable, chain_disposal::ChainDisposal};
3use crate::observable::{Observable, Subscription};
4use crate::observer::Observer;
5use crate::operators::connectable::connectable_controller::{
6 ConnectableController, Connected, Disconnected,
7};
8use crate::subject::Subject;
9use crate::subject::subject_observable::SubjectObservable;
10use crate::utils::mutable::{Mutable, MutableHelper};
11use crate::utils::on_panic::OnPanic;
12use crate::utils::types::{MaybeSend, Shared};
13use educe::Educe;
14use std::num::NonZeroUsize;
15
16#[derive(Educe)]
17#[educe(Debug)]
18enum State<OE, S, D>
19where
20 D: Disposable,
21{
22 Disconnected {
23 controller: ConnectableController<OE, S, Disconnected>,
24 },
25 ConnectingOrDisconnecting {
26 subscribers: usize,
29 },
30 Connected {
31 subscribers: NonZeroUsize,
32 controller: ConnectableController<OE, S, Connected<D>>,
33 },
34}
35
36#[derive(Educe)]
79#[educe(Debug, Clone)]
80pub struct RefCount<'or, T, E, OE, S>
81where
82 OE: Observable<'or, T, E>,
83{
84 observable: SubjectObservable<S>,
85 state: Shared<Mutable<State<OE, S, OE::D>>>,
86}
87
88impl<'or, T, E, OE, S> RefCount<'or, T, E, OE, S>
89where
90 OE: Observable<'or, T, E>,
91 S: Clone,
92{
93 pub fn new(controller: ConnectableController<OE, S, Disconnected>) -> Self {
94 Self {
95 observable: controller.observable(),
96 state: Shared::new(Mutable::new(State::Disconnected { controller })),
97 }
98 }
99}
100
101delegate_disposal!(
102 Disposal<'or, T, E, OE, S>,
103 ChainDisposal<S::D, RefCountDisposal<'or, T, E, OE, S>>,
104 where OE: Observable<'or, T, E>,
105 S: Subject<'or, T, E>
106);
107
108impl<'or, T, E, OE, S> Observable<'or, T, E> for RefCount<'or, T, E, OE, S>
109where
110 OE: Observable<'or, T, E> + Clone,
111 S: Subject<'or, T, E> + Clone + MaybeSend + 'or,
112{
113 type D = Disposal<'or, T, E, OE, S>;
114
115 fn subscribe(self, observer: impl Observer<T, E> + MaybeSend + 'or) -> Subscription<Self::D> {
116 let controller = self.state.with_mut(|current| match &mut *current {
117 state @ State::Disconnected { .. } => {
118 let State::Disconnected { controller } =
119 std::mem::replace(state, State::ConnectingOrDisconnecting { subscribers: 1 })
120 else {
121 unreachable!()
122 };
123 Some(controller)
124 }
125 State::ConnectingOrDisconnecting { subscribers } => {
126 *subscribers = subscribers
127 .checked_add(1)
128 .expect("RefCount subscriber count overflowed");
129 None
130 }
131 State::Connected { subscribers, .. } => {
132 *subscribers = subscribers
133 .checked_add(1)
134 .expect("RefCount subscriber count overflowed");
135 None
136 }
137 });
138 let state = self.state.clone();
143 let guard = OnPanic::new(controller, move |controller| {
144 restore_subscriber(&state, controller);
145 });
146 let sub = self.observable.subscribe(observer);
147 let controller = guard.disarm();
148 if let Some(controller) = controller {
149 handle_connecting_or_disconnecting(self.state.clone(), Purpose::Connect(controller));
150 }
151 sub.then(RefCountDisposal { state: self.state }).map_into()
152 }
153}
154
155fn restore_subscriber<'or, T, E, OE, S>(
163 state: &Shared<Mutable<State<OE, S, OE::D>>>,
164 controller: Option<ConnectableController<OE, S, Disconnected>>,
165) where
166 OE: Observable<'or, T, E> + Clone,
167 S: Observer<T, E> + Clone + MaybeSend + 'or,
168{
169 let Some(controller) = controller else {
170 RefCountDisposal {
173 state: state.clone(),
174 }
175 .dispose();
176 return;
177 };
178 let controller = state.with_mut(|current| {
181 let State::ConnectingOrDisconnecting { subscribers } = current else {
182 unreachable!("the controller of the connecting state is held by this guard")
183 };
184 let remaining = subscribers
185 .checked_sub(1)
186 .expect("RefCount subscriber count underflowed");
187 if remaining == 0 {
188 *current = State::Disconnected { controller };
190 None
191 } else {
192 *subscribers = remaining;
193 Some(controller)
194 }
195 });
196 if let Some(controller) = controller {
197 handle_connecting_or_disconnecting(state.clone(), Purpose::Connect(controller));
200 }
201}
202
203struct RefCountDisposal<'or, T, E, OE, S>
204where
205 OE: Observable<'or, T, E>,
206{
207 state: Shared<Mutable<State<OE, S, OE::D>>>,
208}
209
210impl<'or, T, E, OE, S> Disposable for RefCountDisposal<'or, T, E, OE, S>
211where
212 OE: Observable<'or, T, E> + Clone,
213 S: Observer<T, E> + Clone + MaybeSend + 'or,
214{
215 fn dispose(self) {
216 let controller = self.state.with_mut(|current| match &mut *current {
217 State::Disconnected { .. } => unreachable!(),
218 State::ConnectingOrDisconnecting { subscribers } => {
219 *subscribers = subscribers
220 .checked_sub(1)
221 .expect("RefCount subscriber count underflowed");
222 None
223 }
224 State::Connected { subscribers, .. } if subscribers.get() > 1 => {
225 *subscribers = NonZeroUsize::new(subscribers.get() - 1)
226 .expect("decremented subscriber count is non-zero");
227 None
228 }
229 State::Connected { .. } => {
230 let State::Connected { controller, .. } =
231 std::mem::replace(current, State::ConnectingOrDisconnecting { subscribers: 0 })
232 else {
233 unreachable!()
234 };
235 Some(controller)
236 }
237 });
238 if let Some(controller) = controller {
239 handle_connecting_or_disconnecting(self.state.clone(), Purpose::Disconnect(controller));
240 }
241 }
242}
243
244enum Purpose<'or, T, E, OE, S>
245where
246 OE: Observable<'or, T, E>,
247{
248 Connect(ConnectableController<OE, S, Disconnected>),
249 Disconnect(ConnectableController<OE, S, Connected<OE::D>>),
250}
251
252fn handle_connecting_or_disconnecting<'or, T, E, OE, S>(
253 state: Shared<Mutable<State<OE, S, OE::D>>>,
254 mut purpose: Purpose<'or, T, E, OE, S>,
255) where
256 OE: Observable<'or, T, E> + Clone,
257 S: Observer<T, E> + Clone + MaybeSend + 'or,
258{
259 loop {
260 let next = match purpose {
261 Purpose::Connect(controller) => {
262 let controller = controller.connect();
263 state.with_mut(|current| match &mut *current {
264 State::Disconnected { .. } => unreachable!(),
265 State::ConnectingOrDisconnecting { subscribers } => {
266 if *subscribers == 0 {
267 Some(Purpose::Disconnect(controller))
269 } else {
270 *current = State::Connected {
271 subscribers: NonZeroUsize::new(*subscribers).unwrap(),
272 controller,
273 };
274 None
275 }
276 }
277 State::Connected { .. } => unreachable!(),
278 })
279 }
280 Purpose::Disconnect(controller) => {
281 let controller = controller.disconnect();
282 state.with_mut(|current| match &mut *current {
283 State::Disconnected { .. } => unreachable!(),
284 State::ConnectingOrDisconnecting { subscribers } => {
285 if *subscribers == 0 {
286 *current = State::Disconnected { controller };
287 None
288 } else {
289 Some(Purpose::Connect(controller))
291 }
292 }
293 State::Connected { .. } => unreachable!(),
294 })
295 }
296 };
297 match next {
298 Some(next) => purpose = next,
299 None => break,
300 }
301 }
302}