Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Capturer.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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 , captureSets{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Collection<CaptureSet> {
30 const Capturer* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Capturer::captureSets);
31 return thisObj->fCaptureSets_.load ();
32 },
33 [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] auto* property, const auto& captureSets) {
34 Capturer* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Capturer::captureSets);
35 thisObj->fCaptureSets_ = captureSets;
36 }}
37{
38 Require (Debug::AppearsDuringMainLifetime ());
39}
40
42 : Capturer{}
43{
44 AddCaptureSet (cs);
45}
46
47auto Capturer::AddMeasurementsCallback (const NewMeasurementsCallbackType& cb) -> MeasurementsCallbackID
48{
49 return fCallbacks_.Add (cb);
50}
51
53{
54 fCallbacks_.Remove (cb);
55}
56
57void Capturer::AddCaptureSet (const CaptureSet& cs)
58{
59 RunnerOnce_ (cs);
60 auto rwLock = fCaptureSets_.rwget ();
61 rwLock->Add (cs);
62 ++fCaptureSetChangeCount_;
63 ManageRunner_ (true); // start while still holding lock
64}
65
66void Capturer::ManageRunner_ (bool on)
67{
68 bool threadPoolRunning = fThreadPool_.GetTasksCount () != 0;
69 if (on) {
70 if (not threadPoolRunning) {
71 fThreadPool_.AddTask ([this] () { Runner_ (); });
72 fThreadPool_.SetPoolSize (1);
73 }
74 }
75 else {
76 if (threadPoolRunning) {
77 fThreadPool_.AbortTasks ();
78 fThreadPool_.SetPoolSize (0);
79 }
80 }
81}
82
83void Capturer::Runner_ ()
84{
85 //
86 // really only need one thread - and just wait right amount of time to wakeup to service next captureset.
87 //
88 // Compute a list of the 'next runs' (sorted by next first). Then WaitUntil () on that.
89 // and then RunOnce_(thatCaptureSet); this doesn't exactly guarantee they run at the right time but close enuf and with just one thread
90 //
91
92 // First walk list of capture-sets and produce such an 'queue' as a local variable. At top of loop, recompute if change count
93 // indicates in needs recomputing (rare). Else just keep adjusting it.
94 uint64_t changeCountForCaptureSet{0};
95
96 // NOTE - we use a SortedMapping instead of an acutal Queue, because we always know WHEN each item should run next, and a sorted mapping
97 // 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
98 // not belong at the end of the Queue cuz it runs on a different schedule
100
101 auto recomputeSortOrder = [&] () {
102 TimePointSeconds now = Time::GetTickCount ();
103 auto lock = fCaptureSets_.cget (); // hold lock until I've examined changeCount
104
105 // it would be nice to be able to preserve the timing info in the runQueue for items not changed, but that would require
106 // being able to tell which CaptureSets were new and old, which is not always possible given the current API (SetCaptureSet).
107 // and updating the capture sets should be rare, so it should cause little problem to recompute it, and reset the counters.
108 runQueue.clear ();
110 for (const auto& i : lock.load ()) {
111 runQueue.Add (now + i.runPeriod (), i);
112 }
113 changeCountForCaptureSet = fCaptureSetChangeCount_;
114 };
115
116 // 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
117 while (true) {
118 if (changeCountForCaptureSet < fCaptureSetChangeCount_) {
119 recomputeSortOrder (); // updates runQueue as a side-effect
120 }
121
122 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)
123
124 // 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
125 // 'next' time.
126 auto iterator = runQueue.begin ();
127 Assert (iterator != runQueue.end ());
129 SleepUntil (runNext.fKey);
130 RunnerOnce_ (runNext.fValue);
131 runQueue.erase (iterator);
132 runQueue.Add (runNext.fKey + runNext.fValue.runPeriod (), runNext.fValue); // interpret time offset as wrt leading edge
133 }
134}
135
136void Capturer::RunnerOnce_ (const CaptureSet& cs)
137{
138 MeasurementSet measurements;
139 for (Instrument i : cs.instruments ()) {
140 try {
141 measurements.MergeAdditions (i.Capture ());
142 }
143 catch (const Thread::AbortException&) {
144 ReThrow ();
145 }
146 catch (...) {
147 using namespace Characters::Literals;
148 DbgTrace ("Eating exception '{}' in Capturer runner"_f, current_exception ());
149 }
150 }
151 UpdateMeasurementSet_ (measurements);
152}
153
154void Capturer::UpdateMeasurementSet_ (const MeasurementSet& ms)
155{
156 fCurrentMeasurementSet_.rwget ().rwref ().MergeAdditions (ms);
157 fCallbacks_.Call (ms);
158}
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:327
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:186
nonvirtual void erase(ArgByValueType< key_type > key)
Definition Mapping.inl:466
nonvirtual void AbortTasks(Time::DurationSeconds timeout=Time::kInfinity)
nonvirtual TaskID 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:317
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
nonvirtual MeasurementsCallbackID AddMeasurementsCallback(const NewMeasurementsCallbackType &cb)
Call cb with each MeasurementSet captured from now on - on the capturing thread (and,...
Definition Capturer.cpp:47
nonvirtual void RemoveMeasurementsCallback(MeasurementsCallbackID cb)
Once this returns, that callback is not running, and is never called again - except that,...
Definition Capturer.cpp:52
Execution::CallbackRegistry< void(const MeasurementSet &)>::ID MeasurementsCallbackID
Names a callback AddMeasurementsCallback added, for RemoveMeasurementsCallback.
Definition Capturer.h:157
An Instrument is a stateful object from which you can Capture () a series of measurements about a sys...
Definition Instrument.h:68
void SleepUntil(Time::TimePointSeconds untilTickCount)
Definition Sleep.cpp:19
nonvirtual void MergeAdditions(const MeasurementSet &m)