0025748: Parallel version of progress indicator
[occt.git] / src / Message / Message_ProgressScope.hxx
1 // Created on: 2002-02-22
2 // Created by: Andrey BETENEV
3 // Copyright (c) 2002-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #ifndef _Message_ProgressScope_HeaderFile
17 #define _Message_ProgressScope_HeaderFile
18
19 #include <Standard_Assert.hxx>
20 #include <Standard_TypeDef.hxx>
21 #include <Standard_DefineAlloc.hxx>
22 #include <Standard_Handle.hxx>
23 #include <Precision.hxx>
24 #include <TCollection_AsciiString.hxx>
25
26 class Message_ProgressRange;
27 class Message_ProgressIndicator;
28
29 //! Message_ProgressScope class provides convenient way to advance progress
30 //! indicator in context of complex program organized in hierarchical way,
31 //! where usually it is difficult (or even not possible) to consider process 
32 //! as linear with fixed step.
33 //!
34 //! On every level (sub-operation) in hierarchy of operations
35 //! the local instance of the Message_ProgressScope class is created.
36 //! It takes a part of the upper-level scope (via Message_ProgressRange) and provides 
37 //! a way to consider this part as independent scale with locally defined range. 
38 //!
39 //! The position on the local scale may be advanced using the method Next(),
40 //! which allows iteration-like advancement. This method can take argument to 
41 //! advance on the needed value. And, this method returns ProgressRange object
42 //! that takes responsibility of making the specified step at its destruction.
43 //! The ProgressRange can be used to create a new progress sub-scope.
44 //!
45 //! It is important that sub-scope must have life time less than 
46 //! the life time of its parent scope that provided the range.
47 //!
48 //! The scope has a name that can be used in visualization of the progress.
49 //! It can be null. Note that the string is not copied, just pointer is stored.
50 //! So, the pointer must point to the string with life time
51 //! greater than that of the scope object.
52 //!
53 //! In multithreaded programs, for each task running concurrently it is recommended
54 //! to create a separate progress scope. The same instance of the progress scope
55 //! must not be used concurrently from different threads.
56 //!
57 //! A progress scope created with empty constructor is not connected to any
58 //! progress indicator, and passing the range created on it to any algorithm 
59 //! allows it executing safely without progress indication.
60 //!
61 //! Example of preparation of progress indicator:
62 //!
63 //! @code{.cpp}
64 //!   Handle(Message_ProgressIndicator) aProgress = ...; // assume it can be null
65 //!   func (Message_ProgressIndicator::Start (aProgress));
66 //! @endcode
67 //!
68 //! Example of usage in sequential process:
69 //!
70 //! @code{.cpp}
71 //!   Message_ProgressScope aWholePS(aRange, "Whole process", 100);
72 //!
73 //!   // do one step taking 20%
74 //!   func1 (aWholePS.Next (20)); // func1 will take 20% of the whole scope
75 //!   if (aWholePS.UserBreak()) // exit prematurely if the user requested break
76 //!     return;
77 //!
78 //!   // ... do next step taking 50%
79 //!   func2 (aWholePS.Next (50));
80 //!   if (aWholePS.UserBreak())
81 //!     return;
82 //! @endcode
83 //!
84 //! Example of usage in nested cycle:
85 //!
86 //! @code{.cpp}
87 //!   // Outer cycle
88 //!   Message_ProgressScope anOuter (theProgress, "Outer", nbOuter);
89 //!   for (Standard_Integer i = 0; i < nbOuter && anOuter.More(); i++)
90 //!   {
91 //!     // Inner cycle
92 //!     Message_ProgressScope anInner (anOuter.Next(), "Inner", nbInner);
93 //!     for (Standard_Integer j = 0; j < nbInner && anInner.More(); j++)
94 //!     {
95 //!       // Cycle body
96 //!       func (anInner.Next());
97 //!     }
98 //!   }
99 //! @endcode
100 //!
101 //! Example of use in function:
102 //!
103 //! @code{.cpp}
104 //! //! Implementation of iterative algorithm showing its progress
105 //! func (const Message_ProgressRange& theProgress)
106 //! {
107 //!   // Create local scope covering the given progress range.
108 //!   // Set this scope to count aNbSteps steps.
109 //!   Message_ProgressScope aScope (theProgress, "", aNbSteps);
110 //!   for (Standard_Integer i = 0; i < aNbSteps && aScope.More(); i++)
111 //!   {
112 //!     // Optional: pass range returned by method Next() to the nested algorithm 
113 //!     // to allow it to show its progress too (by creating its own scope object).
114 //!     // In any case the progress will advance to the next step by the end of the func2 call.
115 //!     func2 (aScope.Next());
116 //!   }
117 //! }
118 //! @endcode
119 //!
120 //! Example of usage in parallel process:
121 //!
122 //! @code{.cpp}
123 //! struct Task
124 //! {
125 //!   Data& Data;
126 //!   Message_ProgressRange Range;
127 //!
128 //!   Task (const Data& theData, const Message_ProgressRange& theRange)
129 //!     : Data (theData), Range (theRange) {}
130 //! };
131 //! struct Functor
132 //! {
133 //!   void operator() (Task& theTask) const
134 //!   {
135 //!     // Note: it is essential that this method is executed only once
136 //!     // for the same Task object
137 //!     Message_ProgressScope aPS (theTask.Range, "Processing task", 1);
138 //!     if (aPS.More())
139 //!     {
140 //!       // ... process data
141 //!     }
142 //!   }
143 //! };
144 //! ...
145 //! {
146 //!   std::vector<Data> aData = ...;
147 //!   std::vector<Task> aTasks;
148 //!
149 //!   Message_ProgressScope aPS (aRootRange, "Data processing", aData.size());
150 //!   for (Standard_Integer i = 0; i < aData.size(); ++i)
151 //!     aTasks.push_back (Task (aData[i], aPS.Next()));
152 //!   
153 //!   OSD_Parallel::ForEach (aTasks.begin(), aTasks.end(), Functor());
154 //! }
155 //! @endcode
156 class Message_ProgressScope
157 {
158 public:
159   class NullString; //!< auxiliary type for passing NULL name to Message_ProgressScope constructor
160 public: //! @name Preparation methods
161
162   //! Creates dummy scope.
163   //! It can be safely passed to algorithms; no progress indication will be done.
164   Message_ProgressScope()
165   : myProgress (0),
166     myParent (0),
167     myName (0),
168     myPortion (1.), myMax (1.), myValue (0.),
169     myIsActive (false),
170     myIsOwnName (false),
171     myIsInfinite (false)
172   {}
173
174   //! Creates a new scope taking responsibility of the part of the progress 
175   //! scale described by theRange. The new scope has own range from 0 to 
176   //! theMax, which is mapped to the given range.
177   //!
178   //! The topmost scope is created and owned by Message_ProgressIndicator
179   //! and its pointer is contained in the Message_ProgressRange returned by the Start() method of progress indicator.
180   //!
181   //! @param theRange [in][out] range to fill (will be disarmed)
182   //! @param theName  [in]      new scope name
183   //! @param theMax   [in]      number of steps in scope
184   //! @param isInfinite [in]    infinite flag
185   Message_ProgressScope (const Message_ProgressRange& theRange,
186                          const TCollection_AsciiString& theName,
187                          Standard_Real theMax,
188                          Standard_Boolean isInfinite = false);
189
190   //! Creates a new scope taking responsibility of the part of the progress 
191   //! scale described by theRange. The new scope has own range from 0 to 
192   //! theMax, which is mapped to the given range.
193   //!
194   //! The topmost scope is created and owned by Message_ProgressIndicator
195   //! and its pointer is contained in the Message_ProgressRange returned by the Start() method of progress indicator.
196   //!
197   //! @param theRange [in][out] range to fill (will be disarmed)
198   //! @param theName  [in]      new scope name constant (will be stored by pointer with no deep copy)
199   //! @param theMax   [in]      number of steps in scope
200   //! @param isInfinite [in]    infinite flag
201   template<size_t N>
202   Message_ProgressScope (const Message_ProgressRange& theRange,
203                          const char (&theName)[N],
204                          Standard_Real theMax,
205                          Standard_Boolean isInfinite = false);
206
207   //! Creates a new scope taking responsibility of the part of the progress 
208   //! scale described by theRange. The new scope has own range from 0 to 
209   //! theMax, which is mapped to the given range.
210   //!
211   //! The topmost scope is created and owned by Message_ProgressIndicator
212   //! and its pointer is contained in the Message_ProgressRange returned by the Start() method of progress indicator.
213   //!
214   //! @param theRange [in][out] range to fill (will be disarmed)
215   //! @param theName  [in]      empty scope name (only NULL is accepted as argument)
216   //! @param theMax   [in]      number of steps in scope
217   //! @param isInfinite [in]    infinite flag
218   Message_ProgressScope (const Message_ProgressRange& theRange,
219                          const NullString* theName,
220                          Standard_Real theMax,
221                          Standard_Boolean isInfinite = false);
222
223   //! Sets the name of the scope.
224   void SetName (const TCollection_AsciiString& theName)
225   {
226     if (myIsOwnName)
227     {
228       Standard::Free (myName);
229       myIsOwnName = false;
230     }
231     myName = NULL;
232     if (!theName.IsEmpty())
233     {
234       myIsOwnName = true;
235       myName = (char* )Standard::Allocate (theName.Length() + 1);
236       char* aName = (char* )myName;
237       memcpy (aName, theName.ToCString(), theName.Length());
238       aName[theName.Length()] = '\0';
239     }
240   }
241
242   //! Sets the name of the scope; can be null.
243   //! Note! Just pointer to the given string is copied,
244   //! so do not pass string from a temporary variable whose
245   //! lifetime is less than that of this object.
246   template<size_t N>
247   void SetName (const char (&theName)[N])
248   {
249     if (myIsOwnName)
250     {
251       Standard::Free (myName);
252       myIsOwnName = false;
253     }
254     myName = theName;
255   }
256
257 public: //! @name Advance by iterations
258
259   //! Returns true if ProgressIndicator signals UserBreak
260   Standard_Boolean UserBreak() const;
261
262   //! Returns false if ProgressIndicator signals UserBreak
263   Standard_Boolean More() const
264   {
265     return !UserBreak();
266   }
267
268   //! Advances position by specified step and returns the range
269   //! covering this step
270   Message_ProgressRange Next (Standard_Real theStep = 1.);
271
272 public: //! @name Auxiliary methods to use in ProgressIndicator
273
274   //! Force update of presentation of the progress indicator.
275   //! Should not be called concurrently.
276   void Show();
277
278   //! Returns true if this progress scope is attached to some indicator.
279   Standard_Boolean IsActive() const
280   {
281     return myIsActive;
282   }
283
284   //! Returns the name of the scope (may be null).
285   //! Scopes with null name (e.g. root scope) should
286   //! be bypassed when reporting progress to the user.
287   Standard_CString Name() const
288   {
289     return myName;
290   }
291
292   //! Returns parent scope (null for top-level scope)
293   const Message_ProgressScope* Parent() const
294   {
295     return myParent;
296   }
297
298   //! Returns the maximal value of progress in this scope
299   Standard_Real MaxValue() const
300   {
301     return myMax;
302   }
303
304   //! Returns the current value of progress in this scope.
305   //! If this scope is being advanced by sub-scoping, that value is
306   //! computed by mapping current global progress into this scope range.
307   Standard_Real Value() const
308   {
309     return myIsActive ? myValue : myMax;
310   }
311
312   //! Returns the infinite flag
313   Standard_Boolean IsInfinite() const
314   {
315     return myIsInfinite;
316   }
317
318   //! Get the portion of the indicator covered by this scope (from 0 to 1)
319   Standard_Real GetPortion() const
320   {
321     return myPortion;
322   }
323
324 public: //! @name Destruction, allocation
325
326   //! Destructor - closes the scope and adds its scale to the total progress
327   ~Message_ProgressScope()
328   {
329     Relieve();
330     if (myIsOwnName)
331     {
332       Standard::Free (myName);
333       myIsOwnName = false;
334       myName = NULL;
335     }
336   }
337
338   //! Closes the scope and adds its scale to the total progress.
339   //! Relieved scope should not be used.
340   void Relieve();
341
342   DEFINE_STANDARD_ALLOC
343
344 private: //! @name Internal methods
345   
346   //! Creates a top-level scope with default range [0,1] and step 1.
347   //! Called only by Message_ProgressIndicator constructor.
348   Message_ProgressScope (Message_ProgressIndicator* theProgress);
349
350   //! Convert value from this scale to global one 
351   Standard_Real localToGlobal(const Standard_Real theVal) const;
352
353   //! Convert value from global scale to this one 
354   Standard_Real globalToLocal(const Standard_Real theVal) const;
355
356 private:
357   //! Copy constructor is prohibited
358   Message_ProgressScope (const Message_ProgressScope& theOther);
359
360   //! Copy assignment is prohibited
361   Message_ProgressScope& operator= (const Message_ProgressScope& theOther);
362
363 private:
364
365   Message_ProgressIndicator* myProgress; //!< Pointer to progress indicator instance
366   const Message_ProgressScope* myParent; //!< Pointer to parent scope
367   Standard_CString   myName;        //!< Name of the operation being done in this scope, or null
368
369   Standard_Real      myPortion;     //!< The portion of the global scale covered by this scope (from 0 to 1)
370   Standard_Real      myMax;         //!< Maximal value of progress in this scope
371   Standard_Real      myValue;       //!< Current position advanced within this scope [0, Max]
372
373   Standard_Boolean   myIsActive;    //!< flag indicating armed/disarmed state
374   Standard_Boolean   myIsOwnName;   //!< flag indicating if name was allocated or not
375   Standard_Boolean   myIsInfinite;  //!< Option to advance by hyperbolic law
376
377 private:
378   friend class Message_ProgressIndicator;
379   friend class Message_ProgressRange;
380 };
381
382 #include <Message_ProgressRange.hxx>
383
384 //=======================================================================
385 //function : Message_ProgressScope
386 //purpose  :
387 //=======================================================================
388 inline Message_ProgressScope::Message_ProgressScope (Message_ProgressIndicator* theProgress)
389 : myProgress(theProgress),
390   myParent(0),
391   myName(0),
392   myPortion(1.),
393   myMax(1.),
394   myValue(0.),
395   myIsActive(theProgress != NULL),
396   myIsOwnName(false),
397   myIsInfinite(false)
398 {
399 }
400
401 //=======================================================================
402 //function : Message_ProgressScope
403 //purpose  :
404 //=======================================================================
405 inline Message_ProgressScope::Message_ProgressScope (const Message_ProgressRange& theRange,
406                                                      const TCollection_AsciiString& theName,
407                                                      Standard_Real theMax,
408                                                      Standard_Boolean isInfinite)
409 : myProgress (theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
410   myParent (theRange.myParentScope),
411   myName (NULL),
412   myPortion (theRange.myDelta),
413   myMax (Max (1.e-6, theMax)), // protection against zero range
414   myValue (0.),
415   myIsActive (myProgress != NULL && !theRange.myWasUsed),
416   myIsOwnName (false),
417   myIsInfinite (isInfinite)
418 {
419   SetName (theName);
420   Standard_ASSERT_VOID (! theRange.myWasUsed, "Message_ProgressRange is used to initialize more than one scope");
421   theRange.myWasUsed = true; // Disarm the range
422 }
423
424 //=======================================================================
425 //function : Message_ProgressScope
426 //purpose  :
427 //=======================================================================
428 template<size_t N>
429 Message_ProgressScope::Message_ProgressScope (const Message_ProgressRange& theRange,
430                                               const char (&theName)[N],
431                                               Standard_Real theMax,
432                                               Standard_Boolean isInfinite)
433 : myProgress (theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
434   myParent (theRange.myParentScope),
435   myName (theName),
436   myPortion (theRange.myDelta),
437   myMax (Max (1.e-6, theMax)), // protection against zero range
438   myValue (0.),
439   myIsActive (myProgress != NULL && !theRange.myWasUsed),
440   myIsOwnName (false),
441   myIsInfinite (isInfinite)
442 {
443   Standard_ASSERT_VOID (! theRange.myWasUsed, "Message_ProgressRange is used to initialize more than one scope");
444   theRange.myWasUsed = true; // Disarm the range
445 }
446
447 //=======================================================================
448 //function : Message_ProgressScope
449 //purpose  :
450 //=======================================================================
451 inline Message_ProgressScope::Message_ProgressScope (const Message_ProgressRange& theRange,
452                                                      const NullString* ,
453                                                      Standard_Real theMax,
454                                                      Standard_Boolean isInfinite)
455 : myProgress (theRange.myParentScope != NULL ? theRange.myParentScope->myProgress : NULL),
456   myParent (theRange.myParentScope),
457   myName (NULL),
458   myPortion (theRange.myDelta),
459   myMax (Max (1.e-6, theMax)), // protection against zero range
460   myValue (0.),
461   myIsActive (myProgress != NULL && !theRange.myWasUsed),
462   myIsOwnName (false),
463   myIsInfinite (isInfinite)
464 {
465   Standard_ASSERT_VOID (! theRange.myWasUsed, "Message_ProgressRange is used to initialize more than one scope");
466   theRange.myWasUsed = true; // Disarm the range
467 }
468
469 //=======================================================================
470 //function : Relieve
471 //purpose  :
472 //=======================================================================
473 inline void Message_ProgressScope::Relieve()
474 {
475   if (!myIsActive)
476   {
477     return;
478   }
479
480   // Advance indicator to the end of the scope
481   Standard_Real aCurr = localToGlobal (myValue);
482   myValue = (myIsInfinite ? Precision::Infinite() : myMax);
483   Standard_Real aDelta = myPortion - aCurr;
484   if (aDelta > 0.)
485   {
486     myProgress->Increment (aDelta, *this);
487   }
488   Standard_ASSERT_VOID (myParent == 0 || myParent->myIsActive,
489     "Parent progress scope has been closed before child");
490
491   myIsActive = false;
492 }
493
494 //=======================================================================
495 //function : UserBreak
496 //purpose  :
497 //=======================================================================
498 inline Standard_Boolean Message_ProgressScope::UserBreak() const
499 {
500   return myProgress && myProgress->UserBreak();
501 }
502
503 //=======================================================================
504 //function : Next
505 //purpose  :
506 //=======================================================================
507 inline Message_ProgressRange Message_ProgressScope::Next (Standard_Real theStep)
508 {
509   if (myIsActive)
510   {
511     if (theStep > 0.)
512     {
513       Standard_Real aCurr = localToGlobal (myValue);
514       Standard_Real aNext = localToGlobal (myValue += theStep);
515       Standard_Real aDelta = aNext - aCurr;
516       if (aDelta > 0.)
517       {
518         return Message_ProgressRange (*this, aDelta);
519       }
520     }
521   }
522   return Message_ProgressRange();
523 }
524
525 //=======================================================================
526 //function : Show
527 //purpose  :
528 //=======================================================================
529
530 inline void Message_ProgressScope::Show ()
531 {
532   if (myIsActive)
533   {
534     myProgress->Show (*this, Standard_True);
535   }
536 }
537
538 //=======================================================================
539 //function : localToGlobal
540 //purpose  :
541 //=======================================================================
542 inline Standard_Real Message_ProgressScope::localToGlobal (const Standard_Real theVal) const
543 {
544   if (theVal <= 0.)
545     return 0.;
546
547   if (!myIsInfinite)
548   {
549     if (myMax - theVal < RealSmall())
550       return myPortion;
551     return myPortion * theVal / myMax;
552   }
553
554   double x = theVal / myMax;
555   // return myPortion * ( 1. - std::exp ( -x ) ); // exponent
556   return myPortion * x / (1. + x);  // hyperbola
557 }
558
559 //=======================================================================
560 //function : globalToLocal
561 //purpose  :
562 //=======================================================================
563
564 inline Standard_Real Message_ProgressScope::globalToLocal (const Standard_Real theVal) const
565 {
566   // if at end of the scope (or behind), report the maximum
567   Standard_Real aDist = myPortion - theVal;
568   if (aDist <= Precision::Confusion())
569     return myIsInfinite ? Precision::Infinite() : myMax;
570
571   if (!myIsInfinite)
572     return myMax * theVal / myPortion;
573
574   //  Standard_Real x = log (theVal / aDist); // exponent
575   Standard_Real x = theVal / aDist;         // hyperbola
576   return x * myMax;
577 }
578
579 #endif // _Message_ProgressScope_HeaderFile