Skip to main content

datum/concurrent/
subscription.rs

1use std::{
2    collections::{HashMap, VecDeque},
3    hint,
4    marker::PhantomData,
5    sync::{
6        Arc, Condvar, Mutex, MutexGuard,
7        atomic::{AtomicBool, AtomicU8, AtomicU64, AtomicUsize, Ordering, fence},
8        mpsc,
9    },
10    thread,
11    time::Duration,
12};
13
14use arc_swap::{ArcSwap, ArcSwapOption};
15use ractor::{Actor, ActorProcessingErr, ActorRef};
16use tokio::sync::Notify;
17
18use crate::{
19    StreamError, StreamResult,
20    actor::block_on_ractor_runtime,
21    stream::{BoxStream, NotUsed, Source, current_stream_cancelled},
22};
23
24const SLOT_WAIT_BACKSTOP: Duration = Duration::from_millis(10);
25const SUBSCRIPTION_DRAIN_BATCH: usize = 256;
26const STATE_OPEN: u8 = 0;
27const STATE_CLOSING: u8 = 1;
28const STATE_CLOSED: u8 = 2;
29const UNSEEDED_CURSOR: u64 = u64::MAX;
30const NO_DROP_FROM: u64 = u64::MAX;
31const NO_TERMINAL_FROM: u64 = u64::MAX;
32
33type Ack = mpsc::Sender<StreamResult<()>>;
34
35/// Overflow policy for a [`Subscription`] subscriber buffer.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum SubscriptionOverflow {
38    /// Preserve every change by parking the producer on the caller thread until every active
39    /// subscriber cursor has ring capacity. No actor handler waits for data-plane backpressure.
40    Backpressure,
41    /// Apply the state transition, but mark the new feed item as dropped for subscribers whose
42    /// logical cursor is full. This is not lossless for slow subscribers and is intended only when
43    /// dropping is an explicit part of the chosen contract.
44    DropNew,
45    /// Apply the state transition, fail subscribers whose logical cursor is full after they drain
46    /// already-accepted items, and return an error to the acknowledged producer.
47    Fail,
48}
49
50/// Latest-value state cell with a bounded every-change feed.
51///
52/// `Subscription` uses a two-plane implementation. The Ractor actor owns only subscribe,
53/// unsubscribe, close, terminal delivery, and the registry that is periodically published as an
54/// `ArcSwap` slot-table snapshot. State transitions run on the caller thread.
55///
56/// The lossless data plane is a sequence-claimed ring: each writer claims a global sequence,
57/// waits for its publish turn, writes the ring slot, stores the `ArcSwap` mirror, publishes an
58/// internal `(sequence, value)` snapshot, wakes current subscribers, and then returns. Under
59/// [`SubscriptionOverflow::Backpressure`], producers wait outside the actor while any active
60/// subscriber cursor would lag past the logical capacity. Subscribers consume by cursor in total
61/// sequence order, so they see no gaps or duplicates.
62///
63/// Subscribe has no actor-serialized-set gap. The control actor first publishes the new
64/// slot-table snapshot, then seeds the slot from the current published `(sequence, value)` and sets
65/// the cursor to `sequence + 1`; any writer that races after registration is either represented by
66/// the seed or consumed from the ring.
67///
68/// `update` uses `ArcSwap::compare_and_swap` under the write publication turn. The update function
69/// may be re-invoked if a concurrent writer wins the CAS race, matching the usual Ref/atomic
70/// update contract.
71pub struct Subscription<T: Send + Sync + 'static> {
72    inner: Arc<SubscriptionInner<T>>,
73}
74
75struct SubscriptionInner<T: Send + Sync + 'static> {
76    actor: ActorRef<SubscriptionMessage<T>>,
77    shared: Arc<SubscriptionShared<T>>,
78    next_subscriber_id: Arc<AtomicU64>,
79}
80
81struct SubscriptionShared<T: Send + Sync + 'static> {
82    mirror: Arc<ArcSwap<T>>,
83    published: Arc<ArcSwap<PublishedValue<T>>>,
84    subscribers: Arc<ArcSwap<SubscriptionSlotTable<T>>>,
85    ring: SubscriptionRing<T>,
86    overflow: SubscriptionOverflow,
87    lifecycle: AtomicU8,
88    active_writers: AtomicUsize,
89    next_sequence: AtomicU64,
90    published_sequence: AtomicU64,
91    parked_slots: Arc<AtomicUsize>,
92}
93
94struct PublishedValue<T: Send + Sync + 'static> {
95    sequence: u64,
96    value: Arc<T>,
97}
98
99struct SubscriptionSlotTable<T: Send + Sync + 'static> {
100    slots: Vec<Arc<SubscriptionSlot<T>>>,
101}
102
103struct SubscriptionRing<T: Send + Sync + 'static> {
104    logical_capacity: u64,
105    physical_capacity: usize,
106    slots: Vec<SubscriptionRingSlot<T>>,
107    space_lock: Mutex<()>,
108    space_available: Condvar,
109    space_waiters: AtomicUsize,
110}
111
112struct SubscriptionRingSlot<T: Send + Sync + 'static> {
113    sequence: AtomicU64,
114    value: ArcSwapOption<T>,
115}
116
117impl<T: Send + Sync + 'static> Clone for Subscription<T> {
118    fn clone(&self) -> Self {
119        Self {
120            inner: Arc::clone(&self.inner),
121        }
122    }
123}
124
125impl<T: Send + Sync + 'static> Subscription<T> {
126    /// Create a subscription initialized to `initial`.
127    ///
128    /// Panics if `capacity == 0`.
129    pub fn new(initial: T, capacity: usize, overflow: SubscriptionOverflow) -> StreamResult<Self> {
130        assert!(
131            capacity > 0,
132            "subscription capacity must be greater than zero"
133        );
134        let value = Arc::new(initial);
135        let shared = Arc::new(SubscriptionShared {
136            mirror: Arc::new(ArcSwap::from(Arc::clone(&value))),
137            published: Arc::new(ArcSwap::from_pointee(PublishedValue {
138                sequence: 0,
139                value: Arc::clone(&value),
140            })),
141            subscribers: Arc::new(ArcSwap::from_pointee(SubscriptionSlotTable {
142                slots: Vec::new(),
143            })),
144            ring: SubscriptionRing::new(capacity),
145            overflow,
146            lifecycle: AtomicU8::new(STATE_OPEN),
147            active_writers: AtomicUsize::new(0),
148            next_sequence: AtomicU64::new(0),
149            published_sequence: AtomicU64::new(0),
150            parked_slots: Arc::new(AtomicUsize::new(0)),
151        });
152        let state = SubscriptionActorState {
153            shared: Arc::clone(&shared),
154            subscribers: HashMap::new(),
155            closed: false,
156        };
157        let (actor, _handle) =
158            block_on_ractor_runtime(Actor::spawn(None, SubscriptionActor::<T>::default(), state))?
159                .map_err(|error| {
160                    StreamError::Failed(format!("subscription actor failed to spawn: {error}"))
161                })?;
162        Ok(Self {
163            inner: Arc::new(SubscriptionInner {
164                actor,
165                shared,
166                next_subscriber_id: Arc::new(AtomicU64::new(1)),
167            }),
168        })
169    }
170
171    /// Return the current immutable snapshot without sending an actor message.
172    #[must_use]
173    pub fn get(&self) -> Arc<T> {
174        self.inner.shared.mirror.load_full()
175    }
176
177    /// Return a cloned value using `ArcSwap::load()`'s guarded read path.
178    ///
179    /// This avoids cloning the `Arc` itself on the hot read path. For scalar `Copy`/cheap-`Clone`
180    /// values, this is the fair equivalent of JVM refs returning the value directly; use
181    /// [`Subscription::get`] when the caller wants an owned snapshot shared by `Arc`.
182    #[must_use]
183    pub fn get_cloned(&self) -> T
184    where
185        T: Clone,
186    {
187        self.inner.shared.mirror.load().as_ref().clone()
188    }
189
190    /// Set the state and wait for the transition to be accepted according to the overflow policy.
191    pub fn set(&self, value: T) -> StreamResult<()> {
192        self.publish_set(Arc::new(value))
193    }
194
195    /// Set the state on the caller thread.
196    ///
197    /// With [`SubscriptionOverflow::Backpressure`], this may still park the caller until subscriber
198    /// cursors make ring capacity available. It does not send an actor message.
199    pub fn set_eventually(&self, value: T) -> StreamResult<()> {
200        self.publish_set(Arc::new(value))
201    }
202
203    /// Update the state atomically and wait for the transition to be accepted.
204    ///
205    /// The update function may be called more than once if a concurrent writer wins the CAS race.
206    pub fn update<F>(&self, update: F) -> StreamResult<()>
207    where
208        F: FnMut(&T) -> T + Send + 'static,
209    {
210        self.publish_update(update)
211    }
212
213    /// Update the state atomically on the caller thread.
214    ///
215    /// The update function may be called more than once if a concurrent writer wins the CAS race.
216    pub fn update_eventually<F>(&self, update: F) -> StreamResult<()>
217    where
218        F: FnMut(&T) -> T + Send + 'static,
219    {
220        self.publish_update(update)
221    }
222
223    /// Close the subscription, re-emitting the current final snapshot to current subscribers.
224    pub fn close(&self) -> StreamResult<()> {
225        self.send_close(None)
226    }
227
228    /// Set a final value, then close the subscription in one control-plane turn.
229    pub fn close_with(&self, final_value: T) -> StreamResult<()> {
230        self.send_close(Some(final_value))
231    }
232
233    fn publish_set(&self, value: Arc<T>) -> StreamResult<()> {
234        let _permit = self.inner.shared.begin_write()?;
235        let sequence = self.inner.shared.claim_sequence();
236        self.inner.shared.wait_publish_turn(sequence);
237        self.inner.shared.wait_for_ring_capacity(sequence);
238        let overflow = self.inner.shared.apply_overflow_policy(sequence);
239        self.inner.shared.finish_publish(sequence, value);
240        overflow
241    }
242
243    fn publish_update<F>(&self, mut update: F) -> StreamResult<()>
244    where
245        F: FnMut(&T) -> T + Send + 'static,
246    {
247        let _permit = self.inner.shared.begin_write()?;
248        let sequence = self.inner.shared.claim_sequence();
249        self.inner.shared.wait_publish_turn(sequence);
250        self.inner.shared.wait_for_ring_capacity(sequence);
251        let value = loop {
252            let current = self.inner.shared.mirror.load();
253            let next = Arc::new(update(current.as_ref()));
254            let previous = self
255                .inner
256                .shared
257                .mirror
258                .compare_and_swap(&*current, Arc::clone(&next));
259            if std::ptr::eq(current.as_ref(), previous.as_ref()) {
260                break next;
261            }
262        };
263        let overflow = self.inner.shared.apply_overflow_policy(sequence);
264        self.inner.shared.ring.store(sequence, Arc::clone(&value));
265        self.inner
266            .shared
267            .finish_publish_after_mirror(sequence, value);
268        overflow
269    }
270
271    fn send_close(&self, final_value: Option<T>) -> StreamResult<()> {
272        let (reply, receiver) = mpsc::channel();
273        self.inner
274            .actor
275            .send_message(SubscriptionMessage::Close { final_value, reply })
276            .map_err(|error| StreamError::ActorAskSendFailed {
277                reason: error.to_string(),
278            })?;
279        receiver.recv().unwrap_or(Err(StreamError::ActorTerminated))
280    }
281
282    fn register_slot(&self, slot: Arc<SubscriptionSlot<T>>, id: u64) -> StreamResult<()> {
283        let (reply, receiver) = mpsc::channel();
284        self.inner
285            .actor
286            .send_message(SubscriptionMessage::Subscribe { id, slot, reply })
287            .map_err(|error| StreamError::ActorAskSendFailed {
288                reason: error.to_string(),
289            })?;
290        receiver.recv().unwrap_or(Err(StreamError::ActorTerminated))
291    }
292}
293
294impl<T: Clone + Send + Sync + 'static> Subscription<T> {
295    /// A bounded source of the current value followed by every accepted change.
296    ///
297    /// Under [`SubscriptionOverflow::Backpressure`], every subscriber observes every change. Under
298    /// `DropNew`, slow subscribers may miss changes. Under `Fail`, a full subscriber fails with
299    /// `StreamError::Failed`.
300    #[must_use]
301    pub fn changes(&self) -> Source<T> {
302        let actor = self.inner.actor.clone();
303        let subscription = self.clone();
304        let shared = Arc::clone(&self.inner.shared);
305        let next_subscriber_id = Arc::clone(&self.inner.next_subscriber_id);
306        Source::from_materialized_factory(move |_materializer| {
307            let id = next_subscriber_id.fetch_add(1, Ordering::Relaxed);
308            let slot = SubscriptionSlot::new(id, actor.clone(), Arc::clone(&shared.parked_slots));
309            subscription.register_slot(Arc::clone(&slot), id)?;
310            let stream: BoxStream<T> = Box::new(SubscriptionChangesStream {
311                shared: Arc::clone(&shared),
312                slot,
313                pending: VecDeque::new(),
314                terminated: false,
315            });
316            Ok((stream, NotUsed))
317        })
318    }
319
320    #[doc(hidden)]
321    pub fn __benchmark_changes(&self) -> StreamResult<SubscriptionBenchmarkStream<T>> {
322        let id = self
323            .inner
324            .next_subscriber_id
325            .fetch_add(1, Ordering::Relaxed);
326        let slot = SubscriptionSlot::new(
327            id,
328            self.inner.actor.clone(),
329            Arc::clone(&self.inner.shared.parked_slots),
330        );
331        self.register_slot(Arc::clone(&slot), id)?;
332        Ok(SubscriptionBenchmarkStream {
333            shared: Arc::clone(&self.inner.shared),
334            slot,
335            pending: VecDeque::new(),
336            terminated: false,
337        })
338    }
339}
340
341impl<T: Send + Sync + 'static> SubscriptionShared<T> {
342    fn begin_write(&self) -> StreamResult<WritePermit<'_>> {
343        if self.lifecycle.load(Ordering::Acquire) != STATE_OPEN {
344            return Err(closed_error());
345        }
346        self.active_writers.fetch_add(1, Ordering::AcqRel);
347        if self.lifecycle.load(Ordering::Acquire) == STATE_OPEN {
348            Ok(WritePermit {
349                active_writers: &self.active_writers,
350            })
351        } else {
352            self.active_writers.fetch_sub(1, Ordering::AcqRel);
353            Err(closed_error())
354        }
355    }
356
357    fn claim_sequence(&self) -> u64 {
358        self.next_sequence.fetch_add(1, Ordering::AcqRel) + 1
359    }
360
361    fn wait_publish_turn(&self, sequence: u64) {
362        let mut spins = 0_u32;
363        while self.published_sequence.load(Ordering::Acquire) + 1 != sequence {
364            spins = spins.wrapping_add(1);
365            if spins < 64 {
366                hint::spin_loop();
367            } else {
368                thread::yield_now();
369            }
370        }
371    }
372
373    fn wait_for_ring_capacity(&self, sequence: u64) {
374        if self.overflow != SubscriptionOverflow::Backpressure {
375            return;
376        }
377        let mut guard = self
378            .ring
379            .space_lock
380            .lock()
381            .unwrap_or_else(|poison| poison.into_inner());
382        while self.sequence_would_overflow(sequence) {
383            self.ring.space_waiters.fetch_add(1, Ordering::AcqRel);
384            if !self.sequence_would_overflow(sequence) {
385                self.ring.space_waiters.fetch_sub(1, Ordering::AcqRel);
386                break;
387            }
388            guard = self
389                .ring
390                .space_available
391                .wait_timeout(guard, SLOT_WAIT_BACKSTOP)
392                .unwrap_or_else(|poison| poison.into_inner())
393                .0;
394            self.ring.space_waiters.fetch_sub(1, Ordering::AcqRel);
395        }
396    }
397
398    fn sequence_would_overflow(&self, sequence: u64) -> bool {
399        let Some(cursor) = self.min_active_cursor() else {
400            return false;
401        };
402        sequence >= cursor.saturating_add(self.ring.logical_capacity)
403    }
404
405    fn min_active_cursor(&self) -> Option<u64> {
406        let table = self.subscribers.load();
407        table
408            .slots
409            .iter()
410            .filter_map(|slot| slot.backpressure_cursor())
411            .min()
412    }
413
414    fn apply_overflow_policy(&self, sequence: u64) -> StreamResult<()> {
415        match self.overflow {
416            SubscriptionOverflow::Backpressure => Ok(()),
417            SubscriptionOverflow::DropNew => {
418                let table = self.subscribers.load();
419                for slot in &table.slots {
420                    if slot.is_full_for(sequence, self.ring.logical_capacity) {
421                        slot.drop_new(sequence, self.ring.logical_capacity);
422                    }
423                }
424                Ok(())
425            }
426            SubscriptionOverflow::Fail => {
427                let table = self.subscribers.load();
428                let mut overflowed = false;
429                let error = overflow_error(self.ring.logical_capacity);
430                for slot in &table.slots {
431                    if slot.is_full_for(sequence, self.ring.logical_capacity) {
432                        overflowed = true;
433                        slot.fail_after(sequence, error.clone());
434                    }
435                }
436                if overflowed { Err(error) } else { Ok(()) }
437            }
438        }
439    }
440
441    fn finish_publish(&self, sequence: u64, value: Arc<T>) {
442        self.ring.store(sequence, Arc::clone(&value));
443        self.mirror.store(Arc::clone(&value));
444        self.finish_publish_after_mirror(sequence, value);
445    }
446
447    fn finish_publish_after_mirror(&self, sequence: u64, value: Arc<T>) {
448        self.published.store(Arc::new(PublishedValue {
449            sequence,
450            value: Arc::clone(&value),
451        }));
452        self.published_sequence.store(sequence, Ordering::Release);
453        if self.parked_slots.load(Ordering::Acquire) != 0 {
454            let table = self.subscribers.load();
455            for slot in &table.slots {
456                slot.wake_for_sequence(sequence);
457            }
458        }
459    }
460
461    fn wait_for_writers_to_drain(&self) {
462        while self.active_writers.load(Ordering::Acquire) != 0 {
463            thread::yield_now();
464        }
465    }
466}
467
468impl<T: Send + Sync + 'static> SubscriptionRing<T> {
469    fn new(logical_capacity: usize) -> Self {
470        let physical_capacity = logical_capacity.max(1_024).next_power_of_two();
471        let mut slots = Vec::with_capacity(physical_capacity);
472        for _ in 0..physical_capacity {
473            slots.push(SubscriptionRingSlot {
474                sequence: AtomicU64::new(0),
475                value: ArcSwapOption::empty(),
476            });
477        }
478        Self {
479            logical_capacity: logical_capacity as u64,
480            physical_capacity,
481            slots,
482            space_lock: Mutex::new(()),
483            space_available: Condvar::new(),
484            space_waiters: AtomicUsize::new(0),
485        }
486    }
487
488    fn store(&self, sequence: u64, value: Arc<T>) {
489        let slot = &self.slots[self.index(sequence)];
490        slot.value.store(Some(value));
491        slot.sequence.store(sequence, Ordering::Release);
492    }
493
494    fn load(&self, sequence: u64) -> Option<Arc<T>> {
495        let slot = &self.slots[self.index(sequence)];
496        if slot.sequence.load(Ordering::Acquire) == sequence {
497            slot.value.load_full()
498        } else {
499            None
500        }
501    }
502
503    fn has(&self, sequence: u64) -> bool {
504        let slot = &self.slots[self.index(sequence)];
505        slot.sequence.load(Ordering::Acquire) == sequence
506    }
507
508    fn oldest_available(&self, published_sequence: u64) -> u64 {
509        published_sequence
510            .saturating_sub(self.physical_capacity as u64)
511            .saturating_add(1)
512            .max(1)
513    }
514
515    fn notify_space(&self) {
516        if self.space_waiters.load(Ordering::Acquire) == 0 {
517            return;
518        }
519        let _guard = self
520            .space_lock
521            .lock()
522            .unwrap_or_else(|poison| poison.into_inner());
523        self.space_available.notify_all();
524    }
525
526    fn index(&self, sequence: u64) -> usize {
527        sequence as usize & (self.physical_capacity - 1)
528    }
529}
530
531struct WritePermit<'a> {
532    active_writers: &'a AtomicUsize,
533}
534
535impl Drop for WritePermit<'_> {
536    fn drop(&mut self) {
537        self.active_writers.fetch_sub(1, Ordering::AcqRel);
538    }
539}
540
541impl<T: Send + Sync + 'static> Drop for SubscriptionInner<T> {
542    fn drop(&mut self) {
543        self.actor.stop(None);
544    }
545}
546
547enum SubscriptionMessage<T: Send + Sync + 'static> {
548    Close {
549        final_value: Option<T>,
550        reply: Ack,
551    },
552    Subscribe {
553        id: u64,
554        slot: Arc<SubscriptionSlot<T>>,
555        reply: Ack,
556    },
557    Unsubscribe {
558        id: u64,
559    },
560}
561
562struct SubscriptionActor<T> {
563    _marker: PhantomData<fn() -> T>,
564}
565
566impl<T> Default for SubscriptionActor<T> {
567    fn default() -> Self {
568        Self {
569            _marker: PhantomData,
570        }
571    }
572}
573
574struct SubscriptionActorState<T: Send + Sync + 'static> {
575    shared: Arc<SubscriptionShared<T>>,
576    subscribers: HashMap<u64, Arc<SubscriptionSlot<T>>>,
577    closed: bool,
578}
579
580impl<T: Send + Sync + 'static> Actor for SubscriptionActor<T> {
581    type Msg = SubscriptionMessage<T>;
582    type State = SubscriptionActorState<T>;
583    type Arguments = SubscriptionActorState<T>;
584
585    async fn pre_start(
586        &self,
587        _myself: ActorRef<Self::Msg>,
588        args: Self::Arguments,
589    ) -> Result<Self::State, ActorProcessingErr> {
590        Ok(args)
591    }
592
593    async fn handle(
594        &self,
595        _myself: ActorRef<Self::Msg>,
596        message: Self::Msg,
597        state: &mut Self::State,
598    ) -> Result<(), ActorProcessingErr> {
599        match message {
600            SubscriptionMessage::Close { final_value, reply } => {
601                close_subscription(state, final_value);
602                let _ = reply.send(Ok(()));
603            }
604            SubscriptionMessage::Subscribe { id, slot, reply } => {
605                if state.closed || state.shared.lifecycle.load(Ordering::Acquire) == STATE_CLOSED {
606                    let published = state.shared.published.load_full();
607                    slot.complete_post_close(Arc::clone(&published.value));
608                } else {
609                    state.subscribers.insert(id, Arc::clone(&slot));
610                    publish_subscription_slot_table(state);
611                    let published = state.shared.published.load_full();
612                    slot.seed(
613                        published.sequence.saturating_add(1),
614                        Arc::clone(&published.value),
615                    );
616                }
617                let _ = reply.send(Ok(()));
618            }
619            SubscriptionMessage::Unsubscribe { id } => {
620                state.subscribers.remove(&id);
621                publish_subscription_slot_table(state);
622                state.shared.ring.notify_space();
623            }
624        }
625        Ok(())
626    }
627
628    async fn post_stop(
629        &self,
630        _myself: ActorRef<Self::Msg>,
631        state: &mut Self::State,
632    ) -> Result<(), ActorProcessingErr> {
633        if !state.closed {
634            for slot in state.subscribers.values() {
635                slot.fail_now(StreamError::ActorTerminated);
636            }
637            state.subscribers.clear();
638            publish_subscription_slot_table(state);
639            state.shared.ring.notify_space();
640        }
641        Ok(())
642    }
643}
644
645fn close_subscription<T: Send + Sync + 'static>(
646    state: &mut SubscriptionActorState<T>,
647    final_value: Option<T>,
648) {
649    if state.closed {
650        return;
651    }
652    match state.shared.lifecycle.compare_exchange(
653        STATE_OPEN,
654        STATE_CLOSING,
655        Ordering::AcqRel,
656        Ordering::Acquire,
657    ) {
658        Ok(_) => {}
659        Err(STATE_CLOSED) => {
660            state.closed = true;
661            return;
662        }
663        Err(_) => {}
664    }
665    state.shared.wait_for_writers_to_drain();
666
667    let sequence = state.shared.claim_sequence();
668    state.shared.wait_publish_turn(sequence);
669    let value = final_value
670        .map(Arc::new)
671        .unwrap_or_else(|| state.shared.mirror.load_full());
672    state.shared.mirror.store(Arc::clone(&value));
673    state.shared.published.store(Arc::new(PublishedValue {
674        sequence,
675        value: Arc::clone(&value),
676    }));
677    state
678        .shared
679        .published_sequence
680        .store(sequence, Ordering::Release);
681    state
682        .shared
683        .lifecycle
684        .store(STATE_CLOSED, Ordering::Release);
685
686    for slot in state.subscribers.values() {
687        slot.complete_with_final(sequence, Arc::clone(&value));
688    }
689    state.subscribers.clear();
690    publish_subscription_slot_table(state);
691    state.shared.ring.notify_space();
692    state.closed = true;
693}
694
695fn publish_subscription_slot_table<T: Send + Sync + 'static>(state: &SubscriptionActorState<T>) {
696    let slots = state.subscribers.values().cloned().collect::<Vec<_>>();
697    state
698        .shared
699        .subscribers
700        .store(Arc::new(SubscriptionSlotTable { slots }));
701}
702
703fn closed_error() -> StreamError {
704    StreamError::Failed("subscription is closed".into())
705}
706
707fn overflow_error(capacity: u64) -> StreamError {
708    StreamError::Failed(format!(
709        "subscription buffer overflow (max capacity was: {capacity})"
710    ))
711}
712
713fn atomic_fetch_min(target: &AtomicU64, value: u64) {
714    let mut current = target.load(Ordering::Acquire);
715    while value < current {
716        match target.compare_exchange(current, value, Ordering::AcqRel, Ordering::Acquire) {
717            Ok(_) => return,
718            Err(observed) => current = observed,
719        }
720    }
721}
722
723fn atomic_fetch_max(target: &AtomicU64, value: u64) {
724    let mut current = target.load(Ordering::Acquire);
725    while value > current {
726        match target.compare_exchange(current, value, Ordering::AcqRel, Ordering::Acquire) {
727            Ok(_) => return,
728            Err(observed) => current = observed,
729        }
730    }
731}
732
733struct SubscriptionSlot<T: Send + Sync + 'static> {
734    id: u64,
735    actor: ActorRef<SubscriptionMessage<T>>,
736    parked_count: Arc<AtomicUsize>,
737    cursor: AtomicU64,
738    active: AtomicBool,
739    parked: AtomicBool,
740    drop_from: AtomicU64,
741    drop_through: AtomicU64,
742    terminal_from: AtomicU64,
743    state: Mutex<SubscriptionSlotState<T>>,
744    available: Condvar,
745    async_available: Notify,
746}
747
748struct SubscriptionSlotState<T: Send + Sync + 'static> {
749    seed: Option<Arc<T>>,
750    terminal: Option<SubscriptionSlotTerminal<T>>,
751}
752
753#[derive(Clone)]
754enum SubscriptionSlotTerminal<T: Send + Sync + 'static> {
755    Complete {
756        final_sequence: u64,
757        final_value: Option<Arc<T>>,
758    },
759    Error {
760        after_sequence: u64,
761        error: StreamError,
762    },
763}
764
765impl<T: Send + Sync + 'static> SubscriptionSlot<T> {
766    fn new(
767        id: u64,
768        actor: ActorRef<SubscriptionMessage<T>>,
769        parked_count: Arc<AtomicUsize>,
770    ) -> Arc<Self> {
771        Arc::new(Self {
772            id,
773            actor,
774            parked_count,
775            cursor: AtomicU64::new(UNSEEDED_CURSOR),
776            active: AtomicBool::new(true),
777            parked: AtomicBool::new(false),
778            drop_from: AtomicU64::new(NO_DROP_FROM),
779            drop_through: AtomicU64::new(0),
780            terminal_from: AtomicU64::new(NO_TERMINAL_FROM),
781            state: Mutex::new(SubscriptionSlotState {
782                seed: None,
783                terminal: None,
784            }),
785            available: Condvar::new(),
786            async_available: Notify::new(),
787        })
788    }
789
790    fn lock(&self) -> MutexGuard<'_, SubscriptionSlotState<T>> {
791        self.state
792            .lock()
793            .unwrap_or_else(|poison| poison.into_inner())
794    }
795
796    fn seed(&self, next_sequence: u64, value: Arc<T>) {
797        self.cursor.store(next_sequence, Ordering::Release);
798        let mut state = self.lock();
799        state.seed = Some(value);
800        drop(state);
801        self.wake();
802    }
803
804    fn complete_post_close(&self, value: Arc<T>) {
805        self.cursor.store(0, Ordering::Release);
806        let mut state = self.lock();
807        state.seed = Some(value);
808        state.terminal = Some(SubscriptionSlotTerminal::Complete {
809            final_sequence: 0,
810            final_value: None,
811        });
812        drop(state);
813        self.terminal_from.store(0, Ordering::Release);
814        self.active.store(false, Ordering::Release);
815        self.wake();
816    }
817
818    fn complete_with_final(&self, final_sequence: u64, value: Arc<T>) {
819        let mut state = self.lock();
820        if state.terminal.is_none() {
821            state.terminal = Some(SubscriptionSlotTerminal::Complete {
822                final_sequence,
823                final_value: Some(value),
824            });
825        }
826        drop(state);
827        self.terminal_from
828            .fetch_min(final_sequence, Ordering::AcqRel);
829        self.wake();
830    }
831
832    fn fail_after(&self, after_sequence: u64, error: StreamError) {
833        let mut state = self.lock();
834        if state.terminal.is_none() {
835            state.terminal = Some(SubscriptionSlotTerminal::Error {
836                after_sequence,
837                error,
838            });
839        }
840        drop(state);
841        self.terminal_from
842            .fetch_min(after_sequence, Ordering::AcqRel);
843        self.active.store(false, Ordering::Release);
844        self.wake();
845    }
846
847    fn fail_now(&self, error: StreamError) {
848        let cursor = self.cursor.load(Ordering::Acquire);
849        let after_sequence = if cursor == UNSEEDED_CURSOR { 0 } else { cursor };
850        self.fail_after(after_sequence, error);
851    }
852
853    fn is_full_for(&self, sequence: u64, capacity: u64) -> bool {
854        if !self.active.load(Ordering::Acquire) {
855            return false;
856        }
857        let cursor = self.cursor.load(Ordering::Acquire);
858        cursor != UNSEEDED_CURSOR && sequence >= cursor.saturating_add(capacity)
859    }
860
861    fn backpressure_cursor(&self) -> Option<u64> {
862        if !self.active.load(Ordering::Acquire) {
863            return None;
864        }
865        let cursor = self.cursor.load(Ordering::Acquire);
866        (cursor != UNSEEDED_CURSOR).then_some(cursor)
867    }
868
869    fn drop_new(&self, sequence: u64, capacity: u64) {
870        let cursor = self.cursor.load(Ordering::Acquire);
871        if cursor == UNSEEDED_CURSOR {
872            return;
873        }
874        let from = cursor.saturating_add(capacity);
875        if sequence >= from {
876            atomic_fetch_min(&self.drop_from, from);
877            atomic_fetch_max(&self.drop_through, sequence);
878            self.wake();
879        }
880    }
881
882    fn skip_dropped(&self, cursor: u64) -> Option<u64> {
883        let from = self.drop_from.load(Ordering::Acquire);
884        let through = self.drop_through.load(Ordering::Acquire);
885        if from != NO_DROP_FROM && cursor >= from && cursor <= through {
886            self.drop_from.store(NO_DROP_FROM, Ordering::Release);
887            self.drop_through.store(0, Ordering::Release);
888            Some(through.saturating_add(1))
889        } else {
890            None
891        }
892    }
893
894    fn has_dropped(&self, cursor: u64) -> bool {
895        let from = self.drop_from.load(Ordering::Acquire);
896        let through = self.drop_through.load(Ordering::Acquire);
897        from != NO_DROP_FROM && cursor >= from && cursor <= through
898    }
899
900    fn terminal_blocks(&self, cursor: u64) -> bool {
901        cursor >= self.terminal_from.load(Ordering::Acquire)
902    }
903
904    fn wake(&self) {
905        if self.parked.swap(false, Ordering::AcqRel) {
906            self.parked_count.fetch_sub(1, Ordering::AcqRel);
907            self.available.notify_all();
908            self.async_available.notify_waiters();
909        }
910    }
911
912    fn park(&self) {
913        if !self.parked.swap(true, Ordering::AcqRel) {
914            self.parked_count.fetch_add(1, Ordering::AcqRel);
915        }
916    }
917
918    fn unpark(&self) {
919        if self.parked.swap(false, Ordering::AcqRel) {
920            self.parked_count.fetch_sub(1, Ordering::AcqRel);
921        }
922    }
923
924    fn wake_for_sequence(&self, sequence: u64) {
925        if self.cursor.load(Ordering::Acquire) == sequence {
926            self.wake();
927        }
928    }
929
930    fn unsubscribe(&self) {
931        self.active.store(false, Ordering::Release);
932        let _ = self
933            .actor
934            .send_message(SubscriptionMessage::Unsubscribe { id: self.id });
935    }
936}
937
938struct SubscriptionChangesStream<T: Clone + Send + Sync + 'static> {
939    shared: Arc<SubscriptionShared<T>>,
940    slot: Arc<SubscriptionSlot<T>>,
941    pending: VecDeque<Arc<T>>,
942    terminated: bool,
943}
944
945#[doc(hidden)]
946pub struct SubscriptionBenchmarkStream<T: Clone + Send + Sync + 'static> {
947    shared: Arc<SubscriptionShared<T>>,
948    slot: Arc<SubscriptionSlot<T>>,
949    pending: VecDeque<Arc<T>>,
950    terminated: bool,
951}
952
953impl<T: Clone + Send + Sync + 'static> Iterator for SubscriptionChangesStream<T> {
954    type Item = StreamResult<T>;
955
956    fn next(&mut self) -> Option<Self::Item> {
957        if self.terminated {
958            return None;
959        }
960
961        loop {
962            if let Some(value) = self.pending.pop_front() {
963                return Some(Ok(value.as_ref().clone()));
964            }
965
966            if let Some(item) = self.poll_seed_or_terminal() {
967                return item;
968            }
969
970            let cursor = self.slot.cursor.load(Ordering::Acquire);
971            if cursor == UNSEEDED_CURSOR {
972                self.wait_for_wake();
973                continue;
974            }
975
976            if let Some(next_cursor) = self.slot.skip_dropped(cursor) {
977                self.slot.cursor.store(next_cursor, Ordering::Release);
978                self.shared.ring.notify_space();
979                continue;
980            }
981
982            if let Some(value) = self.drain_available(cursor) {
983                return Some(Ok(value.as_ref().clone()));
984            }
985
986            let published = self.shared.published_sequence.load(Ordering::Acquire);
987            if cursor <= published {
988                let oldest = self.shared.ring.oldest_available(published);
989                if cursor < oldest {
990                    match self.shared.overflow {
991                        SubscriptionOverflow::DropNew => {
992                            self.slot.cursor.store(oldest, Ordering::Release);
993                            self.shared.ring.notify_space();
994                            continue;
995                        }
996                        SubscriptionOverflow::Backpressure | SubscriptionOverflow::Fail => {
997                            self.terminated = true;
998                            return Some(Err(overflow_error(self.shared.ring.logical_capacity)));
999                        }
1000                    }
1001                }
1002            }
1003
1004            if current_stream_cancelled()
1005                .as_ref()
1006                .is_some_and(|cancelled| cancelled.load(Ordering::SeqCst))
1007            {
1008                self.terminated = true;
1009                return Some(Err(StreamError::Cancelled));
1010            }
1011
1012            self.wait_for_wake();
1013        }
1014    }
1015}
1016
1017impl<T: Clone + Send + Sync + 'static> SubscriptionChangesStream<T> {
1018    fn drain_available(&mut self, start_cursor: u64) -> Option<Arc<T>> {
1019        let mut cursor = start_cursor;
1020        let first = self.shared.ring.load(cursor)?;
1021        cursor = cursor.saturating_add(1);
1022        let mut drained = 1_usize;
1023
1024        while drained < SUBSCRIPTION_DRAIN_BATCH {
1025            if self.slot.has_dropped(cursor) || self.slot.terminal_blocks(cursor) {
1026                break;
1027            }
1028            let Some(value) = self.shared.ring.load(cursor) else {
1029                break;
1030            };
1031            self.pending.push_back(value);
1032            cursor = cursor.saturating_add(1);
1033            drained += 1;
1034        }
1035
1036        self.slot.cursor.store(cursor, Ordering::Release);
1037        self.shared.ring.notify_space();
1038        Some(first)
1039    }
1040
1041    fn poll_seed_or_terminal(&mut self) -> Option<Option<StreamResult<T>>> {
1042        let mut state = self.slot.lock();
1043        if let Some(seed) = state.seed.take() {
1044            return Some(Some(Ok(seed.as_ref().clone())));
1045        }
1046
1047        let cursor = self.slot.cursor.load(Ordering::Acquire);
1048        if let Some(terminal) = &mut state.terminal {
1049            match terminal {
1050                SubscriptionSlotTerminal::Complete {
1051                    final_sequence,
1052                    final_value,
1053                } => {
1054                    if cursor >= *final_sequence {
1055                        if let Some(value) = final_value.take() {
1056                            return Some(Some(Ok(value.as_ref().clone())));
1057                        }
1058                        self.terminated = true;
1059                        return Some(None);
1060                    }
1061                }
1062                SubscriptionSlotTerminal::Error {
1063                    after_sequence,
1064                    error,
1065                } => {
1066                    if cursor >= *after_sequence {
1067                        self.terminated = true;
1068                        return Some(Some(Err(error.clone())));
1069                    }
1070                }
1071            }
1072        }
1073        None
1074    }
1075
1076    fn wait_for_wake(&self) {
1077        let state = self.slot.lock();
1078        self.slot.park();
1079        fence(Ordering::SeqCst);
1080        let cursor = self.slot.cursor.load(Ordering::Acquire);
1081        if state.seed.is_some()
1082            || state.terminal.is_some()
1083            || self.slot.has_dropped(cursor)
1084            || (cursor != UNSEEDED_CURSOR && self.shared.ring.has(cursor))
1085        {
1086            self.slot.unpark();
1087            return;
1088        }
1089        let _guard = self
1090            .slot
1091            .available
1092            .wait_timeout(state, SLOT_WAIT_BACKSTOP)
1093            .unwrap_or_else(|poison| poison.into_inner())
1094            .0;
1095        self.slot.unpark();
1096    }
1097}
1098
1099impl<T: Clone + Send + Sync + 'static> Drop for SubscriptionChangesStream<T> {
1100    fn drop(&mut self) {
1101        self.slot.unsubscribe();
1102        self.shared.ring.notify_space();
1103    }
1104}
1105
1106impl<T: Clone + Send + Sync + 'static> SubscriptionBenchmarkStream<T> {
1107    #[doc(hidden)]
1108    pub async fn next(&mut self) -> Option<StreamResult<T>> {
1109        if self.terminated {
1110            return None;
1111        }
1112
1113        loop {
1114            if let Some(value) = self.pending.pop_front() {
1115                return Some(Ok(value.as_ref().clone()));
1116            }
1117
1118            if let Some(item) = self.poll_seed_or_terminal() {
1119                return item;
1120            }
1121
1122            let cursor = self.slot.cursor.load(Ordering::Acquire);
1123            if cursor == UNSEEDED_CURSOR {
1124                self.wait_for_wake().await;
1125                continue;
1126            }
1127
1128            if let Some(next_cursor) = self.slot.skip_dropped(cursor) {
1129                self.slot.cursor.store(next_cursor, Ordering::Release);
1130                self.shared.ring.notify_space();
1131                continue;
1132            }
1133
1134            if let Some(value) = self.drain_available(cursor) {
1135                return Some(Ok(value.as_ref().clone()));
1136            }
1137
1138            let published = self.shared.published_sequence.load(Ordering::Acquire);
1139            if cursor <= published {
1140                let oldest = self.shared.ring.oldest_available(published);
1141                if cursor < oldest {
1142                    match self.shared.overflow {
1143                        SubscriptionOverflow::DropNew => {
1144                            self.slot.cursor.store(oldest, Ordering::Release);
1145                            self.shared.ring.notify_space();
1146                            continue;
1147                        }
1148                        SubscriptionOverflow::Backpressure | SubscriptionOverflow::Fail => {
1149                            self.terminated = true;
1150                            return Some(Err(overflow_error(self.shared.ring.logical_capacity)));
1151                        }
1152                    }
1153                }
1154            }
1155
1156            self.wait_for_wake().await;
1157        }
1158    }
1159
1160    #[doc(hidden)]
1161    pub async fn count_changes(&mut self, target: u64) -> StreamResult<u64> {
1162        let mut count = 0_u64;
1163        while count < target {
1164            if self.terminated {
1165                return Err(StreamError::Failed(
1166                    "subscription stream ended before requested count".into(),
1167                ));
1168            }
1169
1170            if !self.pending.is_empty() {
1171                let drained = self.pending.len().min((target - count) as usize);
1172                self.pending.drain(..drained);
1173                count += drained as u64;
1174                continue;
1175            }
1176
1177            if let Some(item) = self.poll_seed_or_terminal() {
1178                match item {
1179                    Some(Ok(_)) => {
1180                        count += 1;
1181                        continue;
1182                    }
1183                    Some(Err(error)) => return Err(error),
1184                    None => {
1185                        return Err(StreamError::Failed(
1186                            "subscription stream completed before requested count".into(),
1187                        ));
1188                    }
1189                }
1190            }
1191
1192            let cursor = self.slot.cursor.load(Ordering::Acquire);
1193            if cursor == UNSEEDED_CURSOR {
1194                self.wait_for_wake().await;
1195                continue;
1196            }
1197
1198            if let Some(next_cursor) = self.slot.skip_dropped(cursor) {
1199                self.slot.cursor.store(next_cursor, Ordering::Release);
1200                self.shared.ring.notify_space();
1201                continue;
1202            }
1203
1204            if let Some(drained) = self.drain_available_count(cursor, (target - count) as usize) {
1205                count += drained as u64;
1206                continue;
1207            }
1208
1209            let published = self.shared.published_sequence.load(Ordering::Acquire);
1210            if cursor <= published {
1211                let oldest = self.shared.ring.oldest_available(published);
1212                if cursor < oldest {
1213                    return Err(overflow_error(self.shared.ring.logical_capacity));
1214                }
1215            }
1216
1217            self.wait_for_wake().await;
1218        }
1219        Ok(count)
1220    }
1221
1222    fn drain_available(&mut self, start_cursor: u64) -> Option<Arc<T>> {
1223        let mut cursor = start_cursor;
1224        let first = self.shared.ring.load(cursor)?;
1225        cursor = cursor.saturating_add(1);
1226        let mut drained = 1_usize;
1227
1228        while drained < SUBSCRIPTION_DRAIN_BATCH {
1229            if self.slot.has_dropped(cursor) || self.slot.terminal_blocks(cursor) {
1230                break;
1231            }
1232            let Some(value) = self.shared.ring.load(cursor) else {
1233                break;
1234            };
1235            self.pending.push_back(value);
1236            cursor = cursor.saturating_add(1);
1237            drained += 1;
1238        }
1239
1240        self.slot.cursor.store(cursor, Ordering::Release);
1241        self.shared.ring.notify_space();
1242        Some(first)
1243    }
1244
1245    fn drain_available_count(&mut self, start_cursor: u64, limit: usize) -> Option<usize> {
1246        if self.slot.has_dropped(start_cursor) || self.slot.terminal_blocks(start_cursor) {
1247            return None;
1248        }
1249        let published = self.shared.published_sequence.load(Ordering::Acquire);
1250        if start_cursor > published {
1251            return None;
1252        }
1253        let oldest = self.shared.ring.oldest_available(published);
1254        if start_cursor < oldest {
1255            return None;
1256        }
1257        let available = published.saturating_sub(start_cursor).saturating_add(1) as usize;
1258        let limit = limit.min(SUBSCRIPTION_DRAIN_BATCH);
1259        let drained = available.min(limit);
1260        if drained == 0 {
1261            return None;
1262        }
1263
1264        self.slot.cursor.store(
1265            start_cursor.saturating_add(drained as u64),
1266            Ordering::Release,
1267        );
1268        self.shared.ring.notify_space();
1269        Some(drained)
1270    }
1271
1272    fn poll_seed_or_terminal(&mut self) -> Option<Option<StreamResult<T>>> {
1273        let mut state = self.slot.lock();
1274        if let Some(seed) = state.seed.take() {
1275            return Some(Some(Ok(seed.as_ref().clone())));
1276        }
1277
1278        let cursor = self.slot.cursor.load(Ordering::Acquire);
1279        if let Some(terminal) = &mut state.terminal {
1280            match terminal {
1281                SubscriptionSlotTerminal::Complete {
1282                    final_sequence,
1283                    final_value,
1284                } => {
1285                    if cursor >= *final_sequence {
1286                        if let Some(value) = final_value.take() {
1287                            return Some(Some(Ok(value.as_ref().clone())));
1288                        }
1289                        self.terminated = true;
1290                        return Some(None);
1291                    }
1292                }
1293                SubscriptionSlotTerminal::Error {
1294                    after_sequence,
1295                    error,
1296                } => {
1297                    if cursor >= *after_sequence {
1298                        self.terminated = true;
1299                        return Some(Some(Err(error.clone())));
1300                    }
1301                }
1302            }
1303        }
1304        None
1305    }
1306
1307    async fn wait_for_wake(&self) {
1308        let notified = self.slot.async_available.notified();
1309        tokio::pin!(notified);
1310        notified.as_mut().enable();
1311
1312        {
1313            let state = self.slot.lock();
1314            self.slot.park();
1315            fence(Ordering::SeqCst);
1316            let cursor = self.slot.cursor.load(Ordering::Acquire);
1317            if state.seed.is_some()
1318                || state.terminal.is_some()
1319                || self.slot.has_dropped(cursor)
1320                || (cursor != UNSEEDED_CURSOR && self.shared.ring.has(cursor))
1321            {
1322                self.slot.unpark();
1323                return;
1324            }
1325        }
1326
1327        notified.await;
1328        self.slot.unpark();
1329    }
1330}
1331
1332impl<T: Clone + Send + Sync + 'static> Drop for SubscriptionBenchmarkStream<T> {
1333    fn drop(&mut self) {}
1334}
1335
1336#[cfg(test)]
1337mod tests {
1338    use super::*;
1339    use crate::{Sink, stream::Materializer};
1340    use std::{
1341        sync::{
1342            Arc,
1343            atomic::{AtomicBool, AtomicUsize},
1344        },
1345        thread,
1346        time::{Duration, Instant},
1347    };
1348
1349    fn wait<T>(completion: crate::StreamCompletion<T>) -> T {
1350        completion.wait().unwrap()
1351    }
1352
1353    fn wait_until<F>(timeout: Duration, mut condition: F) -> bool
1354    where
1355        F: FnMut() -> bool,
1356    {
1357        let deadline = Instant::now() + timeout;
1358        while Instant::now() < deadline {
1359            if condition() {
1360                return true;
1361            }
1362            thread::yield_now();
1363        }
1364        condition()
1365    }
1366
1367    #[test]
1368    fn get_snapshot_and_acked_set_read_your_writes() {
1369        let subscription = Subscription::new(1_u64, 8, SubscriptionOverflow::Backpressure).unwrap();
1370        assert_eq!(*subscription.get(), 1);
1371        assert_eq!(subscription.get_cloned(), 1);
1372        subscription.set(2).unwrap();
1373        assert_eq!(*subscription.get(), 2);
1374        assert_eq!(subscription.get_cloned(), 2);
1375        subscription.update(|value| *value + 1).unwrap();
1376        assert_eq!(*subscription.get(), 3);
1377        assert_eq!(subscription.get_cloned(), 3);
1378    }
1379
1380    #[test]
1381    fn lossless_backpressure_subscribers_see_all_changes() {
1382        const SUBSCRIBERS: usize = 4;
1383        const WRITES: u64 = 128;
1384        let subscription =
1385            Subscription::new(0_u64, 256, SubscriptionOverflow::Backpressure).unwrap();
1386        let completions = (0..SUBSCRIBERS)
1387            .map(|_| subscription.changes().run_with(Sink::collect()).unwrap())
1388            .collect::<Vec<_>>();
1389
1390        for value in 1..=WRITES {
1391            subscription.set(value).unwrap();
1392        }
1393        subscription.close_with(WRITES + 1).unwrap();
1394
1395        for completion in completions {
1396            let values = wait(completion);
1397            let expected = (0..=WRITES + 1).collect::<Vec<_>>();
1398            assert_eq!(values, expected);
1399        }
1400    }
1401
1402    #[test]
1403    fn backpressure_parks_producer_ack_until_capacity_returns() {
1404        let subscription = Subscription::new(0_u64, 1, SubscriptionOverflow::Backpressure).unwrap();
1405        let seen = Arc::new(Mutex::new(Vec::new()));
1406        let gate = Arc::new(AtomicBool::new(false));
1407        let sink_seen = Arc::clone(&seen);
1408        let sink_gate = Arc::clone(&gate);
1409        let completion = subscription
1410            .changes()
1411            .run_with(Sink::foreach(move |item| {
1412                sink_seen.lock().unwrap().push(item);
1413                while !sink_gate.load(Ordering::SeqCst) {
1414                    thread::yield_now();
1415                }
1416            }))
1417            .unwrap();
1418
1419        assert!(wait_until(Duration::from_secs(1), || {
1420            seen.lock().unwrap().as_slice() == [0]
1421        }));
1422        subscription.set(1).unwrap();
1423
1424        let producer_subscription = subscription.clone();
1425        let completed = Arc::new(AtomicBool::new(false));
1426        let producer_completed = Arc::clone(&completed);
1427        let producer = thread::spawn(move || {
1428            producer_subscription.set(2).unwrap();
1429            producer_completed.store(true, Ordering::SeqCst);
1430        });
1431
1432        assert!(!wait_until(Duration::from_millis(25), || completed
1433            .load(Ordering::SeqCst)));
1434        assert_eq!(*subscription.get(), 1);
1435        gate.store(true, Ordering::SeqCst);
1436        assert!(wait_until(Duration::from_secs(1), || completed.load(Ordering::SeqCst)));
1437        producer.join().unwrap();
1438        assert_eq!(*subscription.get(), 2);
1439        subscription.close_with(3).unwrap();
1440        wait(completion);
1441        assert_eq!(seen.lock().unwrap().as_slice(), [0, 1, 2, 3]);
1442    }
1443
1444    #[test]
1445    fn drop_new_policy_drops_only_full_subscribers() {
1446        let subscription = Subscription::new(0_u64, 1, SubscriptionOverflow::DropNew).unwrap();
1447        let seen = Arc::new(Mutex::new(Vec::new()));
1448        let gate = Arc::new(AtomicBool::new(false));
1449        let sink_seen = Arc::clone(&seen);
1450        let sink_gate = Arc::clone(&gate);
1451        let completion = subscription
1452            .changes()
1453            .run_with(Sink::foreach(move |item| {
1454                sink_seen.lock().unwrap().push(item);
1455                while !sink_gate.load(Ordering::SeqCst) {
1456                    thread::yield_now();
1457                }
1458            }))
1459            .unwrap();
1460        assert!(wait_until(Duration::from_secs(1), || {
1461            seen.lock().unwrap().as_slice() == [0]
1462        }));
1463        subscription.set(1).unwrap();
1464        subscription.set(2).unwrap();
1465        subscription.close_with(3).unwrap();
1466        gate.store(true, Ordering::SeqCst);
1467        wait(completion);
1468        assert_eq!(seen.lock().unwrap().as_slice(), [0, 1, 3]);
1469        assert_eq!(*subscription.get(), 3);
1470    }
1471
1472    #[test]
1473    fn fail_policy_fails_full_subscriber_and_reports_overflow() {
1474        let subscription = Subscription::new(0_u64, 1, SubscriptionOverflow::Fail).unwrap();
1475        let seen = Arc::new(Mutex::new(Vec::new()));
1476        let gate = Arc::new(AtomicBool::new(false));
1477        let sink_seen = Arc::clone(&seen);
1478        let sink_gate = Arc::clone(&gate);
1479        let completion = subscription
1480            .changes()
1481            .run_with(Sink::foreach(move |item| {
1482                sink_seen.lock().unwrap().push(item);
1483                while !sink_gate.load(Ordering::SeqCst) {
1484                    thread::yield_now();
1485                }
1486            }))
1487            .unwrap();
1488        assert!(wait_until(Duration::from_secs(1), || {
1489            seen.lock().unwrap().as_slice() == [0]
1490        }));
1491        subscription.set(1).unwrap();
1492        assert!(matches!(
1493            subscription.set(2),
1494            Err(StreamError::Failed(message)) if message.contains("subscription buffer overflow")
1495        ));
1496        gate.store(true, Ordering::SeqCst);
1497        assert!(matches!(
1498            completion.wait(),
1499            Err(StreamError::Failed(message)) if message.contains("subscription buffer overflow")
1500        ));
1501        assert_eq!(seen.lock().unwrap().as_slice(), [0, 1]);
1502        assert_eq!(*subscription.get(), 2);
1503    }
1504
1505    #[test]
1506    fn terminal_ordering_and_post_close_subscribe() {
1507        let subscription = Subscription::new(0_u64, 8, SubscriptionOverflow::Backpressure).unwrap();
1508        let completion = subscription.changes().run_with(Sink::collect()).unwrap();
1509        subscription.set(1).unwrap();
1510        subscription.close_with(9).unwrap();
1511        assert_eq!(wait(completion), vec![0, 1, 9]);
1512
1513        let post_close = subscription.changes().run_collect().unwrap();
1514        assert_eq!(post_close, vec![9]);
1515    }
1516
1517    #[test]
1518    fn dropping_feed_source_cancels_and_unsubscribes() {
1519        let subscription = Subscription::new(0_u64, 1, SubscriptionOverflow::Backpressure).unwrap();
1520        let pulled = Arc::new(AtomicUsize::new(0));
1521        let sink_pulled = Arc::clone(&pulled);
1522        let completion = subscription
1523            .changes()
1524            .run_with(Sink::foreach(move |_| {
1525                sink_pulled.fetch_add(1, Ordering::SeqCst);
1526            }))
1527            .unwrap();
1528        assert!(wait_until(Duration::from_secs(1), || {
1529            pulled.load(Ordering::SeqCst) == 1
1530        }));
1531        drop(completion);
1532        assert!(wait_until(Duration::from_secs(1), || subscription
1533            .set(1)
1534            .is_ok()));
1535    }
1536
1537    #[test]
1538    fn actor_death_fails_feed() {
1539        let subscription = Subscription::new(0_u64, 8, SubscriptionOverflow::Backpressure).unwrap();
1540        let materializer = Materializer::new();
1541        let completion = subscription
1542            .changes()
1543            .drop(1)
1544            .run_with_materializer(Sink::head(), &materializer)
1545            .unwrap();
1546        drop(subscription);
1547        match completion.wait() {
1548            Err(StreamError::ActorTerminated) => {}
1549            other => panic!("expected actor termination, got {other:?}"),
1550        }
1551    }
1552}