/*
 * 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.concurrent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.LongSupplier;

/**
 * Strategy employed to wait for specific {@link LongSupplier} values with various spinning strategies.
 */
public abstract class WaitStrategy {

    /**
     * A no-op {@link Runnable} that can be used as a placeholder spin observer  with
     * {@link #waitFor(long, LongSupplier, Runnable)}
     */
    public static final Runnable NOOP_SPIN_OBSERVER = () -> { };

    /**
     * Blocking strategy that uses a lock and condition variable for consumer waiting on a barrier.
     *
     * This strategy can be used when throughput and low-latency are not as important as CPU resource.
     * @return the wait strategy
     */
    public static WaitStrategy blocking() {
        return new Blocking();
    }

    /**
     * Busy Spin strategy that uses a busy spin loop for consumers waiting on a barrier.
     *
     * This strategy will use CPU resource to avoid syscalls which can introduce latency jitter.  It is best
     * used when threads can be bound to specific CPU cores.
     * @return the wait strategy
     */
    public static WaitStrategy busySpin() {
        return BusySpin.INSTANCE;
    }

    /**
     * Test if exception is alert
     * @param t exception checked
     * @return true if this is an alert signal
     */
    public static boolean isAlert(Throwable t){
	    return t == AlertException.INSTANCE;
    }

    /**
     * Variation of the {@link #blocking()} that attempts to elide conditional wake-ups when the lock is uncontended.
     * Shows performance improvements on microbenchmarks.  However this wait strategy should be considered experimental
     * as I have not full proved the correctness of the lock elision code.
     * @return the wait strategy
     */
    public static WaitStrategy liteBlocking() {
        return new LiteBlocking();
    }

    /**
     * Parking strategy that initially spins, then uses a Thread.yield(), and eventually sleep
     * (LockSupport.parkNanos(1)) for the minimum number of nanos the OS and JVM will allow while the
     * consumers are waiting on a barrier.
     *

     * This strategy is a good compromise between performance and CPU resource. Latency spikes can occur after quiet
     * periods.
     * @return the wait strategy
     */
    public static WaitStrategy parking() {
        return Parking.INSTANCE;
    }

    /**
     * Parking strategy that initially spins, then uses a Thread.yield(), and eventually
     * sleep (LockSupport.parkNanos(1)) for the minimum number of nanos the
     * OS and JVM will allow while the consumers are waiting on a barrier.
     *

     * This strategy is a good compromise between performance and CPU resource. Latency
     * spikes can occur after quiet periods.
     *
     * @param retries the spin cycle count before parking
     * @return the wait strategy
     */
    public static WaitStrategy parking(int retries) {
        return new Parking(retries);
    }

    /**
     *
Phased wait strategy for waiting consumers on a barrier.

     *

     *
This strategy can be used when throughput and low-latency are not as important as CPU resource. Spins, then
     * yields, then waits using the configured fallback WaitStrategy.

     *
     * @param spinTimeout the spin timeout
     * @param yieldTimeout the yield timeout
     * @param units the time unit
     * @param delegate the target wait strategy to fallback on
     * @return the wait strategy
     */
    public static WaitStrategy phasedOff(long spinTimeout, long yieldTimeout, TimeUnit units, WaitStrategy delegate) {
        return new PhasedOff(spinTimeout, yieldTimeout, units, delegate);
    }

    /**
     * Block with wait/notifyAll semantics
     * @param spinTimeout the spin timeout
     * @param yieldTimeout the yield timeout
     * @param units the time unit
     * @return the wait strategy
     */
    public static WaitStrategy phasedOffLiteLock(long spinTimeout, long yieldTimeout, TimeUnit units) {
        return phasedOff(spinTimeout, yieldTimeout, units, liteBlocking());
    }

    /**
     * Block with wait/notifyAll semantics
     * @param spinTimeout the spin timeout
     * @param yieldTimeout the yield timeout
     * @param units the time unit
     * @return the wait strategy
     */
    public static WaitStrategy phasedOffLock(long spinTimeout, long yieldTimeout, TimeUnit units) {
        return phasedOff(spinTimeout, yieldTimeout, units, blocking());
    }

    /**
     * Block by parking in a loop
     * @param spinTimeout the spin timeout
     * @param yieldTimeout the yield timeout
     * @param units the time unit
     * @return the wait strategy
     */
    public static WaitStrategy phasedOffSleep(long spinTimeout, long yieldTimeout, TimeUnit units) {
        return phasedOff(spinTimeout, yieldTimeout, units, parking(0));
    }

