
reactor.core.publisher.FluxBufferWhen Maven / Gradle / Ivy
/*
* 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.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;
/**
* buffers elements into possibly overlapping buffers whose boundaries are determined
* by a start Publisher's element and a signal of a derived Publisher
*
* @param the source value type
* @param the value type of the publisher opening the buffers
* @param the value type of the publisher closing the individual buffers
* @param the collection type that holds the buffered values
*
* @see Reactive-Streams-Commons
*/
final class FluxBufferWhen>
extends FluxOperator {
final Publisher start;
final Function super U, ? extends Publisher> end;
final Supplier bufferSupplier;
final Supplier extends Queue> queueSupplier;
FluxBufferWhen(Flux extends T> source,
Publisher start,
Function super U, ? extends Publisher> end,
Supplier bufferSupplier,
Supplier extends Queue> 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(CoreSubscriber super C> actual) {
Queue q = queueSupplier.get();
BufferStartEndMainSubscriber parent =
new BufferStartEndMainSubscriber<>(actual, bufferSupplier, q, end);
actual.onSubscribe(parent);
start.subscribe(parent.starter);
source.subscribe(parent);
}
static final class BufferStartEndMainSubscriber>
implements InnerOperator {
final Supplier bufferSupplier;
final Queue queue;
final Function super U, ? extends Publisher> end;
final CoreSubscriber super C> actual;
Set endSubscriptions;
final BufferStartEndStarter starter;
Map buffers;
volatile Subscription s;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater
S =
AtomicReferenceFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class,
Subscription.class,
"s");
volatile long requested;
@SuppressWarnings("rawtypes")
static final AtomicLongFieldUpdater REQUESTED =
AtomicLongFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class,
"requested");
long index;
volatile int wip;
@SuppressWarnings("rawtypes")
static final AtomicIntegerFieldUpdater WIP =
AtomicIntegerFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class,
"wip");
volatile Throwable error;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater
ERROR = AtomicReferenceFieldUpdater.newUpdater(
BufferStartEndMainSubscriber.class,
Throwable.class,
"error");
volatile boolean done;
volatile boolean cancelled;
volatile int open;
@SuppressWarnings("rawtypes")
static final AtomicIntegerFieldUpdater OPEN =
AtomicIntegerFieldUpdater.newUpdater(BufferStartEndMainSubscriber.class,
"open");
BufferStartEndMainSubscriber(CoreSubscriber super C> actual,
Supplier bufferSupplier,
Queue queue,
Function super U, ? extends Publisher> 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 final CoreSubscriber super C> actual() {
return actual;
}
@Override
public void onNext(T t) {
synchronized (this) {
Map set = buffers;
if (set != null) {
for (C b : set.values()) {
b.add(t);
}
return;
}
}
Operators.onNextDropped(t, actual.currentContext());
}
@Override
public void onError(Throwable t) {
boolean report;
synchronized (this) {
Map set = buffers;
if (set != null) {
buffers = null;
report = true;
}
else {
report = false;
}
}
if (report) {
anyError(t);
}
else {
Operators.onErrorDropped(t, actual.currentContext());
}
}
@Override
public void onComplete() {
Map 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.addCap(REQUESTED, this, n);
}
}
void cancelMain() {
Operators.terminate(S, this);
}
void cancelStart() {
starter.cancel();
}
void cancelEnds() {
Set set;
synchronized (starter) {
set = endSubscriptions;
if (set == null) {
return;
}
endSubscriptions = null;
}
for (Subscription s : set) {
s.cancel();
}
}
boolean addEndSubscription(Subscription s) {
synchronized (starter) {
Set 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, actual.currentContext());
}
}
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, actual.currentContext()));
return;
}
synchronized (this) {
Map set = buffers;
if (set == null) {
return;
}
set.put(idx, b);
}
Publisher p;
try {
p = Objects.requireNonNull(end.apply(u),
"The end returned a null publisher");
}
catch (Throwable e) {
anyError(Operators.onOperatorError(starter, e, u, actual.currentContext()));
return;
}
BufferStartEndEnder 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 ender) {
synchronized (this) {
Map 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 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
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.PARENT) return s;
if (key == Attr.TERMINATED) return done;
if (key == Attr.CANCELLED) return cancelled;
if (key == Attr.PREFETCH) return Integer.MAX_VALUE;
if (key == Attr.BUFFERED) return buffers.values()
.stream()
.mapToInt(Collection::size)
.sum();
if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested;
return InnerOperator.super.scanUnsafe(key);
}
}
static final class BufferStartEndStarter extends Operators.DeferredSubscription
implements InnerConsumer {
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 Context currentContext() {
return main.currentContext();
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.ACTUAL) {
return main;
}
return super.scanUnsafe(key);
}
}
static final class BufferStartEndEnder>
extends Operators.DeferredSubscription implements InnerConsumer {
final BufferStartEndMainSubscriber main;
final C buffer;
final long index;
BufferStartEndEnder(BufferStartEndMainSubscriber main,
C buffer,
long index) {
this.main = main;
this.buffer = buffer;
this.index = index;
}
@Override
public Context currentContext() {
return main.currentContext();
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.ACTUAL) {
return main;
}
return super.scanUnsafe(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);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy