module util::Monitor
Monitor the progress of a task/job.
Usage
import util::Monitor;
Dependencies
import util::Math;
import IO;
function jobStart
Log the start of a job.
void jobStart(str label, int work=1, int totalWork=100)
Job Start registers a new current job on the job stack with an amount of steps todo and how much work it contributes (when it ends) to its parent job (if any).
- The
label
identifies as running task, which could be presented in the UI with a specific progress bar (for example) - Using Job Step with the same label, you can advance the amount of work done for the task.
- All tasks should eventually end with Job End, but most UI's will clean up open left-over tasks at natural intervals as well.
- Use Job Todo to register additional dynamically discovered work for a task. The
totalWork
constant for that specific task will be increased by the given amount.
Benefits
- It is adviced to use the "block" functions
job
instead of the rawjobStart
,jobStep
andjobEnd
functions because these guarantee each started task is always ended, with and without exceptions. This improves the user experience for your users. Also these functions help by providing the job label in the scope of the task, such that this "magic constant" does not need to be repeated.
Pitfalls
- The job label is both an identity and a user facing string constant. Future versions of the API may split the identify from the label for better accuracy and better UI.
function jobStep
Log to the user that a certain event has happened under the currently registered Job.
void jobStep(str label, str message, int work = 1)
function jobEnd
Log the end of a job.
int jobEnd(str label, bool success=true)
function jobTodo
Register additional work for the identied job.
void jobTodo(str label, int work=1)
function jobIsCancelled
Poll if the given job has been cancelled by a user interaction.
void jobIsCancelled(str label)
function jobWarning
Register a warning in the same UI that job progress is shown in.
void jobWarning(str message, loc src)
function job
A job block guarantees a start and end, and provides easy access to the stepper interface.
&T job(str label, &T (void (str message, int worked) step) block, int totalWork=100)
The convenience function that is passed to the block can be used inside the block to register steps with a parameterized workload and the same label as the job name.
Benefits
- the block body does not need to repeat the
name
parameter when ending the job or making steps - the job is always properly ended, even when exceptions are thrown
Pitfalls
- additional work with Job Todo is still possible, but you have to repeat the right job label.
function job
A job block guarantees a start and end, and provides easy access to the stepper interface.
&T job(str label, &T (void (int worked) step) block, int totalWork=1)
The convenience function that is passed to the block can be used inside the block to register steps with a parameterized workload and the same label as the job name.
Benefits
- the block body does not need to repeat the
name
parameter when ending the job or making steps - the job is always properly ended, even when exceptions are thrown
Pitfalls
- additional work with Job Todo is still possible, but you have to repeat the right job label.
function job
A job block guarantees a start and end, and provides easy access to the stepper interface.
&T job(str label, &T (void () step) block, int totalWork=1)
The convenience function that is passed to the block can be used inside the block to register steps
with workload 1
and the same label as the job name.
Benefits
- the block body does not need to repeat the
name
parameter when ending the job or making steps - the job is always properly ended, even when exceptions are thrown
Pitfalls
- additional work with Job Todo is still possible, but you have to repeat the right job label.
function job
A job block guarantees a start and end, and provides easy access to the stepper interface.
&T job(str label, &T () block, int totalWork=1)
Benefits
- the block code does not need to remember to end the job with the same job name.
- the job is always properly ended, even when exceptions are thrown
Tests
test horseRaceTest
Puts the monitor API to work by racing 5 horses against each other.
test bool horseRaceTest() {
distance = 3000000;
stride = 50;
horses = 5;
handicaps = [ arbInt(stride * 15 / 100) | _ <- [0..horses]];
labels = [ "Horse <h> (handicap is <handicaps[h]>)" | h <- [0..horses]];
progress = [ 0 | _ <- [0..horses]];
for (int h <- [0..horses])
jobStart(labels[h], totalWork=distance);
race:while (true)
for(int h <- [0..horses]) {
advance = arbInt(stride - handicaps[h]);
progress[h] += advance;
jobStep(labels[h], "Pacing horse <h> with <advance>...", work=advance);
if (progress[h] >= distance) {
break race;
}
}
for (int h <- [0..horses])
jobEnd(labels[h]);
return true;
}
test simpleAsyncPrintTest
test bool simpleAsyncPrintTest() {
jobStart("job", totalWork=3);
println("a");
jobStep("job", "step 1", work=1);
println("b");
jobStep("job", "step 2", work=1);
println("c");
jobStep("job", "step 3", work=1);
println("d");
jobEnd("job");
return true;
}
test unfinishedInputTest
test bool unfinishedInputTest() {
jobStart("job", totalWork=26);
for (/<l:[a-z]>/ := "abcdefghijklmnopqrstuwvxyz") {
print(l); // no newline!
jobStep("job", "letter <l>", work=1);
if (arbInt(10) == 0) {
println(); // break it
}
}
// println(); // flush it
jobEnd("job");
return true;
}
test unfinishedLinesAtTheEndTest
test bool unfinishedLinesAtTheEndTest() {
jobStart("job", totalWork=3);
print("ab\nc");
jobStep("job", "1.5", work=1);
print("d\ne");
jobStep("job", "2.5", work=1);
print("f\ngh\n");
jobStep("job", "3", work=1);
jobEnd("job");
return true;
}