Skip to main content

datum/actor/
interop.rs

1use std::{
2    collections::VecDeque,
3    fmt,
4    marker::PhantomData,
5    panic::{AssertUnwindSafe, catch_unwind},
6    sync::{
7        Arc, Condvar, Mutex, MutexGuard,
8        atomic::{AtomicBool, Ordering},
9    },
10    thread::{self, Thread, ThreadId},
11    time::{Duration, Instant},
12};
13
14use crate::{
15    context::FlowWithContext,
16    stream::{BoxStream, Flow, NotUsed, Sink, Source, StreamCompletion, StreamError, StreamResult},
17};
18
19use super::{
20    ASK_IDLE_YIELDS, ASK_MAX_PARK, ASK_READY_SPINS, ASK_TIME_REFRESH_ITERS, Actor, ActorFlow,
21    ActorRef, ActorResult, InFlightAsk, Message, ReplyPoll, ReplyPort, ReplyState,
22    block_on_ractor_runtime, panic_reason, recycle_reply_state, wait_for_ready_ask,
23};
24
25/// Status envelope used by [`ActorFlow::ask_with_status`].
26///
27/// `Ok` replies are unwrapped into stream elements. `Err` replies fail the
28/// stream with the supplied [`StreamError`].
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum ActorStatus<T> {
31    Ok(T),
32    Err(StreamError),
33}
34
35impl<T> From<StreamResult<T>> for ActorStatus<T> {
36    fn from(value: StreamResult<T>) -> Self {
37        match value {
38            Ok(value) => Self::Ok(value),
39            Err(error) => Self::Err(error),
40        }
41    }
42}
43
44impl<T> ActorStatus<T> {
45    fn into_result(self) -> StreamResult<T> {
46        match self {
47            Self::Ok(value) => Ok(value),
48            Self::Err(error) => Err(error),
49        }
50    }
51}
52
53/// Protocol accepted by [`ActorSource::actor_ref`] and
54/// [`ActorSource::actor_ref_with_backpressure`].
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum ActorSourceMessage<T> {
57    Element(T),
58    Complete,
59    Fail(String),
60}
61
62/// Default typed protocol emitted by [`ActorSink::typed`].
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum ActorSinkMessage<T> {
65    Element(T),
66    Complete,
67    Fail(StreamError),
68}
69
70/// Default typed protocol emitted by [`ActorSink::typed_with_backpressure`].
71#[derive(Debug)]
72pub enum ActorSinkBackpressureMessage<T, Ack> {
73    Init(ReplyPort<Ack>),
74    Element(T, ReplyPort<Ack>),
75    Complete,
76    Fail(StreamError),
77}
78
79/// Watch failure details used to build the stream failure for
80/// [`ActorFlow::watch`].
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct WatchEvent {
83    reason: String,
84}
85
86impl WatchEvent {
87    #[must_use]
88    pub fn reason(&self) -> &str {
89        &self.reason
90    }
91
92    fn into_stream_error(self) -> StreamError {
93        StreamError::Failed(self.reason)
94    }
95}
96
97impl fmt::Display for WatchEvent {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        f.write_str(&self.reason)
100    }
101}
102
103/// Collection of actor-backed source constructors.
104pub struct ActorSource;
105
106/// Collection of actor-backed sink constructors.
107pub struct ActorSink;
108
109/// pg-backed PubSub bridge.
110///
111/// Ractor's `pg` process groups are the natural local primitive for named actor
112/// subscriber sets. A PubSub source joins its materialized actor to a pg group;
113/// a PubSub sink broadcasts to the current members of that group. Groups are
114/// expected to be type-homogeneous for the `ActorSourceMessage<T>` protocol.
115pub struct ActorPubSub;
116
117impl ActorFlow {
118    /// Sends each stream element to an actor and unwraps a status reply.
119    ///
120    /// `ActorStatus::Ok` emits the wrapped value. `ActorStatus::Err` fails the
121    /// stream with that error.
122    #[must_use]
123    pub fn ask_with_status<In, Msg, Out, F>(
124        actor_ref: ActorRef<Msg>,
125        parallelism: usize,
126        timeout: Duration,
127        make_msg: F,
128    ) -> Flow<In, Out, NotUsed>
129    where
130        In: Send + 'static,
131        Msg: Message,
132        Out: Send + 'static,
133        F: Fn(In, ReplyPort<ActorStatus<Out>>) -> Msg + Send + Sync + 'static,
134    {
135        ask_flow_with_pending(
136            actor_ref,
137            parallelism,
138            timeout,
139            move |input, reply_to| (make_msg(input, reply_to), ()),
140            |(), reply| reply.into_result(),
141        )
142    }
143
144    /// Sends each `(element, context)` pair to an actor while preserving context.
145    ///
146    /// The user message builder receives only the element and a reply port. The
147    /// original context is reattached to the actor reply in-order.
148    #[must_use]
149    pub fn ask_with_context<In, Ctx, Msg, Out, F>(
150        actor_ref: ActorRef<Msg>,
151        parallelism: usize,
152        timeout: Duration,
153        make_msg: F,
154    ) -> FlowWithContext<In, Ctx, Out, Ctx, NotUsed>
155    where
156        In: Send + 'static,
157        Ctx: Send + 'static,
158        Msg: Message,
159        Out: Send + 'static,
160        F: Fn(In, ReplyPort<Out>) -> Msg + Send + Sync + 'static,
161    {
162        FlowWithContext::from_flow(ask_flow_with_pending(
163            actor_ref,
164            parallelism,
165            timeout,
166            move |(input, context), reply_to| (make_msg(input, reply_to), context),
167            |context, reply| Ok((reply, context)),
168        ))
169    }
170
171    /// Status-reply variant of [`ActorFlow::ask_with_context`].
172    #[must_use]
173    pub fn ask_with_status_and_context<In, Ctx, Msg, Out, F>(
174        actor_ref: ActorRef<Msg>,
175        parallelism: usize,
176        timeout: Duration,
177        make_msg: F,
178    ) -> FlowWithContext<In, Ctx, Out, Ctx, NotUsed>
179    where
180        In: Send + 'static,
181        Ctx: Send + 'static,
182        Msg: Message,
183        Out: Send + 'static,
184        F: Fn(In, ReplyPort<ActorStatus<Out>>) -> Msg + Send + Sync + 'static,
185    {
186        FlowWithContext::from_flow(ask_flow_with_pending(
187            actor_ref,
188            parallelism,
189            timeout,
190            move |(input, context), reply_to| (make_msg(input, reply_to), context),
191            |context, reply| reply.into_result().map(|reply| (reply, context)),
192        ))
193    }
194
195    /// Fails a pass-through flow if `actor_ref` terminates while the stream is
196    /// being consumed.
197    ///
198    /// This binds to Ractor's monitor supervision events. Once the monitor sees
199    /// termination, the stream fails with `StreamError::Failed` and a
200    /// watched-actor termination message, even if upstream is idle.
201    #[must_use]
202    pub fn watch<T, Msg>(actor_ref: ActorRef<Msg>) -> Flow<T, T, NotUsed>
203    where
204        T: Send + 'static,
205        Msg: Message,
206    {
207        Flow::from_runtime_transform(move |input, materializer| {
208            let shared = Arc::new(WatchShared::default());
209            let (monitor_ref, _handle) =
210                spawn_watch_monitor(actor_ref.get_cell(), Arc::clone(&shared))?;
211            let producer_shared = Arc::clone(&shared);
212            let producer_completion = materializer.spawn_stream(move |cancelled| {
213                run_watch_upstream(input, producer_shared, cancelled)
214            });
215            Ok(Box::new(WatchStream {
216                shared,
217                monitor_ref: Some(monitor_ref),
218                producer_completion: Some(producer_completion),
219                terminated: false,
220            }) as BoxStream<T>)
221        })
222    }
223}
224
225impl ActorSource {
226    /// Materializes a typed actor ref that feeds this source.
227    ///
228    /// # Mailbox risk
229    ///
230    /// This source uses Ractor's regular actor mailbox. It is not backpressured:
231    /// a fast sender can enqueue messages faster than the stream consumes them,
232    /// and the actor mailbox and this source's internal buffer can grow without
233    /// bound. Prefer [`ActorSource::actor_ref_with_backpressure`] when the
234    /// producer may outpace downstream demand.
235    #[must_use]
236    pub fn actor_ref<T>() -> Source<T, ActorRef<ActorSourceMessage<T>>>
237    where
238        T: Send + 'static,
239    {
240        actor_source(None::<SourceBackpressure<ActorSourceMessage<T>>>)
241    }
242
243    /// Alias for [`ActorSource::actor_ref`] using Datum's explicit protocol
244    /// enum.
245    #[must_use]
246    pub fn typed<T>() -> Source<T, ActorRef<ActorSourceMessage<T>>>
247    where
248        T: Send + 'static,
249    {
250        Self::actor_ref()
251    }
252
253    /// Materializes a typed actor ref that accepts one element per ack.
254    ///
255    /// The source sends `ack_message` to `ack_to` at startup and after each
256    /// emitted element. Sending another `Element` before the ack arrives at the
257    /// producer violates the protocol and fails the stream.
258    #[must_use]
259    pub fn actor_ref_with_backpressure<T, AckMsg>(
260        ack_to: ActorRef<AckMsg>,
261        ack_message: AckMsg,
262    ) -> Source<T, ActorRef<ActorSourceMessage<T>>>
263    where
264        T: Send + 'static,
265        AckMsg: Message + Clone + Sync,
266    {
267        actor_source(Some(SourceBackpressure {
268            ack_to,
269            make_ack: Arc::new(move || ack_message.clone()),
270            _marker: PhantomData,
271        }))
272    }
273}
274
275impl ActorSink {
276    /// Sends stream elements to `actor_ref` without waiting for an actor ack.
277    ///
278    /// # Mailbox risk
279    ///
280    /// This sink has no backpressure signal from the destination actor. A fast
281    /// upstream can enqueue messages faster than the actor processes them, so
282    /// the target actor's mailbox may grow without bound. Use
283    /// [`ActorSink::actor_ref_with_backpressure`] for slow or untrusted
284    /// consumers.
285    #[must_use]
286    pub fn actor_ref<In, Msg, Elem, Complete, Failure>(
287        actor_ref: ActorRef<Msg>,
288        make_element_message: Elem,
289        on_complete_message: Complete,
290        on_failure_message: Failure,
291    ) -> Sink<In, StreamCompletion<NotUsed>>
292    where
293        In: Send + 'static,
294        Msg: Message,
295        Elem: Fn(In) -> Msg + Send + Sync + 'static,
296        Complete: Fn() -> Msg + Send + Sync + 'static,
297        Failure: Fn(StreamError) -> Msg + Send + Sync + 'static,
298    {
299        let make_element_message = Arc::new(make_element_message);
300        let on_complete_message = Arc::new(on_complete_message);
301        let on_failure_message = Arc::new(on_failure_message);
302        Sink::from_runner(move |mut input, materializer| {
303            let actor_ref = actor_ref.clone();
304            let make_element_message = Arc::clone(&make_element_message);
305            let on_complete_message = Arc::clone(&on_complete_message);
306            let on_failure_message = Arc::clone(&on_failure_message);
307            Ok(materializer.spawn_stream(move |cancelled| {
308                run_actor_ref_sink(
309                    &mut input,
310                    cancelled,
311                    actor_ref,
312                    make_element_message,
313                    on_complete_message,
314                    on_failure_message,
315                )
316            }))
317        })
318    }
319
320    /// Sends stream elements using [`ActorSinkMessage`].
321    ///
322    /// # Mailbox risk
323    ///
324    /// This is the typed-protocol form of [`ActorSink::actor_ref`] and has the
325    /// same unbounded-mailbox risk.
326    #[must_use]
327    pub fn typed<In>(
328        actor_ref: ActorRef<ActorSinkMessage<In>>,
329    ) -> Sink<In, StreamCompletion<NotUsed>>
330    where
331        In: Send + 'static,
332    {
333        Self::actor_ref(
334            actor_ref,
335            ActorSinkMessage::Element,
336            || ActorSinkMessage::Complete,
337            ActorSinkMessage::Fail,
338        )
339    }
340
341    /// Sends an init message, then sends one element per actor ack.
342    ///
343    /// `make_init_message` and `make_element_message` receive a [`ReplyPort`].
344    /// The destination actor must send any value to that port to acknowledge
345    /// readiness for the next message. Completion and failure messages are sent
346    /// without waiting for an ack.
347    #[must_use]
348    pub fn actor_ref_with_backpressure<In, Msg, Ack, Init, Elem, Complete, Failure>(
349        actor_ref: ActorRef<Msg>,
350        timeout: Duration,
351        make_init_message: Init,
352        make_element_message: Elem,
353        on_complete_message: Complete,
354        on_failure_message: Failure,
355    ) -> Sink<In, StreamCompletion<NotUsed>>
356    where
357        In: Send + 'static,
358        Msg: Message,
359        Ack: Send + 'static,
360        Init: Fn(ReplyPort<Ack>) -> Msg + Send + Sync + 'static,
361        Elem: Fn(In, ReplyPort<Ack>) -> Msg + Send + Sync + 'static,
362        Complete: Fn() -> Msg + Send + Sync + 'static,
363        Failure: Fn(StreamError) -> Msg + Send + Sync + 'static,
364    {
365        let make_init_message = Arc::new(make_init_message);
366        let make_element_message = Arc::new(make_element_message);
367        let on_complete_message = Arc::new(on_complete_message);
368        let on_failure_message = Arc::new(on_failure_message);
369        Sink::from_runner(move |mut input, materializer| {
370            let actor_ref = actor_ref.clone();
371            let make_init_message = Arc::clone(&make_init_message);
372            let make_element_message = Arc::clone(&make_element_message);
373            let on_complete_message = Arc::clone(&on_complete_message);
374            let on_failure_message = Arc::clone(&on_failure_message);
375            Ok(materializer.spawn_stream(move |cancelled| {
376                send_and_wait_ack(&actor_ref, timeout, |reply_to| make_init_message(reply_to))?;
377                run_actor_ref_backpressure_sink(
378                    &mut input,
379                    cancelled,
380                    actor_ref,
381                    timeout,
382                    make_element_message,
383                    on_complete_message,
384                    on_failure_message,
385                )
386            }))
387        })
388    }
389
390    /// Backpressured typed-protocol sink.
391    #[must_use]
392    pub fn typed_with_backpressure<In, Ack>(
393        actor_ref: ActorRef<ActorSinkBackpressureMessage<In, Ack>>,
394        timeout: Duration,
395    ) -> Sink<In, StreamCompletion<NotUsed>>
396    where
397        In: Send + 'static,
398        Ack: Send + 'static,
399    {
400        Self::actor_ref_with_backpressure(
401            actor_ref,
402            timeout,
403            ActorSinkBackpressureMessage::Init,
404            ActorSinkBackpressureMessage::Element,
405            || ActorSinkBackpressureMessage::Complete,
406            ActorSinkBackpressureMessage::Fail,
407        )
408    }
409}
410
411impl ActorPubSub {
412    /// Creates a source whose materialized actor joins a Ractor pg group.
413    ///
414    /// Messages broadcast by [`ActorPubSub::sink`] to the same group are emitted
415    /// by this source.
416    #[must_use]
417    pub fn source<T>(group: impl Into<String>) -> Source<T, ActorRef<ActorSourceMessage<T>>>
418    where
419        T: Send + 'static,
420    {
421        let group = group.into();
422        ActorSource::actor_ref().map_materialized_value(move |actor_ref| {
423            ractor::pg::join(group.clone(), vec![actor_ref.get_cell()]);
424            actor_ref
425        })
426    }
427
428    /// Broadcasts each stream element to current members of a Ractor pg group.
429    ///
430    /// Group members are expected to be actors accepting
431    /// `ActorSourceMessage<T>`. Members that terminate before Ractor's pg
432    /// membership catches up are skipped so one stale entry does not prevent
433    /// delivery to the remaining subscribers.
434    #[must_use]
435    pub fn sink<T>(group: impl Into<String>) -> Sink<T, StreamCompletion<NotUsed>>
436    where
437        T: Clone + Send + 'static,
438    {
439        let group = group.into();
440        Sink::from_runner(move |mut input, materializer| {
441            let group = group.clone();
442            Ok(materializer.spawn_stream(move |cancelled| {
443                loop {
444                    if cancelled.load(Ordering::SeqCst) {
445                        return Err(StreamError::Cancelled);
446                    }
447                    match input.next() {
448                        Some(Ok(item)) => {
449                            broadcast_to_group(&group, ActorSourceMessage::Element(item))?;
450                        }
451                        Some(Err(error)) => {
452                            let _ = broadcast_to_group(
453                                &group,
454                                ActorSourceMessage::<T>::Fail(error.to_string()),
455                            );
456                            return Err(error);
457                        }
458                        None => {
459                            broadcast_to_group(&group, ActorSourceMessage::<T>::Complete)?;
460                            return Ok(NotUsed);
461                        }
462                    }
463                }
464            }))
465        })
466    }
467}
468
469fn ask_flow_with_pending<In, Msg, Reply, Out, Pending, Prepare, Finish>(
470    actor_ref: ActorRef<Msg>,
471    parallelism: usize,
472    timeout: Duration,
473    prepare: Prepare,
474    finish: Finish,
475) -> Flow<In, Out, NotUsed>
476where
477    In: Send + 'static,
478    Msg: Message,
479    Reply: Send + 'static,
480    Out: Send + 'static,
481    Pending: Send + 'static,
482    Prepare: Fn(In, ReplyPort<Reply>) -> (Msg, Pending) + Send + Sync + 'static,
483    Finish: Fn(Pending, Reply) -> StreamResult<Out> + Send + Sync + 'static,
484{
485    assert!(
486        parallelism > 0,
487        "ActorFlow ask parallelism must be greater than zero"
488    );
489    let prepare = Arc::new(prepare);
490    let finish = Arc::new(finish);
491    Flow::from_transform(move |input| {
492        ask_ractor_ordered_with_pending(
493            input,
494            actor_ref.clone(),
495            parallelism,
496            timeout,
497            Arc::clone(&prepare),
498            Arc::clone(&finish),
499        )
500    })
501}
502
503fn ask_ractor_ordered_with_pending<In, Msg, Reply, Out, Pending, Prepare, Finish>(
504    mut input: BoxStream<In>,
505    actor_ref: ActorRef<Msg>,
506    parallelism: usize,
507    timeout: Duration,
508    prepare: Arc<Prepare>,
509    finish: Arc<Finish>,
510) -> BoxStream<Out>
511where
512    In: Send + 'static,
513    Msg: Message,
514    Reply: Send + 'static,
515    Out: Send + 'static,
516    Pending: Send + 'static,
517    Prepare: Fn(In, ReplyPort<Reply>) -> (Msg, Pending) + Send + Sync + 'static,
518    Finish: Fn(Pending, Reply) -> StreamResult<Out> + Send + Sync + 'static,
519{
520    let mut in_flight = Vec::<InFlightAskWith<Reply, Pending>>::with_capacity(parallelism);
521    let mut next_index = 0_usize;
522    let mut next_to_emit = 0_usize;
523    let mut completed = Vec::with_capacity(parallelism);
524    let mut reply_pool = Vec::with_capacity(parallelism);
525    let mut input_done = false;
526
527    Box::new(std::iter::from_fn(move || {
528        loop {
529            if let Some(result) = take_completed_with_pending(&mut completed, next_to_emit) {
530                next_to_emit += 1;
531                return Some(result);
532            }
533
534            while in_flight.len() < parallelism && !input_done {
535                match input.next() {
536                    Some(Ok(item)) => {
537                        let index = next_index;
538                        next_index += 1;
539                        match start_ractor_ask_with_pending(
540                            index,
541                            actor_ref.clone(),
542                            timeout,
543                            item,
544                            Arc::clone(&prepare),
545                            &mut reply_pool,
546                        ) {
547                            Ok(ask) => in_flight.push(ask),
548                            Err(error) => {
549                                completed.push((index, Err(error)));
550                                input_done = true;
551                            }
552                        }
553                    }
554                    Some(Err(error)) => {
555                        completed.push((next_index, Err(error)));
556                        next_index += 1;
557                        input_done = true;
558                    }
559                    None => input_done = true,
560                }
561            }
562
563            if let Some(result) = take_completed_with_pending(&mut completed, next_to_emit) {
564                next_to_emit += 1;
565                return Some(result);
566            }
567
568            if in_flight.is_empty() {
569                return None;
570            }
571
572            let ask = wait_for_ready_ask_with_pending(&mut in_flight, timeout, finish.as_ref());
573            let index = ask.index;
574            let result = ask.result;
575            recycle_reply_state(ask.state, &mut reply_pool);
576            if index == next_to_emit {
577                next_to_emit += 1;
578                return Some(result);
579            }
580            completed.push((index, result));
581        }
582    }))
583}
584
585fn take_completed_with_pending<Out>(
586    completed: &mut Vec<(usize, StreamResult<Out>)>,
587    index: usize,
588) -> Option<StreamResult<Out>> {
589    let position = completed
590        .iter()
591        .position(|(completed_index, _)| *completed_index == index)?;
592    Some(completed.swap_remove(position).1)
593}
594
595struct InFlightAskWith<Reply, Pending> {
596    index: usize,
597    state: Option<Arc<ReplyState<Reply>>>,
598    pending: Option<Pending>,
599    deadline: Option<Instant>,
600}
601
602impl<Reply, Pending> InFlightAskWith<Reply, Pending> {
603    fn state(&self) -> &Arc<ReplyState<Reply>> {
604        self.state.as_ref().expect("in-flight ask has reply state")
605    }
606
607    fn into_parts(mut self) -> (Arc<ReplyState<Reply>>, Pending) {
608        let state = self.state.take().expect("in-flight ask has reply state");
609        let pending = self
610            .pending
611            .take()
612            .expect("in-flight ask has pending state");
613        (state, pending)
614    }
615}
616
617impl<Reply, Pending> Drop for InFlightAskWith<Reply, Pending> {
618    fn drop(&mut self) {
619        if let Some(state) = &self.state {
620            state.close_receiver();
621        }
622    }
623}
624
625struct CompletedAskWith<Reply, Out> {
626    index: usize,
627    result: StreamResult<Out>,
628    state: Arc<ReplyState<Reply>>,
629}
630
631fn start_ractor_ask_with_pending<In, Msg, Reply, Pending, Prepare>(
632    index: usize,
633    actor_ref: ActorRef<Msg>,
634    timeout: Duration,
635    input: In,
636    prepare: Arc<Prepare>,
637    reply_pool: &mut Vec<Arc<ReplyState<Reply>>>,
638) -> StreamResult<InFlightAskWith<Reply, Pending>>
639where
640    In: Send + 'static,
641    Msg: Message,
642    Reply: Send + 'static,
643    Pending: Send + 'static,
644    Prepare: Fn(In, ReplyPort<Reply>) -> (Msg, Pending) + Send + Sync + 'static,
645{
646    let reply_state = match reply_pool.pop() {
647        Some(state) => {
648            state.reset(timeout);
649            state
650        }
651        None => Arc::new(ReplyState::new(timeout)),
652    };
653    let reply_to = ReplyPort::new(Arc::clone(&reply_state));
654    let (message, pending) =
655        catch_unwind(AssertUnwindSafe(|| prepare(input, reply_to))).map_err(|panic| {
656            StreamError::ActorAskTaskFailed {
657                reason: panic_reason(panic),
658            }
659        })?;
660
661    send_actor_message(&actor_ref, message)?;
662
663    Ok(InFlightAskWith {
664        index,
665        state: Some(reply_state),
666        pending: Some(pending),
667        deadline: Instant::now().checked_add(timeout),
668    })
669}
670
671fn wait_for_ready_ask_with_pending<Reply, Pending, Out, Finish>(
672    in_flight: &mut Vec<InFlightAskWith<Reply, Pending>>,
673    timeout: Duration,
674    finish: &Finish,
675) -> CompletedAskWith<Reply, Out>
676where
677    Reply: Send + 'static,
678    Pending: Send + 'static,
679    Out: Send + 'static,
680    Finish: Fn(Pending, Reply) -> StreamResult<Out>,
681{
682    let mut idle_spins = 0;
683    let mut idle_yields = 0;
684    let mut time_refresh = 0_u32;
685    let mut now = Instant::now();
686    loop {
687        if time_refresh == 0 {
688            now = Instant::now();
689        }
690        time_refresh = (time_refresh + 1) % ASK_TIME_REFRESH_ITERS;
691        if let Some(ask) = take_ready_ask_with_pending(in_flight, timeout, now, finish) {
692            return ask;
693        }
694
695        if idle_spins < ASK_READY_SPINS {
696            idle_spins += 1;
697            std::hint::spin_loop();
698        } else if idle_yields < ASK_IDLE_YIELDS {
699            idle_yields += 1;
700            time_refresh = 0;
701            thread::yield_now();
702        } else {
703            idle_spins = 0;
704            idle_yields = 0;
705            time_refresh = 0;
706            let current = thread::current();
707            let registered = register_ask_waiters_with_pending(in_flight, &current);
708            now = Instant::now();
709            if let Some(ask) = take_ready_ask_with_pending(in_flight, timeout, now, finish) {
710                unregister_ask_waiters_with_pending(registered, current.id());
711                return ask;
712            }
713            thread::park_timeout(next_ask_park_with_pending(in_flight, now));
714            unregister_ask_waiters_with_pending(registered, current.id());
715        }
716    }
717}
718
719fn take_ready_ask_with_pending<Reply, Pending, Out, Finish>(
720    in_flight: &mut Vec<InFlightAskWith<Reply, Pending>>,
721    timeout: Duration,
722    now: Instant,
723    finish: &Finish,
724) -> Option<CompletedAskWith<Reply, Out>>
725where
726    Finish: Fn(Pending, Reply) -> StreamResult<Out>,
727{
728    let mut index = 0;
729    while index < in_flight.len() {
730        match in_flight[index].state().poll() {
731            ReplyPoll::Ready(reply) => {
732                let ask = in_flight.swap_remove(index);
733                let ask_index = ask.index;
734                let (state, pending) = ask.into_parts();
735                return Some(CompletedAskWith {
736                    index: ask_index,
737                    result: finish(pending, reply),
738                    state,
739                });
740            }
741            ReplyPoll::Dropped => {
742                let ask = in_flight.swap_remove(index);
743                let ask_index = ask.index;
744                let (state, _pending) = ask.into_parts();
745                return Some(CompletedAskWith {
746                    index: ask_index,
747                    result: Err(StreamError::ActorAskResponseDropped),
748                    state,
749                });
750            }
751            ReplyPoll::Pending => {
752                if in_flight[index]
753                    .deadline
754                    .is_some_and(|deadline| now >= deadline)
755                {
756                    let outcome = in_flight[index].state().close_on_timeout();
757                    let ask = in_flight.swap_remove(index);
758                    let ask_index = ask.index;
759                    let (state, pending) = ask.into_parts();
760                    let result = match outcome {
761                        ReplyPoll::Ready(reply) => finish(pending, reply),
762                        ReplyPoll::Dropped => Err(StreamError::ActorAskResponseDropped),
763                        ReplyPoll::Pending => Err(StreamError::ActorAskTimeout { timeout }),
764                    };
765                    return Some(CompletedAskWith {
766                        index: ask_index,
767                        result,
768                        state,
769                    });
770                }
771                index += 1;
772            }
773        }
774    }
775    None
776}
777
778fn register_ask_waiters_with_pending<Reply, Pending>(
779    in_flight: &[InFlightAskWith<Reply, Pending>],
780    current: &Thread,
781) -> Vec<Arc<ReplyState<Reply>>> {
782    let mut registered = Vec::with_capacity(in_flight.len());
783    for ask in in_flight {
784        ask.state().register_waiter(current.clone());
785        registered.push(Arc::clone(ask.state()));
786    }
787    registered
788}
789
790fn unregister_ask_waiters_with_pending<Reply>(
791    registered: Vec<Arc<ReplyState<Reply>>>,
792    current_id: ThreadId,
793) {
794    for state in registered {
795        state.unregister_waiter(current_id);
796    }
797}
798
799fn next_ask_park_with_pending<Reply, Pending>(
800    in_flight: &[InFlightAskWith<Reply, Pending>],
801    now: Instant,
802) -> Duration {
803    in_flight
804        .iter()
805        .filter_map(|ask| ask.deadline)
806        .map(|deadline| deadline.saturating_duration_since(now))
807        .min()
808        .unwrap_or(ASK_MAX_PARK)
809        .min(ASK_MAX_PARK)
810}
811
812struct SourceBackpressure<AckMsg> {
813    ack_to: ActorRef<AckMsg>,
814    make_ack: Arc<dyn Fn() -> AckMsg + Send + Sync>,
815    _marker: PhantomData<fn() -> AckMsg>,
816}
817
818impl<AckMsg> Clone for SourceBackpressure<AckMsg> {
819    fn clone(&self) -> Self {
820        Self {
821            ack_to: self.ack_to.clone(),
822            make_ack: Arc::clone(&self.make_ack),
823            _marker: PhantomData,
824        }
825    }
826}
827
828fn actor_source<T, AckMsg>(
829    backpressure: Option<SourceBackpressure<AckMsg>>,
830) -> Source<T, ActorRef<ActorSourceMessage<T>>>
831where
832    T: Send + 'static,
833    AckMsg: Message,
834{
835    Source::from_materialized_factory(move |_materializer| {
836        let shared = Arc::new(ActorSourceShared::new(backpressure.is_none()));
837        let actor = SourceActor {
838            shared: Arc::clone(&shared),
839            backpressure: backpressure.clone(),
840        };
841        let (actor_ref, _handle) =
842            block_on_ractor_runtime(Actor::spawn(None, actor, SourceActorState::default()))?
843                .map_err(|error| {
844                    StreamError::Failed(format!("actor source failed to spawn: {error}"))
845                })?;
846        let stream = ActorSourceStream {
847            shared,
848            actor_ref: Some(actor_ref.clone()),
849            backpressure: backpressure.clone(),
850            terminated: false,
851        };
852        Ok((Box::new(stream) as BoxStream<T>, actor_ref))
853    })
854}
855
856struct ActorSourceShared<T> {
857    inner: Mutex<ActorSourceInner<T>>,
858    available: Condvar,
859}
860
861impl<T> ActorSourceShared<T> {
862    fn new(ready: bool) -> Self {
863        Self {
864            inner: Mutex::new(ActorSourceInner {
865                queue: VecDeque::new(),
866                completed: false,
867                ready,
868            }),
869            available: Condvar::new(),
870        }
871    }
872
873    fn lock(&self) -> MutexGuard<'_, ActorSourceInner<T>> {
874        self.inner
875            .lock()
876            .unwrap_or_else(|poison| poison.into_inner())
877    }
878
879    fn push(&self, item: T) {
880        let mut inner = self.lock();
881        if !inner.completed {
882            inner.queue.push_back(Ok(item));
883        }
884        drop(inner);
885        self.available.notify_all();
886    }
887
888    fn complete(&self) {
889        let mut inner = self.lock();
890        inner.completed = true;
891        drop(inner);
892        self.available.notify_all();
893    }
894
895    fn fail(&self, error: StreamError) {
896        let mut inner = self.lock();
897        inner.queue.clear();
898        inner.queue.push_back(Err(error));
899        inner.completed = true;
900        drop(inner);
901        self.available.notify_all();
902    }
903
904    fn mark_ready(&self) {
905        let mut inner = self.lock();
906        if !inner.completed {
907            inner.ready = true;
908        }
909        drop(inner);
910        self.available.notify_all();
911    }
912}
913
914struct ActorSourceInner<T> {
915    queue: VecDeque<StreamResult<T>>,
916    completed: bool,
917    ready: bool,
918}
919
920struct SourceActor<T, AckMsg> {
921    shared: Arc<ActorSourceShared<T>>,
922    backpressure: Option<SourceBackpressure<AckMsg>>,
923}
924
925#[derive(Default)]
926struct SourceActorState {
927    stopped_by_stream: bool,
928}
929
930impl<T, AckMsg> Actor for SourceActor<T, AckMsg>
931where
932    T: Send + 'static,
933    AckMsg: Message,
934{
935    type Msg = ActorSourceMessage<T>;
936    type State = SourceActorState;
937    type Arguments = SourceActorState;
938
939    async fn pre_start(
940        &self,
941        myself: ActorRef<Self::Msg>,
942        args: Self::Arguments,
943    ) -> ActorResult<Self::State> {
944        if let Some(backpressure) = &self.backpressure {
945            match send_source_ack(backpressure) {
946                Ok(()) => {
947                    self.shared.mark_ready();
948                }
949                Err(error) => {
950                    self.shared.fail(error);
951                    myself.stop(None);
952                }
953            }
954        }
955        Ok(args)
956    }
957
958    async fn handle(
959        &self,
960        myself: ActorRef<Self::Msg>,
961        message: Self::Msg,
962        _state: &mut Self::State,
963    ) -> ActorResult {
964        match message {
965            ActorSourceMessage::Element(item) => {
966                if self.backpressure.is_some() {
967                    let mut inner = self.shared.lock();
968                    if !inner.ready {
969                        inner.queue.clear();
970                        inner.queue.push_back(Err(actor_interop_error(
971                            "actor source backpressure protocol violation: element arrived before ack",
972                        )));
973                        inner.completed = true;
974                        drop(inner);
975                        self.shared.available.notify_all();
976                        myself.stop(None);
977                        return Ok(());
978                    }
979                    inner.ready = false;
980                    drop(inner);
981                }
982                self.shared.push(item);
983            }
984            ActorSourceMessage::Complete => {
985                self.shared.complete();
986                myself.stop(None);
987            }
988            ActorSourceMessage::Fail(reason) => {
989                self.shared.fail(StreamError::Failed(reason));
990                myself.stop(None);
991            }
992        }
993        Ok(())
994    }
995
996    async fn post_stop(
997        &self,
998        _myself: ActorRef<Self::Msg>,
999        state: &mut Self::State,
1000    ) -> ActorResult {
1001        if !state.stopped_by_stream {
1002            self.shared.complete();
1003        }
1004        Ok(())
1005    }
1006}
1007
1008struct ActorSourceStream<T, AckMsg> {
1009    shared: Arc<ActorSourceShared<T>>,
1010    actor_ref: Option<ActorRef<ActorSourceMessage<T>>>,
1011    backpressure: Option<SourceBackpressure<AckMsg>>,
1012    terminated: bool,
1013}
1014
1015impl<T, AckMsg> Iterator for ActorSourceStream<T, AckMsg>
1016where
1017    T: Send + 'static,
1018    AckMsg: Message,
1019{
1020    type Item = StreamResult<T>;
1021
1022    fn next(&mut self) -> Option<Self::Item> {
1023        if self.terminated {
1024            return None;
1025        }
1026
1027        let next = {
1028            let mut inner = self.shared.lock();
1029            loop {
1030                if let Some(item) = inner.queue.pop_front() {
1031                    break Some(item);
1032                }
1033                if inner.completed {
1034                    self.terminated = true;
1035                    break None;
1036                }
1037                inner = self
1038                    .shared
1039                    .available
1040                    .wait(inner)
1041                    .unwrap_or_else(|poison| poison.into_inner());
1042            }
1043        };
1044
1045        if let Some(Ok(_)) = &next
1046            && let Some(backpressure) = &self.backpressure
1047        {
1048            match send_source_ack(backpressure) {
1049                Ok(()) => self.shared.mark_ready(),
1050                Err(error) => {
1051                    self.shared.fail(error.clone());
1052                    return Some(Err(error));
1053                }
1054            }
1055        }
1056
1057        next
1058    }
1059}
1060
1061impl<T, AckMsg> Drop for ActorSourceStream<T, AckMsg> {
1062    fn drop(&mut self) {
1063        if let Some(actor_ref) = self.actor_ref.take() {
1064            actor_ref.stop(None);
1065        }
1066    }
1067}
1068
1069fn send_source_ack<AckMsg>(backpressure: &SourceBackpressure<AckMsg>) -> StreamResult<()>
1070where
1071    AckMsg: Message,
1072{
1073    let ack = catch_unwind(AssertUnwindSafe(|| (backpressure.make_ack)())).map_err(|panic| {
1074        StreamError::Failed(format!(
1075            "actor source ack builder failed: {}",
1076            panic_reason(panic)
1077        ))
1078    })?;
1079    send_actor_message(&backpressure.ack_to, ack)
1080}
1081
1082fn run_actor_ref_sink<In, Msg, Elem, Complete, Failure>(
1083    input: &mut BoxStream<In>,
1084    cancelled: Arc<AtomicBool>,
1085    actor_ref: ActorRef<Msg>,
1086    make_element_message: Arc<Elem>,
1087    on_complete_message: Arc<Complete>,
1088    on_failure_message: Arc<Failure>,
1089) -> StreamResult<NotUsed>
1090where
1091    Msg: Message,
1092    Elem: Fn(In) -> Msg,
1093    Complete: Fn() -> Msg,
1094    Failure: Fn(StreamError) -> Msg,
1095{
1096    loop {
1097        if cancelled.load(Ordering::SeqCst) {
1098            return Err(StreamError::Cancelled);
1099        }
1100        match input.next() {
1101            Some(Ok(item)) => {
1102                let message = catch_unwind(AssertUnwindSafe(|| make_element_message(item)))
1103                    .map_err(actor_sink_panic)?;
1104                send_actor_message(&actor_ref, message)?;
1105            }
1106            Some(Err(error)) => {
1107                let message = catch_unwind(AssertUnwindSafe(|| on_failure_message(error.clone())))
1108                    .map_err(actor_sink_panic)?;
1109                send_actor_message(&actor_ref, message)?;
1110                return Err(error);
1111            }
1112            None => {
1113                let message = catch_unwind(AssertUnwindSafe(|| on_complete_message()))
1114                    .map_err(actor_sink_panic)?;
1115                send_actor_message(&actor_ref, message)?;
1116                return Ok(NotUsed);
1117            }
1118        }
1119    }
1120}
1121
1122fn run_actor_ref_backpressure_sink<In, Msg, Ack, Elem, Complete, Failure>(
1123    input: &mut BoxStream<In>,
1124    cancelled: Arc<AtomicBool>,
1125    actor_ref: ActorRef<Msg>,
1126    timeout: Duration,
1127    make_element_message: Arc<Elem>,
1128    on_complete_message: Arc<Complete>,
1129    on_failure_message: Arc<Failure>,
1130) -> StreamResult<NotUsed>
1131where
1132    Msg: Message,
1133    Ack: Send + 'static,
1134    Elem: Fn(In, ReplyPort<Ack>) -> Msg,
1135    Complete: Fn() -> Msg,
1136    Failure: Fn(StreamError) -> Msg,
1137{
1138    loop {
1139        if cancelled.load(Ordering::SeqCst) {
1140            return Err(StreamError::Cancelled);
1141        }
1142        match input.next() {
1143            Some(Ok(item)) => {
1144                send_and_wait_ack(&actor_ref, timeout, |reply_to| {
1145                    make_element_message(item, reply_to)
1146                })?;
1147            }
1148            Some(Err(error)) => {
1149                let message = catch_unwind(AssertUnwindSafe(|| on_failure_message(error.clone())))
1150                    .map_err(actor_sink_panic)?;
1151                send_actor_message(&actor_ref, message)?;
1152                return Err(error);
1153            }
1154            None => {
1155                let message = catch_unwind(AssertUnwindSafe(|| on_complete_message()))
1156                    .map_err(actor_sink_panic)?;
1157                send_actor_message(&actor_ref, message)?;
1158                return Ok(NotUsed);
1159            }
1160        }
1161    }
1162}
1163
1164fn send_and_wait_ack<Msg, Ack, Build>(
1165    actor_ref: &ActorRef<Msg>,
1166    timeout: Duration,
1167    build: Build,
1168) -> StreamResult<()>
1169where
1170    Msg: Message,
1171    Ack: Send + 'static,
1172    Build: FnOnce(ReplyPort<Ack>) -> Msg,
1173{
1174    let reply_state = Arc::new(ReplyState::new(timeout));
1175    let reply_to = ReplyPort::new(Arc::clone(&reply_state));
1176    let message = catch_unwind(AssertUnwindSafe(|| build(reply_to))).map_err(actor_sink_panic)?;
1177    send_actor_message(actor_ref, message)?;
1178    let ask = InFlightAsk {
1179        index: 0,
1180        state: Some(reply_state),
1181        deadline: Instant::now().checked_add(timeout),
1182    };
1183    let mut in_flight = vec![ask];
1184    wait_for_ready_ask(&mut in_flight, timeout)
1185        .result
1186        .map(|_ack| ())
1187}
1188
1189fn actor_sink_panic(panic: Box<dyn std::any::Any + Send>) -> StreamError {
1190    StreamError::Failed(format!(
1191        "actor sink message builder failed: {}",
1192        panic_reason(panic)
1193    ))
1194}
1195
1196fn send_actor_message<Msg>(actor_ref: &ActorRef<Msg>, message: Msg) -> StreamResult<()>
1197where
1198    Msg: Message,
1199{
1200    match actor_ref.cast(message) {
1201        Ok(()) => Ok(()),
1202        Err(ractor::MessagingErr::SendErr(_)) | Err(ractor::MessagingErr::ChannelClosed) => {
1203            Err(StreamError::ActorTerminated)
1204        }
1205        Err(error) => Err(StreamError::ActorAskSendFailed {
1206            reason: error.to_string(),
1207        }),
1208    }
1209}
1210
1211fn broadcast_to_group<T>(group: &str, message: ActorSourceMessage<T>) -> StreamResult<()>
1212where
1213    T: Clone + Send + 'static,
1214{
1215    for member in ractor::pg::get_members(&group.to_owned()) {
1216        let actor_ref: ActorRef<ActorSourceMessage<T>> = member.into();
1217        match send_actor_message(&actor_ref, message.clone()) {
1218            Ok(()) | Err(StreamError::ActorTerminated) => {}
1219            Err(error) => return Err(error),
1220        }
1221    }
1222    Ok(())
1223}
1224
1225struct WatchShared<T> {
1226    inner: Mutex<WatchInner<T>>,
1227    available: Condvar,
1228}
1229
1230impl<T> Default for WatchShared<T> {
1231    fn default() -> Self {
1232        Self {
1233            inner: Mutex::new(WatchInner {
1234                queue: VecDeque::new(),
1235                event: None,
1236                upstream_done: false,
1237            }),
1238            available: Condvar::new(),
1239        }
1240    }
1241}
1242
1243impl<T> WatchShared<T> {
1244    fn lock(&self) -> MutexGuard<'_, WatchInner<T>> {
1245        self.inner
1246            .lock()
1247            .unwrap_or_else(|poison| poison.into_inner())
1248    }
1249
1250    fn push(&self, item: StreamResult<T>) -> bool {
1251        let mut inner = self.lock();
1252        if inner.event.is_some() || inner.upstream_done {
1253            return false;
1254        }
1255        inner.queue.push_back(item);
1256        drop(inner);
1257        self.available.notify_all();
1258        true
1259    }
1260
1261    fn complete(&self) {
1262        let mut inner = self.lock();
1263        inner.upstream_done = true;
1264        drop(inner);
1265        self.available.notify_all();
1266    }
1267
1268    fn fail(&self, event: WatchEvent) {
1269        let mut inner = self.lock();
1270        if inner.event.is_none() {
1271            inner.queue.clear();
1272            inner.event = Some(event);
1273            inner.upstream_done = true;
1274        }
1275        drop(inner);
1276        self.available.notify_all();
1277    }
1278}
1279
1280struct WatchInner<T> {
1281    queue: VecDeque<StreamResult<T>>,
1282    event: Option<WatchEvent>,
1283    upstream_done: bool,
1284}
1285
1286struct WatchMonitorActor<T> {
1287    watched: ractor::ActorCell,
1288    shared: Arc<WatchShared<T>>,
1289    ready: Mutex<Option<std::sync::mpsc::Sender<()>>>,
1290}
1291
1292impl<T> Actor for WatchMonitorActor<T>
1293where
1294    T: Send + 'static,
1295{
1296    type Msg = ();
1297    type State = ();
1298    type Arguments = ();
1299
1300    async fn pre_start(
1301        &self,
1302        myself: ActorRef<Self::Msg>,
1303        _args: Self::Arguments,
1304    ) -> ActorResult<Self::State> {
1305        myself.monitor(self.watched.clone());
1306        let ready = self
1307            .ready
1308            .lock()
1309            .unwrap_or_else(|poison| poison.into_inner())
1310            .take();
1311        if let Some(ready) = ready {
1312            let _ = ready.send(());
1313        }
1314        Ok(())
1315    }
1316
1317    async fn handle_supervisor_evt(
1318        &self,
1319        myself: ActorRef<Self::Msg>,
1320        event: ractor::SupervisionEvent,
1321        _state: &mut Self::State,
1322    ) -> ActorResult {
1323        match event {
1324            ractor::SupervisionEvent::ActorTerminated(who, _, reason)
1325                if who.get_id() == self.watched.get_id() =>
1326            {
1327                let detail = reason.unwrap_or_else(|| "terminated".to_owned());
1328                self.shared.fail(WatchEvent {
1329                    reason: format!("watched actor terminated: {detail}"),
1330                });
1331                myself.stop(None);
1332            }
1333            ractor::SupervisionEvent::ActorFailed(who, error)
1334                if who.get_id() == self.watched.get_id() =>
1335            {
1336                self.shared.fail(WatchEvent {
1337                    reason: format!("watched actor terminated: {error}"),
1338                });
1339                myself.stop(None);
1340            }
1341            _ => {}
1342        }
1343        Ok(())
1344    }
1345
1346    async fn post_stop(
1347        &self,
1348        myself: ActorRef<Self::Msg>,
1349        _state: &mut Self::State,
1350    ) -> ActorResult {
1351        myself.unmonitor(self.watched.clone());
1352        Ok(())
1353    }
1354}
1355
1356fn spawn_watch_monitor<T>(
1357    watched: ractor::ActorCell,
1358    shared: Arc<WatchShared<T>>,
1359) -> StreamResult<(ActorRef<()>, ractor::concurrency::JoinHandle<()>)>
1360where
1361    T: Send + 'static,
1362{
1363    let (ready_tx, ready_rx) = std::sync::mpsc::channel();
1364    block_on_ractor_runtime(Actor::spawn(
1365        None,
1366        WatchMonitorActor {
1367            watched,
1368            shared,
1369            ready: Mutex::new(Some(ready_tx)),
1370        },
1371        (),
1372    ))?
1373    .map_err(|error| StreamError::Failed(format!("watch monitor failed to spawn: {error}")))
1374    .and_then(|(monitor_ref, handle)| {
1375        ready_rx.recv_timeout(Duration::from_secs(1)).map_err(|_| {
1376            monitor_ref.stop(None);
1377            StreamError::Failed("watch monitor did not become ready".to_owned())
1378        })?;
1379        Ok((monitor_ref, handle))
1380    })
1381}
1382
1383struct WatchStream<T> {
1384    shared: Arc<WatchShared<T>>,
1385    monitor_ref: Option<ActorRef<()>>,
1386    producer_completion: Option<StreamCompletion<NotUsed>>,
1387    terminated: bool,
1388}
1389
1390impl<T> Iterator for WatchStream<T>
1391where
1392    T: Send + 'static,
1393{
1394    type Item = StreamResult<T>;
1395
1396    fn next(&mut self) -> Option<Self::Item> {
1397        if self.terminated {
1398            return None;
1399        }
1400
1401        let mut inner = self.shared.lock();
1402        loop {
1403            if let Some(event) = inner.event.take() {
1404                self.terminated = true;
1405                return Some(Err(event.into_stream_error()));
1406            }
1407            if let Some(item) = inner.queue.pop_front() {
1408                if item.is_err() {
1409                    self.terminated = true;
1410                }
1411                return Some(item);
1412            }
1413            if inner.upstream_done {
1414                self.terminated = true;
1415                return None;
1416            }
1417            inner = self
1418                .shared
1419                .available
1420                .wait(inner)
1421                .unwrap_or_else(|poison| poison.into_inner());
1422        }
1423    }
1424}
1425
1426impl<T> Drop for WatchStream<T> {
1427    fn drop(&mut self) {
1428        if let Some(monitor_ref) = self.monitor_ref.take() {
1429            monitor_ref.stop(None);
1430        }
1431        drop(self.producer_completion.take());
1432    }
1433}
1434
1435fn run_watch_upstream<T>(
1436    mut input: BoxStream<T>,
1437    shared: Arc<WatchShared<T>>,
1438    cancelled: Arc<AtomicBool>,
1439) -> StreamResult<NotUsed>
1440where
1441    T: Send + 'static,
1442{
1443    loop {
1444        if cancelled.load(Ordering::SeqCst) {
1445            shared.complete();
1446            return Err(StreamError::Cancelled);
1447        }
1448        match input.next() {
1449            Some(Ok(item)) => {
1450                if !shared.push(Ok(item)) {
1451                    return Err(StreamError::Cancelled);
1452                }
1453            }
1454            Some(Err(error)) => {
1455                let _ = shared.push(Err(error.clone()));
1456                shared.complete();
1457                return Err(error);
1458            }
1459            None => {
1460                shared.complete();
1461                return Ok(NotUsed);
1462            }
1463        }
1464    }
1465}
1466
1467fn actor_interop_error(reason: impl Into<String>) -> StreamError {
1468    StreamError::Failed(reason.into())
1469}
1470
1471#[cfg(test)]
1472mod tests {
1473    use super::*;
1474    use crate::stream::{Keep, Sink};
1475    use std::{
1476        sync::{
1477            Arc as StdArc, Mutex as StdMutex,
1478            atomic::{AtomicUsize, Ordering},
1479            mpsc,
1480        },
1481        time::{Duration as StdDuration, Instant as StdInstant},
1482    };
1483
1484    fn wait_until<F>(timeout: StdDuration, mut condition: F) -> bool
1485    where
1486        F: FnMut() -> bool,
1487    {
1488        let deadline = StdInstant::now() + timeout;
1489        while StdInstant::now() < deadline {
1490            if condition() {
1491                return true;
1492            }
1493            std::thread::park_timeout(StdDuration::from_millis(1));
1494        }
1495        condition()
1496    }
1497
1498    fn spawn_actor<A>(
1499        actor: A,
1500        args: A::Arguments,
1501    ) -> (ActorRef<A::Msg>, ractor::concurrency::JoinHandle<()>)
1502    where
1503        A: Actor,
1504        A::Msg: Message,
1505        A::Arguments: Send + 'static,
1506    {
1507        block_on_ractor_runtime(Actor::spawn(None, actor, args))
1508            .expect("ractor runtime runs")
1509            .expect("actor spawns")
1510    }
1511
1512    fn stop_actor<Msg>(actor_ref: ActorRef<Msg>, handle: ractor::concurrency::JoinHandle<()>)
1513    where
1514        Msg: Message,
1515    {
1516        actor_ref.stop(None);
1517        block_on_ractor_runtime(async move {
1518            handle.await.expect("actor task joins");
1519        })
1520        .expect("ractor runtime joins actor");
1521    }
1522
1523    enum AskInteropMsg {
1524        Status {
1525            input: u64,
1526            reply_to: ReplyPort<ActorStatus<u64>>,
1527        },
1528        Plain {
1529            input: u64,
1530            reply_to: ReplyPort<u64>,
1531        },
1532    }
1533
1534    struct AskInteropActor;
1535
1536    impl Actor for AskInteropActor {
1537        type Msg = AskInteropMsg;
1538        type State = ();
1539        type Arguments = ();
1540
1541        async fn pre_start(
1542            &self,
1543            _myself: ActorRef<Self::Msg>,
1544            _args: Self::Arguments,
1545        ) -> ActorResult<Self::State> {
1546            Ok(())
1547        }
1548
1549        async fn handle(
1550            &self,
1551            _myself: ActorRef<Self::Msg>,
1552            message: Self::Msg,
1553            _state: &mut Self::State,
1554        ) -> ActorResult {
1555            match message {
1556                AskInteropMsg::Status { input: 0, reply_to } => {
1557                    let _ =
1558                        reply_to.send(ActorStatus::Err(StreamError::Failed("bad status".into())));
1559                }
1560                AskInteropMsg::Status { input, reply_to } => {
1561                    let _ = reply_to.send(ActorStatus::Ok(input + 10));
1562                }
1563                AskInteropMsg::Plain { input, reply_to } => {
1564                    let _ = reply_to.send(input * 2);
1565                }
1566            }
1567            Ok(())
1568        }
1569    }
1570
1571    #[test]
1572    fn ask_with_status_unwraps_ok_and_fails_err() {
1573        let (actor_ref, handle) = spawn_actor(AskInteropActor, ());
1574
1575        let values = Source::from_iter([1_u64, 2])
1576            .via(ActorFlow::ask_with_status(
1577                actor_ref.clone(),
1578                2,
1579                Duration::from_secs(1),
1580                |input, reply_to| AskInteropMsg::Status { input, reply_to },
1581            ))
1582            .run_collect()
1583            .unwrap();
1584        assert_eq!(values, vec![11, 12]);
1585
1586        let failed = Source::single(0_u64)
1587            .via(ActorFlow::ask_with_status(
1588                actor_ref.clone(),
1589                1,
1590                Duration::from_secs(1),
1591                |input, reply_to| AskInteropMsg::Status { input, reply_to },
1592            ))
1593            .run_collect();
1594        assert_eq!(failed, Err(StreamError::Failed("bad status".into())));
1595
1596        stop_actor(actor_ref, handle);
1597    }
1598
1599    #[test]
1600    fn ask_with_context_preserves_context() {
1601        let (actor_ref, handle) = spawn_actor(AskInteropActor, ());
1602
1603        let values = Source::from_iter([1_u64, 2, 3])
1604            .as_source_with_context(|item| item + 100)
1605            .via(ActorFlow::ask_with_context(
1606                actor_ref.clone(),
1607                2,
1608                Duration::from_secs(1),
1609                |input, reply_to| AskInteropMsg::Plain { input, reply_to },
1610            ))
1611            .run_collect()
1612            .unwrap();
1613
1614        assert_eq!(values, vec![(2, 101), (4, 102), (6, 103)]);
1615        stop_actor(actor_ref, handle);
1616    }
1617
1618    #[test]
1619    fn ask_with_status_and_context_preserves_context_and_fails_status() {
1620        let (actor_ref, handle) = spawn_actor(AskInteropActor, ());
1621
1622        let values = Source::from_iter([1_u64, 2])
1623            .as_source_with_context(|item| item + 10)
1624            .via(ActorFlow::ask_with_status_and_context(
1625                actor_ref.clone(),
1626                2,
1627                Duration::from_secs(1),
1628                |input, reply_to| AskInteropMsg::Status { input, reply_to },
1629            ))
1630            .run_collect()
1631            .unwrap();
1632        assert_eq!(values, vec![(11, 11), (12, 12)]);
1633
1634        let failed = Source::single(0_u64)
1635            .as_source_with_context(|_| 99_u64)
1636            .via(ActorFlow::ask_with_status_and_context(
1637                actor_ref.clone(),
1638                1,
1639                Duration::from_secs(1),
1640                |input, reply_to| AskInteropMsg::Status { input, reply_to },
1641            ))
1642            .run_collect();
1643        assert_eq!(failed, Err(StreamError::Failed("bad status".into())));
1644
1645        stop_actor(actor_ref, handle);
1646    }
1647
1648    #[test]
1649    fn actor_source_actor_ref_emits_and_completes() {
1650        let (actor_ref, completion) = ActorSource::actor_ref::<u64>()
1651            .to_mat(Sink::collect(), Keep::both)
1652            .run()
1653            .unwrap();
1654
1655        actor_ref.cast(ActorSourceMessage::Element(1)).unwrap();
1656        actor_ref.cast(ActorSourceMessage::Element(2)).unwrap();
1657        actor_ref.cast(ActorSourceMessage::Complete).unwrap();
1658
1659        assert_eq!(completion.wait().unwrap(), vec![1, 2]);
1660    }
1661
1662    #[derive(Clone)]
1663    enum SourceAckMsg {
1664        Ack,
1665    }
1666
1667    struct SourceAckActor {
1668        count: StdArc<AtomicUsize>,
1669    }
1670
1671    impl Actor for SourceAckActor {
1672        type Msg = SourceAckMsg;
1673        type State = ();
1674        type Arguments = ();
1675
1676        async fn pre_start(
1677            &self,
1678            _myself: ActorRef<Self::Msg>,
1679            _args: Self::Arguments,
1680        ) -> ActorResult<Self::State> {
1681            Ok(())
1682        }
1683
1684        async fn handle(
1685            &self,
1686            _myself: ActorRef<Self::Msg>,
1687            _message: Self::Msg,
1688            _state: &mut Self::State,
1689        ) -> ActorResult {
1690            self.count.fetch_add(1, Ordering::SeqCst);
1691            Ok(())
1692        }
1693    }
1694
1695    #[test]
1696    fn actor_source_with_backpressure_acks_startup_and_each_element() {
1697        let ack_count = StdArc::new(AtomicUsize::new(0));
1698        let (ack_ref, ack_handle) = spawn_actor(
1699            SourceAckActor {
1700                count: StdArc::clone(&ack_count),
1701            },
1702            (),
1703        );
1704
1705        let (actor_ref, completion) =
1706            ActorSource::actor_ref_with_backpressure::<u64, SourceAckMsg>(
1707                ack_ref.clone(),
1708                SourceAckMsg::Ack,
1709            )
1710            .to_mat(Sink::collect(), Keep::both)
1711            .run()
1712            .unwrap();
1713
1714        assert!(wait_until(StdDuration::from_secs(1), || {
1715            ack_count.load(Ordering::SeqCst) >= 1
1716        }));
1717        actor_ref.cast(ActorSourceMessage::Element(1)).unwrap();
1718        assert!(wait_until(StdDuration::from_secs(1), || {
1719            ack_count.load(Ordering::SeqCst) >= 2
1720        }));
1721        actor_ref.cast(ActorSourceMessage::Element(2)).unwrap();
1722        assert!(wait_until(StdDuration::from_secs(1), || {
1723            ack_count.load(Ordering::SeqCst) >= 3
1724        }));
1725        actor_ref.cast(ActorSourceMessage::Complete).unwrap();
1726
1727        assert_eq!(completion.wait().unwrap(), vec![1, 2]);
1728        stop_actor(ack_ref, ack_handle);
1729    }
1730
1731    #[derive(Debug, Clone, PartialEq, Eq)]
1732    enum SinkEvent {
1733        Element(u64),
1734        Complete,
1735        Fail(String),
1736    }
1737
1738    struct EventActor {
1739        sender: mpsc::Sender<SinkEvent>,
1740    }
1741
1742    impl Actor for EventActor {
1743        type Msg = SinkEvent;
1744        type State = ();
1745        type Arguments = ();
1746
1747        async fn pre_start(
1748            &self,
1749            _myself: ActorRef<Self::Msg>,
1750            _args: Self::Arguments,
1751        ) -> ActorResult<Self::State> {
1752            Ok(())
1753        }
1754
1755        async fn handle(
1756            &self,
1757            _myself: ActorRef<Self::Msg>,
1758            message: Self::Msg,
1759            _state: &mut Self::State,
1760        ) -> ActorResult {
1761            self.sender
1762                .send(message)
1763                .expect("event receiver stays open");
1764            Ok(())
1765        }
1766    }
1767
1768    #[test]
1769    fn actor_sink_actor_ref_sends_elements_and_complete() {
1770        let (tx, rx) = mpsc::channel();
1771        let (actor_ref, handle) = spawn_actor(EventActor { sender: tx }, ());
1772
1773        Source::from_iter([1_u64, 2])
1774            .run_with(ActorSink::actor_ref(
1775                actor_ref.clone(),
1776                SinkEvent::Element,
1777                || SinkEvent::Complete,
1778                |error| SinkEvent::Fail(error.to_string()),
1779            ))
1780            .unwrap()
1781            .wait()
1782            .unwrap();
1783
1784        assert_eq!(
1785            [
1786                rx.recv_timeout(StdDuration::from_secs(1)).unwrap(),
1787                rx.recv_timeout(StdDuration::from_secs(1)).unwrap(),
1788                rx.recv_timeout(StdDuration::from_secs(1)).unwrap(),
1789            ],
1790            [
1791                SinkEvent::Element(1),
1792                SinkEvent::Element(2),
1793                SinkEvent::Complete,
1794            ]
1795        );
1796        stop_actor(actor_ref, handle);
1797    }
1798
1799    #[derive(Debug, Clone, PartialEq, Eq)]
1800    enum BackpressureEvent {
1801        Init,
1802        Element(u64),
1803        Complete,
1804        Fail(String),
1805    }
1806
1807    enum BackpressureSinkMsg {
1808        Init(ReplyPort<()>),
1809        Element(u64, ReplyPort<()>),
1810        Complete,
1811        Fail(String),
1812    }
1813
1814    struct BackpressureSinkActor {
1815        sender: mpsc::Sender<BackpressureEvent>,
1816    }
1817
1818    impl Actor for BackpressureSinkActor {
1819        type Msg = BackpressureSinkMsg;
1820        type State = ();
1821        type Arguments = ();
1822
1823        async fn pre_start(
1824            &self,
1825            _myself: ActorRef<Self::Msg>,
1826            _args: Self::Arguments,
1827        ) -> ActorResult<Self::State> {
1828            Ok(())
1829        }
1830
1831        async fn handle(
1832            &self,
1833            _myself: ActorRef<Self::Msg>,
1834            message: Self::Msg,
1835            _state: &mut Self::State,
1836        ) -> ActorResult {
1837            match message {
1838                BackpressureSinkMsg::Init(reply_to) => {
1839                    self.sender.send(BackpressureEvent::Init).unwrap();
1840                    let _ = reply_to.send(());
1841                }
1842                BackpressureSinkMsg::Element(item, reply_to) => {
1843                    self.sender.send(BackpressureEvent::Element(item)).unwrap();
1844                    let _ = reply_to.send(());
1845                }
1846                BackpressureSinkMsg::Complete => {
1847                    self.sender.send(BackpressureEvent::Complete).unwrap();
1848                }
1849                BackpressureSinkMsg::Fail(error) => {
1850                    self.sender.send(BackpressureEvent::Fail(error)).unwrap();
1851                }
1852            }
1853            Ok(())
1854        }
1855    }
1856
1857    #[test]
1858    fn actor_sink_with_backpressure_waits_for_init_and_element_acks() {
1859        let (tx, rx) = mpsc::channel();
1860        let (actor_ref, handle) = spawn_actor(BackpressureSinkActor { sender: tx }, ());
1861
1862        Source::from_iter([1_u64, 2])
1863            .run_with(ActorSink::actor_ref_with_backpressure(
1864                actor_ref.clone(),
1865                Duration::from_secs(1),
1866                BackpressureSinkMsg::Init,
1867                BackpressureSinkMsg::Element,
1868                || BackpressureSinkMsg::Complete,
1869                |error| BackpressureSinkMsg::Fail(error.to_string()),
1870            ))
1871            .unwrap()
1872            .wait()
1873            .unwrap();
1874
1875        assert_eq!(
1876            [
1877                rx.recv_timeout(StdDuration::from_secs(1)).unwrap(),
1878                rx.recv_timeout(StdDuration::from_secs(1)).unwrap(),
1879                rx.recv_timeout(StdDuration::from_secs(1)).unwrap(),
1880                rx.recv_timeout(StdDuration::from_secs(1)).unwrap(),
1881            ],
1882            [
1883                BackpressureEvent::Init,
1884                BackpressureEvent::Element(1),
1885                BackpressureEvent::Element(2),
1886                BackpressureEvent::Complete,
1887            ]
1888        );
1889        stop_actor(actor_ref, handle);
1890    }
1891
1892    enum WatchMsg {
1893        Stop,
1894    }
1895
1896    struct WatchActor;
1897
1898    impl Actor for WatchActor {
1899        type Msg = WatchMsg;
1900        type State = ();
1901        type Arguments = ();
1902
1903        async fn pre_start(
1904            &self,
1905            _myself: ActorRef<Self::Msg>,
1906            _args: Self::Arguments,
1907        ) -> ActorResult<Self::State> {
1908            Ok(())
1909        }
1910
1911        async fn handle(
1912            &self,
1913            myself: ActorRef<Self::Msg>,
1914            _message: Self::Msg,
1915            _state: &mut Self::State,
1916        ) -> ActorResult {
1917            myself.stop(Some("watched-stop".into()));
1918            Ok(())
1919        }
1920    }
1921
1922    #[test]
1923    fn watch_fails_idle_stream_when_actor_terminates() {
1924        let (watched_ref, watched_handle) = spawn_actor(WatchActor, ());
1925        let (source_ref, completion) = ActorSource::actor_ref::<u64>()
1926            .via(ActorFlow::watch(watched_ref.clone()))
1927            .to_mat(Sink::collect(), Keep::both)
1928            .run()
1929            .unwrap();
1930
1931        watched_ref.cast(WatchMsg::Stop).unwrap();
1932        assert!(wait_until(StdDuration::from_secs(1), || {
1933            watched_ref.get_status() == ractor::ActorStatus::Stopped
1934        }));
1935
1936        let result = completion.wait();
1937        let _ = source_ref.cast(ActorSourceMessage::Complete);
1938        assert!(
1939            matches!(result, Err(StreamError::Failed(reason)) if reason.contains("watched actor terminated"))
1940        );
1941        block_on_ractor_runtime(async move {
1942            watched_handle.await.expect("watched actor joins");
1943        })
1944        .expect("ractor runtime joins watched actor");
1945    }
1946
1947    static NEXT_GROUP: AtomicUsize = AtomicUsize::new(0);
1948
1949    #[test]
1950    fn pubsub_sink_broadcasts_to_pg_source() {
1951        let group = format!(
1952            "datum-test-pubsub-{}",
1953            NEXT_GROUP.fetch_add(1, Ordering::SeqCst)
1954        );
1955
1956        let (_source_ref, completion) = ActorPubSub::source::<u64>(group.clone())
1957            .to_mat(Sink::collect(), Keep::both)
1958            .run()
1959            .unwrap();
1960
1961        assert!(wait_until(StdDuration::from_secs(1), || {
1962            !ractor::pg::get_members(&group).is_empty()
1963        }));
1964
1965        Source::from_iter([1_u64, 2])
1966            .run_with(ActorPubSub::sink(group))
1967            .unwrap()
1968            .wait()
1969            .unwrap();
1970
1971        assert_eq!(completion.wait().unwrap(), vec![1, 2]);
1972    }
1973
1974    #[test]
1975    fn actor_source_fail_message_fails_stream() {
1976        let (actor_ref, completion) = ActorSource::typed::<u64>()
1977            .to_mat(Sink::collect(), Keep::both)
1978            .run()
1979            .unwrap();
1980
1981        actor_ref
1982            .cast(ActorSourceMessage::Fail("source failed".into()))
1983            .unwrap();
1984
1985        assert_eq!(
1986            completion.wait(),
1987            Err(StreamError::Failed("source failed".into()))
1988        );
1989    }
1990
1991    #[test]
1992    fn actor_sink_typed_sends_protocol_messages() {
1993        let received = StdArc::new(StdMutex::new(Vec::<String>::new()));
1994
1995        struct TypedSinkActor {
1996            received: StdArc<StdMutex<Vec<String>>>,
1997        }
1998
1999        impl Actor for TypedSinkActor {
2000            type Msg = ActorSinkMessage<u64>;
2001            type State = ();
2002            type Arguments = ();
2003
2004            async fn pre_start(
2005                &self,
2006                _myself: ActorRef<Self::Msg>,
2007                _args: Self::Arguments,
2008            ) -> ActorResult<Self::State> {
2009                Ok(())
2010            }
2011
2012            async fn handle(
2013                &self,
2014                _myself: ActorRef<Self::Msg>,
2015                message: Self::Msg,
2016                _state: &mut Self::State,
2017            ) -> ActorResult {
2018                let label = match message {
2019                    ActorSinkMessage::Element(item) => format!("element:{item}"),
2020                    ActorSinkMessage::Complete => "complete".to_owned(),
2021                    ActorSinkMessage::Fail(error) => format!("fail:{error}"),
2022                };
2023                self.received.lock().unwrap().push(label);
2024                Ok(())
2025            }
2026        }
2027
2028        let (actor_ref, handle) = spawn_actor(
2029            TypedSinkActor {
2030                received: StdArc::clone(&received),
2031            },
2032            (),
2033        );
2034
2035        Source::from_iter([7_u64])
2036            .run_with(ActorSink::typed(actor_ref.clone()))
2037            .unwrap()
2038            .wait()
2039            .unwrap();
2040
2041        assert!(wait_until(StdDuration::from_secs(1), || {
2042            received.lock().unwrap().len() == 2
2043        }));
2044        assert_eq!(
2045            *received.lock().unwrap(),
2046            vec!["element:7".to_owned(), "complete".to_owned()]
2047        );
2048        stop_actor(actor_ref, handle);
2049    }
2050}