
dev.miku.r2dbc.mysql.util.DiscardOnCancelSubscriber Maven / Gradle / Ivy
/*
* Copyright 2018-2020 the original author or authors.
*
* 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 dev.miku.r2dbc.mysql.util;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.core.Fuseable;
import reactor.core.Scannable;
import reactor.core.publisher.Operators;
import reactor.util.annotation.Nullable;
import reactor.util.context.Context;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An implementation of {@link CoreSubscriber} for request all elements and drains/discards them from upstream
* when downstream want to cancel its subscribe.
*
* The drains/discards behavior is defined by {@code doOnDiscard}, and applied by {@link Operators#onDiscard}.
*/
class DiscardOnCancelSubscriber> extends AtomicInteger
implements CoreSubscriber, Scannable, Subscription {
private static final int TERMINATED = 2;
private static final int CANCELLED = 1;
final A actual;
final Context ctx;
@Nullable
final Runnable onDone;
S s;
DiscardOnCancelSubscriber(A actual, @Nullable Runnable onDone) {
this.actual = actual;
this.ctx = actual.currentContext();
this.onDone = onDone;
}
@Override
@SuppressWarnings("unchecked")
public final void onSubscribe(Subscription s) {
if (Operators.validate(this.s, s)) {
this.s = (S) s;
this.actual.onSubscribe(this);
}
}
@Override
public final void onNext(T t) {
if (get() == 0) {
this.actual.onNext(t);
} else {
Operators.onDiscard(t, this.ctx);
}
}
@Override
public final void onError(Throwable t) {
if (compareAndSet(0, TERMINATED)) {
Runnable onDone = this.onDone;
if (onDone != null) {
try {
onDone.run();
} catch (Throwable e) {
Operators.onErrorDropped(e, this.ctx);
}
}
this.actual.onError(t);
} else {
Operators.onErrorDropped(t, this.ctx);
}
}
@Override
public final void onComplete() {
if (compareAndSet(0, TERMINATED)) {
Runnable onDone = this.onDone;
if (onDone != null) {
try {
onDone.run();
} catch (Throwable e) {
Operators.onErrorDropped(e, this.ctx);
}
}
this.actual.onComplete();
}
}
@Override
public final void request(long n) {
this.s.request(n);
}
@Override
public final void cancel() {
if (compareAndSet(0, CANCELLED)) {
Runnable onDone = this.onDone;
if (onDone != null) {
try {
onDone.run();
} catch (Throwable e) {
Operators.onErrorDropped(e, this.ctx);
}
}
this.s.request(Long.MAX_VALUE);
}
}
@Override
@SuppressWarnings("rawtypes")
public final Object scanUnsafe(Attr key) {
if (key == Attr.PARENT) {
return this.s;
} else if (key == Attr.ACTUAL) {
return this.actual;
} else if (key == Attr.TERMINATED) {
return get() == TERMINATED;
} else if (key == Attr.CANCELLED) {
return get() == CANCELLED;
} else {
return null;
}
}
@SuppressWarnings("unchecked")
static CoreSubscriber create(CoreSubscriber super T> s, boolean fuseable, @Nullable Runnable onDone) {
if (fuseable) {
if (s instanceof Fuseable.ConditionalSubscriber) {
return new DiscardOnCancelFuseableConditionalSubscriber<>((Fuseable.ConditionalSubscriber super T>) s, onDone);
}
return new DiscardOnCancelFuseableSubscriber>(s, onDone);
}
if (s instanceof Fuseable.ConditionalSubscriber) {
return new DiscardOnCancelConditionalSubscriber<>((Fuseable.ConditionalSubscriber super T>) s, onDone);
}
return new DiscardOnCancelSubscriber>(s, onDone);
}
}
/**
* An extension of {@link DiscardOnCancelSubscriber} for implements {@link Fuseable.QueueSubscription}.
*/
class DiscardOnCancelFuseableSubscriber> extends DiscardOnCancelSubscriber, A>
implements Fuseable.QueueSubscription {
DiscardOnCancelFuseableSubscriber(A actual, @Nullable Runnable onDone) {
super(actual, onDone);
}
@Override
public T poll() {
try {
return this.s.poll();
} catch (Throwable e) {
throw Exceptions.propagate(Operators.onOperatorError(this.s, e, this.actual.currentContext()));
}
}
@Override
public boolean isEmpty() {
return this.s.isEmpty();
}
@Override
public void clear() {
this.s.clear();
}
@Override
public int requestFusion(int modes) {
if ((modes & Fuseable.THREAD_BARRIER) != 0) {
return Fuseable.NONE;
}
return this.s.requestFusion(modes);
}
@Override
public int size() {
return this.s.size();
}
}
/**
* An extension of {@link DiscardOnCancelSubscriber} for implements {@link Fuseable.ConditionalSubscriber}.
*/
final class DiscardOnCancelConditionalSubscriber extends DiscardOnCancelSubscriber>
implements Fuseable.ConditionalSubscriber {
DiscardOnCancelConditionalSubscriber(Fuseable.ConditionalSubscriber super T> actual, @Nullable Runnable onDone) {
super(actual, onDone);
}
@Override
public boolean tryOnNext(T t) {
if (get() == 0) {
return this.actual.tryOnNext(t);
} else {
Operators.onDiscard(t, this.ctx);
return true;
}
}
}
/**
* An extension of {@link DiscardOnCancelFuseableSubscriber} for implements {@link Fuseable.ConditionalSubscriber}.
*/
final class DiscardOnCancelFuseableConditionalSubscriber extends DiscardOnCancelFuseableSubscriber>
implements Fuseable.ConditionalSubscriber {
DiscardOnCancelFuseableConditionalSubscriber(Fuseable.ConditionalSubscriber super T> actual, @Nullable Runnable onDone) {
super(actual, onDone);
}
@Override
public boolean tryOnNext(T t) {
if (get() == 0) {
return this.actual.tryOnNext(t);
} else {
Operators.onDiscard(t, this.ctx);
return true;
}
}
}