/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.Collection;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

import org.reactivestreams.Subscriber;
import reactor.core.CoreSubscriber;
import reactor.core.Fuseable;
import reactor.util.annotation.Nullable;
import reactor.util.function.Tuple2;

/**
 * Emits the contents of an Iterable source.
 *
 * @param <T> the value type
 *
 * @see Reactive-Streams-Commons
 */
final class FluxIterable<T> extends Flux<T> implements Fuseable, SourceProducer<T> {

	final Iterable<? extends T> iterable;
	private final Runnable      onClose;

	FluxIterable(Iterable<? extends T> iterable, Runnable onClose) {
		this.iterable = Objects.requireNonNull(iterable, "iterable");
		this.onClose = onClose;
	}

	FluxIterable(Iterable<? extends T> iterable) {
		this(iterable, null);
	}

	@Override
	public void subscribe(CoreSubscriber<? super T> actual) {
		Iterator<? extends T> it;

		try {
			it = iterable.iterator();
		}
		catch (Throwable e) {
			Operators.error(actual, Operators.onOperatorError(e, actual.currentContext()));
			return;
		}

		subscribe(actual, it, onClose);
	}

	@Override
	public Object scanUnsafe(Attr key) {
		if (key == Attr.BUFFERED) {
			if (iterable instanceof Collection) return ((Collection) iterable).size();
			if (iterable instanceof Tuple2) return ((Tuple2) iterable).size();
		}
		return null;
	}

	/**
	 * Common method to take an Iterator as a source of values.
	 *
	 * @param s the subscriber to feed this iterator to
	 * @param it the iterator to use as a source of values
	 */
	@SuppressWarnings("unchecked")
	static <T> void subscribe(CoreSubscriber<? super T> s, Iterator<? extends T> it) {
		subscribe(s, it, null);
	}

	/**
	 * Common method to take an Iterator as a source of values.
	 *
	 * @param s the subscriber to feed this iterator to
	 * @param it the iterator to use as a source of values
	 * @param onClose close handler to call once we're done with the iterator (provided it
	 * is not null, this includes when the iteration errors or complete or the subscriber
	 * is cancelled). Null to ignore.
	 */
	@SuppressWarnings("unchecked")
	static <T> void subscribe(CoreSubscriber<? super T> s, Iterator<? extends T> it,
			@Nullable Runnable onClose) {
		//noinspection ConstantConditions
		if (it == null) {
			Operators.error(s, new NullPointerException("The iterator is null"));
			return;
		}

		boolean b;

		try {
			b = it.hasNext();
		}
		catch (Throwable e) {
			Operators.error(s, Operators.onOperatorError(e, s.currentContext()));
			if (onClose != null) {
				try {
					onClose.run();
				}
				catch (Throwable t) {
					Operators.onErrorDropped(t, s.currentContext());
				}
			}
			return;
		}
		if (!b) {
			Operators.complete(s);
			if (onClose != null) {
				try {
					onClose.run();
				}
				catch (Throwable t) {
					Operators.onErrorDropped(t, s.currentContext());
				}
			}
			return;
		}

		if (s instanceof ConditionalSubscriber) {
			s.onSubscribe(new IterableSubscriptionConditional<>((ConditionalSubscriber<? super T>) s,
					it, onClose));
		}
		else {
			s.onSubscribe(new IterableSubscription<>(s, it, onClose));
		}
	}

	static final class IterableSubscription<T>
			implements InnerProducer<T>, SynchronousSubscription<T> {

		final CoreSubscriber<? super T> actual;

		final Iterator<? extends T> iterator;
		final Runnable              onClose;

		volatile boolean cancelled;

		volatile long requested;
		@SuppressWarnings("rawtypes")
		static final AtomicLongFieldUpdater<IterableSubscription> REQUESTED =
				AtomicLongFieldUpdater.newUpdater(IterableSubscription.class,
						"requested");

		int state;

		/**
		 * Indicates that the iterator's hasNext returned true before but the value is not
		 * yet retrieved.
		 */
		static final int STATE_HAS_NEXT_NO_VALUE  = 0;
		/**
		 * Indicates that there is a value available in current.
		 */
		static final int STATE_HAS_NEXT_HAS_VALUE = 1;
		/**
		 * Indicates that there are no more values available.
		 */
		static final int STATE_NO_NEXT            = 2;
		/**
		 * Indicates that the value has been consumed and a new value should be retrieved.
		 */
		static final int STATE_CALL_HAS_NEXT      = 3;

		T current;

		IterableSubscription(CoreSubscriber<? super T> actual,
				Iterator<? extends T> iterator, @Nullable Runnable onClose) {
			this.actual = actual;
			this.iterator = iterator;
			this.onClose = onClose;
		}

		IterableSubscription(CoreSubscriber<? super T> actual,
				Iterator<? extends T> iterator) {
			this(actual, iterator, null);
		}

		@Override
		public void request(long n) {
			if (Operators.validate(n)) {
				if (Operators.addCap(REQUESTED, this, n) == 0) {
					if (n == Long.MAX_VALUE) {
						fastPath();
					}
					else {
						slowPath(n);
					}
				}
			}
		}

		private void onCloseWithDropError() {
			if (onClose != null) {
				try {
					onClose.run();
				}
				catch (Throwable t) {
					Operators.onErrorDropped(t, actual.currentContext());
				}
			}
		}

		void slowPath(long n) {
			final Iterator<? extends T> a = iterator;
			final Subscriber<? super T> s = actual;

			long e = 0L;

			for (; ; ) {

				while (e != n) {
					T t;

					try {
						t = Objects.requireNonNull(a.next(),
								"The iterator returned a null value");
					}
					catch (Throwable ex) {
						s.onError(ex);
						onCloseWithDropError();
						return;
					}

					if (cancelled) {
						return;
					}

					s.onNext(t);

					if (cancelled) {
						return;
					}

					boolean b;

					try {
						b = a.hasNext();
					}
					catch (Throwable ex) {
						s.onError(ex);
						onCloseWithDropError();
						return;
					}

					if (cancelled) {
						return;
					}

					if (!b) {
						s.onComplete();
						onCloseWithDropError();
						return;
					}

					e++;
				}

				n = requested;

				if (n == e) {
					n = REQUESTED.addAndGet(this, -e);
					if (n == 0L) {
						return;
					}
					e = 0L;
				}
			}
		}

		void fastPath() {
			final Iterator<? extends T> a = iterator;
			final Subscriber<? super T> s = actual;

			for (; ; ) {

				if (cancelled) {
					return;
				}

				T t;

				try {
					t = Objects.requireNonNull(a.next(),
							"The iterator returned a null value");
				}
				catch (Exception ex) {
					s.onError(ex);
					onCloseWithDropError();
					return;
				}

				if (cancelled) {
					return;
				}

				s.onNext(t);

				if (cancelled) {
					return;
				}

				boolean b;

				try {
					b = a.hasNext();
				}
				catch (Exception ex) {
					s.onError(ex);
					onCloseWithDropError();
					return;
				}

				if (cancelled) {
					return;
				}

				if (!b) {
					s.onComplete();
					onCloseWithDropError();
					return;
				}
			}
		}

		@Override
		public void cancel() {
			onCloseWithDropError();
			cancelled = true;
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return actual;
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.CANCELLED) return cancelled;
			if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested;
			if (key == Attr.TERMINATED) return state == STATE_NO_NEXT;

			return InnerProducer.super.scanUnsafe(key);
		}

		@Override
		public void clear() {
			state = STATE_NO_NEXT;
		}

		@Override
		public boolean isEmpty() {
			int s = state;
			if (s == STATE_NO_NEXT) {
				return true;
			}
			else if (s == STATE_HAS_NEXT_HAS_VALUE || s == STATE_HAS_NEXT_NO_VALUE) {
				return false;
			}
			else if (iterator.hasNext()) {
				state = STATE_HAS_NEXT_NO_VALUE;
				return false;
			}
			state = STATE_NO_NEXT;
			return true;
		}

		@Override
		@Nullable
		public T poll() {
			if (!isEmpty()) {
				T c;
				if (state == STATE_HAS_NEXT_NO_VALUE) {
					c = iterator.next();
				}
				else {
					c = current;
					current = null;
				}
				state = STATE_CALL_HAS_NEXT;
				if (c == null) {
					onCloseWithDropError();
					throw new NullPointerException("iterator returned a null value");
				}
				return c;
			}
			onCloseWithDropError();
			return null;
		}

		@Override
		public int size() {
			if (state == STATE_NO_NEXT) {
				return 0;
			}
			return 1;
		}
	}

	static final class IterableSubscriptionConditional<T>
			implements InnerProducer<T>, SynchronousSubscription<T> {

		final ConditionalSubscriber<? super T> actual;

		final Iterator<? extends T> iterator;
		final Runnable              onClose;

		volatile boolean cancelled;

		volatile long requested;
		@SuppressWarnings("rawtypes")
		static final AtomicLongFieldUpdater<IterableSubscriptionConditional> REQUESTED =
				AtomicLongFieldUpdater.newUpdater(IterableSubscriptionConditional.class,
						"requested");

		int state;

		/**
		 * Indicates that the iterator's hasNext returned true before but the value is not
		 * yet retrieved.
		 */
		static final int STATE_HAS_NEXT_NO_VALUE  = 0;
		/**
		 * Indicates that there is a value available in current.
		 */
		static final int STATE_HAS_NEXT_HAS_VALUE = 1;
		/**
		 * Indicates that there are no more values available.
		 */
		static final int STATE_NO_NEXT            = 2;
		/**
		 * Indicates that the value has been consumed and a new value should be retrieved.
		 */
		static final int STATE_CALL_HAS_NEXT      = 3;

		T current;

		IterableSubscriptionConditional(ConditionalSubscriber<? super T> actual,
				Iterator<? extends T> iterator, @Nullable Runnable onClose) {
			this.actual = actual;
			this.iterator = iterator;
			this.onClose = onClose;
		}

		IterableSubscriptionConditional(ConditionalSubscriber<? super T> actual,
				Iterator<? extends T> iterator) {
			this(actual, iterator, null);
		}

		@Override
		public void request(long n) {
			if (Operators.validate(n)) {
				if (Operators.addCap(REQUESTED, this, n) == 0) {
					if (n == Long.MAX_VALUE) {
						fastPath();
					}
					else {
						slowPath(n);
					}
				}
			}
		}

		private void onCloseWithDropError() {
			if (onClose != null) {
				try {
					onClose.run();
				}
				catch (Throwable t) {
					Operators.onErrorDropped(t, actual.currentContext());
				}
			}
		}

		void slowPath(long n) {
			final Iterator<? extends T> a = iterator;
			final ConditionalSubscriber<? super T> s = actual;

			long e = 0L;

			for (; ; ) {

				while (e != n) {
					T t;

					try {
						t = Objects.requireNonNull(a.next(),
								"The iterator returned a null value");
					}
					catch (Throwable ex) {
						s.onError(ex);
						onCloseWithDropError();
						return;
					}

					if (cancelled) {
						return;
					}

					boolean consumed = s.tryOnNext(t);

					if (cancelled) {
						return;
					}

					boolean b;

					try {
						b = a.hasNext();
					}
					catch (Throwable ex) {
						s.onError(ex);
						onCloseWithDropError();
						return;
					}

					if (cancelled) {
						return;
					}

					if (!b) {
						s.onComplete();
						onCloseWithDropError();
						return;
					}

					if (consumed) {
						e++;
					}
				}

				n = requested;

				if (n == e) {
					n = REQUESTED.addAndGet(this, -e);
					if (n == 0L) {
						return;
					}
					e = 0L;
				}
			}
		}

		void fastPath() {
			final Iterator<? extends T> a = iterator;
			final ConditionalSubscriber<? super T> s = actual;

			for (; ; ) {

				if (cancelled) {
					return;
				}

				T t;

				try {
					t = Objects.requireNonNull(a.next(),
							"The iterator returned a null value");
				}
				catch (Exception ex) {
					s.onError(ex);
					onCloseWithDropError();
					return;
				}

				if (cancelled) {
					return;
				}

				s.tryOnNext(t);

				if (cancelled) {
					return;
				}

				boolean b;

				try {
					b = a.hasNext();
				}
				catch (Exception ex) {
					s.onError(ex);
					onCloseWithDropError();
					return;
				}

				if (cancelled) {
					return;
				}

				if (!b) {
					s.onComplete();
					onCloseWithDropError();
					return;
				}
			}
		}

		@Override
		public void cancel() {
			onCloseWithDropError();
			cancelled = true;
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return actual;
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.CANCELLED) return cancelled;
			if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested;
			if (key == Attr.TERMINATED) return state == STATE_NO_NEXT;

			return InnerProducer.super.scanUnsafe(key);
		}

		@Override
		public void clear() {
			state = STATE_NO_NEXT;
		}

		@Override
		public boolean isEmpty() {
			int s = state;
			if (s == STATE_NO_NEXT) {
				return true;
			}
			else if (s == STATE_HAS_NEXT_HAS_VALUE || s == STATE_HAS_NEXT_NO_VALUE) {
				return false;
			}
			else if (iterator.hasNext()) {
				state = STATE_HAS_NEXT_NO_VALUE;
				return false;
			}
			state = STATE_NO_NEXT;
			return true;
		}

		@Override
		@Nullable
		public T poll() {
			if (!isEmpty()) {
				T c;
				if (state == STATE_HAS_NEXT_NO_VALUE) {
					c = iterator.next();
				}
				else {
					c = current;
					current = null;
				}
				state = STATE_CALL_HAS_NEXT;
				return c;
			}
			onCloseWithDropError();
			return null;
		}

		@Override
		public int size() {
			if (state == STATE_NO_NEXT) {
				return 0;
			}
			return 1; // no way of knowing without enumerating first
		}
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Supplier;

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.util.annotation.Nullable;
import reactor.util.concurrent.Queues;
import reactor.util.concurrent.WaitStrategy;

/**
 ** An implementation of a RingBuffer backed message-passing Processor implementing publish-subscribe with async event
 * loops.
 *

 *
 *

 *  Created from {@link #share}, the {@link TopicProcessor} will authorize concurrent publishing (multi-producer)
 *  from its receiving side {@link Subscriber#onNext(Object)}.
 *  Additionally, any of the {@link TopicProcessor} will stop the event loop thread if an error occurs.
 *
 *

 * The processor
 * respects the Reactive Streams contract and must not be signalled concurrently on any
 * onXXXX method if {@link #share} has not been used. Each subscriber will be assigned a unique thread that will only
 * stop on
 * terminal event: Complete, Error or Cancel. If Auto-Cancel is enabled, when all
 * subscribers are unregistered, a cancel signal is sent to the upstream Publisher if any.
 * Executor can be customized and will define how many concurrent subscribers are allowed
 * (fixed thread). When a Subscriber requests Long.MAX, there won't be any backpressure
 * applied and the producer will run at risk of being throttled if the subscribers don't
 * catch up. With any other strictly positive demand, a subscriber will stop reading new
 * Next signals (Complete and Error will still be read) as soon as the demand has been
 * fully consumed by the publisher.
 When more than 1 subscriber listens to that
 * processor, they will all receive the exact same events if their respective demand is
 * still strictly positive, very much like a Fan-Out scenario.
 When the backlog has
 * been completely booked and no subscribers is draining the signals, the publisher will
 * start throttling. In effect the smaller the backlog size is defined, the smaller the
 * difference in processing rate between subscribers must remain. Since the sequence for
 * each subscriber will point to various ringBuffer locations, the processor knows when a
 * backlog can't override the previously occupied slot.
 * @param <E> Type of dispatched signal
 * @author Stephane Maldini
 * @author Anatoly Kadyshev
 */
public final class TopicProcessor<E> extends EventLoopProcessor<E>  {

	/**
	 * {@link TopicProcessor} builder that can be used to create new
	 * processors. Instantiate it through the {@link TopicProcessor#builder()} static
	 * method:
	 *

	 * {@code TopicProcessor<String> processor = TopicProcessor.<String>builder().build()}
	 *
	 * @param <T> Type of dispatched signal
	 */
	public final static class Builder<T> {

		String name;
		ExecutorService executor;
		ExecutorService requestTaskExecutor;
		int bufferSize;
		WaitStrategy waitStrategy;
		boolean share;
		boolean autoCancel;
		Supplier<T> signalSupplier;

		Builder() {
			this.bufferSize = Queues.SMALL_BUFFER_SIZE;
			this.autoCancel = true;
			this.share = false;
		}

		/**
		 * Configures name for this builder. Default value is TopicProcessor.
		 * Name is set to default if the provided name is null.
		 * @param name Use a new cached ExecutorService and assign this name to the created threads
		 *             if {@link #executor(ExecutorService)} is not configured.
		 * @return builder with provided name
		 */
		public Builder<T> name(@Nullable String name) {
			if (executor != null)
				throw new IllegalArgumentException("Executor service is configured, name will not be used.");
			this.name = name;
			return this;
		}

		/**
		 * Configures buffer size for this builder. Default value is {@link Queues#SMALL_BUFFER_SIZE}.
		 * @param bufferSize the internal buffer size to hold signals, must be a power of 2.
		 * @return builder with provided buffer size
		 */
		public Builder<T> bufferSize(int bufferSize) {
			if (!Queues.isPowerOfTwo(bufferSize)) {
				throw new IllegalArgumentException("bufferSize must be a power of 2 : " + bufferSize);
			}

			if (bufferSize < 1){
				throw new IllegalArgumentException("bufferSize must be strictly positive, " +
						"was: "+bufferSize);
			}
			this.bufferSize = bufferSize;
			return this;
		}

		/**
		 * Configures wait strategy for this builder. Default value is {@link WaitStrategy#phasedOffLiteLock(long, long, TimeUnit)}.
		 * Wait strategy is set to default if the provided waitStrategy is null.
		 * @param waitStrategy A RingBuffer WaitStrategy to use instead of the default blocking wait strategy.
		 * @return builder with provided wait strategy
		 */
		public Builder<T> waitStrategy(@Nullable WaitStrategy waitStrategy) {
			this.waitStrategy = waitStrategy;
			return this;
		}

		/**
		 * Configures auto-cancel for this builder. Default value is true.
		 * @param autoCancel automatically cancel
		 * @return builder with provided auto-cancel
		 */
		public Builder<T> autoCancel(boolean autoCancel) {
			this.autoCancel = autoCancel;
			return this;
		}

		/**
		 * Configures an {@link ExecutorService} to execute as many event-loop consuming the
		 * ringbuffer as subscribers. Name configured using {@link #name(String)} will be ignored
		 * if executor is set.
		 * @param executor A provided ExecutorService to manage threading infrastructure
		 * @return builder with provided executor
		 */
		public Builder<T> executor(@Nullable ExecutorService executor) {
			this.executor = executor;
			return this;
		}

		/**
		 * Configures an additional {@link ExecutorService} that is used internally
		 * on each subscription.
		 * @param requestTaskExecutor internal request executor
		 * @return builder with provided internal request executor
		 */
		public Builder<T> requestTaskExecutor(@Nullable ExecutorService requestTaskExecutor) {
			this.requestTaskExecutor = requestTaskExecutor;
			return this;
		}

		/**
		 * Configures sharing state for this builder. A shared Processor authorizes
		 * concurrent onNext calls and is suited for multi-threaded publisher that
		 * will fan-in data.
		 * @param share true to support concurrent onNext calls
		 * @return builder with specified sharing
		 */
		public Builder<T> share(boolean share) {
			this.share = share;
			return this;
		}

		/**
		 * Configures a supplier of dispatched signals to preallocate in the ring buffer
		 * @param signalSupplier A supplier of dispatched signals to preallocate
		 * @return builder with provided signal supplier
		 */
		public Builder<T> signalSupplier(@Nullable Supplier<T> signalSupplier) {
			this.signalSupplier = signalSupplier;
			return this;
		}

		/**
		 * Creates a new {@link TopicProcessor} using the properties
		 * of this builder.
		 * @return a fresh processor
		 */
		public TopicProcessor<T>  build() {
			this.name = this.name != null ? this.name : TopicProcessor.class.getSimpleName();
			this.waitStrategy = this.waitStrategy != null ? this.waitStrategy : WaitStrategy.phasedOffLiteLock(200, 100, TimeUnit.MILLISECONDS);
			ThreadFactory threadFactory = this.executor != null ? null : new EventLoopFactory(name, autoCancel);
			ExecutorService requestTaskExecutor = this.requestTaskExecutor != null ? this.requestTaskExecutor : defaultRequestTaskExecutor(defaultName(threadFactory, TopicProcessor.class));
			return new TopicProcessor<>(
					threadFactory,
					executor,
					requestTaskExecutor,
					bufferSize,
					waitStrategy,
					share,
					autoCancel,
					signalSupplier);
		}
	}

	/**
	 * Create a new {@link TopicProcessor} {@link Builder} with default properties.
	 * @return new TopicProcessor builder
	 */
	public static <E> Builder<E> builder()  {
		return new Builder<>();
	}

	/**
	 * Create a new TopicProcessor using {@link Queues#SMALL_BUFFER_SIZE} backlog size,
	 * blockingWait Strategy and auto-cancel.
 A new Cached ThreadExecutorPool will be
	 * implicitly created.
	 * @param <E> Type of processed signals
	 * @return a fresh processor
	 */
	public static <E> TopicProcessor<E> create() {
		return TopicProcessor.<E>builder().build();
	}

	/**
	 * Create a new TopicProcessor using the provided backlog size, with a blockingWait Strategy
	 * and auto-cancellation.
 A new Cached ThreadExecutorPool will be implicitly created and will use the passed name to
	 * qualify the created threads.
	 * @param name Use a new Cached ExecutorService and assign this name to the created
	 * threads
	 * @param bufferSize A Backlog Size to mitigate slow subscribers
	 * @param <E> Type of processed signals
	 * @return the fresh TopicProcessor instance
	 */
	public static <E> TopicProcessor<E> create(String name, int bufferSize) {
		return TopicProcessor.<E>builder().name(name).bufferSize(bufferSize).build();
	}

	/**
	 * Create a new shared TopicProcessor using the passed backlog size, with a blockingWait
	 * Strategy and auto-cancellation.
	 *

	 * A Shared Processor authorizes concurrent onNext calls and is suited for multi-threaded
	 * publisher that will fan-in data.
	 *

	 * A new Cached ThreadExecutorPool will be implicitly created and will use the passed
	 * name to qualify the created threads.
	 * @param name Use a new Cached ExecutorService and assign this name to the created
	 * threads
	 * @param bufferSize A Backlog Size to mitigate slow subscribers
	 * @param <E> Type of processed signals
	 * @return a fresh processor
	 */
	public static <E> TopicProcessor<E> share(String name, int bufferSize) {
		return TopicProcessor.<E>builder().share(true).name(name).bufferSize(bufferSize).build();
	}

	final RingBuffer.Reader barrier;

	final RingBuffer.Sequence minimum;

	TopicProcessor(
			@Nullable ThreadFactory threadFactory,
			@Nullable ExecutorService executor,
			ExecutorService requestTaskExecutor,
			int bufferSize,
			WaitStrategy waitStrategy,
			boolean shared,
			boolean autoCancel,
			@Nullable final Supplier<E> signalSupplier) {
		super(bufferSize, threadFactory, executor, requestTaskExecutor, autoCancel,
				shared, () -> {
			Slot<E> signal = new Slot<>();
			if (signalSupplier != null) {
				signal.value = signalSupplier.get();
			}
			return signal;
		}, waitStrategy);

		this.minimum = RingBuffer.newSequence(-1);
		this.barrier = ringBuffer.newReader();
	}

	@Override
	public void subscribe(final CoreSubscriber<? super E> actual) {
		Objects.requireNonNull(actual, "subscribe");

		if (!alive()) {
			coldSource(ringBuffer, null, error, minimum).subscribe(actual);
			return;
		}

		//create a unique eventProcessor for this subscriber
		final RingBuffer.Sequence pendingRequest = RingBuffer.newSequence(0);
		final TopicInner<E> signalProcessor =
				new TopicInner<>(this, pendingRequest, actual);

		//bind eventProcessor sequence to observe the ringBuffer

		//if only active subscriber, replay missed data
		if (incrementSubscribers()) {

			signalProcessor.sequence.set(minimum.getAsLong());
			ringBuffer.addGatingSequence(signalProcessor.sequence);
			//set eventProcessor sequence to minimum index (replay)
		}
		else {
			//otherwise only listen to new data
			//set eventProcessor sequence to ringbuffer index
			signalProcessor.sequence.set(ringBuffer.getCursor());
			ringBuffer.addGatingSequence(signalProcessor.sequence);

		}

		try {
			//start the subscriber thread
			executor.execute(signalProcessor);

		}
		catch (Throwable t) {
			ringBuffer.removeGatingSequence(signalProcessor.sequence);
			decrementSubscribers();
			if (!alive() && RejectedExecutionException.class.isAssignableFrom(t.getClass())){
				coldSource(ringBuffer, t, error, minimum).subscribe(actual);
			}
			else{
				Operators.error(actual, t);
			}
		}
	}

	@Override
	public Flux<E> drain() {
		return coldSource(ringBuffer, null, error, minimum);
	}

	@Override
	protected void doError(Throwable t) {
		barrier.signal();
		//ringBuffer.markAsTerminated();

	}

	@Override
	protected void doComplete() {
		barrier.signal();
		//ringBuffer.markAsTerminated();
	}

	@Override
	public long getPending() {
		return ringBuffer.getPending();
	}

	@Override
	protected void requestTask(Subscription s) {
		minimum.set(ringBuffer.getCursor());
		ringBuffer.addGatingSequence(minimum);
		requestTaskExecutor.execute(
				createRequestTask(s, this, minimum::set, () ->
								SUBSCRIBER_COUNT.get(TopicProcessor.this) == 0 ?
								minimum.getAsLong() :
						ringBuffer.getMinimumGatingSequence(minimum)));
	}

	@Override
	public void run() {
		if (!alive() && SUBSCRIBER_COUNT.get(TopicProcessor.this) == 0) {
			WaitStrategy.alert();
		}
	}

	/**
	 * Disruptor BatchEventProcessor port that deals with pending demand.
 Convenience
	 * class for handling the batching semantics of consuming entries from a {@link
	 * reactor.core.publisher .rb.disruptor .RingBuffer}.

	 * @param <T> event implementation storing the data for sharing during exchange or
	 * parallel coordination of an event.
	 */
	final static class TopicInner<T>
			implements Runnable, InnerProducer<T> {

		final AtomicBoolean running = new AtomicBoolean(true);

		final RingBuffer.Sequence sequence = RingBuffer.newSequence(RingBuffer.INITIAL_CURSOR_VALUE);

		final TopicProcessor<T> processor;

		final RingBuffer.Sequence pendingRequest;

		final CoreSubscriber<? super T> subscriber;

		final Runnable waiter = new Runnable() {
			@Override
			public void run() {
				if (!running.get() || processor.isTerminated()) {
					WaitStrategy.alert();
				}
			}
		};

		/**
		 * Construct a ringbuffer consumer that will automatically track the progress by
		 * updating its sequence
		 *
		 * @param processor the target processor
		 * @param pendingRequest holder for the number of pending requests
		 * @param subscriber the output Subscriber instance
		 */
		TopicInner(TopicProcessor<T> processor,
		                            RingBuffer.Sequence pendingRequest,
				CoreSubscriber<? super T> subscriber) {
			this.processor = processor;
			this.pendingRequest = pendingRequest;
			this.subscriber = subscriber;
		}

		void halt() {
			running.set(false);
			processor.barrier.alert();
		}

		/**
		 * It is ok to have another thread rerun this method after a halt().
		 */
		@Override
		public void run() {
			try {
				Thread.currentThread()
				      .setContextClassLoader(processor.contextClassLoader);
				subscriber.onSubscribe(this);

				if (!EventLoopProcessor
						.waitRequestOrTerminalEvent(pendingRequest, processor.barrier, running, sequence, waiter)) {
					if(!running.get()){
						return;
					}
					if(processor.terminated == SHUTDOWN) {
						if (processor.ringBuffer.getAsLong() == -1L) {
							if (processor.error != null) {
								subscriber.onError(processor.error);
								return;
							}
							subscriber.onComplete();
							return;
						}
					}
					else if (processor.terminated == FORCED_SHUTDOWN) {
						return;
					}
				}

				Slot<T> event;
				long nextSequence = sequence.getAsLong() + 1L;
				final boolean unbounded = pendingRequest.getAsLong() == Long.MAX_VALUE;

				while (true) {
					try {

						final long availableSequence = processor.barrier.waitFor(nextSequence, waiter);
						while (nextSequence <= availableSequence) {
							event = processor.ringBuffer.get(nextSequence);

								//if bounded and out of capacity
								while (!unbounded && getAndSub(pendingRequest, 1L) ==
												0) {
									//Todo Use WaitStrategy?
									if(!running.get() || processor.isTerminated()){
										WaitStrategy.alert();
									}
									LockSupport.parkNanos(1L);
								}

								//It's an unbounded subscriber or there is enough capacity to process the signal
								subscriber.onNext(event.value);
								nextSequence++;

						}
						sequence.set(availableSequence);

						if (Operators.emptySubscription() !=
								processor.upstreamSubscription) {
							processor.readWait.signalAllWhenBlocking();
						}
					}
					catch (Throwable ex) {
						if(WaitStrategy.isAlert(ex) || Exceptions.isCancel(ex)) {

							if (!running.get()) {
								break;
							}
							else {
								if (processor.terminated == SHUTDOWN) {
									if (processor.error != null) {
										subscriber.onError(processor.error);
										break;
									}
									if (nextSequence > processor.ringBuffer.getAsLong()) {
										subscriber.onComplete();
										break;
									}

									LockSupport.parkNanos(1L);
								}
								else if (processor.terminated == FORCED_SHUTDOWN) {
									break;
								}
								processor.barrier.clearAlert();
							}
						}
						else {
							throw Exceptions.propagate(ex);
						}
					}
				}
			}
			finally {
				processor.ringBuffer.removeGatingSequence(sequence);
				processor.decrementSubscribers();
				running.set(false);
				processor.readWait.signalAllWhenBlocking();
			}
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.PARENT) return processor;
			if (key == Attr.PREFETCH) return Integer.MAX_VALUE;
			if (key == Attr.TERMINATED) return processor.isTerminated();
			if (key == Attr.CANCELLED) return !running.get();
			if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return pendingRequest.getAsLong();
			if (key == Attr.LARGE_BUFFERED) {
				return processor.ringBuffer.getCursor() - sequence.getAsLong();
			}
			if (key == Attr.BUFFERED) {
				long realBuffered = processor.ringBuffer.getCursor() - sequence.getAsLong();
				if (realBuffered <= Integer.MAX_VALUE) {
					return (int) realBuffered;
				}
				return Integer.MIN_VALUE;
			}

			return InnerProducer.super.scanUnsafe(key);
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return subscriber;
		}

		@Override
		public void request(long n) {
			if (!Operators.validate(n) || !running.get()) {
				return;
			}

			addCap(pendingRequest, n);
		}

		@Override
		public void cancel() {
			halt();
		}
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.util.function;

import java.util.Objects;
import java.util.function.Function;

import reactor.util.annotation.NonNull;
import reactor.util.annotation.Nullable;

/**
 * A tuple that holds four non-null values
 *
 * @param <T1> The type of the first non-null value held by this tuple
 * @param <T2> The type of the second non-null value held by this tuple
 * @param <T3> The type of the third non-null value held by this tuple
 * @param <T4> The type of the fourth non-null value held by this tuple
 * @author Jon Brisbin
 * @author Stephane Maldini
 */
public class Tuple4<T1, T2, T3, T4> extends Tuple3<T1, T2, T3> {

	private static final long serialVersionUID = -4898704078143033129L;

	@NonNull final T4 t4;

	Tuple4(T1 t1, T2 t2, T3 t3, T4 t4) {
		super( t1, t2, t3);
		this.t4 = Objects.requireNonNull(t4, "t4");
	}

	/**
	 * Type-safe way to get the fourth object of this {@link Tuples}.
	 *
	 * @return The fourth object
	 */
	public T4 getT4() {
		return t4;
	}

	/**
	 * Map the 1st part (T1) of this {@link Tuple4} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T1 part
	 * @param <R> the new type for the T1 part
	 * @return a new {@link Tuple4} with a different T1 value
	 */
	public <R> Tuple4<R, T2, T3, T4> mapT1(Function<T1, R> mapper) {
		return new Tuple4<>(mapper.apply(t1), t2, t3, t4);
	}

	/**
	 * Map the 2nd part (T2) of this {@link Tuple4} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T2 part
	 * @param <R> the new type for the T2 part
	 * @return a new {@link Tuple4} with a different T2 value
	 */
	public <R> Tuple4<T1, R, T3, T4> mapT2(Function<T2, R> mapper) {
		return new Tuple4<>(t1, mapper.apply(t2), t3, t4);
	}

	/**
	 * Map the 3rd part (T3) of this {@link Tuple4} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T3 part
	 * @param <R> the new type for the T3 part
	 * @return a new {@link Tuple4} with a different T3 value
	 */
	public <R> Tuple4<T1, T2, R, T4> mapT3(Function<T3, R> mapper) {
		return new Tuple4<>(t1, t2, mapper.apply(t3), t4);
	}

	/**
	 * Map the 4th part (T4) of this {@link Tuple4} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T4 part
	 * @param <R> the new type for the T4 part
	 * @return a new {@link Tuple4} with a different T4 value
	 */
	public <R> Tuple4<T1, T2, T3, R> mapT4(Function<T4, R> mapper) {
		return new Tuple4<>(t1, t2, t3, mapper.apply(t4));
	}

	@Nullable
	@Override
	public Object get(int index) {
		switch (index) {
			case 0:
				return t1;
			case 1:
				return t2;
			case 2:
				return t3;
			case 3:
				return t4;
			default:
				return null;
		}
	}

	@Override
	public Object[] toArray() {
		return new Object[]{t1, t2, t3, t4};
	}

	@Override
	public boolean equals(@Nullable Object o) {
		if (this == o) return true;
		if (!(o instanceof Tuple4)) return false;
		if (!super.equals(o)) return false;

		@SuppressWarnings("rawtypes")
        Tuple4 tuple4 = (Tuple4) o;

		return t4.equals(tuple4.t4);

	}

	@Override
	public int hashCode() {
		int result = super.hashCode();
		result = 31 * result + t4.hashCode();
		return result;
	}

	@Override
	public int size() {
		return 4;
	}
}


/*
 * Copyright (c) 2011-2018 Pivotal Software Inc, 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.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Supplier;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.core.Scannable;
import reactor.util.annotation.Nullable;
import reactor.util.concurrent.Queues;

/**
 * Merges the provided sources {@link org.reactivestreams.Publisher}, assuming a total order
 * of the values, by picking the smallest available value from each publisher, resulting in
 * a single totally ordered {@link Flux} sequence. This operator considers its primary
 * parent to be the first of the sources, for the purpose of {@link reactor.core.Scannable.Attr#PARENT}.
 *
 * @param <T> the value type
 * @author David Karnok
 * @author Simon Baslé
 */
//source: https://akarnokd.blogspot.fr/2017/09/java-9-flow-api-ordered-merge.html
final class FluxMergeOrdered<T> extends Flux<T> implements SourceProducer<T> {

	final int                      prefetch;
	final Supplier<Queue<T>>       queueSupplier;
	final Comparator<? super T>    valueComparator;
	final Publisher<? extends T>[] sources;

	@SafeVarargs
	FluxMergeOrdered(int prefetch,
			Supplier<Queue<T>> queueSupplier,
			Comparator<? super T> valueComparator,
			Publisher<? extends T>... sources) {
		if (prefetch <= 0) {
			throw new IllegalArgumentException("prefetch > 0 required but it was " + prefetch);
		}
		this.sources = Objects.requireNonNull(sources, "sources must be non-null");
		this.prefetch = prefetch;
		this.queueSupplier = queueSupplier;
		this.valueComparator = valueComparator;
	}

	/**
	 * Returns a new instance which has the additional source to be merged together with
	 * the current array of sources. The provided {@link Comparator} is tested for equality
	 * with the current comparator, and if different is combined by {@link Comparator#thenComparing(Comparator)}.
	 * <p>
	 * This operation doesn't change the current {@link FluxMergeOrdered} instance.
	 *
	 * @param source the new source to merge with the others
	 * @return the new {@link FluxMergeOrdered} instance
	 */
	FluxMergeOrdered<T> mergeAdditionalSource(Publisher<? extends T> source,
			Comparator<? super T> otherComparator) {
		int n = sources.length;
		@SuppressWarnings("unchecked")
		Publisher<? extends T>[] newArray = new Publisher[n + 1];
		System.arraycopy(sources, 0, newArray, 0, n);
		newArray[n] = source;

		if (!valueComparator.equals(otherComparator)) {
			@SuppressWarnings("unchecked")
			Comparator<T> currentComparator = (Comparator<T>) this.valueComparator;
			final Comparator<T> newComparator = currentComparator.thenComparing(otherComparator);
			return new FluxMergeOrdered<>(prefetch, queueSupplier, newComparator, newArray);
		}
		return new FluxMergeOrdered<>(prefetch, queueSupplier, valueComparator, newArray);
	}

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

	@Override
	@Nullable
	public Object scanUnsafe(Attr key) {
		if (key == Attr.PARENT) return sources.length > 0 ? sources[0] : null;
		if (key == Attr.PREFETCH) return prefetch;
		if (key == Attr.DELAY_ERROR) return true;

		return null;
	}

	@Override
	public void subscribe(CoreSubscriber<? super T> actual) {
		MergeOrderedMainProducer<T> main = new MergeOrderedMainProducer<>(actual, valueComparator, prefetch, sources.length);
		actual.onSubscribe(main);
		main.subscribe(sources);
	}

	static final class MergeOrderedMainProducer<T> implements InnerProducer<T> {

		static final Object DONE = new Object();

		final CoreSubscriber<? super T> actual;
		final MergeOrderedInnerSubscriber<T>[] subscribers;
		final Comparator<? super T> comparator;
		final Object[] values;

		volatile Throwable error;
		static final AtomicReferenceFieldUpdater<MergeOrderedMainProducer, Throwable> ERROR =
				AtomicReferenceFieldUpdater.newUpdater(MergeOrderedMainProducer.class, Throwable.class, "error");

		volatile int cancelled;
		static final AtomicIntegerFieldUpdater<MergeOrderedMainProducer> CANCELLED =
				AtomicIntegerFieldUpdater.newUpdater(MergeOrderedMainProducer.class, "cancelled");

		volatile long requested;
		static final AtomicLongFieldUpdater<MergeOrderedMainProducer> REQUESTED =
				AtomicLongFieldUpdater.newUpdater(MergeOrderedMainProducer.class, "requested");

		volatile long emitted;
		static final AtomicLongFieldUpdater<MergeOrderedMainProducer> EMITTED =
				AtomicLongFieldUpdater.newUpdater(MergeOrderedMainProducer.class, "emitted");

		volatile int wip;
		static final AtomicIntegerFieldUpdater<MergeOrderedMainProducer> WIP =
				AtomicIntegerFieldUpdater.newUpdater(MergeOrderedMainProducer.class, "wip");

		MergeOrderedMainProducer(CoreSubscriber<? super T> actual,
				Comparator<? super T> comparator, int prefetch, int n) {
			this.actual = actual;
			this.comparator = comparator;

			@SuppressWarnings("unchecked")
			MergeOrderedInnerSubscriber<T>[] mergeOrderedInnerSub =
					new MergeOrderedInnerSubscriber[n];
			this.subscribers = mergeOrderedInnerSub;

			for (int i = 0; i < n; i++) {
				this.subscribers[i] = new MergeOrderedInnerSubscriber<>(this, prefetch);
			}
			this.values = new Object[n];
		}

		void subscribe(Publisher<? extends T>[] sources) {
			if (sources.length != subscribers.length) {
				throw new IllegalArgumentException("must subscribe with " + subscribers.length + " sources");
			}
			for (int i = 0; i < sources.length; i++) {
				Objects.requireNonNull(sources[i], "subscribed with a null source: sources[" + i + "]")
				       .subscribe(subscribers[i]);
			}
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return this.actual;
		}

		@Override
		public void request(long n) {
			Operators.addCap(REQUESTED, this, n);
			drain();
		}

		@Override
		public void cancel() {
			if (CANCELLED.compareAndSet(this, 0, 1)) {
				for (MergeOrderedInnerSubscriber<T> subscriber : subscribers) {
					subscriber.cancel();
				}

				if (WIP.getAndIncrement(this) == 0) {
					Arrays.fill(values, null);
					for (MergeOrderedInnerSubscriber<T> subscriber : subscribers) {
						subscriber.queue.clear();
					}
				}
			}
		}

		void onInnerError(MergeOrderedInnerSubscriber<T> inner, Throwable ex) {
			Exceptions.addThrowable(ERROR, this, ex);
			inner.done = true;
			drain();
		}

		void drain() {
			if (WIP.getAndIncrement(this) != 0) {
				return;
			}

			int missed = 1;
			CoreSubscriber<? super T> actual = this.actual;
			Comparator<? super T> comparator = this.comparator;

			MergeOrderedInnerSubscriber<T>[] subscribers = this.subscribers;
			int n = subscribers.length;

			Object[] values = this.values;

			long e = emitted;

			for (;;) {
				long r = requested;

				for (;;) {
					if (cancelled != 0) {
						Arrays.fill(values, null);

						for (MergeOrderedInnerSubscriber<T> inner : subscribers) {
							inner.queue.clear();
						}
						return;
					}

					int done = 0;
					int nonEmpty = 0;
					for (int i = 0; i < n; i++) {
						Object o = values[i];
						if (o == DONE) {
							done++;
							nonEmpty++;
						}
						else if (o == null) {
							boolean innerDone = subscribers[i].done;
							o = subscribers[i].queue.poll();
							if (o != null) {
								values[i] = o;
								nonEmpty++;
							}
							else if (innerDone) {
								values[i] = DONE;
								done++;
								nonEmpty++;
							}
						}
						else {
							nonEmpty++;
						}
					}

					if (done == n) {
						Throwable ex = error;
						if (ex == null) {
							actual.onComplete();
						} else {
							actual.onError(ex);
						}
						return;
					}

					if (nonEmpty != n || e >= r) {
						break;
					}

					T min = null;
					int minIndex = -1;

					int i = 0;
					for (Object o : values) {
						if (o != DONE) {
							boolean smaller;
							try {
								@SuppressWarnings("unchecked")
								T t = (T) o;
								smaller = min == null || comparator.compare(min, t) > 0;
							} catch (Throwable ex) {
								Exceptions.addThrowable(ERROR, this, ex);
								cancel();
								actual.onError(Exceptions.terminate(ERROR, this));
								return;
							}
							if (smaller) {
								@SuppressWarnings("unchecked")
								T t = (T) o;
								min = t;
								minIndex = i;
							}
						}
						i++;
					}

					values[minIndex] = null;

					actual.onNext(min);

					e++;
					subscribers[minIndex].request(1);
				}

				this.emitted = e;
				missed = WIP.addAndGet(this, -missed);
				if (missed == 0) {
					break;
				}
			}
		}

		@Override
		public Object scanUnsafe(Attr key) {
			if (key == Attr.ACTUAL) return actual;
			if (key == Attr.CANCELLED) return this.cancelled > 0;
			if (key == Attr.ERROR) return this.error;
			if (key == Attr.DELAY_ERROR) return true;
			if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return requested - emitted;

			return null;
		}
	}

	static final class MergeOrderedInnerSubscriber<T> implements InnerOperator<T, T> {

		final MergeOrderedMainProducer<T> parent;
		final int prefetch;
		final int limit;
		final Queue<T> queue;

		int consumed;

		volatile boolean done;

		volatile Subscription s;
		AtomicReferenceFieldUpdater<MergeOrderedInnerSubscriber, Subscription> S =
				AtomicReferenceFieldUpdater.newUpdater(MergeOrderedInnerSubscriber.class, Subscription.class, "s");

		MergeOrderedInnerSubscriber(MergeOrderedMainProducer<T> parent,
				int prefetch) {
			this.parent = parent;
			this.prefetch = prefetch;
			this.limit = prefetch - (prefetch >> 2);
			this.queue = Queues.<T>small().get();
		}

		@Override
		public void onSubscribe(Subscription s) {
			if (Operators.setOnce(S, this, s)) {
				s.request(prefetch);
			}
		}

		@Override
		public void onNext(T item) {
			queue.offer(item);
			parent.drain();
		}

		@Override
		public void onError(Throwable throwable) {
			parent.onInnerError(this, throwable);
		}

		@Override
		public void onComplete() {
			done = true;
			parent.drain();
		}

		/**
		 * @param n is ignored and considered to be 1
		 */
		@Override
		public void request(long n) {
			int c = consumed + 1;
			if (c == limit) {
				consumed = 0;
				Subscription sub = s;
				if (sub != this) {
					sub.request(c);
				}
			}
			else {
				consumed = c;
			}
		}

		@Override
		public void cancel() {
			Subscription sub  = S.getAndSet(this, this);
			if (sub != null && sub != this) {
				sub.cancel();
			}
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return parent.actual; //TODO check
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key){
			if (key == Attr.ACTUAL) return parent; //TODO does that check out?
			if (key == Attr.PARENT) return s;
			if (key == Attr.PREFETCH) return prefetch;
			if (key == Attr.TERMINATED) return done;
			if (key == Attr.BUFFERED) return queue.size();

			return null;
		}
	}
}


/*
 * Copyright (c) 2011-2018 Pivotal Software Inc, 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.
 */

/*
 * The code was inspired by the similarly named JCTools class:
 * https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/atomic
 */
package reactor.util.concurrent;

import java.util.AbstractQueue;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BiPredicate;

import reactor.util.annotation.Nullable;

/**
 * A multi-producer single consumer unbounded queue.
 * @param <E> the contained value type
 */
final class MpscLinkedQueue<E> extends AbstractQueue<E> implements BiPredicate<E, E> {
	private volatile LinkedQueueNode<E> producerNode;

