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