0031763: Foundation Classes - reporting of progress within parallel algorithms
authorabv <abv@opencascade.com>
Sun, 13 Sep 2020 16:48:30 +0000 (19:48 +0300)
committerbugmaster <bugmaster@opencascade.com>
Wed, 16 Sep 2020 16:32:57 +0000 (19:32 +0300)
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

dox/dev_guides/upgrade/upgrade.md
src/Draw/Draw_ProgressIndicator.cxx
src/Message/Message_ProgressIndicator.hxx
src/Message/Message_ProgressRange.hxx
src/Message/Message_ProgressScope.hxx
src/Message/Message_ProgressSentry.hxx
src/QABugs/QABugs_11.cxx

index 92cb0d9..89d7d50 100644 (file)
@@ -1935,26 +1935,38 @@ Offset direction, which used in class Adaptor2d_OffsetCurve for evaluating value
 
 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
 {
@@ -1967,7 +1979,10 @@ public:
     {
       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();
     {
@@ -1982,9 +1997,9 @@ private:
   //! 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();
   }
 };
 
@@ -1995,19 +2010,25 @@ anAlgo.Perform ("FileName", aProgress);
 @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:
 
@@ -2023,7 +2044,10 @@ public:
     {
       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());
@@ -2035,9 +2059,9 @@ public:
   //! 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();
   }
 };
 
index f473c7b..dd075df 100644 (file)
@@ -202,7 +202,7 @@ Standard_Boolean Draw_ProgressIndicator::UserBreak()
     {
       OSD::ControlBreak();
     }
-    catch (OSD_Exception_CTRL_BREAK)
+    catch (const OSD_Exception_CTRL_BREAK&)
     {
       myBreak = Standard_True;
     }
index bca03d8..09a83fd 100644 (file)
@@ -99,12 +99,12 @@ protected:
   //! 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
@@ -120,7 +120,7 @@ public:
   //!@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
   {
index cb73125..28c7067 100644 (file)
@@ -32,21 +32,22 @@ class Message_ProgressScope;
 //! 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)
   {
@@ -54,10 +55,11 @@ public:
     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;
@@ -87,15 +89,19 @@ public:
 
 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
 
index b5722c2..5652e68 100644 (file)
@@ -34,29 +34,48 @@ class Message_ProgressIndicator;
 //! 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:
 //!
@@ -132,12 +151,11 @@ class Message_ProgressIndicator;
 //! {
 //!   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());
 //!     }
 //!   }
 //! };
@@ -153,6 +171,24 @@ class Message_ProgressIndicator;
 //!   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:
@@ -165,7 +201,10 @@ public: //! @name Preparation methods
   : 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)
@@ -302,12 +341,16 @@ public: //! @name Auxiliary methods to use in ProgressIndicator
   }
 
   //! 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
@@ -326,7 +369,7 @@ public: //! @name Destruction, allocation
   //! Destructor - closes the scope and adds its scale to the total progress
   ~Message_ProgressScope()
   {
-    Relieve();
+    Close();
     if (myIsOwnName)
     {
       Standard::Free (myName);
@@ -335,9 +378,9 @@ public: //! @name Destruction, allocation
     }
   }
 
-  //! 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
 
@@ -347,12 +390,10 @@ private: //! @name Internal methods
   //! 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);
@@ -366,7 +407,9 @@ private:
   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]
 
@@ -389,6 +432,7 @@ inline Message_ProgressScope::Message_ProgressScope (Message_ProgressIndicator*
 : myProgress(theProgress),
   myParent(0),
   myName(0),
+  myStart(0.),
   myPortion(1.),
   myMax(1.),
   myValue(0.),
@@ -409,6 +453,7 @@ inline Message_ProgressScope::Message_ProgressScope (const Message_ProgressRange
 : 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.),
@@ -433,6 +478,7 @@ Message_ProgressScope::Message_ProgressScope (const Message_ProgressRange& theRa
 : 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.),
@@ -455,6 +501,7 @@ inline Message_ProgressScope::Message_ProgressScope (const Message_ProgressRange
 : 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.),
@@ -467,10 +514,10 @@ inline Message_ProgressScope::Message_ProgressScope (const Message_ProgressRange
 }
 
 //=======================================================================
-//function : Relieve
+//function : Close
 //purpose  :
 //=======================================================================
-inline void Message_ProgressScope::Relieve()
+inline void Message_ProgressScope::Close()
 {
   if (!myIsActive)
   {
@@ -506,17 +553,14 @@ inline Standard_Boolean Message_ProgressScope::UserBreak() const
 //=======================================================================
 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();
@@ -557,23 +601,34 @@ inline Standard_Real Message_ProgressScope::localToGlobal (const Standard_Real t
 }
 
 //=======================================================================
-//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
index 124b874..84c3784 100644 (file)
@@ -38,6 +38,9 @@ public:
     }
   }
 
+  //! 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,
index 9e32a4e..2a62d97 100644 (file)
@@ -4843,12 +4843,12 @@ namespace
   {
     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();
     }
   };
 }