Skip to content

Commit

Permalink
Improved the TaskRunner api so that it now supports passing parameters.
Browse files Browse the repository at this point in the history
  • Loading branch information
codeka committed Sep 24, 2018
1 parent 205f416 commit 819ac25
Show file tree
Hide file tree
Showing 12 changed files with 411 additions and 93 deletions.
@@ -0,0 +1,10 @@
package au.com.codeka.warworlds.client.concurrency;

/** Simple wrapper around GmsCore tasks API. */
public class GmsTask<R> extends Task<Void, R> {
public GmsTask(TaskRunner taskRunner, com.google.android.gms.tasks.Task<R> gmsTask) {
super(taskRunner);
gmsTask.addOnFailureListener(this::onError);
gmsTask.addOnCompleteListener(task -> this.onComplete(task.getResult()));
}
}
@@ -0,0 +1,32 @@
package au.com.codeka.warworlds.client.concurrency;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
* A queue of tasks that we can run at any time. Useful for posting runnables to threads.
*/
public class RunnableQueue {
private final Queue<Runnable> runnables;

public RunnableQueue(int maxQueuedItems) {
runnables = new LinkedBlockingQueue<>(maxQueuedItems);
}

public void post(Runnable runnable) {
synchronized (runnables) {
runnables.add(runnable);
}
}

/** Runs all runnables on the queue. */
public void runAllTasks() {
// TODO: should we pull these off into another list so the we can unblock the thread?
synchronized (runnables) {
while (!runnables.isEmpty()) {
Runnable runnable = runnables.remove();
runnable.run();
}
}
}
}
@@ -0,0 +1,90 @@
package au.com.codeka.warworlds.client.concurrency;

/**
* A {@link Task} that encapsulates a {@link Runnable}, {@link RunnableP}, {@link RunnableR} or
* {@link RunnablePR} that you want to run on a particular thread.
*
* @param <P> The parameter type. Depending on the type of runnable you're using, this may or may
* not be ignored.
* @param <R> The result type. Depending on the type of runnable you're using, this may or may
* not be ignored.
*/
public class RunnableTask<P, R> extends Task<P, R> {
/** A runnable that takes a parameter. */
public interface RunnableP<P> {
void run(P param);
}

/** A runnable that returns a value. */
public interface RunnableR<R> {
R run();
}

/** A runnable that takes a parameter and returns a value. */
public interface RunnablePR<P, R> {
R run(P param);
}

private final Runnable runnable;
private final RunnableP<P> runnableP;
private final RunnableR<R> runnableR;
private final RunnablePR<P, R> runnablePR;
private final Threads thread;

public RunnableTask(TaskRunner taskRunner, Runnable runnable, Threads thread) {
super(taskRunner);
this.runnable = runnable;
this.runnableP = null;
this.runnableR = null;
this.runnablePR = null;
this.thread = thread;
}

public RunnableTask(TaskRunner taskRunner, RunnableP<P> runnable, Threads thread) {
super(taskRunner);
this.runnable = null;
this.runnableP = runnable;
this.runnableR = null;
this.runnablePR = null;
this.thread = thread;
}

public RunnableTask(TaskRunner taskRunner, RunnableR<R> runnable, Threads thread) {
super(taskRunner);
this.runnable = null;
this.runnableP = null;
this.runnableR = runnable;
this.runnablePR = null;
this.thread = thread;
}

public RunnableTask(TaskRunner taskRunner, RunnablePR<P, R> runnable, Threads thread) {
super(taskRunner);
this.runnable = null;
this.runnableP = null;
this.runnableR = null;
this.runnablePR = runnable;
this.thread = thread;
}

@Override
public void run(P param) {
thread.run(() -> {
try {
R result = null;
if (runnable != null) {
runnable.run();
} else if (runnableP != null) {
runnableP.run(param);
} else if (runnableR != null) {
result = runnableR.run();
} else if (runnablePR != null) {
result = runnablePR.run(param);
}
onComplete(result);
} catch (Exception e) {
onError(e);
}
});
}
}
@@ -0,0 +1,162 @@
package au.com.codeka.warworlds.client.concurrency;

import java.util.ArrayList;
import java.util.List;

