Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
ProgressMonitor.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include <atomic>
5
7#include "Stroika/Foundation/Math/Common.h"
10
11#include "Common.h"
12#include "Synchronized.h"
13#include "Thread.h"
14#include "UserCanceledException.h"
15
17
18 /*
19 ********************************************************************************
20 ***************************** ProgressMonitor::Rep_ ****************************
21 ********************************************************************************
22 */
23 class ProgressMonitor::Rep_ : public Memory::UseBlockAllocationIfAppropriate<Rep_> {
24 public:
25 Rep_ () = default;
26
27 Synchronized<Containers::Sequence<shared_ptr<ChangedCallbackType>>> fCallbacks_; // store shared_ptr in case mutable function - saving state - re-used across runs and not copied
28 atomic<bool> fCanceled_{false};
29 atomic<ProgressRangeType> fCurrentProgress_{0.0};
30 Synchronized<CurrentTaskInfo> fCurrentTaskInfo_;
31 Thread::Ptr fWorkThread_; // optional - ignore if empty
32 };
33
34 /*
35 ********************************************************************************
36 *********************** ProgressMonitor::CurrentTaskInfo ***********************
37 ********************************************************************************
38 */
39 inline ProgressMonitor::CurrentTaskInfo::CurrentTaskInfo (const Characters::String& taskName, const DataExchange::VariantValue& details)
40 : fName{taskName}
41 , fDetails{details}
42 {
43 }
44
45 /*
46 ********************************************************************************
47 ****************************** ProgressMonitor *********************************
48 ********************************************************************************
49 */
50 inline ProgressMonitor::ProgressMonitor (const shared_ptr<Rep_>& rep)
51 : fRep_{rep}
52 {
53 }
54 inline ProgressMonitor::ProgressRangeType ProgressMonitor::GetProgress () const
55 {
56 RequireNotNull (fRep_);
57 Ensure (0.0 <= fRep_->fCurrentProgress_ and fRep_->fCurrentProgress_ <= 1.0);
58 return fRep_->fCurrentProgress_;
59 }
60 inline ProgressMonitor::CurrentTaskInfo ProgressMonitor::GetCurrentTaskInfo () const
61 {
62 RequireNotNull (fRep_);
63 return fRep_->fCurrentTaskInfo_;
64 }
65 inline ProgressMonitor::operator Updater ()
66 {
67 return Updater{fRep_};
68 }
69
70 /*
71 ********************************************************************************
72 *************************** ProgressMonitor::Updater ***************************
73 ********************************************************************************
74 */
75 inline ProgressMonitor::Updater::Updater (nullptr_t)
76 {
77 }
78 inline ProgressMonitor::Updater::Updater (const Updater& parentTask, ProgressRangeType fromProg, ProgressRangeType toProg, bool restoreTaskInfoOnDTOR)
79 : fRep_{parentTask.fRep_}
80 , fFromProg_{parentTask.fFromProg_ + fromProg * (parentTask.fToProg_ - parentTask.fFromProg_)}
81 , fToProg_{parentTask.fFromProg_ + toProg * (parentTask.fToProg_ - parentTask.fFromProg_)}
82 {
83 fFromProg_ = Math::PinToSpecialPoint (fFromProg_, parentTask.fFromProg_);
84 fToProg_ = Math::PinToSpecialPoint (fToProg_, parentTask.fToProg_);
85 Require ((0.0f <= parentTask.fFromProg_) and (parentTask.fFromProg_ <= fFromProg_) and (fFromProg_ < fToProg_) and
86 (fToProg_ <= parentTask.fToProg_) and (parentTask.fToProg_ <= 1.0f));
87 if (restoreTaskInfoOnDTOR and fRep_ != nullptr) {
88 fRestoreTaskInfo_ = fRep_->fCurrentTaskInfo_.load ();
89 }
90 }
91 inline ProgressMonitor::Updater::Updater (const Updater& parentTask, ProgressRangeType fromProg, ProgressRangeType toProg,
92 const CurrentTaskInfo& taskInfo, bool restoreTaskInfoOnDTOR)
93 : Updater{parentTask, fromProg, toProg, restoreTaskInfoOnDTOR}
94 {
95 SetCurrentTaskInfo (taskInfo);
96 }
97 inline ProgressMonitor::Updater::Updater (const shared_ptr<Rep_>& r)
98 : fRep_{r}
99 {
100 }
101 inline ProgressMonitor::Updater::~Updater ()
102 {
103 if (fRestoreTaskInfo_) {
104 fRestoreTaskInfo_ = fRep_->fCurrentTaskInfo_.load ();
105 CallNotifyProgress_ ();
106 }
107 }
108 inline void ProgressMonitor::Updater::SetProgress (ProgressRangeType p)
109 {
110 ThrowIfCanceled ();
111 if (fRep_ != nullptr) {
112 WeakAssert (-0.001 < p and p < 1.001); // 'Weak Require' - outside this range, and its probably a caller bug
113 p = Math::PinToSpecialPoint (Math::PinToSpecialPoint (p, 1.0f), 0.0f);
114 Assert (0.0 <= p and p <= 1.0);
115 p = fFromProg_ + p * (fToProg_ - fFromProg_);
116 p = Math::PinToSpecialPoint (Math::PinToSpecialPoint (p, 1.0f), 0.0f);
117 Assert (0.0 <= p and p <= 1.0);
118 // pin-to-special-point to avoid floating point rounding errors triggering bogus assertions/progress changes
119 p = Math::PinToSpecialPoint (p, fRep_->fCurrentProgress_.load ());
120 // disallow moving progress backwards because it is nearly always a bug, and not terribly useful
121 Require (p >= fRep_->fCurrentProgress_);
122 if (fRep_->fCurrentProgress_.exchange (p) != p) {
123 // only call if value changed - but always write so atomic update
124 CallNotifyProgress_ ();
125 }
126 }
127 }
129 {
130 if (fRep_ != nullptr and fRep_->fCanceled_) {
131 if (fRep_->fWorkThread_ != nullptr) {
132 fRep_->fWorkThread_.Abort ();
133 }
134 Throw (UserCanceledException::kThe);
135 }
137 }
138 inline void ProgressMonitor::Updater::SetCurrentTaskInfo (const CurrentTaskInfo& taskInfo)
139 {
140 if (fRep_ != nullptr) {
141 bool changed = fRep_->fCurrentTaskInfo_ != taskInfo;
142 fRep_->fCurrentTaskInfo_ = taskInfo;
143 if (changed) {
144 CallNotifyProgress_ ();
145 }
146 }
147 ThrowIfCanceled ();
148 }
149 inline void ProgressMonitor::Updater::SetCurrentProgressAndThrowIfCanceled (ProgressRangeType currentProgress)
150 {
151 if (fRep_ != nullptr) {
152 SetProgress (currentProgress);
153 ThrowIfCanceled ();
154 }
155 }
156
157 /*
158 ********************************************************************************
159 ************************ MakeInputStreamWithProgress ***************************
160 ********************************************************************************
161 */
162 template <typename T>
164 {
165 struct inputStreamWithProgress : Streams::InputStreamDelegationHelper<T> {
167 using ProgressType = ProgressMonitor::ProgressRangeType;
168 inputStreamWithProgress (const Streams::InputStream::Ptr<T>& in, Execution::ProgressMonitor::Updater progress)
169 : inherited{in}
170 , fProgress_{progress}
171 , fInitialSeek_{in.GetOffset ()}
172 , fHighWaterMark_{fInitialSeek_}
173 , fKnownEnd_{in.RemainingLength ()}
174 , fEstimatedEnd_{static_cast<ProgressType> (fKnownEnd_.value_or (fInitialSeek_ + 1024))} // exponentially grow if we ever exceed
175 {
176 }
177 // Intentionally ignore Seek, because that could be used to estimate total file size and anything contributing to progress must be an actual READ
178 virtual optional<span<T>> Read (span<T> intoBuffer, Streams::NoDataAvailableHandling blockFlag) override
179 {
180 auto r = inherited::fRealIn.ReadOrThrow (intoBuffer, blockFlag);
181 Streams::SeekOffsetType curOff = inherited::fRealIn.GetOffset ();
182 fHighWaterMark_ = max (curOff, fHighWaterMark_);
183 if (not fKnownEnd_ and curOff > 0.75 * fEstimatedEnd_) {
184 fEstimatedEnd_ *= 1.5;
185 }
186 ProgressType progress =
187 static_cast<ProgressType> (fHighWaterMark_ - fInitialSeek_) / static_cast<ProgressType> (fEstimatedEnd_ - fInitialSeek_);
188 if (progress > fLastProgressSent_) [[likely]] {
189 fProgress_.SetProgress (progress);
190 fLastProgressSent_ = progress;
191 }
192 return r;
193 }
194 ProgressMonitor::Updater fProgress_;
195 Streams::SeekOffsetType fInitialSeek_;
196 Streams::SeekOffsetType fHighWaterMark_;
197 optional<Streams::SeekOffsetType> fKnownEnd_;
198 ProgressType fEstimatedEnd_;
199 ProgressType fLastProgressSent_{0};
200 };
201 return Streams::InputStream::Ptr<T>{make_shared<inputStreamWithProgress> (in, progress)};
202 }
203
204}
#define RequireNotNull(p)
Definition Assertions.h:347
#define WeakAssert(c)
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be t...
Definition Assertions.h:438
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
Definition Stream.h:90
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
nonvirtual SeekOffsetType GetOffset() const
nonvirtual optional< SeekOffsetType > RemainingLength() const
returns nullopt if not known (typical, and the default) - but sometimes it is known,...
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
Streams::InputStream::Ptr< T > MakeInputStreamWithProgress(const Streams::InputStream::Ptr< T > &in, ProgressMonitor::Updater progress)