./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
40 / 41
97.6%
Branches:
22 / 24
91.7%
Functions:
11 / 11
100.0%

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 11183319 LifecycleContext &lifecycle() noexcept
20 {
21 11183319 return s_lifecycle;
22 }
23
24 29124 bool process_is_exiting() noexcept
25 {
26
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 29094 times.
29124 if (lifecycle().loader_context() == LoaderContext::ProcessExit)
27 {
28 2 return true;
29 }
30 // A consumer without bootstrap_detach publishes no context, so the loader supplies the answer.
31 using ShutdownProbe = BOOLEAN(NTAPI *)();
32 29094 const HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
33 // GCC treats void (*)() as compatible with every function type.
34 using AnyFunction = void (*)();
35
1/2
✓ Branch 7 → 8 taken 29143 times.
✗ Branch 7 → 10 not taken.
29143 const auto probe = ntdll != nullptr ? reinterpret_cast<ShutdownProbe>(reinterpret_cast<AnyFunction>(
36 29143 ::GetProcAddress(ntdll, "RtlDllShutdownInProgress")
37 ))
38 ✗ : nullptr;
39
3/4
✓ Branch 11 → 12 taken 29143 times.
✗ Branch 11 → 15 not taken.
✓ Branch 13 → 14 taken 6 times.
✓ Branch 13 → 15 taken 29137 times.
29143 return probe != nullptr && probe() != FALSE;
40 }
41
42 1663 bool LifecycleContext::begin_start() noexcept
43 {
44 1663 LifecycleState expected = LifecycleState::Stopped;
45
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 5 taken 1660 times.
1663 if (!m_state.compare_exchange_strong(expected, LifecycleState::Starting, std::memory_order_acq_rel))
46 {
47 3 return false;
48 }
49 // Publish the new generation and neutral loader context before mark_running() releases the admitted
50 // session.
51 1660 m_generation.fetch_add(1, std::memory_order_acq_rel);
52 1660 m_loader_context.store(LoaderContext::Normal, std::memory_order_release);
53 1660 return true;
54 }
55
56 1644 void LifecycleContext::mark_running() noexcept
57 {
58 1644 m_state.store(LifecycleState::Running, std::memory_order_release);
59 1644 }
60
61 1637 void LifecycleContext::begin_stop() noexcept
62 {
63 1637 m_state.store(LifecycleState::Stopping, std::memory_order_release);
64 1637 }
65
66 1657 void LifecycleContext::mark_stopped() noexcept
67 {
68 1657 m_state.store(LifecycleState::Stopped, std::memory_order_release);
69 1657 }
70
71 52 void LifecycleContext::publish_worker_thread() noexcept
72 {
73 52 m_worker_thread_id.store(static_cast<std::uint32_t>(GetCurrentThreadId()), std::memory_order_release);
74 52 }
75
76 97 void LifecycleContext::clear_worker_thread() noexcept
77 {
78 97 m_worker_thread_id.store(0, std::memory_order_release);
79 97 }
80
81 86 bool LifecycleContext::is_worker_thread() const noexcept
82 {
83 86 const std::uint32_t worker = worker_thread_id();
84
4/4
✓ Branch 3 → 4 taken 46 times.
✓ Branch 3 → 7 taken 40 times.
✓ Branch 5 → 6 taken 9 times.
✓ Branch 5 → 7 taken 37 times.
86 return worker != 0 && worker == static_cast<std::uint32_t>(GetCurrentThreadId());
85 }
86
87 44327 bool teardown_caller_authorized() noexcept
88 {
89 44327 const LoaderContext context = lifecycle().loader_context();
90
6/6
✓ Branch 4 → 5 taken 175 times.
✓ Branch 4 → 9 taken 44152 times.
✓ Branch 5 → 6 taken 33 times.
✓ Branch 5 → 9 taken 142 times.
✓ Branch 8 → 9 taken 7 times.
✓ Branch 8 → 10 taken 26 times.
44360 return context == LoaderContext::Normal || context == LoaderContext::ExplicitDrain ||
91 44360 lifecycle().is_worker_thread();
92 }
93
94 44291 bool blocking_teardown_permitted() noexcept
95 {
96 // Authorization comes from the explicit context or the worker identity; the heuristic only ever subtracts
97 // from it. Evaluated in this order so the cheap atomic loads reject a loader callback without paying for
98 // the PEB probe.
99
4/4
✓ Branch 3 → 4 taken 44265 times.
✓ Branch 3 → 7 taken 26 times.
✓ Branch 5 → 6 taken 42686 times.
✓ Branch 5 → 7 taken 1579 times.
44291 return teardown_caller_authorized() && !is_loader_lock_held();
100 }
101 } // namespace detail
102 } // namespace DetourModKit
103