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

co.easimart.TaskQueue Maven / Gradle / Ivy

package co.easimart;

import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import bolts.Continuation;
import bolts.Task;

/**
 * A helper class for enqueueing tasks
 */
/** package */ class TaskQueue {
  /**
   * We only need to keep the tail of the queue. Cancelled tasks will just complete
   * normally/immediately when their turn arrives.
   */
  private Task tail;
  private final Lock lock = new ReentrantLock();

  /**
   * Gets a task that can be safely awaited and is dependent on the current tail of the queue. This
   * essentially gives us a proxy for the tail end of the queue that can be safely cancelled.
   * 
   * @return A new task that should be awaited by enqueued tasks.
   */
  private Task getTaskToAwait() {
    lock.lock();
    try {
      Task toAwait = tail != null ? tail : Task. forResult(null);
      return toAwait.continueWith(new Continuation() {
        @Override
        public Void then(Task task) throws Exception {
          return null;
        }
      });
    } finally {
      lock.unlock();
    }
  }

  /**
   * Enqueues a task created by taskStart.
   * 
   * @param taskStart
   *          A function given a task to await once state is snapshotted (e.g. after capturing
   *          session tokens at the time of the save call). Awaiting this task will wait for the
   *          created task's turn in the queue.
   * @return The task created by the taskStart function.
   */
   Task enqueue(Continuation> taskStart) {
    lock.lock();
    try {
      Task task;
      Task oldTail = tail != null ? tail : Task. forResult(null);
      // The task created by taskStart is responsible for waiting for the task passed into it before
      // doing its work (this gives it an opportunity to do startup work or save state before
      // waiting for its turn in the queue)
      try {
        Task toAwait = getTaskToAwait();
        task = taskStart.then(toAwait);
      } catch (RuntimeException e) {
        throw e;
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

      // The tail task should be dependent on the old tail as well as the newly-created task. This
      // prevents cancellation of the new task from causing the queue to run out of order.
      tail = Task.whenAll(Arrays.asList(oldTail, task));
      return task;
    } finally {
      lock.unlock();
    }
  }

  /**
   * Creates a continuation that will wait for the given task to complete before running the next
   * continuations.
   */
  static  Continuation> waitFor(final Task toAwait) {
    return new Continuation>() {
      @Override
      public Task then(final Task task) throws Exception {
        return toAwait.continueWithTask(new Continuation>() {
          @Override
          public Task then(Task ignored) throws Exception {
            return task;
          }
        });
      }
    };
  }

  Lock getLock() {
    return lock;
  }

  void waitUntilFinished() throws InterruptedException {
    lock.lock();
    try {
      if (tail == null) {
        return;
      }
      tail.waitForCompletion();
    } finally {
      lock.unlock();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy