Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Capturer.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include "Stroika/Foundation/Containers/SortedMapping.h"
7#include "Stroika/Foundation/Debug/Main.h"
9
10#include "Capturer.h"
11
12using namespace Stroika::Foundation;
14using namespace Stroika::Foundation::Execution;
15
16using namespace Stroika::Frameworks;
17using namespace Stroika::Frameworks::SystemPerformance;
18
19/*
20 ********************************************************************************
21 ************************* SystemPerformance::Capturer **************************
22 ********************************************************************************
23 */
25 : pMostRecentMeasurements{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> MeasurementSet {
26 const Capturer* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Capturer::pMostRecentMeasurements);
27 return thisObj->fCurrentMeasurementSet_.load ();
28 }}
29 , measurementsCallbacks{
30 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Collection<NewMeasurementsCallbackType> {
31 const Capturer* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Capturer::measurementsCallbacks);
32 return thisObj->fCallbacks_.load ();
33 },
34 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] auto* property, const auto& callbacks) {
35 Capturer* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Capturer::measurementsCallbacks);
36 thisObj->fCallbacks_ = callbacks;
37 }}
38 , captureSets{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Collection<CaptureSet> {
39 const Capturer* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Capturer::captureSets);
40 return thisObj->fCaptureSets_.load ();
41 },
42 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] auto* property, const auto& captureSets) {
43 Capturer* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Capturer::captureSets);
44 thisObj->fCaptureSets_ = captureSets;
45 }}
46{
47 Require (Debug::AppearsDuringMainLifetime ());
48}
49
51 : Capturer{}
52{
53 AddCaptureSet (cs);
54}
55
56void Capturer::AddMeasurementsCallback (const NewMeasurementsCallbackType& cb)
57{
58 fCallbacks_.rwget ()->Add (cb);
59}
60
61void Capturer::RemoveMeasurementsCallback (const NewMeasurementsCallbackType& cb)
62{
63 fCallbacks_.rwget ()->Remove (cb);
64}
65
66void Capturer::AddCaptureSet (const CaptureSet& cs)
67{
68 RunnerOnce_ (cs);
69 auto rwLock = fCaptureSets_.rwget ();
70 rwLock->Add (cs);
71 ++fCaptureSetChangeCount_;
72 ManageRunner_ (true); // start while still holding lock
73}
74
75void Capturer::ManageRunner_ (bool on)
76{
77 bool threadPoolRunning = fThreadPool_.GetTasksCount () != 0;
78 if (on) {
79 if (not threadPoolRunning) {
80 fThreadPool_.AddTask ([this] () { Runner_ (); });
81 fThreadPool_.SetPoolSize (1);
82 }
83 }
84 else {
85 if (threadPoolRunning) {
86 fThreadPool_.AbortTasks ();
87 fThreadPool_.SetPoolSize (0);
88 }
89 }
90}
91
92void Capturer::Runner_ ()
93{
94 //
95 // really only need one thread - and just wait right amount of time to wakeup to service next captureset.
96 //
97 // Compute a list of the 'next runs' (sorted by next first). Then WaitUntil () on that.
98 // and then RunOnce_(thatCaptureSet); this doesn't exactly guarantee they run at the right time but close enuf and with just one thread
99 //
100
101 // First walk list of capture-sets and produce such an 'queue' as a local variable. At top of loop, recompute if change count
102 // indicates in needs recomputing (rare). Else just keep adjusting it.
103 uint64_t changeCountForCaptureSet{0};
104
105 // NOTE - we use a SortedMapping instead of an acutal Queue, because we always know WHEN each item should run next, and a sorted mapping
106 // tells is quicly and easily the 'next' item to run. If we used a Q, we would add back the just ran item at the end but it might
107 // not belong at the end of the Queue cuz it runs on a different schedule
109
110 auto recomputeSortOrder = [&] () {
111 TimePointSeconds now = Time::GetTickCount ();
112 auto lock = fCaptureSets_.cget (); // hold lock until I've examined changeCount
113
114 // it would be nice to be able to preserve the timing info in the runQueue for items not changed, but that would require
115 // being able to tell which CaptureSets were new and old, which is not always possible given the current API (SetCaptureSet).
116 // and updating the capture sets should be rare, so it should cause little problem to recompute it, and reset the counters.
117 runQueue.clear ();
119 for (const auto& i : lock.load ()) {
120 runQueue.Add (now + i.runPeriod (), i);
121 }
122 changeCountForCaptureSet = fCaptureSetChangeCount_;
123 };
124
125 // Note this runs in a threadpool which can be canceled as needed, so this need not check for a termination condition - it will be ended by a thread abort
126 while (true) {
127 if (changeCountForCaptureSet < fCaptureSetChangeCount_) {
128 recomputeSortOrder (); // updates runQueue as a side-effect
129 }
130
131 Assert (not runQueue.empty ()); // because otherwise the thread would have been aborted, and we wouldn't get this far (race??? - maybe need to do ifcheck)
132
133 // otherwise pop the first item from the Q, and wait til then. Then process it, and push it back onto the Q with the appropriate
134 // 'next' time.
135 auto iterator = runQueue.begin ();
136 Assert (iterator != runQueue.end ());
138 SleepUntil (runNext.fKey);
139 RunnerOnce_ (runNext.fValue);
140 runQueue.erase (iterator);
141 runQueue.Add (runNext.fKey + runNext.fValue.runPeriod (), runNext.fValue); // interpret time offset as wrt leading edge
142 }
143}
144
145void Capturer::RunnerOnce_ (const CaptureSet& cs)
146{
147 MeasurementSet measurements;
148 for (Instrument i : cs.instruments ()) {
149 try {
150 measurements.MergeAdditions (i.Capture ());
151 }
152 catch (const Thread::AbortException&) {
153 ReThrow ();
154 }
155 catch (...) {
156 using namespace Characters::Literals;
157 DbgTrace ("Eating exception '{}' in Capturer runner"_f, current_exception ());
158 }
159 }
160 UpdateMeasurementSet_ (measurements);
161}
162
163void Capturer::UpdateMeasurementSet_ (const MeasurementSet& ms)
164{
165 fCurrentMeasurementSet_.rwget ().rwref ().MergeAdditions (ms);
166 for (const auto& cb : fCallbacks_.load ()) {
167 cb (ms);
168 }
169}
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
#define DbgTrace
Definition Trace.h:309
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:190
nonvirtual void erase(ArgByValueType< key_type > key)
Definition Mapping.inl:431
nonvirtual void AbortTasks(Time::DurationSeconds timeout=Time::kInfinity)
nonvirtual TaskType AddTask(const TaskType &task, const optional< Characters::String > &name=nullopt)
nonvirtual size_t GetTasksCount() const
return total number of tasks, either pending, or currently running.
nonvirtual void SetPoolSize(unsigned int poolSize)
nonvirtual Iterator< T > begin() const
Support for ranged for, and STL syntax in general.
nonvirtual bool empty() const
Returns true iff size() == 0.
Definition Iterable.inl:306
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
An Instrument is a stateful object from which you can Capture () a series of measurements about a sys...
Definition Instrument.h:69
void SleepUntil(Time::TimePointSeconds untilTickCount)
Definition Sleep.inl:91
nonvirtual void MergeAdditions(const MeasurementSet &m)