Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Stroika::Foundation::Execution::ProgressMonitor Class Referencefinal

#include <ProgressMonitor.h>

Classes

struct  CurrentTaskInfo
 
class  Updater
 

Public Types

using ChangedCallbackType = Execution::Function< void(const ProgressMonitor &progressMonitor)>
 

Public Member Functions

 ProgressMonitor ()
 
nonvirtual void AddOnProgressCallback (const ChangedCallbackType &progressChangedCallback)
 
nonvirtual ProgressRangeType GetProgress () const
 
nonvirtual void Cancel ()
 
nonvirtual operator Updater ()
 
nonvirtual CurrentTaskInfo GetCurrentTaskInfo () const
 

Detailed Description

ProgressMonitor is the basic interface used for progress tracking. Progress tracking both measures progress, and supports the notion of canceling. The reason progress and cancelability are tied together is that its only for 'long lived' tasks one might want to measure the progress of, that one might want to allow canceling.

A progress Monitor owns a list of ChangedCallbackType that can be associated with the ProgressMonitor. These callbacks are each called whenever the progress is known to have changed. Note - this callback uses std::function<>, and the callback runs on an arbitrary thread, not necessarily the one used by the creator of the ProgressMonitor.

A ProgressMonitor also has associated with it an arbitrary number of Updater objects. These are the things that one hands to processes (not OS processes, but long lived procedures or threads) which they then callback to to notify of their progress.

An updater is retrieved from the root ProgressMonitor, and it has 'scope' of 0..1. You can construct sub-updaters by passing a base Updater to the Updater constructor along with a subrange (inside 0..1). That way - if you have sub-procedures, they can report on their progress (0..1) and that is mapped to a subrange of the overall progress.

Note - in order to help debug the progress values, ProgressMonitor strictly enforces some rules. Progress starts at zero, and successive values are non-degreasing. This means the progress bar grows monotonically (though not necessarily smoothly). In order to avoid common floating point bugs with rounding errors, ProgressMonitor employs Math::PinToSpecialPoint ().

Users of ProgressMonitor can call "Cancel" on the ProgressMonitor at any point. This records a cancelation in the Updater object, so that when it calls Updater::SetProgress () - and perhaps in other situations (see thread support below) - the progress will terminate immediately.

ProgressMonitor supports having the underlying long-lived-task happen EITHER in the current thread, or in another thread (the ProgressMonitor and related code is fully threadsafe).

If ProgressMonitor is constructed with an argument Thread (optional) - then attempts to Cancel the ProgressMonitor will also send an Abort() command to the associated thread. This can accelerate - depending less on co-operative checking - to cancel the long-lived progress-monitored process.

Example Usage
void CompileData (const filesystem::path& sourceFile, ProgressMonitor::Updater progress)
{
...
progress.SetCurrentTaskInfo ("Compiling Strings"_k);
progress.SetProgress (0.3f);
SubTask_ (ProgressMonitor::Updater{progress, 0.50f, 0.60f}); // also may do progress calls (0..1 in subtask mapped into .5 to .6 range here)
...
}
ProgressMonitor progMon{[lastProg = 0u, lastDesc = String{}] (const ProgressMonitor& pm) mutable {
unsigned int curProgPct = static_cast<unsigned int> (pm.GetProgress () * 100);
Assert (0 <= curProgPct and curProgPct <= 100);
if (lastDesc != pm.GetCurrentTaskInfo ().fName) {
cout << "\r\n";
lastDesc = pm.GetCurrentTaskInfo ().fName;
cout << "\t" << lastDesc << "\r\n";
}
if (lastProg != curProgPct) {
cout << "\r";
cout << Format ("\t\t{}% complete "_f, curProgPct); /// spaces to wipe-out rest of line
lastProg = curProgPct;
}
}};
CompileData (file, progMon); // CompileData() takes updater, but ProgressMonitor has conversion op to create Updater...

Definition at line 105 of file ProgressMonitor.h.

Member Typedef Documentation

◆ ChangedCallbackType

This is for consumers of progress information. Consumers MAY either poll the ProgressMonitor, or may register a callback to be notified of progress.

Callback should be short lived, not hold any locks (because that could make it long lived and create a deadlock).

Also don't throw exceptions in these callbacks. Just record the info needed, and schedule further work in a GUI or whatever (queue it maybe).

\todo revisit 'noexcept' in C++23 - see https://stackoverflow.com/questions/41293025/stdfunction-with-noexcept-in-c17
      but for now, cannot declare teh function as noexcept
      Execution::Function<void (const ProgressMonitor& progressMonitor) noexcept>;

\note - though un-enforced by the language, callers should still treat these callbacks as noexcept

Definition at line 128 of file ProgressMonitor.h.

Constructor & Destructor Documentation

◆ ProgressMonitor()

ProgressMonitor::ProgressMonitor ( )

If work thread is specified (optional) - then thread cancelation will work more efficiently. But this is not required.

Definition at line 16 of file ProgressMonitor.cpp.

Member Function Documentation

◆ AddOnProgressCallback()

void ProgressMonitor::AddOnProgressCallback ( const ChangedCallbackType progressChangedCallback)

This doesn't need to be used. You can use ProgressMonitor progress monitor just periodically calling GetProgress(). But you may use AddCallback () to receive notifications of progress changes.

Also note, these callbacks may be mutable, and the same instance will be re-used on each progress callback (but it maybe a copy of what is originally passed in ).

Definition at line 41 of file ProgressMonitor.cpp.

◆ GetProgress()

ProgressMonitor::ProgressRangeType Stroika::Foundation::Execution::ProgressMonitor::GetProgress ( ) const

Return the progress value (between 0..1). This values starts at zero, and increases monotonically to 1.0

Definition at line 54 of file ProgressMonitor.inl.

◆ Cancel()

void ProgressMonitor::Cancel ( )

Cancelability. Anyone can call Cancel () on this progress object. If the progress object is handed to some long-lived task, that task may (at its discretion) - check the progress callback, and cancel its operation by throwing a UserCanceledException.

It is safe to call multiple times (and just may have no additional effect).

If a work thread is associated with the ProgressMonitor, it will be automatically aborted.

Definition at line 47 of file ProgressMonitor.cpp.

◆ operator Updater()

Stroika::Foundation::Execution::ProgressMonitor::operator Updater ( )

Progress isn't updated directly through the ProgressMonitor object. Instead, get an Updater, and call methods on it to update the progress.

Example Usage
ProgressMonitor prog = ...;
static_cast<Updater> (prog).SetProgress (0.3);
static_cast<Updater> (prog).SetCurrentTaskInfo ("doing stuff"_k);

Definition at line 65 of file ProgressMonitor.inl.

◆ GetCurrentTaskInfo()

ProgressMonitor::CurrentTaskInfo Stroika::Foundation::Execution::ProgressMonitor::GetCurrentTaskInfo ( ) const

Often in displaying progress, its useful to have a notion of what the system is doing, and that is usually displayed far away from where the notion of progress stage resides. This API is usually called by the bit of code performing actions (to set the current task) and by the calling GUI to Get the current task description.

Note also - for reasons of localization - its often helpful to pass back specific information about the task in progress (like file 1 of 4).

Use the 'fExtraData' field of the CurrentTaskInfo.

Definition at line 60 of file ProgressMonitor.inl.


The documentation for this class was generated from the following files: