All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactor.core.publisher.FluxSwitchOnFirst Maven / Gradle / Ivy

Go to download

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

There is a newer version: 3.40.2
Show newest version
/*
 * Copyright (c) 2018-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.CancellationException;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.BiFunction;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.core.Fuseable;
import reactor.util.annotation.Nullable;

/**
 * @param 
 * @param 
 * @author Oleh Dokuka
 */
final class FluxSwitchOnFirst extends InternalFluxOperator {

	final BiFunction, Flux, Publisher> transformer;
	final boolean cancelSourceOnComplete;

	FluxSwitchOnFirst(Flux source,
			BiFunction, Flux, Publisher> transformer,
			boolean cancelSourceOnComplete) {
		super(source);
		this.transformer = Objects.requireNonNull(transformer, "transformer");
		this.cancelSourceOnComplete = cancelSourceOnComplete;
	}

	@Override
	public int getPrefetch() {
		return 1;
	}

	@Override
	@SuppressWarnings("unchecked")
	public CoreSubscriber subscribeOrReturn(CoreSubscriber actual) {
		if (actual instanceof Fuseable.ConditionalSubscriber) {
			return new SwitchOnFirstConditionalMain<>((Fuseable.ConditionalSubscriber) actual,
					transformer,
					cancelSourceOnComplete,
					null);
		}
		return new SwitchOnFirstMain<>(actual, transformer, cancelSourceOnComplete);
	}

	@Override
	public Object scanUnsafe(Attr key) {
		if (key == Attr.RUN_STYLE) {
			return Attr.RunStyle.SYNC;
		}
		return super.scanUnsafe(key);
	}

	static final int HAS_FIRST_VALUE_RECEIVED_FLAG       =
			0b0000_0000_0000_0000_0000_0000_0000_0001;
	static final int HAS_INBOUND_SUBSCRIBED_ONCE_FLAG    =
			0b0000_0000_0000_0000_0000_0000_0000_0010;
	static final int HAS_INBOUND_SUBSCRIBER_SET_FLAG     =
			0b0000_0000_0000_0000_0000_0000_0000_0100;
	static final int HAS_INBOUND_REQUESTED_ONCE_FLAG     =
			0b0000_0000_0000_0000_0000_0000_0000_1000;
	static final int HAS_FIRST_VALUE_SENT_FLAG           =
			0b0000_0000_0000_0000_0000_0000_0001_0000;
	static final int HAS_INBOUND_CANCELLED_FLAG          =
			0b0000_0000_0000_0000_0000_0000_0010_0000;
	static final int HAS_INBOUND_CLOSED_PREMATURELY_FLAG =
			0b0000_0000_0000_0000_0000_0000_0100_0000;
	static final int HAS_INBOUND_TERMINATED_FLAG         =
			0b0000_0000_0000_0000_0000_0000_1000_0000;

	static final int HAS_OUTBOUND_SUBSCRIBED_FLAG =
			0b0000_0000_0000_0000_0000_0001_0000_0000;
	static final int HAS_OUTBOUND_CANCELLED_FLAG  =
			0b0000_0000_0000_0000_0000_0010_0000_0000;
	static final int HAS_OUTBOUND_TERMINATED_FLAG =
			0b0000_0000_0000_0000_0000_0100_0000_0000;

	/**
	 * Adds a flag which indicate that the first inbound onNext signal has already been
	 * received. Fails if inbound is cancelled.
	 *
	 * @return previous observed state
	 */
	static  long markFirstValueReceived(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundCancelled(state) || hasInboundClosedPrematurely(state)) {
				return state;
			}

			final int nextState = state | HAS_FIRST_VALUE_RECEIVED_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "mfvr", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the inbound has already been subscribed once.
	 * Fails if inbound has already been subscribed once.
	 *
	 * @return previous observed state
	 */
	static  long markInboundSubscribedOnce(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundSubscribedOnce(state)) {
				return state;
			}

			final int nextState = state | HAS_INBOUND_SUBSCRIBED_ONCE_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "miso", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the inbound subscriber has already been set.
	 * Fails if inbound is cancelled.
	 *
	 * @return previous observed state
	 */
	static  long markInboundSubscriberSet(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundCancelled(state) || hasInboundClosedPrematurely(state)) {
				return state;
			}

