Skip to content

Commit a9ed67a

Browse files
gselzerctrueden
authored andcommitted
Multi-stage progress infrastructure: first cut
1 parent 77a2c01 commit a9ed67a

3 files changed

Lines changed: 93 additions & 26 deletions

File tree

scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/progress/Progress.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,20 @@ public static void update(long numElements) {
8080
pingListeners();
8181
}
8282

83-
public static void defineStages(long numStages) {
84-
currentTask().defineStages(numStages);
83+
public static void defineTotalProgress(int opStages) {
84+
currentTask().defineTotalProgress(opStages);
85+
}
86+
87+
public static void defineTotalProgress(int opStages, int totalSubTasks) {
88+
currentTask().defineTotalProgress(opStages, totalSubTasks);
8589
}
8690

8791
public static void registerSubtasks() {
8892

8993
}
9094

91-
public static void maxForStage(long stage, long max) {
92-
ProgressibleObject o = progressibleStack.get().peek();
93-
o.task().setMax(max);
95+
public static void setStageMax(long max) {
96+
currentTask().setStageMax(max);
9497
}
9598

9699
private Progress() {}

scijava/scijava-ops-engine/src/main/java/org/scijava/ops/engine/progress/Task.java

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,24 @@
66

77
public class Task {
88

9+
private final Task parent;
10+
11+
public Task() {
12+
this.parent = null;
13+
}
14+
15+
public Task(Task parent) {
16+
this.parent = parent;
17+
}
18+
19+
long numStages = -1;
20+
AtomicLong stagesCompleted = new AtomicLong(0);
21+
22+
long numSubTasks = -1;
923
public List<Task> subTasks = new ArrayList<>();
24+
AtomicLong subTasksCompleted = new AtomicLong(0);
25+
26+
boolean tasksDefined = false;
1027

1128
AtomicLong max = new AtomicLong(0);
1229

@@ -15,38 +32,43 @@ public class Task {
1532
public String status;
1633

1734
public synchronized Task createSubtask() {
18-
Task sub = new Task();
35+
Task sub = new Task(this);
1936
subTasks.add(sub);
2037
return sub;
2138
}
2239

2340

2441
public void complete() {
2542
current.set(max.get());
26-
43+
if (parent != null) parent.recordSubtaskCompletion(this);
2744
}
2845

29-
public void setMax(long max) {
30-
this.max.set(max);
46+
private void recordSubtaskCompletion(Task task) {
47+
if (!subTasks.contains(task)) throw new IllegalArgumentException("Task " +
48+
task + " is not a subtask of Task " + this);
49+
subTasksCompleted.getAndIncrement();
50+
}
51+
52+
public boolean isComplete() {
53+
return max.get() == current.get() && subTasks.stream().allMatch(t -> t.isComplete());
3154
}
3255

56+
public void defineTotalProgress(int opStages) {
57+
defineTotalProgress(opStages, 0);
58+
}
3359

34-
// TODO: If an Op tries to call Progress.update() without these variables
35-
// being declared, throw an error!!!
36-
public void defineStages(long numStages) {
37-
38-
60+
public void defineTotalProgress(int opStages, int totalSubTasks) {
61+
this.numStages = opStages;
62+
this.numSubTasks = totalSubTasks;
63+
this.tasksDefined = true;
3964
}
4065

41-
// TODO: If an Op tries to call Progress.update() without these variables
42-
// being declared, throw an error!!!
43-
public void setSubTaskCount( long numSubTasks) {
44-
45-
66+
public boolean progressDefined () {
67+
return tasksDefined;
4668
}
4769

48-
public void setCurrent(long current) {
49-
this.current.set(current);
70+
public void setStageMax(long max) {
71+
this.max.set(max);
5072
}
5173

5274
public void setStatus(String status) {
@@ -62,11 +84,16 @@ public long current( ) {
6284
}
6385

6486
public double progress() {
65-
return (double) current() / max();
87+
if(!this.tasksDefined) throw new IllegalStateException("Progress undefined - Op does not define total progress");
88+
double totalCompletion = stagesCompleted.get();
89+
totalCompletion += current.doubleValue() / max.doubleValue();
90+
totalCompletion += subTasksCompleted.get();
91+
return totalCompletion / (numStages + numSubTasks);
6692
}
6793

6894

6995
public void update(long numElements) {
96+
if(numStages == 0) throw new IllegalStateException("");
7097
current.addAndGet(numElements);
7198
}
7299

scijava/scijava-ops-engine/src/test/java/org/scijava/ops/engine/progress/DefaultProgressTest.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
package org.scijava.ops.engine.progress;
33

4+
import java.util.function.BiFunction;
45
import java.util.function.Function;
56

67
import org.junit.Assert;
@@ -16,17 +17,32 @@ public class DefaultProgressTest extends AbstractTestEnvironment {
1617
@OpField(names = "test.progressReporter")
1718
public final Function<Integer, Integer> iterator = (iterations) -> {
1819
// set up progress reporter
19-
Progress.defineStages(1);
20-
Progress.maxForStage(0, iterations);
20+
Progress.defineTotalProgress(1);
21+
Progress.setStageMax(iterations);
2122

22-
for(int i = 0; i < iterations; i++) {
23+
for (int i = 0; i < iterations; i++) {
2324
Progress.update();
2425
}
2526
return iterations;
2627
};
2728

29+
@OpField(names = "test.progressReporter")
30+
public final BiFunction<Integer, Integer, Integer> doubleIterator = (
31+
numStages, iterationsPerStage) -> {
32+
// set up progress reporter
33+
Progress.defineTotalProgress(numStages);
34+
for (int j = 0; j < numStages; j++) {
35+
Progress.setStageMax(iterationsPerStage);
36+
37+
for (int i = 0; i < iterationsPerStage; i++) {
38+
Progress.update();
39+
}
40+
}
41+
return numStages * iterationsPerStage;
42+
};
43+
2844
@Test
29-
public void testLongOp() throws InterruptedException {
45+
public void testSimpleReporter() throws InterruptedException {
3046
// obtain the Op
3147
Function<Integer, Integer> op = //
3248
ops.op("test.progressReporter") //
@@ -45,6 +61,27 @@ public void testLongOp() throws InterruptedException {
4561

4662
}
4763

64+
@Test
65+
public void testMultiStageReporter() throws InterruptedException {
66+
// obtain the Op
67+
BiFunction<Integer,Integer, Integer> op = //
68+
ops.op("test.progressReporter") //
69+
.inType(Integer.class, Integer.class) //
70+
.outType(Integer.class) //
71+
.function();
72+
73+
int numIterations = 100;
74+
int numStages = 10;
75+
Progress.addListener(op, (t) -> {
76+
testProgress(t.progress(), numStages * numIterations);
77+
});
78+
Thread t = new Thread(() -> op.apply(numStages, numIterations));
79+
t.start();
80+
t.join();
81+
Assert.assertEquals(numStages * numIterations, this.numUpdates);
82+
83+
}
84+
4885
private int numUpdates = 0;
4986

5087
private void testProgress(double progress, int numIterations) {

0 commit comments

Comments
 (0)