	private final static AtomicReferenceFieldUpdater<MpscLinkedQueue, LinkedQueueNode> PRODUCER_NODE_UPDATER
			= AtomicReferenceFieldUpdater.newUpdater(MpscLinkedQueue.class, LinkedQueueNode.class, "producerNode");

	private volatile LinkedQueueNode<E> consumerNode;
	private final static AtomicReferenceFieldUpdater<MpscLinkedQueue, LinkedQueueNode> CONSUMER_NODE_UPDATER
			= AtomicReferenceFieldUpdater.newUpdater(MpscLinkedQueue.class, LinkedQueueNode.class, "consumerNode");

	public MpscLinkedQueue() {
		LinkedQueueNode<E> node = new LinkedQueueNode<>();
		CONSUMER_NODE_UPDATER.lazySet(this, node);
		PRODUCER_NODE_UPDATER.getAndSet(this, node);// this ensures correct construction:
		// StoreLoad
	}

	/**
	 * {@inheritDoc}

	 *

	 * IMPLEMENTATION NOTES:

	 * Offer is allowed from multiple threads.

	 * Offer allocates a new node and:
	 *

	 *
Swaps it atomically with current producer node (only one producer 'wins')
	 *
Sets the new node as the node following from the swapped producer node
	 *

	 * This works because each producer is guaranteed to 'plant' a new node and link the old node. No 2
	 * producers can get the same producer node as part of XCHG guarantee.
	 *
	 * @see java.util.Queue#offer(java.lang.Object)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public final boolean offer(final E e) {
		Objects.requireNonNull(e, "The offered value 'e' must be non-null");

		final LinkedQueueNode<E> nextNode = new LinkedQueueNode<>(e);
		final LinkedQueueNode<E> prevProducerNode = PRODUCER_NODE_UPDATER.getAndSet(this, nextNode);
		// Should a producer thread get interrupted here the chain WILL be broken until that thread is resumed
		// and completes the store in prev.next.
		prevProducerNode.soNext(nextNode); // StoreStore
		return true;
	}

	/**
	 * This is an additional {@link java.util.Queue} extension for
	 * {@link java.util.Queue#offer} which allows atomically offer two elements at once.
	 *

	 * IMPLEMENTATION NOTES:

	 * Offer over {@link #test} is allowed from multiple threads.

	 * Offer over {@link #test} allocates a two new nodes and:
	 *

	 *
Swaps them atomically with current producer node (only one producer 'wins')
	 *
Sets the new nodes as the node following from the swapped producer node
	 *

	 * This works because each producer is guaranteed to 'plant' a new node and link the old node. No 2
	 * producers can get the same producer node as part of XCHG guarantee.
	 *
	 * @see java.util.Queue#offer(java.lang.Object)
	 *
	 * @param e1 first element to offer
	 * @param e2 second element to offer
	 *
	 * @return indicate whether elements has been successfully offered
	 */
	@Override
	@SuppressWarnings("unchecked")
	public boolean test(E e1, E e2) {
		Objects.requireNonNull(e1, "The offered value 'e1' must be non-null");
		Objects.requireNonNull(e2, "The offered value 'e2' must be non-null");

		final LinkedQueueNode<E> nextNode = new LinkedQueueNode<>(e1);
		final LinkedQueueNode<E> nextNextNode = new LinkedQueueNode<>(e2);

		final LinkedQueueNode<E> prevProducerNode = PRODUCER_NODE_UPDATER.getAndSet(this, nextNextNode);
		// Should a producer thread get interrupted here the chain WILL be broken until that thread is resumed
		// and completes the store in prev.next.
		nextNode.soNext(nextNextNode);
		prevProducerNode.soNext(nextNode); // StoreStore

		return true;
	}

	/**
	 * {@inheritDoc}

	 *

	 * IMPLEMENTATION NOTES:

	 * Poll is allowed from a SINGLE thread.

	 * Poll reads the next node from the consumerNode and:
	 *

	 *
If it is null, the queue is assumed empty (though it might not be).
	 *
If it is not null set it as the consumer node and return it's now evacuated value.
	 *

	 * This means the consumerNode.value is always null, which is also the starting point for the queue.
	 * Because null values are not allowed to be offered this is the only node with it's value set to null at
	 * any one time.
	 *
	 * @see java.util.Queue#poll()
	 */
	@Nullable
	@Override
	public E poll() {
		LinkedQueueNode<E> currConsumerNode = consumerNode; // don't load twice, it's alright
		LinkedQueueNode<E> nextNode = currConsumerNode.lvNext();

		if (nextNode != null)
		{
			// we have to null out the value because we are going to hang on to the node
			final E nextValue = nextNode.getAndNullValue();

			// Fix up the next ref of currConsumerNode to prevent promoted nodes from keeping new ones alive.
			// We use a reference to self instead of null because null is already a meaningful value (the next of
			// producer node is null).
			currConsumerNode.soNext(currConsumerNode);
			CONSUMER_NODE_UPDATER.lazySet(this, nextNode);
			// currConsumerNode is now no longer referenced and can be collected
			return nextValue;
		}
		else if (currConsumerNode != producerNode)
		{
			while ((nextNode = currConsumerNode.lvNext()) == null) { }
			// got the next node...
			// we have to null out the value because we are going to hang on to the node
			final E nextValue = nextNode.getAndNullValue();

			// Fix up the next ref of currConsumerNode to prevent promoted nodes from keeping new ones alive.
			// We use a reference to self instead of null because null is already a meaningful value (the next of
			// producer node is null).
			currConsumerNode.soNext(currConsumerNode);
			CONSUMER_NODE_UPDATER.lazySet(this, nextNode);
			// currConsumerNode is now no longer referenced and can be collected
			return nextValue;
		}
		return null;
	}

	@Nullable
	@Override
	public E peek() {
		LinkedQueueNode<E> currConsumerNode = consumerNode; // don't load twice, it's alright
		LinkedQueueNode<E> nextNode = currConsumerNode.lvNext();

		if (nextNode != null)
		{
			return nextNode.lpValue();
		}
		else if (currConsumerNode != producerNode)
		{
			while ((nextNode = currConsumerNode.lvNext()) == null) { }
			// got the next node...
			return nextNode.lpValue();
		}

		return null;
	}

	@Override
	public boolean remove(Object o) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void clear() {
		while (poll() != null && !isEmpty()) { } // NOPMD
	}

	@Override
	public int size() {
		// Read consumer first, this is important because if the producer is node is 'older' than the consumer
		// the consumer may overtake it (consume past it) invalidating the 'snapshot' notion of size.
		LinkedQueueNode<E> chaserNode = consumerNode;
		LinkedQueueNode<E> producerNode = this.producerNode;
		int size = 0;
		// must chase the nodes all the way to the producer node, but there's no need to count beyond expected head.
		while (chaserNode != producerNode && // don't go passed producer node
				chaserNode != null && // stop at last node
				size < Integer.MAX_VALUE) // stop at max int
		{
			LinkedQueueNode<E> next;
			next = chaserNode.lvNext();
			// check if this node has been consumed, if so return what we have
			if (next == chaserNode)
			{
				return size;
			}
			chaserNode = next;
			size++;
		}
		return size;
	}

	@Override
	public boolean isEmpty() {
		return consumerNode == producerNode;
	}

	@Override
	public Iterator<E> iterator() {
		throw new UnsupportedOperationException();
	}

	static final class LinkedQueueNode<E>
	{
		private volatile LinkedQueueNode<E> next;
		private final static AtomicReferenceFieldUpdater<LinkedQueueNode, LinkedQueueNode> NEXT_UPDATER
				= AtomicReferenceFieldUpdater.newUpdater(LinkedQueueNode.class, LinkedQueueNode.class, "next");

		private E value;

		LinkedQueueNode()
		{
			this(null);
		}

		LinkedQueueNode(@Nullable E val)
		{
			spValue(val);
		}

		/**
		 * Gets the current value and nulls out the reference to it from this node.
		 *
		 * @return value
		 */
		@Nullable
		public E getAndNullValue()
		{
			E temp = lpValue();
			spValue(null);
			return temp;
		}

		@Nullable
		public E lpValue()
		{
			return value;
		}

		public void spValue(@Nullable E newValue)
		{
			value = newValue;
		}

		public void soNext(@Nullable LinkedQueueNode<E> n)
		{
			NEXT_UPDATER.lazySet(this, n);
		}

		@Nullable
		public LinkedQueueNode<E> lvNext()
		{
			return next;
		}
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.util.function;

import java.util.Objects;
import java.util.function.Function;

import reactor.util.annotation.NonNull;
import reactor.util.annotation.Nullable;

/**
 * A tuple that holds five non-null values
 *
 * @param <T1> The type of the first non-null value held by this tuple
 * @param <T2> The type of the second non-null value held by this tuple
 * @param <T3> The type of the third non-null value held by this tuple
 * @param <T4> The type of the fourth non-null value held by this tuple
 * @param <T5> The type of the fifth non-null value held by this tuple
 * @author Jon Brisbin
 * @author Stephane Maldini
 */
public class Tuple5<T1, T2, T3, T4, T5> extends Tuple4<T1, T2, T3, T4> {

	private static final long serialVersionUID = 3541548454198133275L;

	@NonNull final T5 t5;

	Tuple5(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) {
		super(t1, t2, t3, t4);
		this.t5 = Objects.requireNonNull(t5, "t5");
	}

	/**
	 * Type-safe way to get the fifth object of this {@link Tuples}.
	 *
	 * @return The fifth object
	 */
	public T5 getT5() {
		return t5;
	}

	/**
	 * Map the 1st part (T1) of this {@link Tuple5} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T1 part
	 * @param <R> the new type for the T1 part
	 * @return a new {@link Tuple5} with a different T1 value
	 */
	public <R> Tuple5<R, T2, T3, T4, T5> mapT1(Function<T1, R> mapper) {
		return new Tuple5<>(mapper.apply(t1), t2, t3, t4, t5);
	}

	/**
	 * Map the 2nd part (T2) of this {@link Tuple5} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T2 part
	 * @param <R> the new type for the T2 part
	 * @return a new {@link Tuple5} with a different T2 value
	 */
	public <R> Tuple5<T1, R, T3, T4, T5> mapT2(Function<T2, R> mapper) {
		return new Tuple5<>(t1, mapper.apply(t2), t3, t4, t5);
	}

	/**
	 * Map the 3rd part (T3) of this {@link Tuple5} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T3 part
	 * @param <R> the new type for the T3 part
	 * @return a new {@link Tuple5} with a different T3 value
	 */
	public <R> Tuple5<T1, T2, R, T4, T5> mapT3(Function<T3, R> mapper) {
		return new Tuple5<>(t1, t2, mapper.apply(t3), t4, t5);
	}

	/**
	 * Map the 4th part (T4) of this {@link Tuple5} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T4 part
	 * @param <R> the new type for the T4 part
	 * @return a new {@link Tuple5} with a different T4 value
	 */
	public <R> Tuple5<T1, T2, T3, R, T5> mapT4(Function<T4, R> mapper) {
		return new Tuple5<>(t1, t2, t3, mapper.apply(t4), t5);
	}

	/**
	 * Map the 5th part (T5) of this {@link Tuple5} into a different value and type,
	 * keeping the other parts.
	 *
	 * @param mapper the mapping {@link Function} for the T5 part
	 * @param <R> the new type for the T5 part
	 * @return a new {@link Tuple5} with a different T5 value
	 */
	public <R> Tuple5<T1, T2, T3, T4, R> mapT5(Function<T5, R> mapper) {
		return new Tuple5<>(t1, t2, t3, t4, mapper.apply(t5));
	}