    /**
     * Yielding strategy that uses a Thread.sleep(1) for consumers waiting on a
     * barrier
     * after an initially spinning.
     *
     * This strategy will incur up a latency of 1ms and save a maximum CPU resources.
     * @return the wait strategy
     */
    public static WaitStrategy sleeping() {
        return Sleeping.INSTANCE;
    }

    /**
     * Yielding strategy that uses a Thread.yield() for consumers waiting on a
     * barrier
     * after an initially spinning.
     *
     * This strategy is a good compromise between performance and CPU resource without incurring significant latency spikes.
     * @return the wait strategy
     */
    public static WaitStrategy yielding() {
        return Yielding.INSTANCE;
    }

    /**
     * Implementations should signal the waiting consumers that the cursor has advanced.
     */
    public void signalAllWhenBlocking() {
    }

    /**
     * Wait for the given sequence to be available.  It is possible for this method to return a value
     * less than the sequence number supplied depending on the implementation of the WaitStrategy.  A common
     * use for this is to signal a timeout.  Any EventProcessor that is using a WaitStrategy to get notifications
     * about message becoming available should remember to handle this case.
     *
     * @param sequence to be waited on.
     * @param cursor the main sequence from ringbuffer. Wait/notify strategies will
     *    need this as is notified upon update.
     * @param spinObserver Spin observer
     * @return the sequence that is available which may be greater than the requested sequence.
     * @throws InterruptedException if the thread is interrupted.
     */
    public abstract long waitFor(long sequence, LongSupplier cursor, Runnable spinObserver)
            throws InterruptedException;

	/**
	 * Throw an Alert signal exception (singleton) that can be checked against {@link #isAlert(Throwable)}
	 */
    public static void alert(){
	    throw AlertException.INSTANCE;
    }

    /**
     * Used to alert consumers waiting with a {@link WaitStrategy} for status changes.
     *

     * It does not fill in a stack trace for performance reasons.
     */
    @SuppressWarnings("serial")
    static final class AlertException extends RuntimeException {
	    /** Pre-allocated exception to avoid garbage generation */
	    public static final AlertException INSTANCE = new AlertException();

	    /**
	     * Private constructor so only a single instance any.
	     */
	    private AlertException() {
	    }

	    /**
	     * Overridden so the stack trace is not filled in for this exception for performance reasons.
	     *
	     * @return this instance.
	     */
	    @Override
	    public Throwable fillInStackTrace() {
		    return this;
	    }

    }

    final static class Blocking extends WaitStrategy {

        private final Lock      lock                     = new ReentrantLock();
        private final Condition processorNotifyCondition = lock.newCondition();

        @Override
        public void signalAllWhenBlocking()
        {
            lock.lock();
            try
            {
                processorNotifyCondition.signalAll();
            }
            finally
            {
                lock.unlock();
            }
        }

        @SuppressWarnings("UnusedAssignment") //for availableSequence
        @Override
        public long waitFor(long sequence, LongSupplier cursorSequence, Runnable barrier)
                throws InterruptedException
        {
            long availableSequence;
            if ((availableSequence = cursorSequence.getAsLong()) < sequence)
            {
                lock.lock();
                try
                {
                    while ((availableSequence = cursorSequence.getAsLong()) < sequence)
                    {
                        barrier.run();
                        processorNotifyCondition.await();
                    }
                }
                finally
                {
                    lock.unlock();
                }
            }

            while ((availableSequence = cursorSequence.getAsLong()) < sequence)
            {
                barrier.run();
            }

            return availableSequence;
        }
    }

    final static class BusySpin extends WaitStrategy {

	    static final BusySpin INSTANCE = new BusySpin();

        @Override
        public long waitFor(final long sequence, LongSupplier cursor, final Runnable barrier)
                throws InterruptedException
        {
            long availableSequence;

            while ((availableSequence = cursor.getAsLong()) < sequence)
            {
                barrier.run();
            }

            return availableSequence;
        }
    }

    final static class Sleeping extends WaitStrategy {

	    static final Sleeping INSTANCE = new Sleeping();

        @Override
        public long waitFor(final long sequence,
                LongSupplier cursor,
                final Runnable barrier)
                throws InterruptedException {
            long availableSequence;

            while ((availableSequence = cursor.getAsLong()) < sequence) {
                barrier.run();
                Thread.sleep(1);
            }

            return availableSequence;
        }
    }

    final static class LiteBlocking extends WaitStrategy {

        private final Lock          lock                     = new ReentrantLock();
        private final Condition     processorNotifyCondition = lock.newCondition();
        private final AtomicBoolean signalNeeded             = new AtomicBoolean(false);

        @Override
        public void signalAllWhenBlocking()
        {
            if (signalNeeded.getAndSet(false))
            {
                lock.lock();
                try
                {
                    processorNotifyCondition.signalAll();
                }
                finally
                {
                    lock.unlock();
                }
            }
        }

        @SuppressWarnings("UnusedAssignment") //for availableSequence
        @Override
        public long waitFor(long sequence, LongSupplier cursorSequence, Runnable barrier)
                throws InterruptedException
        {
            long availableSequence;
            if ((availableSequence = cursorSequence.getAsLong()) < sequence)
            {
                lock.lock();

                try
                {
                    do
                    {
                        signalNeeded.getAndSet(true);

                        if ((availableSequence = cursorSequence.getAsLong()) >= sequence)
                        {
                            break;
                        }

                        barrier.run();
                        processorNotifyCondition.await();
                    }
                    while ((availableSequence = cursorSequence.getAsLong()) < sequence);
                }
                finally
                {
                    lock.unlock();
                }
            }

            while ((availableSequence = cursorSequence.getAsLong()) < sequence)
            {
                barrier.run();
            }

            return availableSequence;
        }
    }

    final static class PhasedOff extends WaitStrategy {

        private final long spinTimeoutNanos;
        private final long yieldTimeoutNanos;
        private final WaitStrategy fallbackStrategy;
        PhasedOff(long spinTimeout, long yieldTimeout,
                                         TimeUnit units,
                                         WaitStrategy fallbackStrategy)
        {
            this.spinTimeoutNanos = units.toNanos(spinTimeout);
            this.yieldTimeoutNanos = spinTimeoutNanos + units.toNanos(yieldTimeout);
            this.fallbackStrategy = fallbackStrategy;
        }

        @Override
        public void signalAllWhenBlocking()
        {
            fallbackStrategy.signalAllWhenBlocking();
        }

        @Override
        public long waitFor(long sequence, LongSupplier cursor, Runnable barrier)
                throws InterruptedException
        {
            long availableSequence;
            long startTime = 0;
            int counter = SPIN_TRIES;

            do
            {
                if ((availableSequence = cursor.getAsLong()) >= sequence)
                {
                    return availableSequence;
                }

                if (0 == --counter)
                {
                    if (0 == startTime)
                    {
                        startTime = System.nanoTime();
                    }
                    else
                    {
                        long timeDelta = System.nanoTime() - startTime;
                        if (timeDelta > yieldTimeoutNanos)
                        {
                            return fallbackStrategy.waitFor(sequence, cursor, barrier);
                        }
                        else if (timeDelta > spinTimeoutNanos)
                        {
                            Thread.yield();
                        }
                    }
                    counter = SPIN_TRIES;
                }
                barrier.run();
            }
            while (true);
        }
        private static final int SPIN_TRIES = 10000;
    }

    final static class Parking extends WaitStrategy {

	    static final Parking INSTANCE = new Parking();

        private final int retries;

        Parking() {
            this(DEFAULT_RETRIES);
        }

        Parking(int retries) {
            this.retries = retries;
        }

        @Override
        public long waitFor(final long sequence, LongSupplier cursor, final Runnable barrier)
                throws InterruptedException
        {
            long availableSequence;
            int counter = retries;

            while ((availableSequence = cursor.getAsLong()) < sequence)
            {
                counter = applyWaitMethod(barrier, counter);
            }

            return availableSequence;
        }

        private int applyWaitMethod(final Runnable barrier, int counter)
                throws WaitStrategy.AlertException
        {
            barrier.run();

            if (counter > 100)
            {
                --counter;
            }
            else if (counter > 0)
            {
                --counter;
                Thread.yield();
            }
            else
            {
                LockSupport.parkNanos(1L);
            }

            return counter;
        }
        private static final int DEFAULT_RETRIES = 200;
    }

    final static class Yielding extends WaitStrategy {

	    static final Yielding INSTANCE = new Yielding();

	    @Override
	    public long waitFor(final long sequence, LongSupplier cursor, final Runnable barrier)
			    throws InterruptedException {
		    long availableSequence;
		    int counter = SPIN_TRIES;

		    while ((availableSequence = cursor.getAsLong()) < sequence) {
			    counter = applyWaitMethod(barrier, counter);
		    }

		    return availableSequence;
	    }

	    private int applyWaitMethod(final Runnable barrier, int counter)
			    throws WaitStrategy.AlertException {
		    barrier.run();

		    if (0 == counter) {
			    Thread.yield();
		    }
		    else {
			    --counter;
		    }

		    return counter;
	    }

	    private static final int SPIN_TRIES = 100;
    }
}


/*
 * 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.Iterator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.stream.Stream;

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

/**
 * Given a set of source Publishers the values of that Publisher is forwarded to the
 * subscriber which responds first with any signal.
 *
 * @param <T> the value type
 *
 * @see Reactive-Streams-Commons
 */
final class FluxFirstEmitting<T> extends Flux<T> implements SourceProducer<T> {

	final Publisher<? extends T>[] array;

	final Iterable<? extends Publisher<? extends T>> iterable;

	@SafeVarargs
	FluxFirstEmitting(Publisher<? extends T>... array) {
		this.array = Objects.requireNonNull(array, "array");
		this.iterable = null;
	}

	FluxFirstEmitting(Iterable<? extends Publisher<? extends T>> iterable) {
		this.array = null;
		this.iterable = Objects.requireNonNull(iterable);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void subscribe(CoreSubscriber<? super T> actual) {
		Publisher<? extends T>[] a = array;
		int n;
		if (a == null) {
			n = 0;
			a = new Publisher[8];

			Iterator<? extends Publisher<? extends T>> it;

			try {
				it = Objects.requireNonNull(iterable.iterator(),
						"The iterator returned is null");
			}
			catch (Throwable e) {
				Operators.error(actual, Operators.onOperatorError(e,
						actual.currentContext()));
				return;
			}

			for (; ; ) {

				boolean b;

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

				if (!b) {
					break;
				}

				Publisher<? extends T> p;

				try {
					p = Objects.requireNonNull(it.next(),
							"The Publisher returned by the iterator is null");
				}
				catch (Throwable e) {
					Operators.error(actual, Operators.onOperatorError(e,
							actual.currentContext()));
					return;
				}

				if (n == a.length) {
					Publisher<? extends T>[] c = new Publisher[n + (n >> 2)];
					System.arraycopy(a, 0, c, 0, n);
					a = c;
				}
				a[n++] = p;
			}

		}
		else {
			n = a.length;
		}

		if (n == 0) {
			Operators.complete(actual);
			return;
		}
		if (n == 1) {
			Publisher<? extends T> p = a[0];

			if (p == null) {
				Operators.error(actual,
						new NullPointerException("The single source Publisher is null"));
			}
			else {
				p.subscribe(actual);
			}
			return;
		}

		RaceCoordinator<T> coordinator = new RaceCoordinator<>(n);

		coordinator.subscribe(a, n, actual);
	}

	/**
	 * Returns a new instance which has the additional source to be amb'd together with
	 * the current array of sources.
	 *

	 * This operation doesn't change the current FluxFirstEmitting instance.
	 *
	 * @param source the new source to merge with the others
	 *
	 * @return the new FluxFirstEmitting instance or null if the Amb runs with an Iterable
	 */
	@Nullable
	FluxFirstEmitting<T> ambAdditionalSource(Publisher<? extends T> source) {
		if (array != null) {
			int n = array.length;
			@SuppressWarnings("unchecked") Publisher<? extends T>[] newArray =
					new Publisher[n + 1];
			System.arraycopy(array, 0, newArray, 0, n);
			newArray[n] = source;

			return new FluxFirstEmitting<>(newArray);
		}
		return null;
	}

	@Override
	public Object scanUnsafe(Attr key) {
		return null; //no particular key to be represented, still useful in hooks
	}

	static final class RaceCoordinator<T>
			implements Subscription, Scannable {

		final FirstEmittingSubscriber<T>[] subscribers;

		volatile boolean cancelled;

		volatile int wip;
		@SuppressWarnings("rawtypes")
		static final AtomicIntegerFieldUpdater<RaceCoordinator> WIP =
				AtomicIntegerFieldUpdater.newUpdater(RaceCoordinator.class, "wip");

		@SuppressWarnings("unchecked")
		RaceCoordinator(int n) {
			subscribers = new FirstEmittingSubscriber[n];
			wip = Integer.MIN_VALUE;
		}

		@Override
		public Stream<? extends Scannable> inners() {
			return Stream.of(subscribers);
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.CANCELLED) return cancelled;

			return null;
		}

		void subscribe(Publisher<? extends T>[] sources,
				int n,
				CoreSubscriber<? super T> actual) {
			FirstEmittingSubscriber<T>[] a = subscribers;

			for (int i = 0; i < n; i++) {
				a[i] = new FirstEmittingSubscriber<>(actual, this, i);
			}

			actual.onSubscribe(this);

			for (int i = 0; i < n; i++) {
				if (cancelled || wip != Integer.MIN_VALUE) {
					return;
				}

				Publisher<? extends T> p = sources[i];

				if (p == null) {
					if (WIP.compareAndSet(this, Integer.MIN_VALUE, -1)) {
						actual.onError(new NullPointerException("The " + i + " th Publisher source is null"));
					}
					return;
				}

				p.subscribe(a[i]);
			}

		}

		@Override
		public void request(long n) {
			if (Operators.validate(n)) {
				int w = wip;
				if (w >= 0) {
					subscribers[w].request(n);
				}
				else {
					for (FirstEmittingSubscriber<T> s : subscribers) {
						s.request(n);
					}
				}
			}
		}

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

			int w = wip;
			if (w >= 0) {
				subscribers[w].cancel();
			}
			else {
				for (FirstEmittingSubscriber<T> s : subscribers) {
					s.cancel();
				}
			}
		}

		boolean tryWin(int index) {
			if (wip == Integer.MIN_VALUE) {
				if (WIP.compareAndSet(this, Integer.MIN_VALUE, index)) {

					FirstEmittingSubscriber<T>[] a = subscribers;
					int n = a.length;

					for (int i = 0; i < n; i++) {
						if (i != index) {
							a[i].cancel();
						}
					}

					return true;
				}
			}
			return false;
		}
	}

	static final class FirstEmittingSubscriber<T> extends Operators.DeferredSubscription
			implements InnerOperator<T, T> {

		final RaceCoordinator<T> parent;

		final CoreSubscriber<? super T> actual;

		final int index;

		boolean won;

		FirstEmittingSubscriber(CoreSubscriber<? super T> actual,
				RaceCoordinator<T> parent,
				int index) {
			this.actual = actual;
			this.parent = parent;
			this.index = index;
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.PARENT) return s;
			if (key == Attr.CANCELLED) return parent.cancelled;

			return InnerOperator.super.scanUnsafe(key);
		}

		@Override
		public void onSubscribe(Subscription s) {
			set(s);
		}

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

		@Override
		public void onNext(T t) {
			if (won) {
				actual.onNext(t);
			}
			else if (parent.tryWin(index)) {
				won = true;
				actual.onNext(t);
			}
		}

		@Override
		public void onError(Throwable t) {
			if (won) {
				actual.onError(t);
			}
			else if (parent.tryWin(index)) {
				won = true;
				actual.onError(t);
			}
		}

		@Override
		public void onComplete() {
			if (won) {
				actual.onComplete();
			}
			else if (parent.tryWin(index)) {
				won = true;
				actual.onComplete();
			}
		}
	}
}


/*
 * 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.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

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

/**
 * Concatenates a fixed array of Publishers' values.
 *
 * @param <T> the value type
 * @see Reactive-Streams-Commons
 */
final class FluxConcatArray<T> extends Flux<T> implements SourceProducer<T> {

	final Publisher<? extends T>[] array;

	final boolean delayError;

	@SafeVarargs
	FluxConcatArray(boolean delayError, Publisher<? extends T>... array) {
		this.array = Objects.requireNonNull(array, "array");
		this.delayError = delayError;
	}

	@Override
	public void subscribe(CoreSubscriber<? super T> actual) {
		Publisher<? extends T>[] a = array;

		if (a.length == 0) {
			Operators.complete(actual);
			return;
		}
		if (a.length == 1) {
			Publisher<? extends T> p = a[0];

			if (p == null) {
				Operators.error(actual, new NullPointerException("The single source Publisher is null"));
			} else {
				p.subscribe(actual);
			}
			return;
		}

		if (delayError) {
			ConcatArrayDelayErrorSubscriber<T> parent = new
					ConcatArrayDelayErrorSubscriber<>(actual, a);

			actual.onSubscribe(parent);

			if (!parent.isCancelled()) {
				parent.onComplete();
			}
			return;
		}
		ConcatArraySubscriber<T> parent = new ConcatArraySubscriber<>(actual, a);

		actual.onSubscribe(parent);

		if (!parent.isCancelled()) {
			parent.onComplete();
		}
	}

	@Override
	public Object scanUnsafe(Attr key) {
		if (key == Attr.DELAY_ERROR) return delayError;
		return null;
	}

	/**
	 * Returns a new instance which has the additional source to be merged together with
	 * the current array of sources.
	 *

	 * This operation doesn't change the current FluxMerge instance.
	 *
	 * @param source the new source to merge with the others
	 * @return the new FluxConcatArray instance
	 */
	FluxConcatArray<T> concatAdditionalSourceLast(Publisher<? extends T> source) {
		int n = array.length;
		@SuppressWarnings("unchecked")
		Publisher<? extends T>[] newArray = new Publisher[n + 1];
		System.arraycopy(array, 0, newArray, 0, n);
		newArray[n] = source;

		return new FluxConcatArray<>(delayError, newArray);
	}

	/**
	 * Returns a new instance which has the additional source to be merged together with
	 * the current array of sources.
	 *

	 * This operation doesn't change the current FluxMerge instance.
	 *
	 * @param source the new source to merge with the others
	 * @return the new FluxConcatArray instance
	 */
	@SuppressWarnings("unchecked")
	<V> FluxConcatArray<V> concatAdditionalIgnoredLast(Publisher<? extends V>
			source) {
		int n = array.length;
		Publisher<? extends V>[] newArray = new Publisher[n + 1];
		//noinspection SuspiciousSystemArraycopy
		System.arraycopy(array, 0, newArray, 0, n);
		newArray[n - 1] = Mono.ignoreElements(newArray[n - 1]);
		newArray[n] = source;

		return new FluxConcatArray<>(delayError, newArray);
	}

	/**
	 * Returns a new instance which has the additional first source to be concatenated together with
	 * the current array of sources.
	 *

	 * This operation doesn't change the current FluxConcatArray instance.
	 *
	 * @param source the new source to merge with the others
	 * @return the new FluxConcatArray instance
	 */
	FluxConcatArray<T> concatAdditionalSourceFirst(Publisher<? extends T> source) {
		int n = array.length;
		@SuppressWarnings("unchecked")
		Publisher<? extends T>[] newArray = new Publisher[n + 1];
		System.arraycopy(array, 0, newArray, 1, n);
		newArray[0] = source;

		return new FluxConcatArray<>(delayError, newArray);
	}

	static final class ConcatArraySubscriber<T>
			extends Operators.MultiSubscriptionSubscriber<T, T> {

		final Publisher<? extends T>[] sources;

		int index;

		volatile int wip;
		@SuppressWarnings("rawtypes")
		static final AtomicIntegerFieldUpdater<ConcatArraySubscriber> WIP =
		  AtomicIntegerFieldUpdater.newUpdater(ConcatArraySubscriber.class, "wip");

		long produced;

		ConcatArraySubscriber(CoreSubscriber<? super T> actual, Publisher<? extends T>[]
				sources) {
			super(actual);
			this.sources = sources;
		}

		@Override
		public void onNext(T t) {
			produced++;

			actual.onNext(t);
		}

		@Override
		public void onComplete() {
			if (WIP.getAndIncrement(this) == 0) {
				Publisher<? extends T>[] a = sources;
				do {

					if (isCancelled()) {
						return;
					}

					int i = index;
					if (i == a.length) {
						actual.onComplete();
						return;
					}

					Publisher<? extends T> p = a[i];

					if (p == null) {
						actual.onError(new NullPointerException("Source Publisher at index " + i + " is null"));
						return;
					}

					long c = produced;
					if (c != 0L) {
						produced = 0L;
						produced(c);
					}
					p.subscribe(this);

					if (isCancelled()) {
						return;
					}

					index = ++i;
				} while (WIP.decrementAndGet(this) != 0);
			}

		}
	}

	static final class ConcatArrayDelayErrorSubscriber<T>
			extends Operators.MultiSubscriptionSubscriber<T, T> {

		final Publisher<? extends T>[] sources;

		int index;

		volatile int wip;
		@SuppressWarnings("rawtypes")
		static final AtomicIntegerFieldUpdater<ConcatArrayDelayErrorSubscriber> WIP =
		AtomicIntegerFieldUpdater.newUpdater(ConcatArrayDelayErrorSubscriber.class, "wip");

		volatile Throwable error;
		@SuppressWarnings("rawtypes")
		static final AtomicReferenceFieldUpdater<ConcatArrayDelayErrorSubscriber, Throwable> ERROR =
				AtomicReferenceFieldUpdater.newUpdater(ConcatArrayDelayErrorSubscriber.class, Throwable.class, "error");

		long produced;

		ConcatArrayDelayErrorSubscriber(CoreSubscriber<? super T> actual, Publisher<?
				extends T>[] sources) {
			super(actual);
			this.sources = sources;
		}

		@Override
		public void onNext(T t) {
			produced++;

			actual.onNext(t);
		}

		@Override
		public void onError(Throwable t) {
			if (Exceptions.addThrowable(ERROR, this, t)) {
				onComplete();
			} else {
				Operators.onErrorDropped(t, actual.currentContext());
			}
		}

		@Override
		@Nullable
		public Object scanUnsafe(Attr key) {
			if (key == Attr.DELAY_ERROR) return true;
			if (key == Attr.ERROR) return error;

			return super.scanUnsafe(key);
		}

		@Override
		public void onComplete() {
			if (WIP.getAndIncrement(this) == 0) {
				Publisher<? extends T>[] a = sources;
				do {

					if (isCancelled()) {
						return;
					}

					int i = index;
					if (i == a.length) {
						Throwable e = Exceptions.terminate(ERROR, this);
						if (e != null) {
							actual.onError(e);
						} else {
							actual.onComplete();
						}
						return;
					}

					Publisher<? extends T> p = a[i];

					if (p == null) {
						actual.onError(new NullPointerException("Source Publisher at index " + i + " is null"));
						return;
					}

					long c = produced;
					if (c != 0L) {
						produced = 0L;
						produced(c);
					}
					p.subscribe(this);

					if (isCancelled()) {
						return;
					}

					index = ++i;
				} while (WIP.decrementAndGet(this) != 0);
			}

		}
	}

}


/*
 * 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.time.Duration;
import java.util.function.Consumer;

import reactor.core.Disposable;
import reactor.core.Fuseable;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;

/**
 * The abstract base class for connectable publishers that let subscribers pile up
 * before they connect to their data source.
 *
 * @see #publish
 * @see Reactive-Streams-Commons
 * @param <T> the input and output value type
 */
public abstract class ConnectableFlux<T> extends Flux<T> {

	/**
	 * Connects this {@link ConnectableFlux} to the upstream source when the first {@link org.reactivestreams.Subscriber}
	 * subscribes.
	 *
	 *

	 *
	 *
	 * @return a {@link Flux} that connects to the upstream source when the first {@link org.reactivestreams.Subscriber} subscribes
	 */
	public final Flux<T> autoConnect() {
		return autoConnect(1);
	}

	/**
	 * Connects this {@link ConnectableFlux} to the upstream source when the specified amount of
	 * {@link org.reactivestreams.Subscriber} subscribes.
	 *

	 * Subscribing and immediately unsubscribing still contributes to the counter that
	 * triggers the connection.
	 *
	 *

	 *
	 *
	 * @param minSubscribers the minimum number of subscribers
	 *
	 * @return a {@link Flux} that connects to the upstream source when the given amount of Subscribers subscribed
	 */
	public final Flux<T> autoConnect(int minSubscribers) {
		return autoConnect(minSubscribers, NOOP_DISCONNECT);
	}

	/**
	 * Connects this {@link ConnectableFlux} to the upstream source when the specified amount of
	 * {@link org.reactivestreams.Subscriber} subscribes and calls the supplied consumer with a runnable that allows disconnecting.
	 *

	 *
	 *
	 * @param minSubscribers the minimum number of subscribers
	 * @param cancelSupport the consumer that will receive the {@link Disposable} that allows disconnecting
	 * @return a {@link Flux} that connects to the upstream source when the given amount of subscribers subscribed
	 */
	public final Flux<T> autoConnect(int minSubscribers, Consumer<? super Disposable> cancelSupport) {
		if (minSubscribers == 0) {
			connect(cancelSupport);
			return this;
		}
		if(this instanceof Fuseable){
			return onAssembly(new FluxAutoConnectFuseable<>(this,
					minSubscribers,
					cancelSupport));
		}
		return onAssembly(new FluxAutoConnect<>(this, minSubscribers,
				cancelSupport));
	}

