2525import io .reactivex .rxjava4 .functions .*;
2626import io .reactivex .rxjava4 .internal .functions .ObjectHelper ;
2727import io .reactivex .rxjava4 .internal .operators .streamable .*;
28- import io .reactivex .rxjava4 .internal .util .* ;
28+ import io .reactivex .rxjava4 .internal .util .ExceptionHelper ;
2929import io .reactivex .rxjava4 .plugins .RxJavaPlugins ;
3030import io .reactivex .rxjava4 .schedulers .Schedulers ;
3131import io .reactivex .rxjava4 .subscribers .TestSubscriber ;
3232
33- /**
34- * The holographically emergent {@code IAsyncEnumerable} of the Java world.
35- * Runs best with Virtual Threads.
36- * TODO proper docs
37- * @param <T> the element type of the stream.
38- * @since 4.0.0
39- */
33+ /// Represents a virtual-thread capable, multi-valued (a)synchronous sequence of values that
34+ /// builds upon the Java [CompletionStage]-based concurrency-coordination model.
35+ ///
36+ /// The lifecycle of the sequence is as follows:
37+ ///
38+ /// - consumer calls {@link #stream(DisposableContainer)}
39+ /// - consumer calls {@link Streamer#next()} in a loop and consumer checks if what
40+ /// the {@link CompletionStage} outcome is:
41+ /// - if it succeeded with a boolean {@code true}, it is safe to call {@link Streamer#current()}.
42+ /// - if it succeeded with a boolean {@code false}, no further values are coming.
43+ /// - if it failed with a {@code Throwable}, no further values are coming and the error can be propagated further
44+ /// - consumer calls {@link Streamer#finish()}
45+ ///
46+ /// It is always necessary to have the consumer call {@code finish} because that is responsible for cleaning up
47+ /// resources of the upstream.
48+ ///
49+ /// Downstream cancellations are signaled via the [DisposableContainer], where operators can register their own
50+ /// [Disposable]s that get disposed. Because dispose can happen at any time and asynchronously to the consumption loop,
51+ /// the sensitive sources must complete their waiting `CompletionStage` returned by `next` exceptionally via a
52+ /// [CancellationException]. This will unblock the loops and invoke the `finish` method of the lifecycle at
53+ /// the consumer thread. Depending on the operator, the `CancellationException` may not be propagated further.
54+ ///
55+ /// If a source wishes to fail, it must signal the [Throwable] via the returned {@code CompletionStage} of {@code next}.
56+ /// If the `finish` also throws, its `Throwable` should be added as suppressed exception to the original `Throwable`.
57+ ///
58+ /// The `Streamer` methods must be invoked sequentially and non-overlappingly, similar to the
59+ /// <a href='https://github.com/reactive-streams/reactive-streams-jvm#1.3'>Reactive Streams rule §1.3</a>.
60+ ///
61+ /// This reactive type was modeled after the C# `IAsyncEnumerable` and `IAsyncEnumerator` interfaces. Unfortunately,
62+ /// Java never added any `async`/`await` infrastructure, plus `CompletionStage` doesn't even have any native way to blockingly
63+ /// join to it. Therefore, when running in a (virtual) blocking fashion, one may use the {@link Streamer#awaitNext()}
64+ /// or {@link Streamer#awaitFinish()} helper methods.
65+ ///
66+ /// @param <T> the element type of the {@code Streamable} sequence.
67+ /// @since 4.0.0
4068@ FunctionalInterface
4169public interface Streamable <@ NonNull T > {
4270
@@ -258,32 +286,6 @@ static <T> Streamable<T> fromPublisher(@NonNull Flow.Publisher<T> source, @NonNu
258286 .toStreamable ();
259287 }
260288
261- /**
262- * Generates a sequence in order which the stages complete in any form.
263- * @param <T> the common element type
264- * @param stages the iterable of stages to be relayed in the order they complete
265- * @param executor the executor to run the blocking operator
266- * @return the new Streamable instance
267- */
268- @ SuppressWarnings ("unchecked" )
269- @ CheckReturnValue
270- @ NonNull
271- static <@ NonNull T > Streamable <CompletionStage <T >> fromStages (
272- @ NonNull Iterable <? extends CompletionStage <? extends T >> stages , ExecutorService executor ) {
273- Objects .requireNonNull (stages , "stages is null" );
274- Objects .requireNonNull (executor , "executor is null" );
275- return create (emitter -> {
276- var list = new ArrayList <CompletionStage <? extends T >>();
277- for (var stage : stages ) {
278- list .add (stage );
279- }
280- while (!list .isEmpty ()) {
281- var winner = AwaitCoordinatorStatic .awaitFirstIndex (list , emitter .canceller ());
282- emitter .emit ((CompletionStage <T >)list .remove (winner ));
283- }
284- }, executor );
285- }
286-
287289 /**
288290 * Defers the creation of the actual {@code Streamable}, allowing a per streamer
289291 * state to be created along with it.
@@ -330,10 +332,10 @@ static <T> Streamable<T> fromPublisher(@NonNull Flow.Publisher<T> source, @NonNu
330332 return create (emitter -> {
331333 try (var mainSource = sources .forEach (item -> {
332334 try (var innerSource = item .forEach (emitter ::emit , emitter .canceller ().derive (), executor )) {
333- innerSource .await (emitter . canceller () );
335+ innerSource .await ();
334336 }
335337 }, emitter .canceller (), executor )) {
336- mainSource .await (emitter . canceller () );
338+ mainSource .await ();
337339 }
338340 }, executor );
339341 }
@@ -562,7 +564,7 @@ default Flowable<T> toFlowable() {
562564 default Flowable <T > toFlowable (@ NonNull ExecutorService executor ) {
563565 Objects .requireNonNull (executor , "executir is null" );
564566 var me = this ;
565- return Flowable .virtualCreate (emitter -> me .forEach (emitter ::emit ).await (emitter . canceller () ), executor );
567+ return Flowable .virtualCreate (emitter -> me .forEach (emitter ::emit ).await (), executor );
566568 }
567569
568570 /**
@@ -595,7 +597,7 @@ default Flowable<T> toFlowable(@NonNull ExecutorService executor) {
595597 // System.out.println("item " + item);
596598 transformer .transform (item , emitter , stopper );
597599 }, emitter .canceller (), executor )
598- .await (emitter . canceller () ), executor );
600+ .await (), executor );
599601 }
600602
601603 // oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
@@ -658,7 +660,7 @@ default CompletionStageDisposable<Void> forEach(@NonNull Consumer<? super T> con
658660 try {
659661 try {
660662 while (!canceller .isDisposed ()) {
661- if (str .awaitNext (canceller )) {
663+ if (str .awaitNext ()) {
662664 // System.out.println("Received " + str.current());
663665 consumer .accept (Objects .requireNonNull (str .current (), "The upstream Streamable " + me .getClass () + " produced a null element!" ));
664666 } else {
@@ -667,7 +669,7 @@ default CompletionStageDisposable<Void> forEach(@NonNull Consumer<? super T> con
667669 }
668670 }
669671 } finally {
670- str .awaitFinish (canceller );
672+ str .awaitFinish ();
671673 }
672674 } catch (final Throwable crash ) {
673675 Exceptions .throwIfFatal (crash );
@@ -705,7 +707,7 @@ default CompletionStageDisposable<Void> forEach(
705707 try {
706708 var stopper = Disposable .empty ();
707709 while (!canceller .isDisposed () && !stopper .isDisposed ()) {
708- if (str .awaitNext (canceller )) {
710+ if (str .awaitNext ()) {
709711 // System.out.println("Received " + str.current());
710712 var v = Objects .requireNonNull (str .current (), "The upstream Streamable " + me .getClass () + " produced a null element!" );
711713 consumer .accept (v , stopper );
@@ -715,7 +717,7 @@ default CompletionStageDisposable<Void> forEach(
715717 }
716718 }
717719 } finally {
718- str .awaitFinish (canceller );
720+ str .awaitFinish ();
719721 }
720722 } catch (final Throwable crash ) {
721723 Exceptions .throwIfFatal (crash );
@@ -739,7 +741,7 @@ default void subscribe(@NonNull Flow.Subscriber<? super T> subscriber, @NonNull
739741 final Streamable <T > me = this ;
740742 Flowable .<T >virtualCreate (emitter -> {
741743 me .forEach (emitter ::emit , emitter .canceller ().derive (), executor )
742- .await (emitter . canceller () );
744+ .await ();
743745 }, executor )
744746 .subscribe (subscriber );
745747 }
@@ -751,7 +753,7 @@ default void subscribe(@NonNull Flow.Subscriber<? super T> subscriber, @NonNull
751753 default void subscribe (@ NonNull Flow .Subscriber <? super T > subscriber ) {
752754 final Streamable <T > me = this ;
753755 Flowable .<T >virtualCreate (emitter -> {
754- me .forEach (emitter ::emit ).await (emitter . canceller () );
756+ me .forEach (emitter ::emit ).await ();
755757 })
756758 .subscribe (subscriber );
757759 }
0 commit comments