/**
* Wrapper for a "task", which allows chaining of following tasks (via {@link #then}) and handling
* of errors (via {@link #error}).
*
* @param <P> the type of the input parameter to this task. Can be Void if you want nothing.
* @param <R> the type of the return value from the task. Can be Void if you want nothing.
*/
public class Task<P, R> {
private final TaskRunner taskRunner;
private List<Task<R, ?>> thenTasks;
private List<Task<Exception, Void>> errorTasks;
private final Object lock = new Object();
private boolean finished;
private R result;
private Exception error;

Task(TaskRunner taskRunner) {
this.taskRunner = taskRunner;
}

void run(P param) {
}

protected void onComplete(R result) {
synchronized (lock) {
finished = true;
this.result = result;
if (thenTasks != null) {
for (Task<R, ?> task : thenTasks) {
taskRunner.runTask(task, result);
}
thenTasks = null;
}
}
}

protected void onError(Exception error) {
synchronized (lock) {
finished = true;
this.error = error;

if (errorTasks != null) {
for (Task<Exception, Void> task : errorTasks) {
taskRunner.runTask(task, null);
}
errorTasks = null;
}
}
}

/**
* Queues up the given {@link Task} to run after this task. It will be handed this task's result
* as it's parameter.
* @param task The task to queue after this task completes.
* @return The new task (so you can chain .then().then().then() calls to get tasks to run one
* after the other.
*/
public <TR> Task<R, TR> then(Task<R, TR> task) {
synchronized (lock) {
if (finished) {
if (error == null) {
taskRunner.runTask(task, result);
}
return task;
}
if (this.thenTasks == null) {
this.thenTasks = new ArrayList<>();
}
this.thenTasks.add(task);
}

return task;
}

/**
* Queues the given runnable to run after this task. If this task returns a result, obviously the
* runnable will not know what it was.
*
* @param runnable The runnable to run after this task completes.
* @param thread The {@link Threads} on which to run the runnable.
* @return The new task (so you can chain .then().then().then() calls to get tasks to run one
* after the other.
*/
public Task<R, Void> then(Runnable runnable, Threads thread) {
return then(new RunnableTask<R, Void>(taskRunner, runnable, thread));
}

/**
* Queues the given runnable to run after this task. If this task returns a result, obviously the
* runnable will not know what it was.
*
* @param runnable The runnable to run after this task completes.
* @param thread The {@link Threads} on which to run the runnable.
* @return The new task (so you can chain .then().then().then() calls to get tasks to run one
* after the other.
*/
public Task<R, Void> then(RunnableTask.RunnableP<R> runnable, Threads thread) {
return then(new RunnableTask<R, Void>(taskRunner, runnable, thread));
}

/**
* Queues the given runnable to run after this task. If this task returns a result, obviously the
* runnable will not know what it was.
*
* @param runnable The runnable to run after this task completes.
* @param thread The {@link Threads} on which to run the runnable.
* @return The new task (so you can chain .then().then().then() calls to get tasks to run one
* after the other.
*/
public <RR> Task<R, RR> then(RunnableTask.RunnableR<RR> runnable, Threads thread) {
return then(new RunnableTask<>(taskRunner, runnable, thread));
}

/**
* Queues the given runnable to run after this task. If this task returns a result, obviously the
* runnable will not know what it was.
*
* @param runnable The runnable to run after this task completes.
* @param thread The {@link Threads} on which to run the runnable.
* @return The new task (so you can chain .then().then().then() calls to get tasks to run one
* after the other.
*/
public <RR> Task<R, RR> then(RunnableTask.RunnablePR<R, RR> runnable, Threads thread) {
return then(new RunnableTask<>(taskRunner, runnable, thread));
}

/**
* Queue a task to run in case there's an exception running the current task.
*
* @param task The task to run if there's an error.
* @return The current task, so you can queue up calls like task.error().then() to handle both
* the error case and the 'next' case.
*/
public Task<P, R> error(Task<Exception, Void> task) {
synchronized (lock) {
if (finished) {
if (error != null) {
taskRunner.runTask(task, error);
}
return this;
}
if (this.errorTasks == null) {
this.errorTasks = new ArrayList<>();
}
this.errorTasks.add(task);
}
return this;
}

public Task<P, R> error(Runnable runnable, Threads thread) {
return error(new RunnableTask<>(taskRunner, runnable, thread));
}

public Task<P, R> error(RunnableTask.RunnableP<Exception> runnable, Threads thread) {
return error(new RunnableTask<>(taskRunner, runnable, thread));
}
}

This file was deleted.

0 comments on commit 819ac25

Please sign in to comment.