reactor.core.publisher.FluxTimeout 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.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Scannable;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;
/**
* Signals a timeout (or switches to another sequence) in case a per-item
* generated Publisher source fires an item or completes before the next item
* arrives from the main source.
*
* @param the main source type
* @param the value type for the timeout for the very first item
* @param the value type for the timeout for the subsequent items
*
* @see Reactive-Streams-Commons
*/
final class FluxTimeout extends InternalFluxOperator {
final Publisher firstTimeout;
final Function super T, ? extends Publisher> itemTimeout;
final Publisher extends T> other;
final String timeoutDescription; //only useful when no `other`
FluxTimeout(Flux extends T> source,
Publisher firstTimeout,
Function super T, ? extends Publisher> itemTimeout,
String timeoutDescription) {
super(source);
this.firstTimeout = Operators.toFluxOrMono(Objects.requireNonNull(firstTimeout,
"firstTimeout"));
this.itemTimeout = Objects.requireNonNull(itemTimeout, "itemTimeout");
this.other = null;
this.timeoutDescription = addNameToTimeoutDescription(source,
Objects.requireNonNull(timeoutDescription, "timeoutDescription is needed when no fallback"));
}
FluxTimeout(Flux extends T> source,
Publisher firstTimeout,
Function super T, ? extends Publisher> itemTimeout,
Publisher extends T> other) {
super(source);
this.firstTimeout = Operators.toFluxOrMono(Objects.requireNonNull(firstTimeout, "firstTimeout"));
this.itemTimeout = Objects.requireNonNull(itemTimeout, "itemTimeout");
this.other = Operators.toFluxOrMono(Objects.requireNonNull(other, "other"));
this.timeoutDescription = null;
}
@Override
public CoreSubscriber super T> subscribeOrReturn(CoreSubscriber super T> actual) {
return new TimeoutMainSubscriber<>(actual, firstTimeout, itemTimeout, other, timeoutDescription);
}
@Nullable
static String addNameToTimeoutDescription(Publisher> source,
@Nullable String timeoutDescription) {
if (timeoutDescription == null) {
return null;
}
Scannable s = Scannable.from(source);
if (s.isScanAvailable()) {
return timeoutDescription + " in '" + s.name() + "'";
}
else {
return timeoutDescription;
}
}
@Override
public Object scanUnsafe(Attr key) {
if (key == Attr.RUN_STYLE) return Attr.RunStyle.SYNC;
return super.scanUnsafe(key);
}
static final class TimeoutMainSubscriber
extends Operators.MultiSubscriptionSubscriber {
final Publisher> firstTimeout;
final Function super T, ? extends Publisher> itemTimeout;
final Publisher extends T> other;
final String timeoutDescription; //only useful/non-null when no `other`
Subscription s;
volatile IndexedCancellable timeout;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater
TIMEOUT =
AtomicReferenceFieldUpdater.newUpdater(TimeoutMainSubscriber.class,
IndexedCancellable.class,
"timeout");
volatile long index;
@SuppressWarnings("rawtypes")
static final AtomicLongFieldUpdater INDEX =
AtomicLongFieldUpdater.newUpdater(TimeoutMainSubscriber.class, "index");
TimeoutMainSubscriber(
CoreSubscriber super T> actual,
Publisher> firstTimeout,
Function super T, ? extends Publisher> itemTimeout,
@Nullable Publisher extends T> other,
@Nullable String timeoutDescription
) {
super(Operators.serialize(actual));
this.itemTimeout = itemTimeout;
this.other = other;
this.timeoutDescription = timeoutDescription;
this.firstTimeout = firstTimeout;
}
@Override
public void onSubscribe(Subscription s) {
if (Operators.validate(this.s, s)) {
this.s = s;
set(s);
TimeoutTimeoutSubscriber timeoutSubscriber = new TimeoutTimeoutSubscriber(this, 0L);
this.timeout = timeoutSubscriber;
actual.onSubscribe(this);
firstTimeout.subscribe(timeoutSubscriber);
}
}
@Override
protected boolean shouldCancelCurrent() {
return true;
}
@Override
public void onNext(T t) {
timeout.cancel();
long idx = index;
if (idx == Long.MIN_VALUE) {
s.cancel();
Operators.onNextDropped(t, actual.currentContext());
return;
}
if (!INDEX.compareAndSet(this, idx, idx + 1)) {
s.cancel();
Operators.onNextDropped(t, actual.currentContext());
return;
}
actual.onNext(t);
producedOne();
Publisher extends V> p;
try {
p = Objects.requireNonNull(itemTimeout.apply(t),
"The itemTimeout returned a null Publisher");
}
catch (Throwable e) {
actual.onError(Operators.onOperatorError(this, e, t,
actual.currentContext()));
return;
}
TimeoutTimeoutSubscriber ts = new TimeoutTimeoutSubscriber(this, idx + 1);
if (!setTimeout(ts)) {
return;
}
Operators.toFluxOrMono(p).subscribe(ts);
}
@Override
public void onError(Throwable t) {
long idx = index;
if (idx == Long.MIN_VALUE) {
Operators.onErrorDropped(t, actual.currentContext());
return;
}
if (!INDEX.compareAndSet(this, idx, Long.MIN_VALUE)) {
Operators.onErrorDropped(t, actual.currentContext());
return;
}
cancelTimeout();
actual.onError(t);
}
@Override
public void onComplete() {
long idx = index;
if (idx == Long.MIN_VALUE) {
return;
}
if (!INDEX.compareAndSet(this, idx, Long.MIN_VALUE)) {
return;
}
cancelTimeout();
actual.onComplete();
}
void cancelTimeout() {
IndexedCancellable s = timeout;
if (s != CancelledIndexedCancellable.INSTANCE) {
s = TIMEOUT.getAndSet(this, CancelledIndexedCancellable.INSTANCE);
if (s != null && s != CancelledIndexedCancellable.INSTANCE) {
s.cancel();
}
}
}
@Override
public void cancel() {
index = Long.MIN_VALUE;
cancelTimeout();
super.cancel();
}
boolean setTimeout(IndexedCancellable newTimeout) {
for (; ; ) {
IndexedCancellable currentTimeout = timeout;
if (currentTimeout == CancelledIndexedCancellable.INSTANCE) {
newTimeout.cancel();
return false;
}
if (currentTimeout != null && currentTimeout.index() >= newTimeout.index()) {
newTimeout.cancel();
return false;
}
if (TIMEOUT.compareAndSet(this, currentTimeout, newTimeout)) {
if (currentTimeout != null) {
currentTimeout.cancel();
}
return true;
}
}
}
void doTimeout(long i) {
if (index == i && INDEX.compareAndSet(this, i, Long.MIN_VALUE)) {
handleTimeout();
}
}
void doError(long i, Throwable e) {
if (index == i && INDEX.compareAndSet(this, i, Long.MIN_VALUE)) {
super.cancel();
actual.onError(e);
}
}
void handleTimeout() {
if (other == null) {
super.cancel();
actual.onError(new TimeoutException("Did not observe any item or terminal signal within "
+ timeoutDescription + " (and no fallback has been configured)"));
}
else {
set(Operators.emptySubscription());
other.subscribe(new TimeoutOtherSubscriber<>(actual, this));
}
}
@Override
public Object scanUnsafe(Attr key) {
if (key == Attr.RUN_STYLE) return Attr.RunStyle.SYNC;
return super.scanUnsafe(key);
}
}
static final class TimeoutOtherSubscriber implements InnerConsumer {
final CoreSubscriber super T> actual;
final Operators.MultiSubscriptionSubscriber arbiter;
TimeoutOtherSubscriber(CoreSubscriber super T> actual,
Operators.MultiSubscriptionSubscriber arbiter) {
this.actual = actual;
this.arbiter = arbiter;
}
@Override
public Context currentContext() {
return actual.currentContext();
}
@Override
public void onSubscribe(Subscription s) {
arbiter.set(s);
}
@Override
public void onNext(T t) {
actual.onNext(t);
}
@Override
public void onError(Throwable t) {
actual.onError(t);
}
@Override
public void onComplete() {
actual.onComplete();
}
@Override
public Object scanUnsafe(Attr key) {
if (key == Attr.RUN_STYLE) return Attr.RunStyle.SYNC;
return null;
}
}
interface IndexedCancellable {
long index();
void cancel();
}
enum CancelledIndexedCancellable implements IndexedCancellable {
INSTANCE;
@Override
public long index() {
return Long.MAX_VALUE;
}
@Override
public void cancel() {
}
}
static final class TimeoutTimeoutSubscriber
implements InnerConsumer
© 2015 - 2024 Weber Informatics LLC | Privacy Policy