	@Nullable
	@Override
	public Object get(int index) {
		switch (index) {
			case 0:
				return t1;
			case 1:
				return t2;
			case 2:
				return t3;
			case 3:
				return t4;
			case 4:
				return t5;
			default:
				return null;
		}
	}

	@Override
	public Object[] toArray() {
		return new Object[]{t1, t2, t3, t4, t5};
	}

	@Override
	public boolean equals(@Nullable Object o) {
		if (this == o) return true;
		if (!(o instanceof Tuple5)) return false;
		if (!super.equals(o)) return false;

		@SuppressWarnings("rawtypes")
        Tuple5 tuple5 = (Tuple5) o;

		return t5.equals(tuple5.t5);

	}

	@Override
	public int hashCode() {
		int result = super.hashCode();
		result = 31 * result + t5.hashCode();
		return result;
	}

	@Override
	public int size() {
		return 5;
	}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Supplier;

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.Exceptions;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.annotation.Nullable;
import reactor.util.concurrent.Queues;
import reactor.util.concurrent.WaitStrategy;

/**
 ** An implementation of a RingBuffer backed message-passing Processor implementing work-queue distribution with
 * async event loops.
 *

 *
 *

 * Created from {@link #share()}, the {@link WorkQueueProcessor} will authorize concurrent publishing
 * (multi-producer) from its receiving side {@link Subscriber#onNext(Object)}.
 * {@link WorkQueueProcessor} is able to replay up to its buffer size number of failed signals (either
 * dropped or fatally throwing on child {@link Subscriber#onNext}).
 *

 *
 *

 * The processor is very similar to {@link TopicProcessor} but
 * only partially respects the Reactive Streams contract.
 The purpose of this
 * processor is to distribute the signals to only one of the subscribed subscribers and to
 * share the demand amongst all subscribers. The scenario is akin to Executor or
 * Round-Robin distribution. However there is no guarantee the distribution will be
 * respecting a round-robin distribution all the time.
 The core use for this component
 * is to scale up easily without suffering the overhead of an Executor and without using
 * dedicated queues by subscriber, which is less used memory, less GC, more win.
 *
 * @param <E> Type of dispatched signal
 * @author Stephane Maldini
 */
public final class WorkQueueProcessor<E> extends EventLoopProcessor<E> {

	/**
	 * {@link WorkQueueProcessor} builder that can be used to create new
	 * processors. Instantiate it through the {@link WorkQueueProcessor#builder()} static
	 * method:
	 *

	 * {@code WorkQueueProcessor<String> processor = WorkQueueProcessor.<String>builder().build()}
	 *
	 * @param <T> Type of dispatched signal
	 */
	public final static class Builder<T> {

		String name;
		ExecutorService executor;
		ExecutorService requestTaskExecutor;
		int bufferSize;
		WaitStrategy waitStrategy;
		boolean share;
		boolean autoCancel;

		Builder() {
			this.bufferSize = Queues.SMALL_BUFFER_SIZE;
			this.autoCancel = true;
			this.share = false;
		}

		/**
		 * Configures name for this builder. Default value is WorkQueueProcessor.
		 * Name is set to default if the provided name is null.
		 * @param name Use a new cached ExecutorService and assign this name to the created threads
		 *             if {@link #executor(ExecutorService)} is not configured.
		 * @return builder with provided name
		 */
		public Builder<T> name(@Nullable String name) {
			if (executor != null)
				throw new IllegalArgumentException("Executor service is configured, name will not be used.");
			this.name = name;
			return this;
		}

		/**
		 * Configures buffer size for this builder. Default value is {@link Queues#SMALL_BUFFER_SIZE}.
		 * @param bufferSize the internal buffer size to hold signals, must be a power of 2
		 * @return builder with provided buffer size
		 */
		public Builder<T> bufferSize(int bufferSize) {
			if (!Queues.isPowerOfTwo(bufferSize)) {
				throw new IllegalArgumentException("bufferSize must be a power of 2 : " + bufferSize);
			}

			if (bufferSize < 1){
				throw new IllegalArgumentException("bufferSize must be strictly positive, " +
						"was: "+bufferSize);
			}
			this.bufferSize = bufferSize;
			return this;
		}

		/**
		 * Configures wait strategy for this builder. Default value is {@link WaitStrategy#liteBlocking()}.
		 * Wait strategy is set to default if the provided waitStrategy is null.
		 * @param waitStrategy A RingBuffer WaitStrategy to use instead of the default smart blocking wait strategy.
		 * @return builder with provided wait strategy
		 */
		public Builder<T> waitStrategy(@Nullable WaitStrategy waitStrategy) {
			this.waitStrategy = waitStrategy;
			return this;
		}

		/**
		 * Configures auto-cancel for this builder. Default value is true.
		 * @param autoCancel automatically cancel
		 * @return builder with provided auto-cancel
		 */
		public Builder<T> autoCancel(boolean autoCancel) {
			this.autoCancel = autoCancel;
			return this;
		}

		/**
		 * Configures an {@link ExecutorService} to execute as many event-loop consuming the
		 * ringbuffer as subscribers. Name configured using {@link #name(String)} will be ignored
		 * if executor is set.
		 * @param executor A provided ExecutorService to manage threading infrastructure
		 * @return builder with provided executor
		 */
		public Builder<T> executor(@Nullable ExecutorService executor) {
			this.executor = executor;
			return this;
		}

		/**
		 * Configures an additional {@link ExecutorService} that is used internally
		 * on each subscription.
		 * @param requestTaskExecutor internal request executor
		 * @return builder with provided internal request executor
		 */
		public Builder<T> requestTaskExecutor(@Nullable ExecutorService requestTaskExecutor) {
			this.requestTaskExecutor = requestTaskExecutor;
			return this;
		}

		/**
		 * Configures sharing state for this builder. A shared Processor authorizes
		 * concurrent onNext calls and is suited for multi-threaded publisher that
		 * will fan-in data.
		 * @param share true to support concurrent onNext calls
		 * @return builder with specified sharing
		 */
		public Builder<T> share(boolean share) {
			this.share = share;
			return this;
		}

		/**
		 * Creates a new {@link WorkQueueProcessor} using the properties
		 * of this builder.
		 * @return a fresh processor
		 */
		public WorkQueueProcessor<T>  build() {
			String name = this.name != null ? this.name : WorkQueueProcessor.class.getSimpleName();
			WaitStrategy waitStrategy = this.waitStrategy != null ? this.waitStrategy : WaitStrategy.liteBlocking();
			ThreadFactory threadFactory = this.executor != null ? null : new EventLoopFactory(name, autoCancel);
			ExecutorService requestTaskExecutor = this.requestTaskExecutor != null ?
					this.requestTaskExecutor : defaultRequestTaskExecutor(defaultName(threadFactory, WorkQueueProcessor.class));
			return new WorkQueueProcessor<>(
					threadFactory,
					executor,
					requestTaskExecutor,
					bufferSize,
					waitStrategy,
					share,
					autoCancel);
		}
	}

	/**
	 * Create a new {@link WorkQueueProcessor} {@link Builder} with default properties.
	 * @return new WorkQueueProcessor builder
	 */
	public final static <T> Builder<T> builder() {
		return new Builder<>();
	}

	/**
	 * Create a new WorkQueueProcessor using {@link Queues#SMALL_BUFFER_SIZE} backlog size,
	 * blockingWait Strategy and auto-cancel.
 A new Cached ThreadExecutorPool will be
	 * implicitly created.
	 * @param <E> Type of processed signals
	 * @return a fresh processor
	 */
	public static <E> WorkQueueProcessor<E> create() {
		return WorkQueueProcessor.<E>builder().build();
	}

	/**
	 * Create a new WorkQueueProcessor using the passed buffer size, blockingWait
	 * Strategy and auto-cancel.
 A new Cached ThreadExecutorPool will be implicitly
	 * created and will use the passed name to qualify the created threads.
	 * @param name Use a new Cached ExecutorService and assign this name to the created
	 * threads
	 * @param bufferSize A Backlog Size to mitigate slow subscribers
	 * @param <E> Type of processed signals
	 * @return a fresh processor
	 */
	public static <E> WorkQueueProcessor<E> create(String name, int bufferSize) {
		return WorkQueueProcessor.<E>builder().name(name).bufferSize(bufferSize).build();
	}

	/**
	 * Create a new shared WorkQueueProcessor using the passed buffer size, blockingWait
	 * Strategy and auto-cancel.
 A Shared Processor authorizes concurrent onNext calls
	 * and is suited for multi-threaded publisher that will fan-in data.
 A new Cached
	 * ThreadExecutorPool will be implicitly created and will use the passed name to
	 * qualify the created threads.
	 * @param name Use a new Cached ExecutorService and assign this name to the created
	 * threads
	 * @param bufferSize A Backlog Size to mitigate slow subscribers
	 * @param <E> Type of processed signals
	 * @return a fresh processor
	 */
	public static <E> WorkQueueProcessor<E> share(String name, int bufferSize) {
		return WorkQueueProcessor.<E>builder().share(true).name(name).bufferSize(bufferSize).build();
	}

	@SuppressWarnings("rawtypes")
    static final Supplier FACTORY = (Supplier<Slot>) Slot::new;

	/**
	 * Instance
	 */

	final RingBuffer.Sequence workSequence =
			RingBuffer.newSequence(RingBuffer.INITIAL_CURSOR_VALUE);

	final Queue<Object> claimedDisposed = new ConcurrentLinkedQueue<>();

	final WaitStrategy writeWait;

	volatile int replaying;

	@SuppressWarnings("rawtypes")
    static final AtomicIntegerFieldUpdater<WorkQueueProcessor> REPLAYING =
			AtomicIntegerFieldUpdater
					.newUpdater(WorkQueueProcessor.class, "replaying");

	@SuppressWarnings("unchecked")
	WorkQueueProcessor(
			@Nullable ThreadFactory threadFactory,
			@Nullable ExecutorService executor,
			ExecutorService requestTaskExecutor,
			int bufferSize, WaitStrategy waitStrategy, boolean share,
	                                boolean autoCancel) {
		super(bufferSize, threadFactory,
				executor, requestTaskExecutor,
				autoCancel,
				share,
				FACTORY,
				waitStrategy);

		this.writeWait = waitStrategy;

		ringBuffer.addGatingSequence(workSequence);
	}

	@Override
	public void subscribe(final CoreSubscriber<? super E> actual) {
		Objects.requireNonNull(actual, "subscribe");

		if (!alive()) {
			TopicProcessor.coldSource(ringBuffer, null, error, workSequence).subscribe(
					actual);
			return;
		}

		final WorkQueueInner<E> signalProcessor =
				new WorkQueueInner<>(actual, this);
		try {

			incrementSubscribers();

			//bind eventProcessor sequence to observe the ringBuffer
			signalProcessor.sequence.set(workSequence.getAsLong());
			ringBuffer.addGatingSequence(signalProcessor.sequence);

			//best effort to prevent starting the subscriber thread if we can detect the pool is too small
			int maxSubscribers = bestEffortMaxSubscribers(executor);
			if (maxSubscribers > Integer.MIN_VALUE && subscriberCount > maxSubscribers) {
				throw new IllegalStateException("The executor service could not accommodate" +
						" another subscriber, detected limit " + maxSubscribers);
			}

			executor.execute(signalProcessor);
		}
		catch (Throwable t) {
			decrementSubscribers();
			ringBuffer.removeGatingSequence(signalProcessor.sequence);
			if(RejectedExecutionException.class.isAssignableFrom(t.getClass())){
				TopicProcessor.coldSource(ringBuffer, t, error, workSequence).subscribe(
						actual);
			}
			else {
				Operators.error(actual, t);
			}
		}
	}

