reactor.core.publisher.FluxCreate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson-all Show documentation
Show all versions of redisson-all Show documentation
Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC
/*
* Copyright (c) 2016-2023 VMware Inc. or its affiliates, 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
*
* https://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.Objects;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
import org.reactivestreams.Subscriber;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Disposables;
import reactor.core.Exceptions;
import reactor.core.Scannable;
import reactor.core.publisher.FluxSink.OverflowStrategy;
import reactor.util.annotation.Nullable;
import reactor.util.concurrent.Queues;
import reactor.util.context.Context;
import reactor.util.context.ContextView;
/**
* Provides a multi-valued sink API for a callback that is called for each individual
* Subscriber.
*
* @param the value type
*/
final class FluxCreate extends Flux implements SourceProducer {
enum CreateMode {
PUSH_ONLY, PUSH_PULL
}
final Consumer super FluxSink> source;
final OverflowStrategy backpressure;
final CreateMode createMode;
FluxCreate(Consumer super FluxSink> source,
FluxSink.OverflowStrategy backpressure,
CreateMode createMode) {
this.source = Objects.requireNonNull(source, "source");
this.backpressure = Objects.requireNonNull(backpressure, "backpressure");
this.createMode = createMode;
}
static BaseSink createSink(CoreSubscriber super T> t,
OverflowStrategy backpressure) {
switch (backpressure) {
case IGNORE: {
return new IgnoreSink<>(t);
}
case ERROR: {
return new ErrorAsyncSink<>(t);
}
case DROP: {
return new DropAsyncSink<>(t);
}
case LATEST: {
return new LatestAsyncSink<>(t);
}
default: {
return new BufferAsyncSink<>(t, Queues.SMALL_BUFFER_SIZE);
}
}
}
@Override
public void subscribe(CoreSubscriber super T> actual) {
CoreSubscriber super T> wrapped =
Operators.restoreContextOnSubscriberIfAutoCPEnabled(this, actual);
BaseSink sink = createSink(wrapped, backpressure);
wrapped.onSubscribe(sink);
try {
source.accept(
createMode == CreateMode.PUSH_PULL ? new SerializedFluxSink<>(sink) :
sink);
}
catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
sink.error(Operators.onOperatorError(ex, wrapped.currentContext()));
}
}
@Override
public Object scanUnsafe(Attr key) {
if (key == Attr.RUN_STYLE) return Attr.RunStyle.ASYNC;
return SourceProducer.super.scanUnsafe(key);
}
/**
* Serializes calls to onNext, onError and onComplete.
*
* @param the value type
*/
static final class SerializedFluxSink implements FluxSink, Scannable {
final BaseSink sink;
volatile Throwable error;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater ERROR =
AtomicReferenceFieldUpdater.newUpdater(SerializedFluxSink.class,
Throwable.class,
"error");
volatile int wip;
@SuppressWarnings("rawtypes")
static final AtomicIntegerFieldUpdater WIP =
AtomicIntegerFieldUpdater.newUpdater(SerializedFluxSink.class, "wip");
final Queue mpscQueue;
volatile boolean done;
SerializedFluxSink(BaseSink sink) {
this.sink = sink;
this.mpscQueue = Queues.unboundedMultiproducer().get();
}
@Override
@Deprecated
public Context currentContext() {
return sink.currentContext();
}
@Override
public ContextView contextView() {
return sink.contextView();
}
@Override
public FluxSink next(T t) {
Objects.requireNonNull(t, "t is null in sink.next(t)");
if (sink.isTerminated() || done) {
Operators.onNextDropped(t, sink.currentContext());
return this;
}
if (WIP.get(this) == 0 && WIP.compareAndSet(this, 0, 1)) {
try {
sink.next(t);
}
catch (Throwable ex) {
Operators.onOperatorError(sink, ex, t, sink.currentContext());
}
if (WIP.decrementAndGet(this) == 0) {
return this;
}
}
else {
this.mpscQueue.offer(t);
if (WIP.getAndIncrement(this) != 0) {
return this;
}
}
drainLoop();
return this;
}
@Override
public void error(Throwable t) {
Objects.requireNonNull(t, "t is null in sink.error(t)");
if (sink.isTerminated() || done) {
Operators.onOperatorError(t, sink.currentContext());
return;
}
if (Exceptions.addThrowable(ERROR, this, t)) {
done = true;
drain();
}
else {
Context ctx = sink.currentContext();
Operators.onDiscardQueueWithClear(mpscQueue, ctx, null);
Operators.onOperatorError(t, ctx);
}
}
@Override
public void complete() {
if (sink.isTerminated() || done) {
return;
}
done = true;
drain();
}
//impl note: don't use sink.isTerminated() in the drain loop,
//it needs to separately check its own `done` status before calling the base sink
//complete()/error() methods (which do flip the isTerminated), otherwise it could
//bypass the terminate handler (in buffer and latest variants notably).
void drain() {
if (WIP.getAndIncrement(this) == 0) {
drainLoop();
}
}
void drainLoop() {
Context ctx = sink.currentContext();
BaseSink e = sink;
Queue q = mpscQueue;
for (; ; ) {
for (; ; ) {
if (e.isCancelled()) {
Operators.onDiscardQueueWithClear(q, ctx, null);
if (WIP.decrementAndGet(this) == 0) {
return;
}
else {
continue;
}
}
if (ERROR.get(this) != null) {
Operators.onDiscardQueueWithClear(q, ctx, null);
//noinspection ConstantConditions
e.error(Exceptions.terminate(ERROR, this));
return;
}
boolean d = done;
T v = q.poll();
boolean empty = v == null;
if (d && empty) {
e.complete();
return;
}
if (empty) {
break;
}
try {
e.next(v);
}
catch (Throwable ex) {
Operators.onOperatorError(sink, ex, v, sink.currentContext());
}
}
if (WIP.decrementAndGet(this) == 0) {
break;
}
}
}
@Override
public FluxSink onRequest(LongConsumer consumer) {
sink.onPushPullRequest(consumer);
return this;
}
@Override
public FluxSink onCancel(Disposable d) {
sink.onCancel(d);
return this;
}
@Override
public FluxSink onDispose(Disposable d) {
sink.onDispose(d);
return this;
}
@Override
public long requestedFromDownstream() {
return sink.requestedFromDownstream();
}
@Override
public boolean isCancelled() {
return sink.isCancelled();
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.BUFFERED) {
return mpscQueue.size();
}
if (key == Attr.ERROR) {
return error;
}
if (key == Attr.TERMINATED) {
return done;
}
return sink.scanUnsafe(key);
}
@Override
public String toString() {
return sink.toString();
}
}
/**
* Serializes calls to onNext, onError and onComplete if onRequest is invoked.
* Otherwise, non-serialized base sink is used.
*
* @param the value type
*/
static class SerializeOnRequestSink implements FluxSink, Scannable {
final BaseSink baseSink;
SerializedFluxSink serializedSink;
FluxSink sink;
SerializeOnRequestSink(BaseSink sink) {
this.baseSink = sink;
this.sink = sink;
}
@Override
@Deprecated
public Context currentContext() {
return sink.currentContext();
}
@Override
public ContextView contextView() {
return sink.contextView();
}
@Override
public Object scanUnsafe(Attr key) {
return serializedSink != null ? serializedSink.scanUnsafe(key) :
baseSink.scanUnsafe(key);
}
@Override
public void complete() {
sink.complete();
}
@Override
public void error(Throwable e) {
sink.error(e);
}
@Override
public FluxSink next(T t) {
sink.next(t);
return serializedSink == null ? this : serializedSink;
}
@Override
public long requestedFromDownstream() {
return sink.requestedFromDownstream();
}
@Override
public boolean isCancelled() {
return sink.isCancelled();
}
@Override
public FluxSink onRequest(LongConsumer consumer) {
if (serializedSink == null) {
serializedSink = new SerializedFluxSink<>(baseSink);
sink = serializedSink;
}
return sink.onRequest(consumer);
}
@Override
public FluxSink onCancel(Disposable d) {
sink.onCancel(d);
return sink;
}
@Override
public FluxSink onDispose(Disposable d) {
sink.onDispose(d);
return this;
}
@Override
public String toString() {
return baseSink.toString();
}
}
static abstract class BaseSink extends AtomicBoolean
implements FluxSink, InnerProducer {
static final Disposable TERMINATED = OperatorDisposables.DISPOSED;
static final Disposable CANCELLED = Disposables.disposed();
static final LongConsumer NOOP_CONSUMER = n -> {};
final CoreSubscriber super T> actual;
final Context ctx;
volatile Disposable disposable;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater DISPOSABLE =
AtomicReferenceFieldUpdater.newUpdater(BaseSink.class,
Disposable.class,
"disposable");
volatile long requested;
@SuppressWarnings("rawtypes")
static final AtomicLongFieldUpdater REQUESTED =
AtomicLongFieldUpdater.newUpdater(BaseSink.class, "requested");
volatile LongConsumer requestConsumer;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater
REQUEST_CONSUMER = AtomicReferenceFieldUpdater.newUpdater(BaseSink.class,
LongConsumer.class,
"requestConsumer");
BaseSink(CoreSubscriber super T> actual) {
this.actual = actual;
this.ctx = actual.currentContext();
REQUESTED.lazySet(this, Long.MIN_VALUE);
}
@Override
@Deprecated
public Context currentContext() {
//we cache the context for hooks purposes, but this forces to go through the
// chain when queried for context, in case downstream can update the Context...
return actual.currentContext();
}
@Override
public ContextView contextView() {
//we cache the context for hooks purposes, but this forces to go through the
// chain when queried for context, in case downstream can update the Context...
return actual.currentContext();
}
@Override
public void complete() {
if (isTerminated()) {
return;
}
try {
actual.onComplete();
}
finally {
disposeResource(false);
}
}
@Override
public void error(Throwable e) {
if (isTerminated()) {
Operators.onOperatorError(e, ctx);
return;
}
try {
actual.onError(e);
}
finally {
disposeResource(false);
}
}
@Override
public final void cancel() {
disposeResource(true);
onCancel();
}
void disposeResource(boolean isCancel) {
Disposable disposed = isCancel ? CANCELLED : TERMINATED;
Disposable d = disposable;
if (d != TERMINATED && d != CANCELLED) {
d = DISPOSABLE.getAndSet(this, disposed);
if (d != null && d != TERMINATED && d != CANCELLED) {
if (isCancel && d instanceof SinkDisposable) {
((SinkDisposable) d).cancel();
}
d.dispose();
}
}
}
@Override
public long requestedFromDownstream() {
return requested & Long.MAX_VALUE;
}
void onCancel() {
// default is no-op
}
@Override
public final boolean isCancelled() {
return disposable == CANCELLED;
}
final boolean isTerminated() {
return disposable == TERMINATED;
}
@Override
public final void request(long n) {
if (Operators.validate(n)) {
long s = addCap(this, n);
if (hasRequestConsumer(s)) {
LongConsumer consumer = requestConsumer;
if (!isCancelled()) {
consumer.accept(n);
}
}
onRequestedFromDownstream();
}
}
void onRequestedFromDownstream() {
// default is no-op
}
@Override
public CoreSubscriber super T> actual() {
return actual;
}
@Override
public FluxSink onRequest(LongConsumer consumer) {
Objects.requireNonNull(consumer, "onRequest");
onPushRequest(consumer);
return this;
}
protected void onPushRequest(LongConsumer initialRequestConsumer) {
if (!REQUEST_CONSUMER.compareAndSet(this, null, NOOP_CONSUMER)) {
throw new IllegalStateException(
"A consumer has already been assigned to consume requests");
}
// do not change real flag since real consumer is technically absent
initialRequestConsumer.accept(Long.MAX_VALUE);
}
protected void onPushPullRequest(LongConsumer requestConsumer) {
if (!REQUEST_CONSUMER.compareAndSet(this, null, requestConsumer)) {
throw new IllegalStateException(
"A consumer has already been assigned to consume requests");
}
long initialRequest = markRequestConsumerSet(this);
if (initialRequest > 0) {
requestConsumer.accept(initialRequest);
}
}
@Override
public final FluxSink onCancel(Disposable d) {
Objects.requireNonNull(d, "onCancel");
SinkDisposable sd = new SinkDisposable(null, d);
if (!DISPOSABLE.compareAndSet(this, null, sd)) {
Disposable c = disposable;
if (c == CANCELLED) {
d.dispose();
}
else if (c instanceof SinkDisposable) {
SinkDisposable current = (SinkDisposable) c;
if (current.onCancel == null) {
current.onCancel = d;
}
else {
d.dispose();
}
}
}
return this;
}
@Override
public final FluxSink onDispose(Disposable d) {
Objects.requireNonNull(d, "onDispose");
SinkDisposable sd = new SinkDisposable(d, null);
if (!DISPOSABLE.compareAndSet(this, null, sd)) {
Disposable c = disposable;
if (c == TERMINATED || c == CANCELLED) {
d.dispose();
}
else if (c instanceof SinkDisposable) {
SinkDisposable current = (SinkDisposable) c;
if (current.disposable == null) {
current.disposable = d;
}
else {
d.dispose();
}
}
}
return this;
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.TERMINATED) return disposable == TERMINATED;
if (key == Attr.CANCELLED) return disposable == CANCELLED;
if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requestedFromDownstream();
if (key == Attr.RUN_STYLE) return Attr.RunStyle.ASYNC;
return InnerProducer.super.scanUnsafe(key);
}
@Override
public String toString() {
return "FluxSink";
}
static void produced(BaseSink instance, long toSub) {
long s, r, u;
do {
s = instance.requested;
r = s & Long.MAX_VALUE;
if (r == 0 || r == Long.MAX_VALUE) {
return;
}
u = Operators.subOrZero(r, toSub);
} while (!REQUESTED.compareAndSet(instance, s, u | (s & Long.MIN_VALUE)));
}
static long addCap(BaseSink instance, long toAdd) {
long r, u, s;
for (;;) {
s = instance.requested;
r = s & Long.MAX_VALUE;
if (r == Long.MAX_VALUE) {
return s;
}
u = Operators.addCap(r, toAdd);
if (REQUESTED.compareAndSet(instance, s, u | (s & Long.MIN_VALUE))) {
return s;
}
}
}
static long markRequestConsumerSet(BaseSink instance) {
long u, s;
for (;;) {
s = instance.requested;
if (hasRequestConsumer(s)) {
return s;
}
u = s & Long.MAX_VALUE;
if (REQUESTED.compareAndSet(instance, s, u)) {
return u;
}
}
}
static boolean hasRequestConsumer(long requestedState) {
return (requestedState & Long.MIN_VALUE) == 0;
}
}
static final class IgnoreSink extends BaseSink {
IgnoreSink(CoreSubscriber super T> actual) {
super(actual);
}
@Override
public FluxSink next(T t) {
if (isTerminated()) {
Operators.onNextDropped(t, ctx);
return this;
}
if (isCancelled()) {
Operators.onDiscard(t, ctx);
return this;
}
actual.onNext(t);
for (; ; ) {
long s = requested;
long r = s & Long.MAX_VALUE;
if (r == 0L || REQUESTED.compareAndSet(this, s, (r - 1) | (s & Long.MIN_VALUE))) {
return this;
}
}
}
@Override
public String toString() {
return "FluxSink(" + OverflowStrategy.IGNORE + ")";
}
}
static abstract class NoOverflowBaseAsyncSink extends BaseSink {
NoOverflowBaseAsyncSink(CoreSubscriber super T> actual) {
super(actual);
}
@Override
public final FluxSink next(T t) {
if (isTerminated()) {
Operators.onNextDropped(t, ctx);
return this;
}
if (requestedFromDownstream() != 0) {
actual.onNext(t);
produced(this, 1);
}
else {
onOverflow();
Operators.onDiscard(t, ctx);
}
return this;
}
abstract void onOverflow();
}
static final class DropAsyncSink extends NoOverflowBaseAsyncSink {
DropAsyncSink(CoreSubscriber super T> actual) {
super(actual);
}
@Override
void onOverflow() {
// nothing to do
}
@Override
public String toString() {
return "FluxSink(" + OverflowStrategy.DROP + ")";
}
}
static final class ErrorAsyncSink extends NoOverflowBaseAsyncSink {
ErrorAsyncSink(CoreSubscriber super T> actual) {
super(actual);
}
@Override
void onOverflow() {
error(Exceptions.failWithOverflow());
}
@Override
public String toString() {
return "FluxSink(" + OverflowStrategy.ERROR + ")";
}
}
static final class BufferAsyncSink extends BaseSink {
final Queue queue;
Throwable error;
volatile boolean done; //done is still useful to be able to drain before the terminated handler is executed
volatile int wip;
@SuppressWarnings("rawtypes")
static final AtomicIntegerFieldUpdater WIP =
AtomicIntegerFieldUpdater.newUpdater(BufferAsyncSink.class, "wip");
BufferAsyncSink(CoreSubscriber super T> actual, int capacityHint) {
super(actual);
this.queue = Queues.unbounded(capacityHint).get();
}
@Override
public FluxSink next(T t) {
queue.offer(t);
drain();
return this;
}
@Override
public void error(Throwable e) {
error = e;
done = true;
drain();
}
@Override
public void complete() {
done = true;
drain();
}
@Override
void onRequestedFromDownstream() {
drain();
}
@Override
void onCancel() {
drain();
}
//impl note: don't use isTerminated() in the drain loop,
//it needs to first check the `done` status before setting `disposable` to TERMINATED
//otherwise it would either loose the ability to drain or the ability to invoke the
//handler at the right time.
void drain() {
if (WIP.getAndIncrement(this) != 0) {
return;
}
final Subscriber super T> a = actual;
final Queue q = queue;
for (; ; ) {
long r = requestedFromDownstream();
long e = 0L;
while (e != r) {
if (isCancelled()) {
Operators.onDiscardQueueWithClear(q, ctx, null);
if (WIP.decrementAndGet(this) != 0) {
continue;
}
else {
return;
}
}
boolean d = done;
T o = q.poll();
boolean empty = o == null;
if (d && empty) {
Throwable ex = error;
if (ex != null) {
super.error(ex);
}
else {
super.complete();
}
return;
}
if (empty) {
break;
}
a.onNext(o);
e++;
}
if (e == r) {
if (isCancelled()) {
Operators.onDiscardQueueWithClear(q, ctx, null);
if (WIP.decrementAndGet(this) != 0) {
continue;
}
else {
return;
}
}
boolean d = done;
boolean empty = q.isEmpty();
if (d && empty) {
Throwable ex = error;
if (ex != null) {
super.error(ex);
}
else {
super.complete();
}
return;
}
}
if (e != 0) {
produced(this, e);
}
if (WIP.decrementAndGet(this) == 0) {
break;
}
}
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.BUFFERED) {
return queue.size();
}
if (key == Attr.TERMINATED) {
return done;
}
if (key == Attr.ERROR) {
return error;
}
return super.scanUnsafe(key);
}
@Override
public String toString() {
return "FluxSink(" + OverflowStrategy.BUFFER + ")";
}
}
static final class LatestAsyncSink extends BaseSink {
final AtomicReference queue;
Throwable error;
volatile boolean done; //done is still useful to be able to drain before the terminated handler is executed
volatile int wip;
@SuppressWarnings("rawtypes")
static final AtomicIntegerFieldUpdater WIP =
AtomicIntegerFieldUpdater.newUpdater(LatestAsyncSink.class, "wip");
LatestAsyncSink(CoreSubscriber super T> actual) {
super(actual);
this.queue = new AtomicReference<>();
}
@Override
public FluxSink next(T t) {
T old = queue.getAndSet(t);
Operators.onDiscard(old, ctx);
drain();
return this;
}
@Override
public void error(Throwable e) {
error = e;
done = true;
drain();
}
@Override
public void complete() {
done = true;
drain();
}
@Override
void onRequestedFromDownstream() {
drain();
}
@Override
void onCancel() {
drain();
}
//impl note: don't use isTerminated() in the drain loop,
//it needs to first check the `done` status before setting `disposable` to TERMINATED
//otherwise it would either loose the ability to drain or the ability to invoke the
//handler at the right time.
void drain() {
if (WIP.getAndIncrement(this) != 0) {
return;
}
final Subscriber super T> a = actual;
final AtomicReference q = queue;
for (; ; ) {
long r = requestedFromDownstream();
long e = 0L;
while (e != r) {
if (isCancelled()) {
T old = q.getAndSet(null);
Operators.onDiscard(old, ctx);
if (WIP.decrementAndGet(this) != 0) {
continue;
}
else {
return;
}
}
boolean d = done;
T o = q.getAndSet(null);
boolean empty = o == null;
if (d && empty) {
Throwable ex = error;
if (ex != null) {
super.error(ex);
}
else {
super.complete();
}
return;
}
if (empty) {
break;
}
a.onNext(o);
e++;
}
if (e == r) {
if (isCancelled()) {
T old = q.getAndSet(null);
Operators.onDiscard(old, ctx);
if (WIP.decrementAndGet(this) != 0) {
continue;
}
else {
return;
}
}
boolean d = done;
boolean empty = q.get() == null;
if (d && empty) {
Throwable ex = error;
if (ex != null) {
super.error(ex);
}
else {
super.complete();
}
return;
}
}
if (e != 0) {
produced(this, e);
}
if (WIP.decrementAndGet(this) == 0) {
break;
}
}
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.BUFFERED) {
return queue.get() == null ? 0 : 1;
}
if (key == Attr.TERMINATED) {
return done;
}
if (key == Attr.ERROR) {
return error;
}
return super.scanUnsafe(key);
}
@Override
public String toString() {
return "FluxSink(" + OverflowStrategy.LATEST + ")";
}
}
static final class SinkDisposable implements Disposable {
Disposable onCancel;
Disposable disposable;
SinkDisposable(@Nullable Disposable disposable, @Nullable Disposable onCancel) {
this.disposable = disposable;
this.onCancel = onCancel;
}
@Override
public void dispose() {
if (disposable != null) {
disposable.dispose();
}
}
public void cancel() {
if (onCancel != null) {
onCancel.dispose();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy