GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 85.4% 35 / 0 / 41
Functions: 100.0% 6 / 0 / 6
Branches: 66.0% 33 / 0 / 50

src/worker.cpp
Line Branch Exec Source
1 #include "DetourModKit/worker.hpp"
2 #include "DetourModKit/diagnostics.hpp"
3 #include "DetourModKit/logger.hpp"
4 #include "platform.hpp"
5
6 #include <utility>
7
8 namespace DetourModKit
9 {
10
2/4
✓ Branch 4 → 5 taken 144 times.
✗ Branch 4 → 34 not taken.
✓ Branch 7 → 8 taken 144 times.
✗ Branch 7 → 48 not taken.
432 StoppableWorker::StoppableWorker(std::string_view name, std::function<void(std::stop_token)> body) : m_name(name)
11 {
12
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 143 times.
144 if (!body)
13 {
14
2/4
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 46 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 37 not taken.
1 Logger::get_instance().error("StoppableWorker '{}': empty body; no thread started.", m_name);
15 1 m_joined.store(true, std::memory_order_release);
16 1 return;
17 }
18
19 143 m_thread = std::jthread(
20
2/6
✓ Branch 19 → 20 taken 143 times.
✗ Branch 19 → 40 not taken.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 143 times.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
286 [fn = std::move(body), label = m_name](std::stop_token st)
21 {
22 try
23 {
24
2/2
✓ Branch 3 → 4 taken 141 times.
✓ Branch 3 → 7 taken 2 times.
145 fn(st);
25 }
26
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 16 taken 1 time.
2 catch (const std::exception &e)
27 {
28
2/4
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 22 not taken.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 20 not taken.
1 Logger::get_instance().error("StoppableWorker '{}': unhandled exception: {}", label, e.what());
29 1 }
30 1 catch (...)
31 {
32
2/4
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 25 not taken.
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 24 not taken.
1 Logger::get_instance().error("StoppableWorker '{}': unknown exception escaped body.", label);
33 1 }
34
1/2
✓ Branch 20 → 21 taken 143 times.
✗ Branch 20 → 38 not taken.
286 });
35
36 143 m_stop_source = m_thread.get_stop_source();
37
38 // Publish liveness through an atomic once both the thread handle and stop source are initialized.
39 // is_running() reads only this snapshot, and request_stop() signals m_stop_source, so neither path touches the
40 // std::jthread handle concurrently with shutdown()'s join()/detach().
41 143 m_started.store(true, std::memory_order_release);
42
43
2/4
✓ Branch 30 → 31 taken 143 times.
✗ Branch 30 → 46 not taken.
✓ Branch 31 → 32 taken 143 times.
✗ Branch 31 → 45 not taken.
143 Logger::get_instance().debug("StoppableWorker '{}' started.", m_name);
44 }
45
46 140 StoppableWorker::~StoppableWorker() noexcept
47 {
48 140 shutdown();
49 140 }
50
51 58072 void StoppableWorker::request_stop() noexcept
52 {
53 // Gate on the liveness atomics rather than m_thread.joinable(): a concurrent shutdown() may be inside join()/
54 // detach(), which mutate the handle. Signal the copied stop_source so this path never touches m_thread.
55
6/6
✓ Branch 3 → 4 taken 55316 times.
✓ Branch 3 → 7 taken 1333 times.
✓ Branch 5 → 6 taken 1036 times.
✓ Branch 5 → 7 taken 56901 times.
✓ Branch 8 → 9 taken 1036 times.
✓ Branch 8 → 10 taken 58234 times.
58072 if (m_started.load(std::memory_order_acquire) && !m_joined.load(std::memory_order_acquire))
56 {
57 1036 m_stop_source.request_stop();
58 }
59 59284 }
60
61 56225 bool StoppableWorker::is_running() const noexcept
62 {
63
4/4
✓ Branch 3 → 4 taken 56234 times.
✓ Branch 3 → 7 taken 1184 times.
✓ Branch 5 → 6 taken 1074 times.
✓ Branch 5 → 7 taken 57334 times.
56225 return m_started.load(std::memory_order_acquire) && !m_joined.load(std::memory_order_acquire);
64 }
65
66 275 void StoppableWorker::shutdown() noexcept
67 {
68 275 bool expected = false;
69
2/2
✓ Branch 3 → 4 taken 132 times.
✓ Branch 3 → 5 taken 143 times.
275 if (!m_joined.compare_exchange_strong(expected, true, std::memory_order_acq_rel))
70 {
71 132 return;
72 }
73
74
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 143 times.
143 if (!m_thread.joinable())
75 {
76 return;
77 }
78
79 143 m_stop_source.request_stop();
80
81
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 15 taken 143 times.
143 if (detail::is_loader_lock_held())
82 {
83 detail::pin_current_module();
84 m_thread.detach();
85 DetourModKit::Diagnostics::record_intentional_leak(DetourModKit::Diagnostics::LeakSubsystem::Worker);
86 return;
87 }
88
89 143 m_thread.join();
90 }
91 } // namespace DetourModKit
92