/** * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See * the License for the specific language governing permissions and limitations under the License. */ package io.reactivex.internal.operators.flowable; import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import org.reactivestreams.*; import io.reactivex.*; import io.reactivex.disposables.Disposable; import io.reactivex.exceptions.Exceptions; import io.reactivex.internal.disposables.DisposableHelper; import io.reactivex.internal.functions.ObjectHelper; import io.reactivex.internal.queue.MpscLinkedQueue; import io.reactivex.internal.subscribers.QueueDrainSubscriber; import io.reactivex.internal.subscriptions.*; import io.reactivex.internal.util.QueueDrainHelper; import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.subscribers.*; public final class FlowableBufferBoundarySupplier<T, U extends Collection<? super T>, B> extends AbstractFlowableWithUpstream<T, U> { final Callable<? extends Publisher<B>> boundarySupplier; final Callable<U> bufferSupplier; public FlowableBufferBoundarySupplier(Flowable<T> source, Callable<? extends Publisher<B>> boundarySupplier, Callable<U> bufferSupplier) { super(source); this.boundarySupplier = boundarySupplier; this.bufferSupplier = bufferSupplier; } @Override protected void subscribeActual(Subscriber<? super U> s) { source.subscribe(new BufferBoundarySupplierSubscriber<T, U, B>(new SerializedSubscriber<U>(s), bufferSupplier, boundarySupplier)); } static final class BufferBoundarySupplierSubscriber<T, U extends Collection<? super T>, B> extends QueueDrainSubscriber<T, U, U> implements FlowableSubscriber<T>, Subscription, Disposable { final Callable<U> bufferSupplier; final Callable<? extends Publisher<B>> boundarySupplier; Subscription s; final AtomicReference<Disposable> other = new AtomicReference<Disposable>(); U buffer; BufferBoundarySupplierSubscriber(Subscriber<? super U> actual, Callable<U> bufferSupplier, Callable<? extends Publisher<B>> boundarySupplier) { super(actual, new MpscLinkedQueue<U>()); this.bufferSupplier = bufferSupplier; this.boundarySupplier = boundarySupplier; } @Override public void onSubscribe(Subscription s) { if (!SubscriptionHelper.validate(this.s, s)) { return; } this.s = s; Subscriber<? super U> actual = this.actual; U b; try { b = ObjectHelper.requireNonNull(bufferSupplier.call(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancelled = true; s.cancel(); EmptySubscription.error(e, actual); return; } buffer = b; Publisher<B> boundary; try { boundary = ObjectHelper.requireNonNull(boundarySupplier.call(), "The boundary publisher supplied is null"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancelled = true; s.cancel(); EmptySubscription.error(ex, actual); return; } BufferBoundarySubscriber<T, U, B> bs = new BufferBoundarySubscriber<T, U, B>(this); other.set(bs); actual.onSubscribe(this); if (!cancelled) { s.request(Long.MAX_VALUE); boundary.subscribe(bs); } } @Override public void onNext(T t) { synchronized (this) { U b = buffer; if (b == null) { return; } b.add(t); } } @Override public void onError(Throwable t) { cancel(); actual.onError(t); } @Override public void onComplete() { U b; synchronized (this) { b = buffer; if (b == null) { return; } buffer = null; } queue.offer(b); done = true; if (enter()) { QueueDrainHelper.drainMaxLoop(queue, actual, false, this, this); } } @Override public void request(long n) { requested(n); } @Override public void cancel() { if (!cancelled) { cancelled = true; s.cancel(); disposeOther(); if (enter()) { queue.clear(); } } } void disposeOther() { DisposableHelper.dispose(other); } void next() { U next; try { next = ObjectHelper.requireNonNull(bufferSupplier.call(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); actual.onError(e); return; } Publisher<B> boundary; try { boundary = ObjectHelper.requireNonNull(boundarySupplier.call(), "The boundary publisher supplied is null"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancelled = true; s.cancel(); actual.onError(ex); return; } BufferBoundarySubscriber<T, U, B> bs = new BufferBoundarySubscriber<T, U, B>(this); Disposable o = other.get(); if (!other.compareAndSet(o, bs)) { return; } U b; synchronized (this) { b = buffer; if (b == null) { return; } buffer = next; } boundary.subscribe(bs); fastPathEmitMax(b, false, this); } @Override public void dispose() { s.cancel(); disposeOther(); } @Override public boolean isDisposed() { return other.get() == DisposableHelper.DISPOSED; } @Override public boolean accept(Subscriber<? super U> a, U v) { actual.onNext(v); return true; } } static final class BufferBoundarySubscriber<T, U extends Collection<? super T>, B> extends DisposableSubscriber<B> { final BufferBoundarySupplierSubscriber<T, U, B> parent; boolean once; BufferBoundarySubscriber(BufferBoundarySupplierSubscriber<T, U, B> parent) { this.parent = parent; } @Override public void onNext(B t) { if (once) { return; } once = true; cancel(); parent.next(); } @Override public void onError(Throwable t) { if (once) { RxJavaPlugins.onError(t); return; } once = true; parent.onError(t); } @Override public void onComplete() { if (once) { return; } once = true; parent.next(); } } }