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 |