reactor.core.publisher.FluxSubscribeOnValue 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.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.reactivestreams.Subscriber;
import reactor.core.CoreSubscriber;
import reactor.core.Disposable;
import reactor.core.Disposables;
import reactor.core.Fuseable;
import reactor.core.Scannable;
import reactor.core.scheduler.Scheduler;
import reactor.util.annotation.Nullable;
/**
* Publisher indicating a scalar/empty source that subscribes on the specified scheduler.
*
* @param
*
* @see https://github.com/reactor/reactive-streams-commons
*/
final class FluxSubscribeOnValue extends Flux implements Fuseable, Scannable {
final T value;
final Scheduler scheduler;
FluxSubscribeOnValue(@Nullable T value, Scheduler scheduler) {
this.value = value;
this.scheduler = Objects.requireNonNull(scheduler, "scheduler");
}
@Override
public void subscribe(CoreSubscriber super T> actual) {
T v = value;
if (v == null) {
ScheduledEmpty parent = new ScheduledEmpty(actual);
actual.onSubscribe(parent);
try {
parent.setFuture(scheduler.schedule(parent));
}
catch (RejectedExecutionException ree) {
if (parent.future != OperatorDisposables.DISPOSED) {
actual.onError(Operators.onRejectedExecution(ree,
actual.currentContext()));
}
}
}
else {
actual.onSubscribe(new ScheduledScalar<>(actual, v, scheduler));
}
}
@Override
public Object scanUnsafe(Attr key) {
if (key == Attr.RUN_ON) return scheduler;
if (key == Attr.RUN_STYLE) return Attr.RunStyle.ASYNC;
if (key == InternalProducerAttr.INSTANCE) return true;
return null;
}
static final class ScheduledScalar
implements QueueSubscription, InnerProducer, Runnable {
final CoreSubscriber super T> actual;
final T value;
final Scheduler scheduler;
volatile int once;
@SuppressWarnings("rawtypes")
static final AtomicIntegerFieldUpdater ONCE =
AtomicIntegerFieldUpdater.newUpdater(ScheduledScalar.class, "once");
volatile Disposable future;
@SuppressWarnings("rawtypes")
static final AtomicReferenceFieldUpdater FUTURE =
AtomicReferenceFieldUpdater.newUpdater(ScheduledScalar.class,
Disposable.class,
"future");
static final Disposable FINISHED = Disposables.disposed();
int fusionState;
static final int NO_VALUE = 1;
static final int HAS_VALUE = 2;
static final int COMPLETE = 3;
ScheduledScalar(CoreSubscriber super T> actual, T value, Scheduler scheduler) {
this.actual = actual;
this.value = value;
this.scheduler = scheduler;
}
@Override
public CoreSubscriber super T> actual() {
return actual;
}
@Override
@Nullable
public Object scanUnsafe(Scannable.Attr key) {
if (key == Attr.CANCELLED) {
return future == OperatorDisposables.DISPOSED;
}
if (key == Attr.TERMINATED) {
return future == FINISHED;
}
if (key == Attr.BUFFERED) {
return 1;
}
if (key == Attr.RUN_ON) return scheduler;
if (key == Attr.RUN_STYLE) return Attr.RunStyle.ASYNC;
return InnerProducer.super.scanUnsafe(key);
}
@Override
public void request(long n) {
if (Operators.validate(n)) {
if (ONCE.compareAndSet(this, 0, 1)) {
try {
Disposable f = scheduler.schedule(this);
if (!FUTURE.compareAndSet(this,
null,
f) && future != FINISHED && future != OperatorDisposables.DISPOSED) {
f.dispose();
}
}
catch (RejectedExecutionException ree) {
if (future != FINISHED && future != OperatorDisposables.DISPOSED) {
actual.onError(Operators.onRejectedExecution(ree,
this,
null,
value, actual.currentContext()));
}
}
}
}
}
@Override
public void cancel() {
ONCE.lazySet(this, 1);
Disposable f = future;
if (f != OperatorDisposables.DISPOSED && future != FINISHED) {
f = FUTURE.getAndSet(this, OperatorDisposables.DISPOSED);
if (f != null && f != OperatorDisposables.DISPOSED && f != FINISHED) {
f.dispose();
}
}
}
@Override
public void run() {
try {
if (fusionState == NO_VALUE) {
fusionState = HAS_VALUE;
}
actual.onNext(value);
actual.onComplete();
}
finally {
FUTURE.lazySet(this, FINISHED);
}
}
@Override
public int requestFusion(int requestedMode) {
if ((requestedMode & Fuseable.ASYNC) != 0) {
fusionState = NO_VALUE;
return Fuseable.ASYNC;
}
return Fuseable.NONE;
}
@Override
@Nullable
public T poll() {
if (fusionState == HAS_VALUE) {
fusionState = COMPLETE;
return value;
}
return null;
}
@Override
public boolean isEmpty() {
return fusionState != HAS_VALUE;
}
@Override
public int size() {
return isEmpty() ? 0 : 1;
}
@Override
public void clear() {
fusionState = COMPLETE;
}
}
static final class ScheduledEmpty implements QueueSubscription, Runnable {
final Subscriber> actual;
volatile Disposable future;
static final AtomicReferenceFieldUpdater FUTURE =
AtomicReferenceFieldUpdater.newUpdater(ScheduledEmpty.class,
Disposable.class,
"future");
static final Disposable FINISHED = Disposables.disposed();
ScheduledEmpty(Subscriber> actual) {
this.actual = actual;
}
@Override
public void request(long n) {
Operators.validate(n);
}
@Override
public void cancel() {
Disposable f = future;
if (f != OperatorDisposables.DISPOSED && f != FINISHED) {
f = FUTURE.getAndSet(this, OperatorDisposables.DISPOSED);
if (f != null && f != OperatorDisposables.DISPOSED && f != FINISHED) {
f.dispose();
}
}
}
@Override
public void run() {
try {
actual.onComplete();
}
finally {
FUTURE.lazySet(this, FINISHED);
}
}
void setFuture(Disposable f) {
if (!FUTURE.compareAndSet(this, null, f)) {
Disposable a = future;
if (a != FINISHED && a != OperatorDisposables.DISPOSED) {
f.dispose();
}
}
}
@Override
public int requestFusion(int requestedMode) {
return requestedMode & Fuseable.ASYNC;
}
@Override
@Nullable
public Void poll() {
return null;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public int size() {
return 0;
}
@Override
public void clear() {
// nothing to do
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy