/* * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved. * * 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 reactor.core.publisher; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; import java.util.function.Supplier; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import reactor.core.Exceptions; /** * buffers elements into possibly overlapping buffers whose boundaries are determined * by a start Publisher's element and a signal of a derived Publisher * * @param <T> the source value type * @param <U> the value type of the publisher opening the buffers * @param <V> the value type of the publisher closing the individual buffers * @param <C> the collection type that holds the buffered values * * @see <a href="https://github.com/reactor/reactive-streams-commons">Reactive-Streams-Commons</a> */ final class FluxBufferWhen<T, U, V, C extends Collection<? super T>> extends FluxSource<T, C> { final Publisher<U> start; final Function<? super U, ? extends Publisher<V>> end; final Supplier<C> bufferSupplier; final Supplier<? extends Queue<C>> queueSupplier; FluxBufferWhen(Flux<? extends T> source, Publisher<U> start, Function<? super U, ? extends Publisher<V>> end, Supplier<C> bufferSupplier, Supplier<? extends Queue<C>> queueSupplier) { super(source); this.start = Objects.requireNonNull(start, "start"); this.end = Objects.requireNonNull(end, "end"); this.bufferSupplier = Objects.requireNonNull(bufferSupplier, "bufferSupplier"); this.queueSupplier = Objects.requireNonNull(queueSupplier, "queueSupplier"); } @Override public int getPrefetch() { return Integer.MAX_VALUE; } @Override public void subscribe(Subscriber<? super C> s) { Queue<C> q = queueSupplier.get(); BufferStartEndMainSubscriber<T, U, V, C> parent = new BufferStartEndMainSubscriber<>(s, bufferSupplier, q, end); s.onSubscribe(parent); start.subscribe(parent.starter); source.subscribe(parent); } static final class BufferStartEndMainSubscriber<T, U, V, C extends Collection<? super T>> implements InnerOperator<T, C> { final Supplier<C> bufferSupplier; final Queue<C> queue; final Function<? super U, ? extends Publisher<V>> end; final Subscriber<? super C> actual; Set<Subscription> endSubscriptions; final BufferStartEndStarter<U> starter; Map<Long, C> buffers; volatile Subscription s; @Override public final Subscriber<? super C> actual() { return actual; } @SuppressWarnings("rawtypes") static final AtomicReferenceFieldUpdater<BufferStartEndMainSubscriber, Subscription> S = AtomicReferenceFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class, Subscription.class, "s"); volatile long requested; @SuppressWarnings("rawtypes") static final AtomicLongFieldUpdater<BufferStartEndMainSubscriber> REQUESTED = AtomicLongFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class, "requested"); long index; volatile int wip; @SuppressWarnings("rawtypes") static final AtomicIntegerFieldUpdater<BufferStartEndMainSubscriber> WIP = AtomicIntegerFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class, "wip"); volatile Throwable error; @SuppressWarnings("rawtypes") static final AtomicReferenceFieldUpdater<BufferStartEndMainSubscriber, Throwable> ERROR = AtomicReferenceFieldUpdater.newUpdater( BufferStartEndMainSubscriber.class, Throwable.class, "error"); volatile boolean done; volatile boolean cancelled; volatile int open; @SuppressWarnings("rawtypes") static final AtomicIntegerFieldUpdater<BufferStartEndMainSubscriber> OPEN = AtomicIntegerFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class, "open"); BufferStartEndMainSubscriber(Subscriber<? super C> actual, Supplier<C> bufferSupplier, Queue<C> queue, Function<? super U, ? extends Publisher<V>> end) { this.actual = actual; this.bufferSupplier = bufferSupplier; this.buffers = new HashMap<>(); this.endSubscriptions = new HashSet<>(); this.queue = queue; this.end = end; this.open = 1; this.starter = new BufferStartEndStarter<>(this); } @Override public void onSubscribe(Subscription s) { if (Operators.setOnce(S, this, s)) { s.request(Long.MAX_VALUE); } } @Override public void onNext(T t) { synchronized (this) { Map<Long, C> set = buffers; if (set != null) { for (C b : set.values()) { b.add(t); } return; } } Operators.onNextDropped(t); } @Override public void onError(Throwable t) { boolean report; synchronized (this) { Map<Long, C> set = buffers; if (set != null) { buffers = null; report = true; } else { report = false; } } if (report) { anyError(t); } else { Operators.onErrorDropped(t); } } @Override public void onComplete() { Map<Long, C> set; synchronized (this) { set = buffers; if (set == null) { return; } } cancelStart(); cancelEnds(); for (C b : set.values()) { queue.offer(b); } done = true; drain(); } @Override public void request(long n) { if (Operators.validate(n)) { Operators.getAndAddCap(REQUESTED, this, n); } } void cancelMain() { Operators.terminate(S, this); } void cancelStart() { starter.cancel(); } void cancelEnds() { Set<Subscription> set; synchronized (starter) { set = endSubscriptions; if (set == null) { return; } endSubscriptions = null; } for (Subscription s : set) { s.cancel(); } } boolean addEndSubscription(Subscription s) { synchronized (starter) { Set<Subscription> set = endSubscriptions; if (set != null) { set.add(s); return true; } } s.cancel(); return false; } @Override public void cancel() { if (!cancelled) { cancelled = true; cancelMain(); cancelStart(); cancelEnds(); } } boolean emit(C b) { long r = requested; if (r != 0L) { actual.onNext(b); if (r != Long.MAX_VALUE) { REQUESTED.decrementAndGet(this); } return true; } else { actual.onError(Exceptions.failWithOverflow( "Could not emit buffer due to lack of requests")); return false; } } void anyError(Throwable t) { if (Exceptions.addThrowable(ERROR, this, t)) { done = true; drain(); } else { Operators.onErrorDropped(t); } } void startNext(U u) { long idx = index; index = idx + 1; C b; try { b = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); } catch (Throwable e) { anyError(Operators.onOperatorError(starter, e, u)); return; } synchronized (this) { Map<Long, C> set = buffers; if (set == null) { return; } set.put(idx, b); } Publisher<V> p; try { p = Objects.requireNonNull(end.apply(u), "The end returned a null publisher"); } catch (Throwable e) { anyError(Operators.onOperatorError(starter, e, u)); return; } BufferStartEndEnder<T, V, C> end = new BufferStartEndEnder<>(this, b, idx); if (addEndSubscription(end)) { OPEN.getAndIncrement(this); p.subscribe(end); } } void startError(Throwable e) { anyError(e); } void startComplete() { if (OPEN.decrementAndGet(this) == 0) { cancelAll(); done = true; drain(); } } void cancelAll() { cancelMain(); cancelStart(); cancelEnds(); } void endSignal(BufferStartEndEnder<T, V, C> ender) { synchronized (this) { Map<Long, C> set = buffers; if (set == null) { return; } if (set.remove(ender.index) == null) { return; } queue.offer(ender.buffer); } if (OPEN.decrementAndGet(this) == 0) { cancelAll(); done = true; } drain(); } void endError(Throwable e) { anyError(e); } void drain() { if (WIP.getAndIncrement(this) != 0) { return; } final Subscriber<? super C> a = actual; final Queue<C> q = queue; int missed = 1; for (; ; ) { for (; ; ) { boolean d = done; C b = q.poll(); boolean empty = b == null; if (checkTerminated(d, empty, a, q)) { return; } if (empty) { break; } long r = requested; if (r != 0L) { actual.onNext(b); if (r != Long.MAX_VALUE) { REQUESTED.decrementAndGet(this); } } else { anyError(Exceptions.failWithOverflow( "Could not emit buffer due to lack of requests")); } } missed = WIP.addAndGet(this, -missed); if (missed == 0) { break; } } } boolean checkTerminated(boolean d, boolean empty, Subscriber<?> a, Queue<?> q) { if (cancelled) { queue.clear(); return true; } if (d) { Throwable e = Exceptions.terminate(ERROR, this); if (e != null && e != Exceptions.TERMINATED) { cancel(); queue.clear(); a.onError(e); return true; } else if (empty) { a.onComplete(); return true; } } return false; } @Override public Object scan(Attr key) { switch (key) { case PARENT: return s; case TERMINATED: return done; case CANCELLED: return cancelled; case PREFETCH: return Integer.MAX_VALUE; case BUFFERED: return buffers.values() .stream() .mapToInt(Collection::size) .sum(); case REQUESTED_FROM_DOWNSTREAM: return requested; } return InnerOperator.super.scan(key); } } static final class BufferStartEndStarter<U> extends Operators.DeferredSubscription implements InnerConsumer<U> { final BufferStartEndMainSubscriber<?, U, ?, ?> main; BufferStartEndStarter(BufferStartEndMainSubscriber<?, U, ?, ?> main) { this.main = main; } @Override public void onSubscribe(Subscription s) { if (set(s)) { s.request(Long.MAX_VALUE); } } @Override public void onNext(U t) { main.startNext(t); } @Override public void onError(Throwable t) { main.startError(t); } @Override public void onComplete() { main.startComplete(); } @Override public Object scan(Attr key) { if (key == Attr.ACTUAL) { return main; } return super.scan(key); } } static final class BufferStartEndEnder<T, V, C extends Collection<? super T>> extends Operators.DeferredSubscription implements InnerConsumer<V> { final BufferStartEndMainSubscriber<T, ?, V, C> main; final C buffer; final long index; BufferStartEndEnder(BufferStartEndMainSubscriber<T, ?, V, C> main, C buffer, long index) { this.main = main; this.buffer = buffer; this.index = index; } @Override public Object scan(Attr key) { if (key == Attr.ACTUAL) { return main; } return super.scan(key); } @Override public void onSubscribe(Subscription s) { if (set(s)) { s.request(Long.MAX_VALUE); } } @Override public void onNext(V t) { if (!isCancelled()) { cancel(); main.endSignal(this); } } @Override public void onError(Throwable t) { main.endError(t); } @Override public void onComplete() { if (!isCancelled()) { main.endSignal(this); } } } }