Package winterwell.utils.threads

Source Code of winterwell.utils.threads.TaskRunner

/**
*
*/
package winterwell.utils.threads;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import winterwell.utils.reporting.Log;
import winterwell.utils.time.StopWatch;

/**
* Run tasks in an offline asynchronous manner. Allows some management of the
* queue.
*
* Intended usage: for handling relatively few relatively slow tasks.
*
* @author daniel
* @testedby {@link TaskRunnerTest}
*/
public class TaskRunner {

  /** default for arbitrary tasks */
  private static TaskRunner dflt;
  /**
   * default for high-priority tasks -- which must be fast!
   */
  private static TaskRunner highPriority;

  /**
   * how many tasks to keep around in done
   */
  private static final int HISTORY = 6;

  private static final int NUM_THREADS_FOR_DEFAULT = 6;

  public static TaskRunner getDefault() {
    if (dflt == null) {
      dflt = new TaskRunner(NUM_THREADS_FOR_DEFAULT);
    }
    return dflt;
  }

  /**
   * This is a default task-runner for fast high-priority tasks. E.g. tasks
   * which cause the user-interface to lag. Do NOT submit slow tasks!
   */
  public static TaskRunner getFastDefault() {
    if (highPriority == null) {
      highPriority = new TaskRunner(NUM_THREADS_FOR_DEFAULT);
    }
    return highPriority;
  }

  public static void setDefault(TaskRunner dflt) {
    TaskRunner.dflt = dflt;
  }

  /**
   * Includes cancelled tasks
   */
  private final Queue<ATask> done = new ConcurrentLinkedQueue<ATask>();

  private final ExecutorService exec;

  /**
   * Includes running tasks
   */
  private final Queue<ATask> todo = new ConcurrentLinkedQueue<ATask>();

  /**
   * Create a 2-thread TaskRunner
   */
  public TaskRunner() {
    this(2);
  }

  /**
   * A TaskRunner which uses the current thread
   *
   * @param thisThread
   *            Must be true
   */
  public TaskRunner(boolean thisThread) {
    assert thisThread;
    exec = new AbstractExecutorService() {
      AtomicInteger running = new AtomicInteger();

      @Override
      public boolean awaitTermination(long timeout, TimeUnit unit)
          throws InterruptedException {
        timeout = unit.toMillis(timeout);
        StopWatch sw = new StopWatch();
        while (running.get() > 0) {
          if (sw.getTime() >= timeout)
            return false;
        }
        return true;
      }

      @Override
      public void execute(Runnable command) {
        running.incrementAndGet();
        command.run();
        running.decrementAndGet();
      }

      @Override
      public boolean isShutdown() {
        return false;
      }

      @Override
      public boolean isTerminated() {
        return false;
      }

      @Override
      public void shutdown() {
      }

      @Override
      public List<Runnable> shutdownNow() {
        return Collections.emptyList();
      }
    };
  }

  public TaskRunner(int numThreads) {
    assert numThreads > 0;
    exec = Executors.newFixedThreadPool(numThreads);
  }

  /**
   * Clean up any references to task. Called by the task on success, failure
   * or cancellation.
   *
   * @param task
   */
  protected synchronized void done(ATask task) {
    todo.remove(task);
    done.add(task);
    if (done.size() >= HISTORY) {
      done.remove();
    }
  }

  /**
   * Most recent finished tasks. Includes cancelled tasks.
   */
  public Collection<ATask> getDone() {
    return done;
  }

  public int getQueueSize() {
    return todo.size();
  }

  /**
   * pending and currently-running tasks
   *
   * @return
   */
  public Collection<ATask> getTodo() {
    return todo;
  }

  /**
   * Is the task running or queued. Task equality is determined by equals() in
   * the usual way.
   *
   * @param task
   * @return true if in {@link #todo}
   */
  public boolean hasTask(ATask task) {
    return todo.contains(task);
  }

  /**
   * Called when a task throws an exception. Reports it to {@link Log}. Can be
   * over-ridden.
   * <p>
   * Note that the {@link Future} object will also still throw an exception.
   *
   * @param noisyRunnable
   * @param e
   */
  public void report(Object runnableOrCallable, Throwable e) {
    Log.report(e);
  }

  /**
   * Submit a task for processing. Throws an @link
   * {@link IllegalArgumentException} if an equivalent task is already running
   * or queued. (unless overridden, an equivalent task is an identical one).
   * TODO: Possibly introduce a trySubmit() that always succeeds
   *
   * @param task
   * @return
   */
  public synchronized Future submit(ATask task) {
    if (todo.contains(task))
      throw new IllegalArgumentException(
          "An equivalent task is already running/queued");
    task.setTaskRunner(this);
    todo.add(task);
    Future f = exec.submit(task);
    return f;
  }

}
TOP

Related Classes of winterwell.utils.threads.TaskRunner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.