	/**
	 * Connect this {@link ConnectableFlux} to its source and return a {@link Runnable} that
	 * can be used for disconnecting.
	 * @return the {@link Disposable} that allows disconnecting the connection after.
	 */
	public final Disposable connect() {
		final Disposable[] out = { null };
		connect(r -> out[0] = r);
		return out[0];
	}

	/**
	 * Connects this {@link ConnectableFlux} to its source and sends a {@link Disposable} to a callback that
	 * can be used for disconnecting.
	 *
	 *
The call should be idempotent in respect of connecting the first
	 * and subsequent times. In addition the disconnection should be also tied
	 * to a particular connection (so two different connection can't disconnect the other).
	 *
	 * @param cancelSupport the callback is called with a Disposable instance that can
	 * be called to disconnect the source, even synchronously.
	 */
	public abstract void connect(Consumer<? super Disposable> cancelSupport);

	@Override
	public final ConnectableFlux<T> hide() {
		return new ConnectableFluxHide<>(this);
	}

	/**
	 * Connects to the upstream source when the first {@link org.reactivestreams.Subscriber} subscribes and disconnects
	 * when all Subscribers cancelled or the upstream source completed.
	 *
	 *

	 *
	 *
	 * @return a reference counting {@link Flux}
	 */
	public final Flux<T> refCount() {
		return refCount(1);
	}

	/**
	 * Connects to the upstream source when the given number of {@link org.reactivestreams.Subscriber} subscribes and disconnects
	 * when all Subscribers cancelled or the upstream source completed.
	 *
	 *

	 *
	 *
	 * @param minSubscribers the number of subscribers expected to subscribe before connection
	 *
	 * @return a reference counting {@link Flux}
	 */
	public final Flux<T> refCount(int minSubscribers) {
		return onAssembly(new FluxRefCount<>(this, minSubscribers));
	}

	/**
	 * Connects to the upstream source when the given number of {@link org.reactivestreams.Subscriber} subscribes.
	 * Disconnection can happen in two scenarios: when the upstream source completes (or errors) then
	 * there is an immediate disconnection. However, when all subscribers have cancelled,
	 * a deferred disconnection is scheduled. If any new subscriber comes
	 * in during the {@code gracePeriod} that follows, the disconnection is cancelled.
	 *
	 *

	 *
	 *
	 * @param minSubscribers the number of subscribers expected to subscribe before connection
	 * @param gracePeriod the {@link Duration} for which to wait for new subscribers before actually
	 * disconnecting when all subscribers have cancelled.
	 *
	 * @return a reference counting {@link Flux} with a grace period for disconnection
	 */
	public final Flux<T> refCount(int minSubscribers, Duration gracePeriod) {
		return refCount(minSubscribers, gracePeriod, Schedulers.parallel());
	}

	/**
	 * Connects to the upstream source when the given number of {@link org.reactivestreams.Subscriber} subscribes.
	 * Disconnection can happen in two scenarios: when the upstream source completes (or errors) then
	 * there is an immediate disconnection. However, when all subscribers have cancelled,
	 * a deferred disconnection is scheduled. If any new subscriber comes
	 * in during the {@code gracePeriod} that follows, the disconnection is cancelled.
	 *
	 *

	 *
	 *
	 * @param minSubscribers the number of subscribers expected to subscribe before connection
	 * @param gracePeriod the {@link Duration} for which to wait for new subscribers before actually
	 * disconnecting when all subscribers have cancelled.
	 * @param scheduler the {@link Scheduler} on which to run timeouts
	 *
	 * @return a reference counting {@link Flux} with a grace period for disconnection
	 */
	public final Flux<T> refCount(int minSubscribers, Duration gracePeriod, Scheduler scheduler) {
		return onAssembly(new FluxRefCountGrace<>(this, minSubscribers, gracePeriod, scheduler));
	}

	static final Consumer<Disposable> NOOP_DISCONNECT = runnable -> {

	};
}