X Tutup
Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

SciJava Progress: an interrupt-based mechanism for progress reporting

This component provides a mechanism for progress-recording operations to notify interested parties of updates to progress.

The Progress class

The Progress class is designed to be the middle-man between an operation that wishes to report its progress and a listener that wishes to be notified of progress updates. Both of these parties will interface with each other through Progress class.

Accessing Progress as an operation

Suppose we have a Function that does some heavy calculation on a List, and wants to notify callers when it finishes one element, before it moves to the next:

public class HeavyCalculator implements Function<List<Integer>, List<Integer>> {

	@Override
	public List<Integer> apply(List<Integer> in) {
		List<Integer> out = new ArrayList<>();
		for(var i = 0; i < in.size(); i++) {
			out.add(doTheComputation(in.get(i)));
		}
		return out;
	}

}

Firstly, we add the bookkeeping steps:

  1. Notify Progress that this Object wants to record its progress by calling Progress.register(Object progressible). This can either be called within our Function (by passing this), or before the apply method is called (by passing our HeavyCalculator instance).
  2. Define what total progress means using Progress.defineTotalProgress(int numStages, int numSubTasks). We make the distinction between numStages and numSubtasks:
  • numStages lets Progress know how many stages of computation will be performed within the current task
  • numSubTasks lets Progress know how many progress-reporting tasks will be called by the current task. Since they will report their own progress to Progress, Progress will automatically update the progress of our current task as the subtasks progress.
  1. For each stage, Progress.setStageMax(long max) must be called so that Progress knows when each stage is completed, and moves onto the next.
  2. Once the computation is complete, notify Progress that the task has completed by calling Progress.complete().

Once these bookkeeping stages are added, we can then call Progress.update. Our Function, updating progress, would then look like:

public class HeavyCalculator implements Function<List<Integer>, List<Integer>> {

	@Override
	public List<Integer> apply(List<Integer> in) {
		Progress.register(this);
		Progress.defineTotalProgress(1, 0);
		Progress.setStageMax(in.size());

		// compute
		List<Integer> out = new ArrayList<>();
		for(var i = 0; i < in.size(); i++) {
			out.add(doTheComputation(in.get(i)));
			Progress.update();
		}

		Progress.complete();
		return out;
	}

}

Ops can also set their status through the Progress framework, using the method Progress.setStatus(String status). In these situations, out Function would not need to call Progress.defineTotalProgress() or Progress.setStageMax(). Progressible code is, of course, allowed to set both status and progress.

Accessing Progress as a listener

Progress is accessed by listeners using the Progress.addListener(Object progressible, Consumer<Task> l) method. This method must be called before progressible's code is executed, and all executions of progressible will then be sent to l.

l can be written as a lambda (shown below), or as an explicit implementation of the Consumer interface. The functional method, accept(Task t), is then called when any execution of progressible calls Progress.register, Progress.complete, Progress.update(), or Progress.setStatus(). Below is an example of how one might write a Consumer<Task> for HeavyCalculator:

HeavyCalculator calc = new HeavyCalculator();

// register a listener that prints progress to console
Progress.addListener(calc, t -> System.out.println(t.progress()));

// call HeavyCalculator
calc.apply(Arrays.asList(1, 2, 3, 4);
X Tutup