			final int nextState = state | HAS_INBOUND_SUBSCRIBER_SET_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "miss", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the inbound has already been requested once.
	 * Fails if inbound is cancelled.
	 *
	 * @return previous observed state
	 */
	static  long markInboundRequestedOnce(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundCancelled(state) || hasInboundClosedPrematurely(state)) {
				return state;
			}

			int nextState = state | HAS_INBOUND_REQUESTED_ONCE_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "miro", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the first onNext value has been successfully
	 * delivered. Fails if inbound is cancelled.
	 *
	 * @return previous observed state
	 */
	static  long markFirstValueSent(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundCancelled(state) || hasInboundClosedPrematurely(state)) {
				return state;
			}

			final int nextState = state | HAS_FIRST_VALUE_SENT_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "mfvs", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the inbound has already been terminated with
	 * onComplete or onError. Fails if inbound is cancelled.
	 *
	 * @return previous observed state
	 */
	static  long markInboundTerminated(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundCancelled(state) || hasInboundClosedPrematurely(state)) {
				return state;
			}

			final int nextState = state | HAS_INBOUND_TERMINATED_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "mitd", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the inbound has already been cancelled. Fails if
	 * inbound is cancelled.
	 *
	 * @return previous observed state
	 */
	static  long markInboundCancelled(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundCancelled(state)) {
				return state;
			}

			final int nextState = state | HAS_INBOUND_CANCELLED_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "micd", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds flags which indicate that the inbound has prematurely from the very bottom
	 * of the pipe. Fails if either inbound is cancelled or terminated before.
	 *
	 * @return previous observed state
	 */
	static  long markInboundClosedPrematurely(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundTerminated(state) || hasInboundCancelled(state)) {
				return state;
			}

			final int nextState = state | HAS_INBOUND_CLOSED_PREMATURELY_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "micp", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds flags which indicate that the inbound has cancelled upstream and errored
	 * the outbound downstream. Fails if either inbound is cancelled or outbound is
	 * cancelled.
	 *
	 * Note, this is a rare case needed to add cancellation for inbound and
	 * termination for outbound and can only be occurred during the onNext calling
	 * transformation function which then throws unexpected error.
	 *
	 * @return previous observed state
	 */
	static  long markInboundCancelledAndOutboundTerminated(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasInboundCancelled(state) || hasOutboundCancelled(state)) {
				return state;
			}

			final int nextState =
					state | HAS_INBOUND_CANCELLED_FLAG | HAS_OUTBOUND_TERMINATED_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "icot", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the outbound has received subscription. Fails if
	 * outbound is cancelled.
	 *
	 * @return previous observed state
	 */
	static  long markOutboundSubscribed(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasOutboundCancelled(state)) {
				return state;
			}

			final int nextState = state | HAS_OUTBOUND_SUBSCRIBED_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "mosd", state, nextState);
				}
				return state;
			}
		}
	}


	/**
	 * Adds a flag which indicate that the outbound has already been
	 * terminated with onComplete or onError. Fails if outbound is cancelled or terminated.
	 *
	 * @return previous observed state
	 */
	static  long markOutboundTerminated(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasOutboundCancelled(state) || hasOutboundTerminated(state)) {
				return state;
			}

			final int nextState = state | HAS_OUTBOUND_TERMINATED_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "motd", state, nextState);
				}
				return state;
			}
		}
	}

	/**
	 * Adds a flag which indicate that the outbound has already been cancelled. Fails
	 * if outbound is cancelled or terminated.
	 *
	 * @return previous observed state
	 */
	static  long markOutboundCancelled(AbstractSwitchOnFirstMain instance) {
		for (;;) {
			final int state = instance.state;

			if (hasOutboundTerminated(state) || hasOutboundCancelled(state)) {
				return state;
			}

			final int nextState = state | HAS_OUTBOUND_CANCELLED_FLAG;
			if (AbstractSwitchOnFirstMain.STATE.compareAndSet(instance, state, nextState)) {
				if (instance.logger != null) {
					instance.logger.log(instance.toString(), "mocd", state, nextState);
				}
				return state;
			}
		}
	}

	static boolean hasInboundCancelled(long state) {
		return (state & HAS_INBOUND_CANCELLED_FLAG) == HAS_INBOUND_CANCELLED_FLAG;
	}

	static boolean hasInboundClosedPrematurely(long state) {
		return (state & HAS_INBOUND_CLOSED_PREMATURELY_FLAG) == HAS_INBOUND_CLOSED_PREMATURELY_FLAG;
	}

	static boolean hasInboundTerminated(long state) {
		return (state & HAS_INBOUND_TERMINATED_FLAG) == HAS_INBOUND_TERMINATED_FLAG;
	}

	static boolean hasFirstValueReceived(long state) {
		return (state & HAS_FIRST_VALUE_RECEIVED_FLAG) == HAS_FIRST_VALUE_RECEIVED_FLAG;
	}

	static boolean hasFirstValueSent(long state) {
		return (state & HAS_FIRST_VALUE_SENT_FLAG) == HAS_FIRST_VALUE_SENT_FLAG;
	}

	static boolean hasInboundSubscribedOnce(long state) {
		return (state & HAS_INBOUND_SUBSCRIBED_ONCE_FLAG) == HAS_INBOUND_SUBSCRIBED_ONCE_FLAG;
	}

	static boolean hasInboundSubscriberSet(long state) {
		return (state & HAS_INBOUND_SUBSCRIBER_SET_FLAG) == HAS_INBOUND_SUBSCRIBER_SET_FLAG;
	}

	static boolean hasInboundRequestedOnce(long state) {
		return (state & HAS_INBOUND_REQUESTED_ONCE_FLAG) == HAS_INBOUND_REQUESTED_ONCE_FLAG;
	}

	static boolean hasOutboundSubscribed(long state) {
		return (state & HAS_OUTBOUND_SUBSCRIBED_FLAG) == HAS_OUTBOUND_SUBSCRIBED_FLAG;
	}

	static boolean hasOutboundCancelled(long state) {
		return (state & HAS_OUTBOUND_CANCELLED_FLAG) == HAS_OUTBOUND_CANCELLED_FLAG;
	}

	static boolean hasOutboundTerminated(long state) {
		return (state & HAS_OUTBOUND_TERMINATED_FLAG) == HAS_OUTBOUND_TERMINATED_FLAG;
	}

	static abstract class AbstractSwitchOnFirstMain extends Flux
			implements InnerOperator {

		@Nullable
		final StateLogger                                                      logger;

		final SwitchOnFirstControlSubscriber
		                                                                       outboundSubscriber;
		final BiFunction, Flux, Publisher> transformer;

		Subscription s;

		boolean isInboundRequestedOnce;
		boolean isFirstOnNextReceivedOnce;
		T       firstValue;

		Throwable throwable;
		boolean   done;

		CoreSubscriber inboundSubscriber;

		volatile int state;
		@SuppressWarnings("rawtypes")
		static final AtomicIntegerFieldUpdater STATE =
				AtomicIntegerFieldUpdater.newUpdater(AbstractSwitchOnFirstMain.class, "state");

		@SuppressWarnings("unchecked")
		AbstractSwitchOnFirstMain(CoreSubscriber outboundSubscriber,
				BiFunction, Flux, Publisher> transformer,
				boolean cancelSourceOnComplete,
				@Nullable StateLogger logger) {
			this.outboundSubscriber = outboundSubscriber instanceof Fuseable.ConditionalSubscriber ?
					new SwitchOnFirstConditionalControlSubscriber<>(this,
							(Fuseable.ConditionalSubscriber) outboundSubscriber,
							cancelSourceOnComplete) :
					new SwitchOnFirstControlSubscriber<>(this, outboundSubscriber,
							cancelSourceOnComplete);
			this.transformer = transformer;
			this.logger = logger;
		}

		@Override
		@Nullable
		public final Object scanUnsafe(Attr key) {
			if (key == Attr.CANCELLED) return hasInboundCancelled(this.state) || hasInboundClosedPrematurely(this.state);
			if (key == Attr.TERMINATED) return hasInboundTerminated(this.state) || hasInboundClosedPrematurely(this.state);
			if (key == Attr.RUN_STYLE) return Attr.RunStyle.SYNC;

			return InnerOperator.super.scanUnsafe(key);
		}

		@Override
		public final CoreSubscriber actual() {
			return this.outboundSubscriber;
		}

		@Override
		public final void onSubscribe(Subscription s) {
			if (Operators.validate(this.s, s)) {
				this.s = s;
				this.outboundSubscriber.sendSubscription();
				if (!hasInboundCancelled(this.state)) {
					s.request(1);
				}
			}
		}

		@Override
		public final void onNext(T t) {
			if (this.done) {
				Operators.onNextDropped(t, currentContext());
				return;
			}

			if (!this.isFirstOnNextReceivedOnce) {
				this.isFirstOnNextReceivedOnce = true;
				this.firstValue = t;

				long previousState = markFirstValueReceived(this);
				if (hasInboundCancelled(previousState) || hasInboundClosedPrematurely(previousState)) {
					this.firstValue = null;
					Operators.onDiscard(t, this.outboundSubscriber.currentContext());
					return;
				}

				Publisher outboundPublisher;
				final SwitchOnFirstControlSubscriber o = this.outboundSubscriber;

				try {
					final Signal signal = Signal.next(t, o.currentContext());
					outboundPublisher = Objects.requireNonNull(this.transformer.apply(signal, this), "The transformer returned a null value");
				}
				catch (Throwable e) {
					this.done = true;

					previousState = markInboundCancelledAndOutboundTerminated(this);
					if (hasInboundCancelled(previousState) || hasOutboundCancelled(previousState)) {
						Operators.onErrorDropped(e, o.currentContext());
						return;
					}

					this.firstValue = null;
					Operators.onDiscard(t, o.currentContext());

					o.errorDirectly(Operators.onOperatorError(this.s, e, t, o.currentContext()));
					return;
				}

				outboundPublisher = Operators.toFluxOrMono(outboundPublisher);
				outboundPublisher.subscribe(o);
				return;
			}

			synchronized (this) {
				this.inboundSubscriber.onNext(t);
			}
		}

		@Override
		public final void onError(Throwable t) {
			if (this.done) {
				Operators.onErrorDropped(t, this.outboundSubscriber.currentContext());
				return;
			}

			this.done = true;
			this.throwable = t;

			final long previousState = markInboundTerminated(this);
			if (hasInboundCancelled(previousState) || hasInboundTerminated(previousState) || hasInboundClosedPrematurely(previousState)) {
				Operators.onErrorDropped(t, this.outboundSubscriber.currentContext());
				return;
			}

			if (hasFirstValueSent(previousState)) {
				synchronized (this) {
					this.inboundSubscriber.onError(t);
				}
				return;
			}

			if (!hasFirstValueReceived(previousState)) {
				Publisher result;
				final CoreSubscriber o = this.outboundSubscriber;
				try {
					final Signal signal = Signal.error(t, o.currentContext());
					result = Objects.requireNonNull(this.transformer.apply(signal, this), "The transformer returned a null value");
				}
				catch (Throwable e) {
					o.onError(Exceptions.addSuppressed(t, e));
					return;
				}

				result = Operators.toFluxOrMono(result);
				result.subscribe(o);
			}
		}

		@Override
		public final void onComplete() {
			if (this.done) {
				return;
			}

			this.done = true;

			final long previousState = markInboundTerminated(this);
			if (hasInboundCancelled(previousState) || hasInboundTerminated(previousState) || hasInboundClosedPrematurely(previousState)) {
				return;
			}

			if (hasFirstValueSent(previousState)) {
				synchronized (this) {
					this.inboundSubscriber.onComplete();
				}
				return;
			}

			if (!hasFirstValueReceived(previousState)) {
				Publisher result;
				final CoreSubscriber o = this.outboundSubscriber;

				try {
					final Signal signal = Signal.complete(o.currentContext());
					result = Objects.requireNonNull(this.transformer.apply(signal, this), "The transformer returned a null value");
				}
				catch (Throwable e) {
					o.onError(e);
					return;
				}

				result = Operators.toFluxOrMono(result);
				result.subscribe(o);
			}
		}

		@Override
		public final void cancel() {
			long previousState = markInboundCancelled(this);
			if (hasInboundCancelled(previousState) || hasInboundTerminated(previousState) || hasInboundClosedPrematurely(previousState)) {
				return;
			}

			this.s.cancel();

			if (hasFirstValueReceived(previousState) && !hasInboundRequestedOnce(previousState)) {
				final T f = this.firstValue;
				this.firstValue = null;
				Operators.onDiscard(f, currentContext());
			}
		}

		final void cancelAndError() {
			long previousState = markInboundClosedPrematurely(this);
			if (hasInboundCancelled(previousState) || hasInboundTerminated(previousState)) {
				return;
			}

			this.s.cancel();

			if (hasFirstValueReceived(previousState) && !hasFirstValueSent(previousState)) {
				if (!hasInboundRequestedOnce(previousState)) {
					final T f = this.firstValue;
					this.firstValue = null;
					Operators.onDiscard(f, currentContext());

					if (hasInboundSubscriberSet(previousState)) {
						this.inboundSubscriber.onError(new CancellationException(
								"FluxSwitchOnFirst has already been cancelled"));
					}
				}
				return;
			}

			if (hasInboundSubscriberSet(previousState)) {
				synchronized (this) {
					this.inboundSubscriber.onError(new CancellationException("FluxSwitchOnFirst has already been cancelled"));
				}
			}
		}

		@Override
		public final void request(long n) {
			if (Operators.validate(n)) {
				// This is a sanity check to avoid extra volatile read in the request
				// context
				if (!this.isInboundRequestedOnce) {
					this.isInboundRequestedOnce = true;

					if (this.isFirstOnNextReceivedOnce) {
						final long previousState = markInboundRequestedOnce(this);
						if (hasInboundCancelled(previousState) || hasInboundClosedPrematurely(previousState)) {
							return;
						}

						final T first = this.firstValue;
						this.firstValue = null;

						final boolean wasDelivered = sendFirst(first);
						if (wasDelivered) {
							if (n != Long.MAX_VALUE) {
								if (--n > 0) {
									this.s.request(n);
								}
								return;
							}
						}
					}
				}

				this.s.request(n);
			}
		}

		@Override
		public final void subscribe(CoreSubscriber inboundSubscriber) {
			long previousState = markInboundSubscribedOnce(this);
			if (hasInboundSubscribedOnce(previousState)) {
				Operators.error(inboundSubscriber, new IllegalStateException("FluxSwitchOnFirst allows only one Subscriber"));
				return;
			}

			if (hasInboundClosedPrematurely(previousState)) {
				Operators.error(inboundSubscriber, new CancellationException("FluxSwitchOnFirst has already been cancelled"));
				return;
			}

			if (!hasFirstValueReceived(previousState)) {
				final Throwable t = this.throwable;
				if (t != null) {
					Operators.error(inboundSubscriber, t);
				}
				else {
					Operators.complete(inboundSubscriber);
				}
				return;
			}

			this.inboundSubscriber = convert(inboundSubscriber);

			inboundSubscriber.onSubscribe(this);

			previousState = markInboundSubscriberSet(this);
			if (hasInboundClosedPrematurely(previousState)
					&& (!hasInboundRequestedOnce(previousState) || hasFirstValueSent(previousState))
					&& !hasInboundCancelled(previousState)) {
				inboundSubscriber.onError(new CancellationException("FluxSwitchOnFirst has already been cancelled"));
			}
		}

		abstract CoreSubscriber convert(CoreSubscriber inboundSubscriber);

		final boolean sendFirst(T firstValue) {
			final CoreSubscriber a = this.inboundSubscriber;

			final boolean sent = tryDirectSend(a, firstValue);

			final long previousState = markFirstValueSent(this);
			if (hasInboundCancelled(previousState)) {
				return sent;
			}

			if (hasInboundClosedPrematurely(previousState)) {
				a.onError(new CancellationException("FluxSwitchOnFirst has already been cancelled"));
				return sent;
			}

			if (hasInboundTerminated(previousState)) {
				Throwable t = this.throwable;
				if (t != null) {
					a.onError(t);
				}
				else {
					a.onComplete();
				}
			}

			return sent;
		}

		abstract boolean tryDirectSend(CoreSubscriber actual, T value);

	}

	static final class SwitchOnFirstMain extends AbstractSwitchOnFirstMain {


		SwitchOnFirstMain(CoreSubscriber outer,
				BiFunction, Flux, Publisher> transformer,
				boolean cancelSourceOnComplete) {
			super(outer, transformer, cancelSourceOnComplete, null);
		}

		SwitchOnFirstMain(CoreSubscriber outer,
				BiFunction, Flux, Publisher> transformer,
				boolean cancelSourceOnComplete,
				@Nullable StateLogger logger) {
			super(outer, transformer, cancelSourceOnComplete, logger);
		}

		@Override
		CoreSubscriber convert(CoreSubscriber inboundSubscriber) {
			return inboundSubscriber;
		}

		@Override
		boolean tryDirectSend(CoreSubscriber actual, T t) {
			actual.onNext(t);
			return true;
		}
	}

	static final class SwitchOnFirstConditionalMain
			extends AbstractSwitchOnFirstMain
			implements Fuseable.ConditionalSubscriber {

		SwitchOnFirstConditionalMain(Fuseable.ConditionalSubscriber outer,
				BiFunction, Flux, Publisher> transformer,
				boolean cancelSourceOnComplete) {
			super(outer, transformer, cancelSourceOnComplete, null);
		}

		SwitchOnFirstConditionalMain(Fuseable.ConditionalSubscriber outer,
				BiFunction, Flux, Publisher> transformer,
				boolean cancelSourceOnComplete,
				@Nullable StateLogger logger) {
			super(outer, transformer, cancelSourceOnComplete, logger);
		}

		@Override
		CoreSubscriber convert(CoreSubscriber inboundSubscriber) {
			return Operators.toConditionalSubscriber(inboundSubscriber);
		}

		@Override
		@SuppressWarnings("unchecked")
		public boolean tryOnNext(T t) {
			if (this.done) {
				Operators.onNextDropped(t, currentContext());
				return false;
			}

			if (!this.isFirstOnNextReceivedOnce) {
				this.isFirstOnNextReceivedOnce = true;
				this.firstValue = t;

				long previousState = markFirstValueReceived(this);
				if (hasInboundCancelled(previousState)) {
					this.firstValue = null;
					Operators.onDiscard(t, this.outboundSubscriber.currentContext());
					return true;
				}

				Publisher result;
				final SwitchOnFirstControlSubscriber o = this.outboundSubscriber;

				try {
					final Signal signal = Signal.next(t, o.currentContext());
					result = Objects.requireNonNull(this.transformer.apply(signal, this),
							"The transformer returned a null value");
				}
				catch (Throwable e) {
					this.done = true;

					previousState = markInboundCancelledAndOutboundTerminated(this);
					if (hasInboundCancelled(previousState) || hasOutboundCancelled(previousState)) {
						Operators.onErrorDropped(e, o.currentContext());
						return true;
					}

					this.firstValue = null;
					Operators.onDiscard(t, o.currentContext());

					o.errorDirectly(Operators.onOperatorError(this.s, e, t, o.currentContext()));
					return true;
				}

				result = Operators.toFluxOrMono(result);
				result.subscribe(o);
				return true;
			}

			synchronized (this) {
				return ((Fuseable.ConditionalSubscriber) this.inboundSubscriber).tryOnNext(t);
			}
		}

		@Override
		@SuppressWarnings("unchecked")
		boolean tryDirectSend(CoreSubscriber actual, T t) {
			return ((Fuseable.ConditionalSubscriber) actual).tryOnNext(t);
		}
	}

	static class SwitchOnFirstControlSubscriber extends Operators.DeferredSubscription
			implements InnerOperator, CoreSubscriber {

		final AbstractSwitchOnFirstMain parent;
		final CoreSubscriber       delegate;
		final boolean                         cancelSourceOnComplete;

		boolean done;

		SwitchOnFirstControlSubscriber(AbstractSwitchOnFirstMain parent,
				CoreSubscriber delegate,
				boolean cancelSourceOnComplete) {
			this.parent = parent;
			this.delegate = delegate;
			this.cancelSourceOnComplete = cancelSourceOnComplete;
		}

		final void sendSubscription() {
			delegate.onSubscribe(this);
		}

		@Override
		public final void onSubscribe(Subscription s) {
			if (set(s)) {
				long previousState = markOutboundSubscribed(this.parent);

				if (hasOutboundCancelled(previousState)) {
					s.cancel();
				}
			}
		}

		@Override
		public final CoreSubscriber actual() {
			return this.delegate;
		}

		@Override
		public final void onNext(T t) {
			if (this.done) {
				Operators.onNextDropped(t, currentContext());
				return;
			}

			this.delegate.onNext(t);
		}

		@Override
		public final void onError(Throwable throwable) {
			if (this.done) {
				Operators.onErrorDropped(throwable, currentContext());
				return;
			}

			this.done = true;

			final AbstractSwitchOnFirstMain parent = this.parent;
			long previousState = markOutboundTerminated(parent);

			if (hasOutboundCancelled(previousState) || hasOutboundTerminated(previousState)) {
				Operators.onErrorDropped(throwable, this.delegate.currentContext());
				return;
			}

			if (!hasInboundCancelled(previousState) && !hasInboundTerminated(previousState)) {
				parent.cancelAndError();
			}

			this.delegate.onError(throwable);
		}

		final void errorDirectly(Throwable t) {
			this.done = true;

			this.delegate.onError(t);
		}

		@Override
		public final void onComplete() {
			if (this.done) {
				return;
			}

			this.done = true;

			final AbstractSwitchOnFirstMain parent = this.parent;
			long previousState = markOutboundTerminated(parent);

			if (cancelSourceOnComplete && !hasInboundCancelled(previousState) && !hasInboundTerminated(previousState)) {
				parent.cancelAndError();
			}

			this.delegate.onComplete();
		}

		@Override
		public final void cancel() {
			Operators.DeferredSubscription.REQUESTED.lazySet(this, STATE_CANCELLED);

			final long previousState = markOutboundCancelled(this.parent);
			if (hasOutboundCancelled(previousState) || hasOutboundTerminated(previousState)) {
				return;
			}

			final boolean shouldCancelInbound =
					!hasInboundTerminated(previousState) && !hasInboundCancelled(previousState);

			if (!hasOutboundSubscribed(previousState)) {
				if (shouldCancelInbound) {
					this.parent.cancel();
				}
				return;
			}

			this.s.cancel();

			if (shouldCancelInbound) {
				this.parent.cancelAndError();
			}
		}

		@Override
		public final Object scanUnsafe(Attr key) {
			if (key == Attr.PARENT) return parent;
			if (key == Attr.ACTUAL) return delegate;
			if (key == Attr.RUN_STYLE) return Attr.RunStyle.SYNC;
			if (key == Attr.CANCELLED) return hasOutboundCancelled(this.parent.state);
			if (key == Attr.TERMINATED) return hasOutboundTerminated(this.parent.state);

			return InnerOperator.super.scanUnsafe(key);
		}
	}

	static final class SwitchOnFirstConditionalControlSubscriber
			extends SwitchOnFirstControlSubscriber
			implements InnerOperator, Fuseable.ConditionalSubscriber {

		final Fuseable.ConditionalSubscriber delegate;

		SwitchOnFirstConditionalControlSubscriber(AbstractSwitchOnFirstMain parent,
				Fuseable.ConditionalSubscriber delegate,
				boolean cancelSourceOnComplete) {
			super(parent, delegate, cancelSourceOnComplete);
			this.delegate = delegate;
		}

		@Override
		public boolean tryOnNext(T t) {
			if (this.done) {
				Operators.onNextDropped(t, currentContext());
				return true;
			}

			return this.delegate.tryOnNext(t);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy