/* This file is part of Reactive Cascade which is released under The MIT License. See license.md , https://github.com/futurice/cascade and http://reactivecascade.com for details. This is open source for the common good. Please contribute improvements by pull request or contact paulirotta@gmail.com */ package com.reactivecascade.util; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.reactivecascade.i.NotCallOrigin; import java.util.Deque; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; /** * The default implementation of the subscribe executor * <p> * Other classes may utilize this with startup parameter, extend it or substitute an own fresh * implementation to the {@link com.reactivecascade.i.IThreadType} interface. */ @NotCallOrigin public class DefaultThreadType extends AbstractThreadType { final boolean inOrderExecution; private volatile boolean wakeUpIsPending = false; // Efficiency filter to wake the ServiceExecutor only once TODO Is there a simpler way with AtomicBoolean? private final Runnable wakeUpRunnable = () -> { // Do nothing, this is just used for insurance fast flushing the ServiceExecutor queue when items are added out-of-order to the associated BlockingQueue wakeUpIsPending = false; }; /** * Construct a new thread group * * @param name of this thread type for debug displays * @param executorService for this thread type * @param queue may be null in which case {@link #isInOrderExecutor()} will return * <code>true</code>; may be {@link java.util.concurrent.BlockingDeque} in which * case {@link #isInOrderExecutor()} will return <code>false</code> */ public DefaultThreadType(@NonNull String name, @NonNull ExecutorService executorService, @Nullable BlockingQueue<Runnable> queue) { super(name, executorService, queue); this.inOrderExecution = queue == null || !(queue instanceof Deque); } @Override // IThreadType public void run(@NonNull Runnable runnable) { executorService.submit(runnable); } @Override // IThreadType @SuppressWarnings("unchecked") @NotCallOrigin public void runNext(@NonNull Runnable runnable) { if (inOrderExecution || queue == null) { run(runnable); return; } int n = queue.size(); if (n == 0) { run(runnable); return; } // Out of order execution is permitted and desirable to finish functional chains we have started before clouding memory and execution queues by starting more if (isInOrderExecutor()) { RCLog.v(this, "WARNING: runNext() on single threaded IThreadType. This will be run FIFO only after previously queued tasks"); queue.add(runnable); } else { ((BlockingDeque) queue).addFirst(runnable); } if (!wakeUpIsPending && ++n != queue.size()) { // The queue changed during submit- just be sure something is submitted to wake the executor right now to pull from the queue wakeUpIsPending = true; executorService.execute(wakeUpRunnable); } } @Override // IThreadType public boolean isInOrderExecutor() { return inOrderExecution; } }