/** * 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 org.reactivestreams.*; import io.reactivex.*; import io.reactivex.disposables.Disposable; import io.reactivex.exceptions.Exceptions; 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.subscribers.*; public final class FlowableBufferExactBoundary<T, U extends Collection<? super T>, B> extends AbstractFlowableWithUpstream<T, U> { final Publisher<B> boundary; final Callable<U> bufferSupplier; public FlowableBufferExactBoundary(Flowable<T> source, Publisher<B> boundary, Callable<U> bufferSupplier) { super(source); this.boundary = boundary; this.bufferSupplier = bufferSupplier; } @Override protected void subscribeActual(Subscriber<? super U> s) { source.subscribe(new BufferExactBoundarySubscriber<T, U, B>(new SerializedSubscriber<U>(s), bufferSupplier, boundary)); } static final class BufferExactBoundarySubscriber<T, U extends Collection<? super T>, B> extends QueueDrainSubscriber<T, U, U> implements FlowableSubscriber<T>, Subscription, Disposable { final Callable<U> bufferSupplier; final Publisher<B> boundary; Subscription s; Disposable other; U buffer; BufferExactBoundarySubscriber(Subscriber<? super U> actual, Callable<U> bufferSupplier, Publisher<B> boundary) { super(actual, new MpscLinkedQueue<U>()); this.bufferSupplier = bufferSupplier; this.boundary = boundary; } @Override public void onSubscribe(Subscription s) { if (!SubscriptionHelper.validate(this.s, s)) { return; } this.s = s; 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; BufferBoundarySubscriber<T, U, B> bs = new BufferBoundarySubscriber<T, U, B>(this); other = 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; other.dispose(); s.cancel(); if (enter()) { queue.clear(); } } } 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; } U b; synchronized (this) { b = buffer; if (b == null) { return; } buffer = next; } fastPathEmitMax(b, false, this); } @Override public void dispose() { cancel(); } @Override public boolean isDisposed() { return cancelled; } @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 BufferExactBoundarySubscriber<T, U, B> parent; BufferBoundarySubscriber(BufferExactBoundarySubscriber<T, U, B> parent) { this.parent = parent; } @Override public void onNext(B t) { parent.next(); } @Override public void onError(Throwable t) { parent.onError(t); } @Override public void onComplete() { parent.onComplete(); } } }