GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 31 / 0 / 31
Functions: 100.0% 10 / 0 / 10
Branches: 100.0% 14 / 0 / 14

src/internal/lifecycle_context.cpp
Line Branch Exec Source
1 #include "lifecycle_context.hpp"
2
3 #include "platform.hpp"
4
5 namespace DetourModKit
6 {
7 namespace detail
8 {
9 namespace
10 {
11 // Constant initialization avoids a dynamic-initialization-order dependency between translation units.
12 constinit LifecycleContext s_lifecycle;
13 } // namespace
14
15 #if defined(DMK_ENABLE_TEST_SEAMS)
16 bool (*g_loader_lock_override)() noexcept = nullptr;
17 #endif
18
19 3035822 LifecycleContext &lifecycle() noexcept
20 {
21 3035822 return s_lifecycle;
22 }
23
24 203 bool LifecycleContext::begin_start() noexcept
25 {
26 203 LifecycleState expected = LifecycleState::Stopped;
27
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 5 taken 201 times.
203 if (!m_state.compare_exchange_strong(expected, LifecycleState::Starting, std::memory_order_acq_rel))
28 {
29 2 return false;
30 }
31 // Publish the new generation and neutral loader context before mark_running() releases the admitted
32 // session.
33 201 m_generation.fetch_add(1, std::memory_order_acq_rel);
34 201 m_loader_context.store(LoaderContext::Normal, std::memory_order_release);
35 201 return true;
36 }
37
38 189 void LifecycleContext::mark_running() noexcept
39 {
40 189 m_state.store(LifecycleState::Running, std::memory_order_release);
41 189 }
42
43 184 void LifecycleContext::begin_stop() noexcept
44 {
45 184 m_state.store(LifecycleState::Stopping, std::memory_order_release);
46 184 }
47
48 198 void LifecycleContext::mark_stopped() noexcept
49 {
50 198 m_state.store(LifecycleState::Stopped, std::memory_order_release);
51 198 }
52
53 51 void LifecycleContext::publish_worker_thread() noexcept
54 {
55 51 m_worker_thread_id.store(static_cast<std::uint32_t>(GetCurrentThreadId()), std::memory_order_release);
56 51 }
57
58 95 void LifecycleContext::clear_worker_thread() noexcept
59 {
60 95 m_worker_thread_id.store(0, std::memory_order_release);
61 95 }
62
63 76 bool LifecycleContext::is_worker_thread() const noexcept
64 {
65 76 const std::uint32_t worker = worker_thread_id();
66
4/4
✓ Branch 3 → 4 taken 45 times.
✓ Branch 3 → 7 taken 31 times.
✓ Branch 5 → 6 taken 8 times.
✓ Branch 5 → 7 taken 37 times.
76 return worker != 0 && worker == static_cast<std::uint32_t>(GetCurrentThreadId());
67 }
68
69 8400 bool teardown_caller_authorized() noexcept
70 {
71
4/4
✓ Branch 4 → 5 taken 25 times.
✓ Branch 4 → 8 taken 8375 times.
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 9 taken 19 times.
8400 return lifecycle().context_permits_blocking() || lifecycle().is_worker_thread();
72 }
73
74 8365 bool blocking_teardown_permitted() noexcept
75 {
76 // Authorization comes from the explicit context or the worker identity; the heuristic only ever subtracts
77 // from it. Evaluated in this order so the cheap atomic loads reject a loader callback without paying for
78 // the PEB probe.
79
4/4
✓ Branch 3 → 4 taken 8346 times.
✓ Branch 3 → 7 taken 19 times.
✓ Branch 5 → 6 taken 8230 times.
✓ Branch 5 → 7 taken 116 times.
8365 return teardown_caller_authorized() && !is_loader_lock_held();
80 }
81 } // namespace detail
82 } // namespace DetourModKit
83