rx_rust/operators/conditional_boolean/
amb.rs1use crate::delegate_disposal;
2use crate::disposable::{Disposable, DisposableExt};
3use crate::utils::mutable::{Mutable, MutableExt, MutableHelper};
4use crate::utils::types::{MaybeSend, Shared, WeakShared};
5use crate::{
6 observable::{Observable, Subscription},
7 observer::{Flow, Observer, Termination},
8};
9use educe::Educe;
10
11#[derive(Educe)]
41#[educe(Debug, Clone)]
42pub struct Amb<I> {
43 sources: I,
44}
45
46impl<I> Amb<I> {
47 pub fn new(sources: I) -> Self {
48 Self { sources }
49 }
50}
51
52delegate_disposal!(
53 Disposal<D>,
54 AmbDisposal<D>,
55 where D: Disposable
56);
57
58impl<'or, T, E, OE, I> Observable<'or, T, E> for Amb<I>
59where
60 I: IntoIterator<Item = OE>,
61 OE: Observable<'or, T, E>,
62 OE::D: MaybeSend + 'or,
63{
64 type D = Disposal<OE::D>;
65
66 fn subscribe(self, observer: impl Observer<T, E> + MaybeSend + 'or) -> Subscription<Self::D> {
67 let sources = self.sources.into_iter();
68 let minimum_source_count = sources.size_hint().0;
69 let observer = Shared::new(Mutable::new(Some(observer)));
70 let context = Shared::new(Mutable::new(AmbState::Racing(Vec::with_capacity(
71 minimum_source_count,
72 ))));
73
74 let mut has_sources = false;
75 for source in sources {
76 has_sources = true;
77 let Some(key) = reserve_subscription_slot(&context) else {
78 break;
79 };
80 let amb_observer = AmbObserver(AmbObserverState::Racing {
81 observer: observer.clone(),
82 context: Shared::downgrade(&context),
83 key,
84 });
85 let subscription = source.subscribe(amb_observer);
86 if !store_subscription(&context, key, subscription) {
87 break;
88 }
89 }
90
91 if !has_sources {
92 let observer = observer
95 .take_value()
96 .expect("a new amb must retain its downstream observer");
97 observer.on_termination(Termination::Completed);
98 }
99
100 AmbDisposal(context).into_subscription()
101 }
102}
103
104enum AmbState<D: Disposable> {
105 Racing(Vec<Option<Subscription<D>>>),
106 Won {
107 key: usize,
108 subscription: Option<Subscription<D>>,
109 },
110 Stopped,
111}
112
113fn reserve_subscription_slot<D>(context: &Mutable<AmbState<D>>) -> Option<usize>
114where
115 D: Disposable,
116{
117 context.with_mut(|state| match state {
118 AmbState::Racing(subscriptions) => {
119 let key = subscriptions.len();
120 subscriptions.push(None);
121 Some(key)
122 }
123 AmbState::Won { .. } | AmbState::Stopped => None,
124 })
125}
126
127fn store_subscription<D>(
128 context: &Mutable<AmbState<D>>,
129 key: usize,
130 subscription: Subscription<D>,
131) -> bool
132where
133 D: Disposable,
134{
135 let mut subscription = Some(subscription);
138 let (keep_subscribing, replaced) = context.with_mut(|state| match state {
139 AmbState::Racing(subscriptions) => {
141 let slot = subscriptions
142 .get_mut(key)
143 .expect("a racing source must retain its subscription slot");
144 let replaced = std::mem::replace(slot, subscription.take());
145 (true, replaced)
146 }
147 AmbState::Won {
149 key: winner_key,
150 subscription: winner_subscription,
151 } if *winner_key == key => {
152 let replaced = std::mem::replace(winner_subscription, subscription.take());
153 (false, replaced)
154 }
155 AmbState::Won { .. } | AmbState::Stopped => (false, None),
157 });
158 debug_assert!(
161 replaced.is_none(),
162 "a subscription slot is written only once"
163 );
164 drop((replaced, subscription)); keep_subscribing
166}
167
168fn try_win<D, OR>(
169 context: &Mutable<AmbState<D>>,
170 shared_observer: &Mutable<Option<OR>>,
171 key: usize,
172) -> Option<OR>
173where
174 D: Disposable,
175{
176 let losing_subscriptions = context.with_mut(|state| {
177 if !matches!(state, AmbState::Racing(_)) {
178 return None;
179 }
180
181 let AmbState::Racing(mut subscriptions) = std::mem::replace(state, AmbState::Stopped)
182 else {
183 unreachable!()
184 };
185 let winner_subscription = subscriptions
186 .get_mut(key)
187 .expect("a racing source must retain its subscription slot")
188 .take();
189 *state = AmbState::Won {
190 key,
191 subscription: winner_subscription,
192 };
193 Some(subscriptions)
194 })?;
195 let observer = shared_observer
198 .take_value()
199 .expect("a racing amb must retain its downstream observer");
200 drop(losing_subscriptions); Some(observer)
202}
203
204enum AmbObserverState<D: Disposable, OR> {
205 Racing {
206 observer: Shared<Mutable<Option<OR>>>,
207 context: WeakShared<Mutable<AmbState<D>>>,
208 key: usize,
209 },
210 Won(OR),
211 Lost,
212}
213
214struct AmbObserver<D: Disposable, OR>(AmbObserverState<D, OR>);
215
216impl<T, E, D, OR> Observer<T, E> for AmbObserver<D, OR>
217where
218 D: Disposable,
219 OR: Observer<T, E>,
220{
221 fn on_next(&mut self, value: T) -> Flow {
222 match &mut self.0 {
223 AmbObserverState::Won(observer) => {
224 return observer.on_next(value);
225 }
226 AmbObserverState::Lost => return Flow::Stop,
228 AmbObserverState::Racing { .. } => {}
229 }
230
231 let AmbObserverState::Racing {
232 observer: shared_observer,
233 context,
234 key,
235 } = std::mem::replace(&mut self.0, AmbObserverState::Lost)
236 else {
237 unreachable!()
238 };
239 let Some(shared_context) = context.upgrade() else {
240 return Flow::Stop;
241 };
242 let Some(mut observer) = try_win(&shared_context, &shared_observer, key) else {
243 return Flow::Stop;
244 };
245 drop(shared_context);
246 drop(shared_observer);
247
248 let flow = observer.on_next(value);
249 self.0 = AmbObserverState::Won(observer);
250 flow
251 }
252
253 fn on_termination(self, termination: Termination<E>) {
254 match self.0 {
255 AmbObserverState::Won(observer) => observer.on_termination(termination),
256 AmbObserverState::Lost => {}
257 AmbObserverState::Racing {
258 observer: shared_observer,
259 context,
260 key,
261 } => {
262 let Some(shared_context) = context.upgrade() else {
263 return;
264 };
265 let Some(observer) = try_win(&shared_context, &shared_observer, key) else {
266 return;
267 };
268 drop(shared_context);
269 drop(shared_observer);
270 observer.on_termination(termination);
271 }
272 }
273 }
274}
275
276struct AmbDisposal<D: Disposable>(Shared<Mutable<AmbState<D>>>);
277
278impl<D> Disposable for AmbDisposal<D>
279where
280 D: Disposable,
281{
282 fn dispose(self) {
283 let old_state = self.0.replace_value(AmbState::Stopped);
284 drop(old_state); }
286}