Added OrderEnforcer class to schedule ordered tasks.

master
Greyson Parrelli 2018-09-20 13:25:40 -07:00
parent bcebf58b76
commit e63773e5c8
2 changed files with 178 additions and 0 deletions

View File

@ -0,0 +1,74 @@
package org.thoughtcrime.securesms.camera;
import android.support.annotation.NonNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
public class OrderEnforcer<E> {
private final E[] stages;
private final Map<E, Integer> stageIndices;
private final Map<E, List<Runnable>> actions;
private final boolean[] completion;
public OrderEnforcer(@NonNull E... stages) {
this.stages = stages;
this.stageIndices = new HashMap<>();
this.actions = new HashMap<>();
this.completion = new boolean[stages.length];
for (int i = 0; i < stages.length; i++) {
stageIndices.put(stages[i], i);
}
}
public synchronized void run(@NonNull E stage, Runnable r) {
if (isCompletedThrough(stage)) {
r.run();
} else {
List<Runnable> stageActions = actions.containsKey(stage) ? actions.get(stage) : new CopyOnWriteArrayList<>();
stageActions.add(r);
actions.put(stage, stageActions);
}
}
public synchronized void markCompleted(@NonNull E stage) {
completion[stageIndices.get(stage)] = true;
int i = 0;
while (i < completion.length && completion[i]) {
List<Runnable> stageActions = actions.get(stages[i]);
if (stageActions != null) {
for (Runnable r : stageActions) {
r.run();
}
stageActions.clear();
}
i++;
}
}
public synchronized void reset() {
for (int i = 0; i < completion.length; i++) {
completion[i] = false;
}
actions.clear();
}
private boolean isCompletedThrough(@NonNull E stage) {
int index = stageIndices.get(stage);
int i = 0;
while (i <= index && i < completion.length) {
if (!completion[i]) {
return false;
}
i++;
}
return true;
}
}

View File

@ -0,0 +1,104 @@
package org.thoughtcrime.securesms.camera;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicInteger;
import static junit.framework.Assert.assertEquals;
public class OrderEnforcerTest {
@Test
public void markCompleted_singleEntry() {
AtomicInteger counter = new AtomicInteger(0);
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
enforcer.run(Stage.A, new CountRunnable(counter));
assertEquals(0, counter.get());
enforcer.markCompleted(Stage.A);
assertEquals(1, counter.get());
}
@Test
public void markCompleted_singleEntry_waterfall() {
AtomicInteger counter = new AtomicInteger(0);
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
enforcer.run(Stage.C, new CountRunnable(counter));
assertEquals(0, counter.get());
enforcer.markCompleted(Stage.A);
assertEquals(0, counter.get());
enforcer.markCompleted(Stage.C);
assertEquals(0, counter.get());
enforcer.markCompleted(Stage.B);
assertEquals(1, counter.get());
}
@Test
public void markCompleted_multipleEntriesPerStage_waterfall() {
AtomicInteger counter = new AtomicInteger(0);
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
enforcer.run(Stage.A, new CountRunnable(counter));
enforcer.run(Stage.A, new CountRunnable(counter));
assertEquals(0, counter.get());
enforcer.run(Stage.B, new CountRunnable(counter));
enforcer.run(Stage.B, new CountRunnable(counter));
assertEquals(0, counter.get());
enforcer.run(Stage.C, new CountRunnable(counter));
enforcer.run(Stage.C, new CountRunnable(counter));
assertEquals(0, counter.get());
enforcer.run(Stage.D, new CountRunnable(counter));
enforcer.run(Stage.D, new CountRunnable(counter));
assertEquals(0, counter.get());
enforcer.markCompleted(Stage.A);
assertEquals(counter.get(), 2);
enforcer.markCompleted(Stage.D);
assertEquals(counter.get(), 2);
enforcer.markCompleted(Stage.B);
assertEquals(counter.get(), 4);
enforcer.markCompleted(Stage.C);
assertEquals(counter.get(), 8);
}
@Test
public void run_alreadyCompleted() {
AtomicInteger counter = new AtomicInteger(0);
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
enforcer.markCompleted(Stage.A);
enforcer.markCompleted(Stage.B);
enforcer.run(Stage.B, new CountRunnable(counter));
assertEquals(1, counter.get());
}
private static class CountRunnable implements Runnable {
private final AtomicInteger counter;
public CountRunnable(AtomicInteger counter) {
this.counter = counter;
}
@Override
public void run() {
counter.incrementAndGet();
}
}
private enum Stage {
A, B, C, D
}
}