FOSSredder 0.5.0
Loading...
Searching...
No Matches
JobManager.cpp
Go to the documentation of this file.
1
5
6#include "JobManager.h"
7
10
11#include <utility>
12
13namespace core::jobs {
14
15namespace {
16
17inline constexpr auto kQueued = "Queued";
18inline constexpr auto kRunning = "Running";
19inline constexpr auto kFinished = "Finished";
20inline constexpr auto kFailed = "Failed";
21inline constexpr auto kCanceled = "Canceled";
22
23JobEvent snapshotToEvent(const JobSnapshot& snapshot, std::string message = {})
24{
25 JobEvent event;
26 event.jobId = snapshot.jobId;
27 event.kind = snapshot.kind;
28 event.state = snapshot.state;
29 event.stage = snapshot.stage;
30 event.progress = snapshot.progress;
31 event.message = message.empty() ? snapshot.message : std::move(message);
32 event.pageIndex = snapshot.pageIndex;
33 event.pageCount = snapshot.pageCount;
34 return event;
35}
36
37void applyEventToSnapshot(JobSnapshot& snapshot, const JobEvent& event)
38{
39 snapshot.kind = event.kind;
40 snapshot.state = event.state;
41 snapshot.stage = event.stage;
42 snapshot.progress = event.progress;
43 if (!event.message.empty()) snapshot.message = event.message;
44 snapshot.pageIndex = event.pageIndex;
45 snapshot.pageCount = event.pageCount;
46}
47
48}
49
51 std::shared_ptr<core::ports::diagnostics::IErrorReporter> errorReporter)
52 : errorReporter_(std::move(errorReporter)) {}
53
54JobId JobManager::makeJobId() {
56}
57
59 auto data = std::make_shared<JobData>();
60 data->snap.jobId = makeJobId();
61 data->snap.kind = kind;
62 data->snap.state = JobState::Pending;
63 data->snap.stage = JobStage::None;
64 data->snap.progress = 0.0;
65 data->snap.message = std::string(kQueued);
66 data->cancel = std::make_shared<std::atomic<bool>>(false);
67 data->pause = std::make_shared<std::atomic<bool>>(false);
68
69 {
70 std::lock_guard<std::mutex> g(jobsMutex_);
71 jobs_.emplace(data->snap.jobId, data);
72 order_.push_back(data->snap.jobId);
73 }
74 prune(kMaxJobs);
75 return data->snap.jobId;
76}
77
79 if (!cb) return 0;
80 std::shared_ptr<JobData> job;
81 {
82 std::lock_guard<std::mutex> g(jobsMutex_);
83 auto it = jobs_.find(id);
84 if (it == jobs_.end()) return 0;
85 job = it->second;
86 }
87
88 std::lock_guard<std::mutex> g(job->m);
89 SubscriptionId sid = job->nextSub++;
90 job->subs.emplace(sid, std::move(cb));
91 return sid;
92}
93
95 if (subId == 0) return;
96 std::shared_ptr<JobData> job;
97 {
98 std::lock_guard<std::mutex> g(jobsMutex_);
99 auto it = jobs_.find(id);
100 if (it == jobs_.end()) return;
101 job = it->second;
102 }
103
104 std::lock_guard<std::mutex> g(job->m);
105 job->subs.erase(subId);
106}
107
108void JobManager::cancel(const JobId& id) {
109 std::shared_ptr<JobData> job;
110 {
111 std::lock_guard<std::mutex> g(jobsMutex_);
112 auto it = jobs_.find(id);
113 if (it == jobs_.end()) return;
114 job = it->second;
115 }
116
117 if (job->cancel) {
118 try { job->cancel->store(true); } catch (...) { core::errors::reportException(errorReporter_.get(), core::errors::ErrorSeverity::Warning, "core::jobs::JobManager::cancel::store", std::current_exception()); }
119 }
120
121 JobEvent ev;
122 {
123 std::lock_guard<std::mutex> g(job->m);
124 job->snap.state = JobState::Canceled;
125 job->snap.message = std::string(kCanceled);
126 ev = snapshotToEvent(job->snap);
127 }
128 publish(ev);
129
130 prune(kMaxJobs);
131}
132
133void JobManager::pause(const JobId& id) {
134 std::shared_ptr<JobData> job;
135 {
136 std::lock_guard<std::mutex> g(jobsMutex_);
137 auto it = jobs_.find(id);
138 if (it == jobs_.end()) return;
139 job = it->second;
140 }
141
142 if (job->pause) {
143 try { job->pause->store(true); } catch (...) { core::errors::reportException(errorReporter_.get(), core::errors::ErrorSeverity::Warning, "core::jobs::JobManager::pause::store", std::current_exception()); }
144 }
145
146 JobEvent ev;
147 {
148 std::lock_guard<std::mutex> g(job->m);
149 if (job->snap.state != JobState::Running) return;
150 job->snap.state = JobState::Paused;
151 job->snap.message = "Paused";
152 ev = snapshotToEvent(job->snap);
153 }
154 publish(ev);
155}
156
157void JobManager::resume(const JobId& id) {
158 std::shared_ptr<JobData> job;
159 {
160 std::lock_guard<std::mutex> g(jobsMutex_);
161 auto it = jobs_.find(id);
162 if (it == jobs_.end()) return;
163 job = it->second;
164 }
165
166 if (job->pause) {
167 try { job->pause->store(false); } catch (...) { core::errors::reportException(errorReporter_.get(), core::errors::ErrorSeverity::Warning, "core::jobs::JobManager::resume::store", std::current_exception()); }
168 }
169
170 JobEvent ev;
171 {
172 std::lock_guard<std::mutex> g(job->m);
173 if (job->snap.state != JobState::Paused) return;
174 job->snap.state = JobState::Running;
175 job->snap.message = std::string(kRunning);
176 ev = snapshotToEvent(job->snap);
177 }
178 publish(ev);
179}
180
181std::optional<JobSnapshot> JobManager::snapshot(const JobId& id) const {
182 std::shared_ptr<JobData> job;
183 {
184 std::lock_guard<std::mutex> g(jobsMutex_);
185 auto it = jobs_.find(id);
186 if (it == jobs_.end()) return std::nullopt;
187 job = it->second;
188 }
189
190 std::lock_guard<std::mutex> g(job->m);
191 return job->snap;
192}
193
194std::shared_ptr<std::atomic<bool>> JobManager::cancelFlag(const JobId& id) const {
195 std::lock_guard<std::mutex> g(jobsMutex_);
196 auto it = jobs_.find(id);
197 if (it == jobs_.end()) return nullptr;
198 return it->second->cancel;
199}
200
201std::shared_ptr<std::atomic<bool>> JobManager::pauseFlag(const JobId& id) const {
202 std::lock_guard<std::mutex> g(jobsMutex_);
203 auto it = jobs_.find(id);
204 if (it == jobs_.end()) return nullptr;
205 return it->second->pause;
206}
207
209 std::shared_ptr<JobData> job;
210 {
211 std::lock_guard<std::mutex> g(jobsMutex_);
212 auto it = jobs_.find(ev.jobId);
213 if (it == jobs_.end()) return;
214 job = it->second;
215 }
216
217 std::vector<JobEventCallback> cbs;
218 {
219 std::lock_guard<std::mutex> g(job->m);
220 applyEventToSnapshot(job->snap, ev);
221
222 cbs.reserve(job->subs.size());
223 for (auto& kv : job->subs) cbs.push_back(kv.second);
224 }
225
226 for (auto& cb : cbs) {
227 try { cb(ev); } catch (...) { core::errors::reportException(errorReporter_.get(), core::errors::ErrorSeverity::Warning, "core::jobs::JobManager::publish::callback", std::current_exception()); }
228 }
229}
230
231void JobManager::start(const JobId& id) {
232 JobEvent ev;
233 std::shared_ptr<JobData> job;
234 {
235 std::lock_guard<std::mutex> g(jobsMutex_);
236 auto it = jobs_.find(id);
237 if (it == jobs_.end()) return;
238 job = it->second;
239 }
240
241 {
242 std::lock_guard<std::mutex> g(job->m);
243 job->snap.state = JobState::Running;
244 job->snap.message = std::string(kRunning);
245 ev = snapshotToEvent(job->snap);
246 }
247
248 publish(ev);
249
250 prune(kMaxJobs);
251}
252
253void JobManager::finish(const JobId& id) {
254 JobEvent ev;
255 std::shared_ptr<JobData> job;
256 {
257 std::lock_guard<std::mutex> g(jobsMutex_);
258 auto it = jobs_.find(id);
259 if (it == jobs_.end()) return;
260 job = it->second;
261 }
262
263 {
264 std::lock_guard<std::mutex> g(job->m);
265 job->snap.state = JobState::Finished;
266 job->snap.progress = 1.0;
267 job->snap.message = std::string(kFinished);
268 ev = snapshotToEvent(job->snap);
269 }
270
271 publish(ev);
272
273 prune(kMaxJobs);
274}
275
276void JobManager::prune(std::size_t maxJobs) {
277 if (maxJobs < 1) maxJobs = 1;
278
279 std::lock_guard<std::mutex> g(jobsMutex_);
280 if (jobs_.size() <= maxJobs) return;
281
282 // Attempt to evict terminal jobs without subscribers in FIFO order.
283 // If none are evictable, keep them (avoid deleting running/subscribed jobs).
284 std::size_t scanned = 0;
285 const std::size_t limit = order_.size();
286 while (jobs_.size() > maxJobs && scanned < limit && !order_.empty()) {
287 JobId id = std::move(order_.front());
288 order_.pop_front();
289 ++scanned;
290
291 auto it = jobs_.find(id);
292 if (it == jobs_.end()) continue;
293 auto job = it->second;
294 if (!job) {
295 jobs_.erase(it);
296 continue;
297 }
298
299 bool evictable = false;
300 {
301 std::lock_guard<std::mutex> gj(job->m);
302 const auto st = job->snap.state;
303 const bool terminal = (st == JobState::Finished || st == JobState::Failed || st == JobState::Canceled);
304 evictable = terminal && job->subs.empty();
305 }
306
307 if (evictable) {
308 jobs_.erase(it);
309 continue;
310 }
311
312 // keep it in rotation
313 order_.push_back(std::move(id));
314 }
315}
316
317void JobManager::fail(const JobId& id, const std::string& error) {
318 JobEvent ev;
319 std::shared_ptr<JobData> job;
320 {
321 std::lock_guard<std::mutex> g(jobsMutex_);
322 auto it = jobs_.find(id);
323 if (it == jobs_.end()) return;
324 job = it->second;
325 }
326
327 {
328 std::lock_guard<std::mutex> g(job->m);
329 job->snap.state = JobState::Failed;
330 job->snap.error = error;
331 job->snap.message = std::string(kFailed);
332 ev = snapshotToEvent(job->snap, error);
333 }
334
335 publish(ev);
336}
337
338}
Provides shared helpers for reporting structured errors and exceptions.
Declares the private generic job state manager used behind JobSystem.
Declares private unique identifier helpers for transient import/job artifacts.
std::shared_ptr< std::atomic< bool > > pauseFlag(const JobId &id) const
Retrieve a job pause flag.
void finish(const JobId &id)
Mark a job as finished.
void start(const JobId &id)
Mark a job as running.
void cancel(const JobId &id)
Mark a job as canceled and set its cancellation flag.
void publish(const JobEvent &ev)
Publish an event to subscribers and update the stored snapshot.
void resume(const JobId &id)
Resume a paused job and clear its pause flag.
JobId submit(JobKind kind)
Create a tracked job in the pending state.
std::shared_ptr< std::atomic< bool > > cancelFlag(const JobId &id) const
Retrieve a job cancellation flag.
SubscriptionId subscribe(const JobId &id, JobEventCallback cb)
Subscribe to events for a job.
void fail(const JobId &id, const std::string &error)
Mark a job as failed.
void pause(const JobId &id)
Mark a running job as paused and set its pause flag.
void unsubscribe(const JobId &id, SubscriptionId subId)
Remove a job event subscription.
std::optional< JobSnapshot > snapshot(const JobId &id) const
Retrieve the latest state snapshot for a job.
JobManager(std::shared_ptr< core::ports::diagnostics::IErrorReporter > errorReporter)
Create a manager for generic job state and event delivery.
void reportException(core::ports::diagnostics::IErrorReporter *reporter, ErrorSeverity severity, const char *origin, std::exception_ptr exception)
Report an exception through the reporter's default exception handling.
std::uint64_t SubscriptionId
Identifier for a job event subscription.
Definition JobTypes.h:88
std::string JobId
Stable identifier for one tracked job.
Definition JobTypes.h:21
JobKind
Classifies the high-level type of job.
Definition JobTypes.h:26
std::function< void(const JobEvent &)> JobEventCallback
Callback invoked for published job events.
Definition JobTypes.h:93
std::string makeTransientId()
Generates a transient unique identifier for import and job artifacts.
Event emitted when a job changes state or progress.
Definition JobTypes.h:52
Snapshot of the latest known job state.
Definition JobTypes.h:70