Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improved the TaskRunner api so that it now supports passing parameters.
- Loading branch information
Showing
12 changed files
with
411 additions
and
93 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
client/src/main/java/au/com/codeka/warworlds/client/concurrency/GmsTask.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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())); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
client/src/main/java/au/com/codeka/warworlds/client/concurrency/RunnableQueue.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
client/src/main/java/au/com/codeka/warworlds/client/concurrency/RunnableTask.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
}); | ||
} | ||
} |
162 changes: 162 additions & 0 deletions
162
client/src/main/java/au/com/codeka/warworlds/client/concurrency/Task.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)); | ||
} | ||
} |
32 changes: 0 additions & 32 deletions
32
client/src/main/java/au/com/codeka/warworlds/client/concurrency/TaskQueue.java
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.