	/**
	 * This method will attempt to compute the maximum amount of subscribers a
	 * {@link WorkQueueProcessor} can accommodate based on a given {@link ExecutorService}.
	 *

	 * It can only accurately detect this for {@link ThreadPoolExecutor} and
	 * {@link ForkJoinPool} instances, and will return {@link Integer#MIN_VALUE} for other
	 * executor implementations.
	 *
	 * @param executor the executor to attempt to introspect.
	 * @return the maximum number of subscribers the executor can accommodate if it can
	 * be computed, or {@link Integer#MIN_VALUE} if it cannot be determined.
	 */
	static int bestEffortMaxSubscribers(ExecutorService executor) {
		int maxSubscribers = Integer.MIN_VALUE;
		if (executor instanceof ThreadPoolExecutor) {
			maxSubscribers = ((ThreadPoolExecutor) executor).getMaximumPoolSize();
		}
		else if (executor instanceof ForkJoinPool) {
			maxSubscribers = ((ForkJoinPool) executor).getParallelism();
		}
		return maxSubscribers;
	}

	@Override
	public Flux<E> drain() {
		return TopicProcessor.coldSource(ringBuffer, null, error, workSequence);
	}

	@Override
	protected void doError(Throwable t) {
		writeWait.signalAllWhenBlocking();
		//ringBuffer.markAsTerminated();
	}

	@Override
	protected void doComplete() {
		writeWait.signalAllWhenBlocking();
		//ringBuffer.markAsTerminated();
	}

	@Override
	protected void requestTask(Subscription s) {
		requestTaskExecutor.execute(createRequestTask(s, this,
				null, ringBuffer::getMinimumGatingSequence));
	}

	@Override
	public long getPending() {
		return (getBufferSize() - ringBuffer.getPending()) + claimedDisposed.size();
	}

	@Override
	public void run() {
		if (!alive()) {
			WaitStrategy.alert();
		}
	}

	/**
	 * Disruptor WorkProcessor port that deals with pending demand.
 Convenience class
	 * for handling the batching semantics of consuming entries from a {@link
	 * RingBuffer}

	 * @param <T> event implementation storing the data for sharing during exchange or
	 * parallel coordination of an event.
	 */
	final static class WorkQueueInner<T>
			implements Runnable, InnerProducer<T> {

		final AtomicBoolean running = new AtomicBoolean(true);

		final RingBuffer.Sequence
				sequence = RingBuffer.newSequence(RingBuffer.INITIAL_CURSOR_VALUE);

		final RingBuffer.Sequence pendingRequest = RingBuffer.newSequence(0);

		final RingBuffer.Reader barrier;

		final WorkQueueProcessor<T> processor;

		final CoreSubscriber<? super T> subscriber;

		final Runnable waiter = new Runnable() {
			@Override
			public void run() {
				if (barrier.isAlerted() || !isRunning() ||
						replay(pendingRequest.getAsLong() == Long.MAX_VALUE)) {
					WaitStrategy.alert();
				}
			}
		};

		/**
		 * Construct a ringbuffer consumer that will automatically track the progress by
		 * updating its sequence
		 * @param subscriber the output Subscriber instance
		 * @param processor the source processor
		 */
		WorkQueueInner(CoreSubscriber<? super T> subscriber,
				WorkQueueProcessor<T> processor) {
			this.processor = processor;
			this.subscriber = subscriber;

			this.barrier = processor.ringBuffer.newReader();
		}

		void halt() {
			running.set(false);
			barrier.alert();
		}

		boolean isRunning() {
			return running.get() && (processor.terminated == 0 ||
					(processor.terminated != FORCED_SHUTDOWN &&
							processor.error == null &&
							processor.ringBuffer.getAsLong() > sequence.getAsLong())
			);
		}

		/**
		 * It is ok to have another thread rerun this method after a halt().
		 */
		@Override
		public void run() {
			long nextSequence;
			boolean processedSequence = true;

			try {

				//while(processor.alive() && processor.upstreamSubscription == null);
				Thread.currentThread()
				      .setContextClassLoader(processor.contextClassLoader);
				subscriber.onSubscribe(this);

				long cachedAvailableSequence = Long.MIN_VALUE;
				nextSequence = sequence.getAsLong();
				Slot<T> event = null;

				final boolean unbounded = pendingRequest.getAsLong() == Long.MAX_VALUE;

				if (!EventLoopProcessor.waitRequestOrTerminalEvent(pendingRequest, barrier, running, sequence,
						waiter) && replay(unbounded)) {
					if(!running.get()){
						return;
					}
					if(processor.terminated == SHUTDOWN) {
						if (processor.ringBuffer.getAsLong() == -1L) {
							if (processor.error != null) {
								subscriber.onError(processor.error);
								return;
							}
							subscriber.onComplete();
							return;
						}
					}
					else if (processor.terminated == FORCED_SHUTDOWN) {
							return;
					}
				}

				while (true) {
					try {
						// if previous sequence was processed - fetch the next sequence and set
						// that we have successfully processed the previous sequence
						// typically, this will be true
						// this prevents the sequence getting too far forward if an error
						// is thrown from the WorkHandler
						if (processedSequence) {
							if(!running.get()){
								break;
							}
							processedSequence = false;
							do {
								nextSequence = processor.workSequence.getAsLong() + 1L;
								while ((!unbounded && pendingRequest.getAsLong() == 0L)) {
									if (!isRunning()) {
										WaitStrategy.alert();
									}
									LockSupport.parkNanos(1L);
								}
								sequence.set(nextSequence - 1L);
							}
							while (!processor.workSequence.compareAndSet(nextSequence - 1L, nextSequence));
						}

						if (cachedAvailableSequence >= nextSequence) {
							event = processor.ringBuffer.get(nextSequence);

							try {
								readNextEvent(unbounded);
							}
							catch (Exception ce) {
								if (!running.get() || !WaitStrategy.isAlert(ce)) {
									throw ce;
								}
								barrier.clearAlert();
							}

							processedSequence = true;
							subscriber.onNext(event.value);

						}
						else {
							processor.readWait.signalAllWhenBlocking();
								cachedAvailableSequence =
										barrier.waitFor(nextSequence, waiter);

						}

					}
					catch (InterruptedException | RuntimeException ce) {
						if (ce instanceof InterruptedException) {
							Thread.currentThread().interrupt();
						}
						if (Exceptions.isCancel(ce)){
							reschedule(event);
							break;
						}
						if (!WaitStrategy.isAlert(ce)) {
							throw Exceptions.propagate(ce);
						}

						barrier.clearAlert();
						if (!running.get()) {
							break;
						}
						if(processor.terminated == SHUTDOWN) {
							if (processor.error != null) {
								processedSequence = true;
								subscriber.onError(processor.error);
								break;
							}
							if (processor.ringBuffer.getPending() == 0) {
								processedSequence = true;
								subscriber.onComplete();
								break;
							}
						}
						else if (processor.terminated == FORCED_SHUTDOWN) {
								break;
						}
						//processedSequence = true;
						//continue event-loop

					}
				}
			}
			finally {
				processor.decrementSubscribers();
				running.set(false);

				if(!processedSequence) {
					processor.claimedDisposed.add(sequence);
				}
				else{
					processor.ringBuffer.removeGatingSequence(sequence);
				}

				processor.writeWait.signalAllWhenBlocking();
			}
		}

		@SuppressWarnings("unchecked")
		boolean replay(final boolean unbounded) {
			if (REPLAYING.compareAndSet(processor, 0, 1)) {
				try {
					RingBuffer.Sequence s = null;
					for (; ; ) {

						if (!running.get()) {
							processor.readWait.signalAllWhenBlocking();
							return true;
						}

						Object v = processor.claimedDisposed.peek();

						if (v == null) {
							processor.readWait.signalAllWhenBlocking();
							return !processor.alive() && processor.ringBuffer.getPending() == 0;
						}

						if (v instanceof RingBuffer.Sequence) {
							s = (RingBuffer.Sequence) v;
							long cursor = s.getAsLong() + 1L;
							if(cursor > processor.ringBuffer.getAsLong()){
								processor.readWait.signalAllWhenBlocking();
								return !processor.alive() && processor.ringBuffer.getPending() == 0;
							}

							barrier.waitFor(cursor, waiter);

							v = processor.ringBuffer.get(cursor).value;

							if (v == null) {
								processor.ringBuffer.removeGatingSequence(s);
								processor.claimedDisposed.poll();
								s = null;
								continue;
							}
						}

						readNextEvent(unbounded);
						subscriber.onNext((T) v);
						processor.claimedDisposed.poll();
						if(s != null){
							processor.ringBuffer.removeGatingSequence(s);
							s = null;
						}
					}
				}
				catch (RuntimeException ce) {
					if (!running.get() || Exceptions.isCancel(ce)) {
						running.set(false);
						return true;
					}
					throw ce;
				}
				catch (InterruptedException e) {
					running.set(false);
					Thread.currentThread().interrupt();
					return true;
				}
				finally {
					REPLAYING.compareAndSet(processor, 1, 0);
				}
			}
			else {
				return !processor.alive() && processor.ringBuffer.getPending() == 0;
			}
		}

		boolean reschedule(@Nullable Slot<T> event) {
			if (event != null &&
					event.value != null) {
				processor.claimedDisposed.add(event.value);
				barrier.alert();
				processor.readWait.signalAllWhenBlocking();
				return true;
			}
			return false;
		}

		void readNextEvent(final boolean unbounded) {
				//pause until request
			while ((!unbounded && getAndSub(pendingRequest, 1L) == 0L)) {
				if (!isRunning()) {
					WaitStrategy.alert();
				}
				//Todo Use WaitStrategy?
				LockSupport.parkNanos(1L);
			}
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.PARENT ) return processor;
			if (key == Attr.PREFETCH) return Integer.MAX_VALUE;
			if (key == Attr.TERMINATED ) return processor.isTerminated();
			if (key == Attr.CANCELLED) return !running.get();
			if (key == Attr.REQUESTED_FROM_DOWNSTREAM) return pendingRequest.getAsLong();

			return InnerProducer.super.scanUnsafe(key);
		}

		@Override
		public CoreSubscriber<? super T> actual() {
			return subscriber;
		}

		@Override
		public void request(long n) {
			if (!Operators.validate(n) || !running.get()) {
				return;
			}

			addCap(pendingRequest, n);
		}

		@Override
		public void cancel() {
			halt();
		}

	}

	static final Logger log = Loggers.getLogger(WorkQueueProcessor.class);

}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.Queue;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.function.BooleanSupplier;

import org.reactivestreams.Subscriber;
import reactor.core.CoreSubscriber;
import reactor.util.annotation.Nullable;

abstract class DrainUtils {

	/**
	 * Indicates the source completed and the value field is ready to be emitted.
	 * <p>
	 * The AtomicLong (this) holds the requested amount in bits 0..62 so there is room
	 * for one signal bit. This also means the standard request accounting helper method doesn't work.
	 */
	static final long COMPLETED_MASK = 0x8000_0000_0000_0000L;
	static final long REQUESTED_MASK = 0x7FFF_FFFF_FFFF_FFFFL;

