rx_rust/operators/utility/observe_on.rs
1use crate::utils::serialized_delivery::{DeliveryStopped, UpdateOutcome};
2use crate::{
3 disposable::{Disposable, bound_drop_disposal::BoundDropDisposal},
4 observable::{Observable, Subscription},
5 observer::{Event, Flow, Observer, Termination},
6 scheduler::{RecursionAction, Scheduler},
7 utils::{
8 pending_events::EventBatch,
9 subscribe_with_context::{self, SubscriptionContext, subscribe_with_context},
10 subscription_slot::SubscriptionSlot,
11 types::{MarkerType, MaybeSend},
12 },
13};
14use educe::Educe;
15
16/// Specifies the `Scheduler` on which an observer will observe this Observable.
17/// See <https://reactivex.io/documentation/operators/observeon.html>
18///
19/// # Examples
20/// ```rust
21/// # #[cfg(not(feature = "tokio-scheduler"))]
22/// # fn main() {}
23/// # #[cfg(feature = "tokio-scheduler")]
24/// #[tokio::main]
25/// async fn main() {
26/// use rx_rust::{
27/// observable::ObservableExt,
28/// observer::Termination,
29/// operators::{
30/// creating::from_iter::FromIter,
31/// utility::observe_on::ObserveOn,
32/// },
33/// };
34/// use std::sync::{Arc, Mutex};
35/// use tokio::time::{sleep, Duration};
36///
37/// let handle = tokio::runtime::Handle::current();
38/// let values = Arc::new(Mutex::new(Vec::new()));
39/// let terminations = Arc::new(Mutex::new(Vec::new()));
40/// let values_observer = Arc::clone(&values);
41/// let terminations_observer = Arc::clone(&terminations);
42///
43/// let subscription = ObserveOn::new(FromIter::new(vec![1, 2, 3]), handle.clone())
44/// .subscribe_with_callback(
45/// move |value| values_observer.lock().unwrap().push(value),
46/// move |termination| terminations_observer
47/// .lock()
48/// .unwrap()
49/// .push(termination),
50/// );
51///
52/// sleep(Duration::from_millis(10)).await;
53/// drop(subscription);
54///
55/// assert_eq!(&*values.lock().unwrap(), &[1, 2, 3]);
56/// assert_eq!(
57/// &*terminations.lock().unwrap(),
58/// &[Termination::Completed]
59/// );
60/// }
61/// ```
62#[derive(Educe)]
63#[educe(Debug, Clone)]
64pub struct ObserveOn<'or, OE, S> {
65 source: OE,
66 scheduler: S,
67 _marker: MarkerType<&'or ()>,
68}
69
70impl<'or, OE, S> ObserveOn<'or, OE, S> {
71 pub fn new(source: OE, scheduler: S) -> Self {
72 Self {
73 source,
74 scheduler,
75 _marker: Default::default(),
76 }
77 }
78}
79
80impl<'or, T, E, OE, S> Observable<'static, T, E> for ObserveOn<'or, OE, S>
81where
82 T: MaybeSend + 'static,
83 E: MaybeSend + 'static,
84 OE: Observable<'or, T, E>,
85 S: Scheduler + Clone + MaybeSend + 'static,
86{
87 type D = subscribe_with_context::Disposal<'or, OE::D>;
88
89 fn subscribe(
90 self,
91 observer: impl Observer<T, E> + MaybeSend + 'static,
92 ) -> Subscription<Self::D> {
93 let model = Model::<T, E, S::D> {
94 values: Vec::new(),
95 termination: None,
96 task: SubscriptionSlot::Idle,
97 };
98 subscribe_with_context(observer, model, |context| {
99 self.source.subscribe(ObserveOnObserver {
100 context,
101 scheduler: self.scheduler,
102 })
103 })
104 }
105}
106
107/// Events waiting to be observed and the scheduler task that delivers them.
108struct Model<T, E, D: Disposable> {
109 values: Vec<T>,
110 termination: Option<Termination<E>>,
111 /// Keeps at most one recursive scheduler task alive while events are waiting. The slot is
112 /// reserved while the task is being scheduled, which covers schedulers that can execute it
113 /// before returning its disposal.
114 task: SubscriptionSlot<BoundDropDisposal<D>>,
115}
116
117struct ObserveOnObserver<T, E, OR, S: Scheduler> {
118 context: SubscriptionContext<T, E, OR, Model<T, E, S::D>>,
119 scheduler: S,
120}
121
122impl<T, E, OR, S: Scheduler> ObserveOnObserver<T, E, OR, S> {
123 /// Queues `event` for the observing scheduler, starting the delivering task if it is stopped.
124 ///
125 /// Only one task exists at a time. `Observer` serializes its callers — `on_next` takes
126 /// `&mut self` and `on_termination` takes `self` — so a task cannot be started here while
127 /// another call is between starting a task and storing its disposal below.
128 ///
129 /// Returns [`Flow::Stop`] once the context has stopped: the event was then dropped, and so
130 /// would every later one be. What downstream itself answers is only known once the task
131 /// delivers, so this is otherwise [`Flow::Continue`].
132 fn queue_event(&self, event: Event<T, E>) -> Flow
133 where
134 T: MaybeSend + 'static,
135 E: MaybeSend + 'static,
136 OR: Observer<T, E> + MaybeSend + 'static,
137 S: Scheduler + Clone + MaybeSend + 'static,
138 {
139 let task_setup = self.context.update(|model| {
140 match event {
141 Event::Next(value) => model.values.push(value),
142 Event::Termination(termination) => model.termination = Some(termination),
143 }
144 UpdateOutcome::new(model.task.reserve_if_idle())
145 });
146 match task_setup {
147 Ok(true) => {}
148 // The task is already running, and will find the event on its next pass.
149 Ok(false) => return Flow::Continue,
150 Err(DeliveryStopped) => return Flow::Stop,
151 }
152
153 // The context owns this task through the model, so the task only holds a weak reference
154 // back: a strong one would form a cycle and leak the subscription.
155 let weak_context = self.context.downgrade();
156 let task = self.scheduler.schedule_recursively(
157 move |_| {
158 let Some(context) = weak_context.upgrade() else {
159 return RecursionAction::Stop;
160 };
161 context
162 .update(|model| {
163 let termination = model.termination.take();
164 let values = std::mem::take(&mut model.values);
165 let (action, events, discarded_values) = match termination {
166 // Nothing left to deliver. An empty batch is a no-op for the context,
167 // and every branch must produce one so their types agree.
168 None if values.is_empty() => (
169 RecursionAction::Stop,
170 EventBatch::NextBatch(Vec::new()),
171 None,
172 ),
173 // Recur instead of stopping: values arriving while this batch is
174 // delivered are pushed onto the model, and only another pass takes
175 // them. They cannot start a task of their own, because this one is
176 // still `Running` until a pass finds the model empty.
177 None => (
178 RecursionAction::ContinueImmediately,
179 EventBatch::NextBatch(values),
180 None,
181 ),
182 Some(completion @ Termination::Completed) => (
183 RecursionAction::Stop,
184 EventBatch::NextBatchAndTermination(values, completion),
185 None,
186 ),
187 // An error preempts the values buffered before it, unlike a completion.
188 Some(error @ Termination::Error(_)) => (
189 RecursionAction::Stop,
190 EventBatch::Termination(error),
191 Some(values),
192 ),
193 };
194 let finished_task = match action {
195 RecursionAction::Stop => model.task.release(),
196 _ => None,
197 };
198 UpdateOutcome::new(action)
199 .with_events(events)
200 .with_drop_outside((finished_task, discarded_values))
201 })
202 .unwrap_or(RecursionAction::Stop)
203 },
204 None,
205 );
206
207 // A scheduler that runs the task at once can have delivered, and ended, the stream before
208 // this runs: the flow of this update is what reports it.
209 self.context.update_flow(move |model| {
210 // If the task already stopped, `fill` gives the handle back to dispose outside the
211 // lock.
212 UpdateOutcome::empty().with_drop_outside(model.task.fill(task))
213 })
214 }
215}
216
217impl<T, E, OR, S> Observer<T, E> for ObserveOnObserver<T, E, OR, S>
218where
219 T: MaybeSend + 'static,
220 E: MaybeSend + 'static,
221 OR: Observer<T, E> + MaybeSend + 'static,
222 S: Scheduler + Clone + MaybeSend + 'static,
223{
224 fn on_next(&mut self, value: T) -> Flow {
225 self.queue_event(Event::Next(value))
226 }
227
228 fn on_termination(self, termination: Termination<E>) {
229 // The termination is the last event, so what the context answers is of no use here.
230 let _ = self.queue_event(Event::Termination(termination));
231 }
232}