GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 15 / 0 / 15
Functions: 100.0% 6 / 0 / 6
Branches: -% 0 / 0 / 0

src/internal/lifecycle_reaper.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_INTERNAL_LIFECYCLE_REAPER_HPP
2 #define DETOURMODKIT_INTERNAL_LIFECYCLE_REAPER_HPP
3
4 /**
5 * @file internal/lifecycle_reaper.hpp
6 * @brief Off-thread retirement for workers and owners torn down from their own worker body.
7 * @details The process-lifetime reaper moves self-joins and dependent owner destruction to another thread,
8 * keeping captured owner state alive until the worker body returns. Loader-lock teardown remains a
9 * detach-and-retain path and does not use this facility.
10 */
11
12 #include <cstdint>
13 #include <memory>
14 #include <thread>
15
16 namespace DetourModKit::diagnostics
17 {
18 enum class ModulePinReason : std::uint8_t; // Full definition: DetourModKit/diagnostics.hpp.
19 } // namespace DetourModKit::diagnostics
20
21 namespace DetourModKit::detail
22 {
23 /// Callback that completes an owner's worker rundown while the owner is still alive.
24 using SharedOwnerRetire = bool (*)(void *) noexcept;
25
26 /**
27 * @brief Moves @p owner's reference to the reaper thread, so its worker can be retired before destruction.
28 * @details The reaper invokes @p retire while the owner is alive, then drops its reference only after the callback
29 * reports that destruction is safe. A retirement the callback refuses stays owned by never-destroyed
30 * reaper storage. Once the reaper exists, queuing is allocation-free while its reserve holds, so a
31 * request made under host OOM still lands; only the first-ever request has to build the reaper and can
32 * therefore be refused under that pressure.
33 * @param owner Reference to transfer; moved from only on success. A null pointer is accepted and reported queued.
34 * @param retire Callback that stops and joins the owner's worker. Required: a null callback is refused, because an
35 * owner whose worker cannot be run down must not be released.
36 * @return true when the reference was transferred. On false @p owner is untouched and its retention becomes the
37 * caller's responsibility (through a precommitted keepalive or its own never-destroyed storage), never
38 * a release on the retiring thread.
39 */
40 [[nodiscard]] bool reap_shared_owner(std::shared_ptr<void> &owner, SharedOwnerRetire retire) noexcept;
41
42 /**
43 * @brief Joins @p thread on the reaper thread, then releases @p module_ref.
44 * @details Ownership of @p thread transfers to the reaper. Use when a StoppableWorker's shutdown() is
45 * reached on the worker's own thread, where an inline join would self-join. The reaper joins
46 * once the body returns, then balances the module reference the worker took at construction.
47 * @param thread Owned worker thread to join off-thread (moved in).
48 * @param module_ref HMODULE-as-void* the worker took at construction; released after the join. May be
49 * null.
50 * @param ref_reason The diagnostics::ModulePinReason used to acquire @p module_ref.
51 * The deferred release reuses it.
52 * @note If queue insertion fails or the reaper cannot start, the function detaches @p thread.
53 * It retains @p module_ref and records an intentional Worker leak.
54 * The function never joins its own thread.
55 */
56 void reap_worker_thread(
57 std::unique_ptr<std::jthread> thread,
58 void *module_ref,
59 diagnostics::ModulePinReason ref_reason
60 ) noexcept;
61
62 namespace reaper_detail
63 {
64 void reap_owner_erased(void *owner, void (*destroy)(void *) noexcept) noexcept;
65 } // namespace reaper_detail
66
67 /**
68 * @brief Destroys @p owner on the reaper thread after its worker body returns.
69 * @tparam Owner Complete owner type whose destructor joins its worker.
70 * @param owner Unique owner to retire off-thread.
71 * @note If queuing fails, ownership is deliberately retained and recorded as an intentional Worker leak.
72 */
73 3 template <typename Owner> void reap_owner(std::unique_ptr<Owner> owner) noexcept
74 {
75 3 Owner *const raw_owner = owner.release();
76 3 reaper_detail::reap_owner_erased(
77 raw_owner,
78 3 [](void *raw) noexcept { std::default_delete<Owner>{}(static_cast<Owner *>(raw)); }
79 );
80 3 }
81 } // namespace DetourModKit::detail
82
83 #endif // DETOURMODKIT_INTERNAL_LIFECYCLE_REAPER_HPP
84