	/**
	 * Perform a potential post-completion request accounting.
	 *
	 * @param <T> the output value type
	 * @param <F> the field type holding the requested amount
	 * @param n the requested amount
	 * @param actual the consumer of values
	 * @param queue the queue holding the available values
	 * @param field the field updater for the requested amount
	 * @param instance the parent instance for the requested field
	 * @param isCancelled callback to detect cancellation
	 * @return true if the state indicates a completion state.
	 */
	static <T, F> boolean postCompleteRequest(long n,
			Subscriber<? super T> actual,
			Queue<T> queue,
			AtomicLongFieldUpdater<F> field,
			F instance,
			BooleanSupplier isCancelled) {

		for (; ; ) {
			long r = field.get(instance);

			// extract the current request amount
			long r0 = r & REQUESTED_MASK;

			// preserve COMPLETED_MASK and calculate new requested amount
			long u = (r & COMPLETED_MASK) | Operators.addCap(r0, n);

			if (field.compareAndSet(instance, r, u)) {
				// (complete, 0) -> (complete, n) transition then replay
				if (r == COMPLETED_MASK) {

					postCompleteDrain(n | COMPLETED_MASK, actual, queue, field, instance, isCancelled);

					return true;
				}
				// (active, r) -> (active, r + n) transition then continue with requesting from upstream
				return false;
			}
		}

	}

	/**
	 * Drains the queue either in a pre- or post-complete state.
	 *
	 * @param n the requested amount
	 * @param actual the consumer of values
	 * @param queue the queue holding available values
	 * @param field the field updater holding the requested amount
	 * @param instance the parent instance of the requested field
	 * @param isCancelled callback to detect cancellation
	 * @return true if the queue was completely drained or the drain process was cancelled
	 */
	static <T, F> boolean postCompleteDrain(long n,
			Subscriber<? super T> actual,
			Queue<T> queue,
			AtomicLongFieldUpdater<F> field,
			F instance,
			BooleanSupplier isCancelled) {

// TODO enable fast-path
//        if (n == -1 || n == Long.MAX_VALUE) {
//            for (;;) {
//                if (isDisposed.getAsBoolean()) {
//                    break;
//                }
//
//                T v = queue.poll();
//
//                if (v == null) {
//                    actual.onComplete();
//                    break;
//                }
//
//                actual.onNext(v);
//            }
//
//            return true;
//        }

		long e = n & COMPLETED_MASK;

		for (; ; ) {

			while (e != n) {
				if (isCancelled.getAsBoolean()) {
					return true;
				}

				T t = queue.poll();

				if (t == null) {
					actual.onComplete();
					return true;
				}

				actual.onNext(t);
				e++;
			}

			if (isCancelled.getAsBoolean()) {
				return true;
			}

			if (queue.isEmpty()) {
				actual.onComplete();
				return true;
			}

			n = field.get(instance);

			if (n == e) {

				n = field.addAndGet(instance, -(e & REQUESTED_MASK));

				if ((n & REQUESTED_MASK) == 0L) {
					return false;
				}

				e = n & COMPLETED_MASK;
			}
		}

	}

	/**
	 * Tries draining the queue if the source just completed.
	 *
     * @param <T> the output value type
     * @param <F> the field type holding the requested amount
	 * @param actual the consumer of values
	 * @param queue the queue holding available values
	 * @param field the field updater holding the requested amount
	 * @param instance the parent instance of the requested field
	 * @param isCancelled callback to detect cancellation
	 */
	public static <T, F> void postComplete(CoreSubscriber<? super T> actual,
			Queue<T> queue,
			AtomicLongFieldUpdater<F> field,
			F instance,
			BooleanSupplier isCancelled) {

		if (queue.isEmpty()) {
			actual.onComplete();
			return;
		}

		if (postCompleteDrain(field.get(instance), actual, queue, field, instance, isCancelled)) {
			return;
		}

		for (; ; ) {
			long r = field.get(instance);

			if ((r & COMPLETED_MASK) != 0L) {
				return;
			}

			long u = r | COMPLETED_MASK;
			// (active, r) -> (complete, r) transition
			if (field.compareAndSet(instance, r, u)) {
				// if the requested amount was non-zero, drain the queue
				if (r != 0L) {
					postCompleteDrain(u, actual, queue, field, instance, isCancelled);
				}

				return;
			}
		}
	}

    /**
     * Perform a potential post-completion request accounting.
     *
     * @param <T> the output value type
     * @param <F> the field type holding the requested amount
     * @param n the request amount
     * @param actual the consumer of values
     * @param queue the queue of available values
     * @param field the field updater for the requested amount
     * @param instance the parent instance of the requested field
     * @param isCancelled callback to detect cancellation
     * @param error if not null, the error to signal after the queue has been drained
     * @return true if the state indicates a completion state.
     */
    public static <T, F> boolean postCompleteRequestDelayError(long n,
            Subscriber<? super T> actual,
            Queue<T> queue,
            AtomicLongFieldUpdater<F> field,
            F instance,
            BooleanSupplier isCancelled, Throwable error) {

        for (; ; ) {
            long r = field.get(instance);

            // extract the current request amount
            long r0 = r & REQUESTED_MASK;

            // preserve COMPLETED_MASK and calculate new requested amount
            long u = (r & COMPLETED_MASK) | Operators.addCap(r0, n);

            if (field.compareAndSet(instance, r, u)) {
                // (complete, 0) -> (complete, n) transition then replay
                if (r == COMPLETED_MASK) {

                    postCompleteDrainDelayError(n | COMPLETED_MASK, actual, queue, field, instance, isCancelled, error);

                    return true;
                }
                // (active, r) -> (active, r + n) transition then continue with requesting from upstream
                return false;
            }
        }

    }

    /**
     * Drains the queue either in a pre- or post-complete state, delaying an
     * optional error to the end of the drain operation.
     *
     * @param n the requested amount
     * @param actual the consumer of values
     * @param queue the queue holding available values
     * @param field the field updater holding the requested amount
     * @param instance the parent instance of the requested field
     * @param isCancelled callback to detect cancellation
     * @param error the delayed error
     * @return true if the queue was completely drained or the drain process was cancelled
     */
    static <T, F> boolean postCompleteDrainDelayError(long n,
            Subscriber<? super T> actual,
            Queue<T> queue,
            AtomicLongFieldUpdater<F> field,
            F instance,
            BooleanSupplier isCancelled,
		    @Nullable Throwable error) {

        long e = n & COMPLETED_MASK;

        for (; ; ) {

            while (e != n) {
                if (isCancelled.getAsBoolean()) {
                    return true;
                }

                T t = queue.poll();

                if (t == null) {
                    if (error == null) {
                        actual.onComplete();
                    } else {
                        actual.onError(error);
                    }
                    return true;
                }

                actual.onNext(t);
                e++;
            }

            if (isCancelled.getAsBoolean()) {
                return true;
            }

            if (queue.isEmpty()) {
                if (error == null) {
                    actual.onComplete();
                } else {
                    actual.onError(error);
                }
                return true;
            }

            n = field.get(instance);

            if (n == e) {

                n = field.addAndGet(instance, -(e & REQUESTED_MASK));

                if ((n & REQUESTED_MASK) == 0L) {
                    return false;
                }

                e = n & COMPLETED_MASK;
            }
        }

    }

    /**
     * Tries draining the queue if the source just completed.
     *
     * @param <T> the output value type
     * @param <F> the field type holding the requested amount
     * @param actual the consumer of values
     * @param queue the queue of available values
     * @param field the field updater for the requested amount
     * @param instance the parent instance of the requested field
     * @param isCancelled callback to detect cancellation
     * @param error if not null, the error to signal after the queue has been drained
     */
    public static <T, F> void postCompleteDelayError(CoreSubscriber<? super T> actual,
            Queue<T> queue,
            AtomicLongFieldUpdater<F> field,
            F instance,
            BooleanSupplier isCancelled,
		    @Nullable Throwable error) {

        if (queue.isEmpty()) {
            if (error == null) {
                actual.onComplete();
            } else {
                actual.onError(error);
            }
            return;
        }

        if (postCompleteDrainDelayError(field.get(instance), actual, queue, field, instance, isCancelled, error)) {
            return;
        }

        for (; ; ) {
            long r = field.get(instance);

            if ((r & COMPLETED_MASK) != 0L) {
                return;
            }

            long u = r | COMPLETED_MASK;
            // (active, r) -> (complete, r) transition
            if (field.compareAndSet(instance, r, u)) {
                // if the requested amount was non-zero, drain the queue
                if (r != 0L) {
                    postCompleteDrainDelayError(u, actual, queue, field, instance, isCancelled, error);
                }

                return;
            }
        }
    }

	DrainUtils(){}
}


/*
 * Copyright (c) 2011-2017 Pivotal Software Inc, 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.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.reactivestreams.Subscription;
import reactor.core.Disposable;
import reactor.core.Exceptions;
import reactor.core.scheduler.Schedulers;
import reactor.util.annotation.Nullable;

/**
 * @see https://github.com/reactor/reactive-streams-commons
 */
abstract class BlockingSingleSubscriber<T> extends CountDownLatch
		implements InnerConsumer<T>, Disposable {

	T         value;
	Throwable error;

	Subscription s;

	volatile boolean cancelled;

	BlockingSingleSubscriber() {
		super(1);
	}

	@Override
	public final void onSubscribe(Subscription s) {
		this.s = s;
		if (!cancelled) {
			s.request(Long.MAX_VALUE);
		}
	}

	@Override
	public final void onComplete() {
		countDown();
	}

	@Override
	public final void dispose() {
		cancelled = true;
		Subscription s = this.s;
		if (s != null) {
			this.s = null;
			s.cancel();
		}
	}

	/**
	 * Block until the first value arrives and return it, otherwise
	 * return null for an empty source and rethrow any exception.
	 *
	 * @return the first value or null if the source is empty
	 */
	@Nullable
	final T blockingGet() {
		if (Schedulers.isInNonBlockingThread()) {
			throw new IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread " + Thread.currentThread().getName());
		}
		if (getCount() != 0) {
			try {
				await();
			}
			catch (InterruptedException ex) {
				dispose();
				throw Exceptions.propagate(ex);
			}
		}

		Throwable e = error;
		if (e != null) {
			RuntimeException re = Exceptions.propagate(e);
			//this is ok, as re is always a new non-singleton instance
			re.addSuppressed(new Exception("#block terminated with an error"));
			throw re;
		}
		return value;
	}

	/**
	 * Block until the first value arrives and return it, otherwise
	 * return null for an empty source and rethrow any exception.
	 *
	 * @param timeout the timeout to wait
	 * @param unit the time unit
	 *
	 * @return the first value or null if the source is empty
	 */
	@Nullable
	final T blockingGet(long timeout, TimeUnit unit) {
		if (Schedulers.isInNonBlockingThread()) {
			throw new IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread " + Thread.currentThread().getName());
		}
		if (getCount() != 0) {
			try {
				if (!await(timeout, unit)) {
					dispose();
					throw new IllegalStateException("Timeout on blocking read for " + timeout + " " + unit);
				}
			}
			catch (InterruptedException ex) {
				dispose();
				RuntimeException re = Exceptions.propagate(ex);
				//this is ok, as re is always a new non-singleton instance
				re.addSuppressed(new Exception("#block has been interrupted"));
				throw re;
			}
		}

		Throwable e = error;
		if (e != null) {
			RuntimeException re = Exceptions.propagate(e);
			//this is ok, as re is always a new non-singleton instance
			re.addSuppressed(new Exception("#block terminated with an error"));
			throw re;
		}
		return value;
	}

	@Override
	@Nullable
	public Object scanUnsafe(Attr key) {
		if (key == Attr.TERMINATED) return getCount() == 0;
		if (key == Attr.PARENT) return  s;
		if (key == Attr.CANCELLED) return cancelled;
		if (key == Attr.ERROR) return error;
		if (key == Attr.PREFETCH) return Integer.MAX_VALUE;

		return null;
	}

	@Override
	public boolean isDisposed() {
		return cancelled || getCount() == 0;
	}
}