Classes Message_ProgressRange and Message_ProgressScope are improved to store start point of the range.
Method Message_ProgressScope::Value() is improved to compute the value in the current scope from the actual current value of the global progress.
Description of Message_ProgressScope class is improved.
Off-topic:
- method Message_ProgressScope::Relieve() is renamed to Close() for semantic consistency
- article in Upgrade Guide is revised and corrected
Adaptor2d_OffsetCurve aOC(BaseCurve, Offset) --> Adaptor2d_OffsetCurve aOC(BaseCurve, -Offset)
-subsection upgrade_750_ProgressIndicator Change of Message_ProgressIndicator
+@subsection upgrade_750_ProgressIndicator Change of progress indication API
-The progress indication mechanism has been revised to eliminate its weak points in previous design (leading to ambiguity and unprotected from an error-prone behavior).
-Redesign also allows using progress indicator in multi-threaded algorithms in more straight-forward way with minimal overhead.
-Note, however, that multi-threaded algorithm should pre-allocate per-thread progress scopes in advance to ensure thread-safety - check new classes API for details.
+The progress indication mechanism has been revised to eliminate its weak points in
+previous design (leading to implementation mistakes).
+Redesign also allows using progress indicator in multi-threaded algorithms
+in more straight-forward way with minimal overhead.
+Note however, that multi-threaded algorithm should pre-allocate per-task
+progress ranges in advance to ensure thread-safety -
+see examples in documentation of class Message_ProgressScope for details.
Classes Message_ProgressSentry and Message_ProgressScale have been removed.
-New classes Message_ProgressScope and Messge_ProgressRange replace them and should be used as main API classes to organize progress indication in the algorithms.
-Instances of the class Message_ProgressRange are used to pass the progress capability to nested levels of the algorithm
-and an instance of the class Message_ProgressScope is to be created (preferably as local variable) to manage progress at each level of the algorithm.
+New classes Message_ProgressScope and Message_ProgressRange should be used as main
+API classes to organize progress indication in the algorithms.
+Instances of the class Message_ProgressRange are used to pass the progress capability to
+nested levels of the algorithm, and an instance of the class Message_ProgressScope is to
+be created (preferably as local variable) to manage progress at each level of the algorithm.
The instance of Message_ProgressIndicator is not passed anymore to sub-algorithms.
See documentation of the class Message_ProgressScope for more details and examples.
-Methods to deal with progress scopes and to advance progress are removed from class Message_ProgressIndicator; now it only provides interface to the application-level progress indicator.
-Virtual method Message_ProgressIndicator::Show() has changed its signature and should be updated accordingly in descendants of Message_ProgressIndicator.
-The scope passed as argument to this method can be used to obtain information on context of the current process (instead of calling method GetScope() in previous implementation).
-Methods Show(), UserBreak(), and Reset() are made protected in class Message_ProgressIndicator; method More() of Message_ProgressScope should be used to know if the cancel event has come.
-See documentation of the class Message_ProgressIndicator for more details and implementation of Draw_ProgressIndicator for an example.
-
-Lets take a look onto typical algorithm using an old API:
+Methods to deal with progress scopes and to advance progress are removed from class
+Message_ProgressIndicator; now it only provides interface to the application-level progress indicator.
+Virtual method Message_ProgressIndicator::Show() has changed its signature and should be
+updated accordingly in descendants of Message_ProgressIndicator.
+The scope passed as argument to this method can be used to obtain information on context
+of the current process (instead of calling method GetScope() in previous implementation).
+Methods Show(), UserBreak(), and Reset() are made protected in class Message_ProgressIndicator;
+methods More() or UserBreak() of classes Message_ProgressScope or Message_ProgressRange should
+be used to know if the cancel event has come.
+See documentation of the class Message_ProgressIndicator for more details and implementation
+of Draw_ProgressIndicator for an example.
+
+Let's take a look onto typical algorithm using an old API:
@code
class MyAlgo
{
{
Message_ProgressSentry aPSentry1 (theProgress, "Stage 1", 0, 153, 1);
for (int anIter = 0; anIter < 153; ++anIter, aPSentry1.Next())
- { if (!aPSentry1.More()) { return false; } }
+ {
+ if (!aPSentry1.More()) { return false; }
+ // do some job here...
+ }
}
aPSentry.Next();
{
//! Nested sub-algorithm taking Progress Indicator.
bool perform2 (const Handle(Message_ProgressIndicator)& theProgress)
{
- Message_ProgressSentry aPSentry2 (theProgress, "Stage 2", 0, 561, 1);
- for (int anIter = 0; anIter < 561 && aPSentry2.More(); ++anIter, aPSentry2.Next()) {}
- return aPSentry2.More();
+ Message_ProgressSentry aPSentry2 (theProgress, "Stage 2", 0, 100, 1);
+ for (int anIter = 0; anIter < 100 && aPSentry2.More(); ++anIter, aPSentry2.Next()) {}
+ return !aPSentry2.UserBreak();
}
};
@endcode
The following guidance can be used to update such code:
-- Replace `const Handle(Message_ProgressIndicator)&` with `const Message_ProgressRange&`.
+- Replace `const Handle(Message_ProgressIndicator)&` with `const Message_ProgressRange&`
+ in arguments of the methods that support progress indication.
Message_ProgressIndicator object should be now created only at place where application starts algorithms.
- Replace `Message_ProgressSentry` with `Message_ProgressScope`.
- Take note that Message_ProgressScope has smaller number of arguments (no "minimal value").
- In other aspects, Message_ProgressScope mimics an iterator-style interface (with methods More() and Next())
- close to the old Message_ProgressSentry (pay attention to extra functionality of Message_ProgressScope::Next() method below).
-- Each Message_ProgressScope should take the next Range to fill in.
- Within old API, Message_ProgressSentry received the root Progress Indicator object and implicitly split it into ranges using error-prone logic.
- Message_ProgressScope in new API takes Message_ProgressRange, which should be created from the Range of the parent Scope using value returned by Message_ProgressScope::Next() method.
- Don't use the same Range passed to the algorithm for all sub-Scopes like it was possible in old API.
-- Check user abortion state using Message_ProgressScope::UserBreak() method;
- Message_ProgressRange is a temporary object with the only purpose to create a new Message_ProgressScope,
- and Message_ProgressIndicator should be never passed directly to algorithms.
+ Take note that Message_ProgressScope has less arguments (no "minimal value").
+ In other aspects, Message_ProgressScope mimics an iterator-style interface
+ (with methods More() and Next()) close to the old Message_ProgressSentry (pay attention
+ to extra functionality of Message_ProgressScope::Next() method below).
+ Note that method Message_ProgressScope::Close() is equivalent of the method
+ Relieve() of Message_ProgressSentry in previous version.
+ Class Message_ProgressSentry is still defined (marked as deprecated) providing
+ API more close to old one, and can be still used to reduce porting efforts.
+- Each Message_ProgressScope should take the next Range object to work with.
+ Within old API, Message_ProgressSentry received the root Progress Indicator
+ object which mantained the sequence of ranges internally.
+ Message_ProgressScope in new API takes Message_ProgressRange, which should be
+ returned by Message_ProgressScope::Next() method of the parent scope.
+ Do not use the same Range passed to the algorithm for all sub-Scopes like
+ it was possible in old API; each range object may be used only once.
Take a look onto ported code and compare with code above to see differences:
{
Message_ProgressScope aPSentry1 (aPSentry.Next(), "Stage 1", 153);
for (int anIter = 0; anIter < 153; ++anIter, aPSentry1.Next())
- { if (!aPSentry1.More()) { return false; }; }
+ {
+ if (!aPSentry1.More()) { return false; };
+ // do some job here...
+ }
}
{
perform2 (aPSentry.Next());
//! Nested sub-algorithm taking Progress sub-Range.
bool perform2 (const Message_ProgressRange& theProgress)
{
- Message_ProgressScope aPSentry2 (theProgress, "Stage 2", 561);
- for (int anIter = 0; anIter < 561 && aPSentry2.More(); ++anIter, aPSentry2.Next()) {}
- return aPSentry2.More();
+ Message_ProgressScope aPSentry2 (theProgress, "Stage 2", 100);
+ for (int anIter = 0; anIter < 100 && aPSentry2.More(); ++anIter, aPSentry2.Next()) {}
+ return !aPSentry2.UserBreak();
}
};
{
OSD::ControlBreak();
}
- catch (OSD_Exception_CTRL_BREAK)
+ catch (const OSD_Exception_CTRL_BREAK&)
{
myBreak = Standard_True;
}
//! Show() should return as soon as possible to reduce thread contention
//! in multithreaded algorithms.
//!
- //! It is recommended to update (redraw, output etc.) only if progress advanced
- //! by at least 1% from previous update.
+ //! It is recommended to update (redraw, output etc.) only if progress is
+ //! advanced by at least 1% from previous update.
//!
- //! Flag isForce is intended for forcing update in case if it is
- //! optimized; all calls to it from inside the core mechanism are
- //! done with this flag equal to False.
+ //! Flag isForce is intended for forcing update in case if it is required
+ //! at particular step of the algorithm; all calls to it from inside the core
+ //! mechanism (Message_Progress... classes) are done with this flag equal to False.
//!
//! The parameter theScope is the current scope being advanced;
//! it can be used to show the names and ranges of the on-going scope and
//!@name Auxiliary methods
//! Returns total progress position ranged from 0 to 1.
- //! Should not be called concurrently while the progress is advancing
+ //! Should not be called concurrently while the progress is advancing,
//! except from implementation of method Show().
Standard_Real GetPosition() const
{
//! A range object can be copied, the responsibility for progress advancement is
//! then taken by the copy.
//! The same range object may be used (either copied or used to create scope) only once.
-//! Any consequenct attempts to use range will give no result on the progress;
+//! Any consequent attempts to use range will give no result on the progress;
//! in debug mode, an assert message will be generated.
//!
//! @sa Message_ProgressScope for more details
class Message_ProgressRange
{
public:
- //! Constructor of the range
+ //! Constructor of the empty range
Message_ProgressRange()
- : myParentScope (0), myDelta (0), myWasUsed (false)
+ : myParentScope (0), myStart(0.), myDelta (0.), myWasUsed (false)
{}
- //! Copy constructor disarms the source.
+ //! Copy constructor disarms the source
Message_ProgressRange (const Message_ProgressRange& theOther)
: myParentScope (theOther.myParentScope),
+ myStart (theOther.myStart),
myDelta (theOther.myDelta),
myWasUsed (theOther.myWasUsed)
{
theOther.myWasUsed = true;
}
- //! Copy assignment disarms the source.
+ //! Copy assignment disarms the source
Message_ProgressRange& operator=(const Message_ProgressRange& theOther)
{
myParentScope = theOther.myParentScope;
+ myStart = theOther.myStart;
myDelta = theOther.myDelta;
myWasUsed = theOther.myWasUsed;
theOther.myWasUsed = true;
private:
//! Constructor is private
- Message_ProgressRange (const Message_ProgressScope& theParent, Standard_Real theDelta)
+ Message_ProgressRange (const Message_ProgressScope& theParent,
+ Standard_Real theStart, Standard_Real theDelta)
: myParentScope (&theParent),
+ myStart (theStart),
myDelta (theDelta),
myWasUsed (false)
{}
private:
const Message_ProgressScope* myParentScope; //!< Pointer to parent scope
- Standard_Real myDelta; //!< Step of incrementation
+ Standard_Real myStart; //!< Start point on the global scale
+ Standard_Real myDelta; //!< Step of incrementation on the global scale
+
mutable Standard_Boolean myWasUsed; //!< Flag indicating that this range
//! was used to create a new scope
//! On every level (sub-operation) in hierarchy of operations
//! the local instance of the Message_ProgressScope class is created.
//! It takes a part of the upper-level scope (via Message_ProgressRange) and provides
-//! a way to consider this part as independent scale with locally defined range.
+//! a way to consider this part as independent scale with locally defined range.
//!
//! The position on the local scale may be advanced using the method Next(),
//! which allows iteration-like advancement. This method can take argument to
-//! advance on the needed value. And, this method returns ProgressRange object
-//! that takes responsibility of making the specified step at its destruction.
-//! The ProgressRange can be used to create a new progress sub-scope.
+//! advance by the specified value (with default step equal to 1).
+//! This method returns Message_ProgressRange object that takes responsibility
+//! of making the specified step, either directly at its destruction or by
+//! delegating this task to another sub-scope created from that range object.
//!
//! It is important that sub-scope must have life time less than
//! the life time of its parent scope that provided the range.
+//! The usage pattern is to create scope objects as local variables in the
+//! functions that do the job, and pass range objects returned by Next() to
+//! the functions of the lower level, to allow them creating their own scopes.
//!
//! The scope has a name that can be used in visualization of the progress.
-//! It can be null. Note that the string is not copied, just pointer is stored.
-//! So, the pointer must point to the string with life time
-//! greater than that of the scope object.
+//! It can be null. Note that when C string literal is used as a name, then its
+//! value is not copied, just pointer is stored. In other variants (char pointer
+//! or a string class) the string is copied, which is additional overhead.
//!
-//! In multithreaded programs, for each task running concurrently it is recommended
-//! to create a separate progress scope. The same instance of the progress scope
-//! must not be used concurrently from different threads.
+//! The same instance of the progress scope! must not be used concurrently from different threads.
+//! For the algorithm running its tasks in parallel threads, a common scope is
+//! created before the parallel execution, and the range objects produced by method
+//! Next() are used to initialise the data pertinent to each task.
+//! Then the progress is advanced within each task using its own range object.
+//! See example below.
+//!
+//! Note that while a range of the scope is specified using Standard_Real
+//! (double) parameter, it is expected to be a positive integer value.
+//! If the range is not an integer, method Next() shall be called with
+//! explicit step argument, and the rounded value returned by method Value()
+//! may be not coherent with the step and range.
+//!
+//! A scope can be created with option "infinite". This is useful when
+//! the number of steps is not known by the time of the scope creation.
+//! In this case the progress will be advanced logarithmically, approaching
+//! the end of the scope at infinite number of steps. The parameter Max
+//! for infinite scope indicates number of steps corresponding to mid-range.
//!
//! A progress scope created with empty constructor is not connected to any
//! progress indicator, and passing the range created on it to any algorithm
-//! allows it executing safely without progress indication.
+//! allows it executing safely without actual progress indication.
//!
//! Example of preparation of progress indicator:
//!
//! {
//! void operator() (Task& theTask) const
//! {
-//! // Note: it is essential that this method is executed only once
-//! // for the same Task object
-//! Message_ProgressScope aPS (theTask.Range, "Processing task", 1);
-//! if (aPS.More())
+//! // Note: it is essential that this method is executed only once for the same Task object
+//! Message_ProgressScope aPS (theTask.Range, NULL, theTask.Data.NbItems);
+//! for (Standard_Integer i = 0; i < theTask.Data.NbSteps && aPS.More(); i++)
//! {
-//! // ... process data
+//! do_job (theTask.Data.Item[i], aPS.Next());
//! }
//! }
//! };
//! OSD_Parallel::ForEach (aTasks.begin(), aTasks.end(), Functor());
//! }
//! @endcode
+//!
+//! For lightweight algorithms that do not need advancing the progress
+//! within individual tasks the code can be simplified to avoid inner scopes:
+//!
+//! @code
+//! struct Functor
+//! {
+//! void operator() (Task& theTask) const
+//! {
+//! if (theTask.Range.More())
+//! {
+//! do_job (theTask.Data);
+//! // advance the progress
+//! theTask.Range.Close();
+//! }
+//! }
+//! };
+//! @endcode
class Message_ProgressScope
{
public:
: myProgress (0),
myParent (0),
myName (0),
- myPortion (1.), myMax (1.), myValue (0.),
+ myStart (0.),
+ myPortion (1.),
+ myMax (1.),
+ myValue (0.),
myIsActive (false),
myIsOwnName (false),
myIsInfinite (false)
}
//! Returns the current value of progress in this scope.
- //! If this scope is being advanced by sub-scoping, that value is
- //! computed by mapping current global progress into this scope range.
- Standard_Real Value() const
- {
- return myIsActive ? myValue : myMax;
- }
+ //!
+ //! The value is computed by mapping current global progress into
+ //! this scope range; the result is rounded up to integer.
+ //! Note that if MaxValue() is not an integer, Value() can be
+ //! greater than MaxValue() due to that rounding.
+ //!
+ //! This method should not be called concurrently while the progress
+ //! is advancing, except from implementation of method Show() in
+ //! descendant of Message_ProgressIndicator.
+ Standard_Real Value() const;
//! Returns the infinite flag
Standard_Boolean IsInfinite() const
//! Destructor - closes the scope and adds its scale to the total progress
~Message_ProgressScope()
{
- Relieve();
+ Close();
if (myIsOwnName)
{
Standard::Free (myName);
}
}
- //! Closes the scope and adds its scale to the total progress.
- //! Relieved scope should not be used.
- void Relieve();
+ //! Closes the scope and advances the progress to its end.
+ //! Closed scope should not be used.
+ void Close();
DEFINE_STANDARD_ALLOC
//! Called only by Message_ProgressIndicator constructor.
Message_ProgressScope (Message_ProgressIndicator* theProgress);
- //! Convert value from this scale to global one
+ //! Convert value from this scope to global scale, but disregarding
+ //! start position of the scope, in the range [0, myPortion]
Standard_Real localToGlobal(const Standard_Real theVal) const;
- //! Convert value from global scale to this one
- Standard_Real globalToLocal(const Standard_Real theVal) const;
-
private:
//! Copy constructor is prohibited
Message_ProgressScope (const Message_ProgressScope& theOther);
const Message_ProgressScope* myParent; //!< Pointer to parent scope
Standard_CString myName; //!< Name of the operation being done in this scope, or null
- Standard_Real myPortion; //!< The portion of the global scale covered by this scope (from 0 to 1)
+ Standard_Real myStart; //!< Start position on the global scale [0, 1]
+ Standard_Real myPortion; //!< The portion of the global scale covered by this scope [0, 1]
+
Standard_Real myMax; //!< Maximal value of progress in this scope
Standard_Real myValue; //!< Current position advanced within this scope [0, Max]
: myProgress(theProgress),
myParent(0),
myName(0),
+ myStart(0.),
myPortion(1.),
myMax(1.),
myValue(0.),
: myProgress (theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
myParent (theRange.myParentScope),
myName (NULL),
+ myStart (theRange.myStart),
myPortion (theRange.myDelta),
myMax (Max (1.e-6, theMax)), // protection against zero range
myValue (0.),
: myProgress (theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
myParent (theRange.myParentScope),
myName (theName),
+ myStart (theRange.myStart),
myPortion (theRange.myDelta),
myMax (Max (1.e-6, theMax)), // protection against zero range
myValue (0.),
: myProgress (theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
myParent (theRange.myParentScope),
myName (NULL),
+ myStart (theRange.myStart),
myPortion (theRange.myDelta),
myMax (Max (1.e-6, theMax)), // protection against zero range
myValue (0.),
}
//=======================================================================
-//function : Relieve
+//function : Close
//purpose :
//=======================================================================
-inline void Message_ProgressScope::Relieve()
+inline void Message_ProgressScope::Close()
{
if (!myIsActive)
{
//=======================================================================
inline Message_ProgressRange Message_ProgressScope::Next (Standard_Real theStep)
{
- if (myIsActive)
+ if (myIsActive && theStep > 0.)
{
- if (theStep > 0.)
+ Standard_Real aCurr = localToGlobal(myValue);
+ Standard_Real aNext = localToGlobal(myValue += theStep);
+ Standard_Real aDelta = aNext - aCurr;
+ if (aDelta > 0.)
{
- Standard_Real aCurr = localToGlobal (myValue);
- Standard_Real aNext = localToGlobal (myValue += theStep);
- Standard_Real aDelta = aNext - aCurr;
- if (aDelta > 0.)
- {
- return Message_ProgressRange (*this, aDelta);
- }
+ return Message_ProgressRange(*this, myStart + aCurr, aDelta);
}
}
return Message_ProgressRange();
}
//=======================================================================
-//function : globalToLocal
+//function : Value
//purpose :
//=======================================================================
-inline Standard_Real Message_ProgressScope::globalToLocal (const Standard_Real theVal) const
+inline Standard_Real Message_ProgressScope::Value () const
{
+ if (!myIsActive)
+ {
+ return myIsInfinite ? Precision::Infinite() : myMax;
+ }
+
+ // get current progress on the global scale counted
+ // from the start of this scope
+ Standard_Real aVal = myProgress->GetPosition() - myStart;
+
+ // if progress has not reached yet the start of this scope, return 0
+ if (aVal <= 0.)
+ return 0.;
+
// if at end of the scope (or behind), report the maximum
- Standard_Real aDist = myPortion - theVal;
+ Standard_Real aDist = myPortion - aVal;
if (aDist <= Precision::Confusion())
return myIsInfinite ? Precision::Infinite() : myMax;
- if (!myIsInfinite)
- return myMax * theVal / myPortion;
-
- // Standard_Real x = log (theVal / aDist); // exponent
- Standard_Real x = theVal / aDist; // hyperbola
- return x * myMax;
+ // map the value to the range of this scope [0, Max],
+ // rounding up to integer, with small correction applied
+ // to avoid rounding errors
+ return std::ceil (myMax * aVal / (myIsInfinite ? aDist : myPortion) - Precision::Confusion());
}
#endif // _Message_ProgressScope_HeaderFile
}
}
+ //! Method Relieve() was replaced by Close() in Message_ProgressScope
+ void Relieve () { Close(); }
+
private:
//! Message_ProgressRange should be passed to constructor instead of Message_ProgressIndicator.
Message_ProgressSentry (const Handle(Message_ProgressIndicator)& theProgress,
{
void operator()(Task& theTask) const
{
- Message_ProgressScope aPS(theTask.Range, NULL, 1);
- if (aPS.More())
+ if (theTask.Range.More())
{
if (theTask.Mat1.RowNumber() > 1)
theTask.Mat3 = theTask.Mat1 * theTask.Mat2;
}
+ theTask.Range.Close();
}
};
}