include/DetourModKit/worker.hpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef DETOURMODKIT_WORKER_HPP | ||
| 2 | #define DETOURMODKIT_WORKER_HPP | ||
| 3 | |||
| 4 | /** | ||
| 5 | * @file worker.hpp | ||
| 6 | * @brief RAII wrapper around std::jthread with a named stop signal. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <atomic> | ||
| 10 | #include <functional> | ||
| 11 | #include <stop_token> | ||
| 12 | #include <string> | ||
| 13 | #include <string_view> | ||
| 14 | #include <thread> | ||
| 15 | |||
| 16 | namespace DetourModKit | ||
| 17 | { | ||
| 18 | /** | ||
| 19 | * @class StoppableWorker | ||
| 20 | * @brief RAII-owned named background worker built on std::jthread. | ||
| 21 | * @details The body receives a std::stop_token and must poll it cooperatively. On destruction the worker requests | ||
| 22 | * stop and joins the thread, unless it detects that the current thread is executing under the Windows | ||
| 23 | * loader lock -- in which case the thread is detached to avoid deadlock. Callers that need to preempt the | ||
| 24 | * loader-lock case should call request_stop() and join() from a thread outside DllMain prior to teardown. | ||
| 25 | * | ||
| 26 | * Non-copyable and non-movable: the name, stop state, and thread handle form a single invariant. Copying | ||
| 27 | * would duplicate the thread handle; moving would race with a running body. | ||
| 28 | */ | ||
| 29 | class StoppableWorker | ||
| 30 | { | ||
| 31 | public: | ||
| 32 | /** | ||
| 33 | * @brief Starts a new worker thread running the supplied body. | ||
| 34 | * @param name Descriptive name for logging. Copied into the worker. | ||
| 35 | * @param body Invocable receiving a stop_token. Must return promptly when stop_requested() becomes true. | ||
| 36 | */ | ||
| 37 | StoppableWorker(std::string_view name, std::function<void(std::stop_token)> body); | ||
| 38 | |||
| 39 | ~StoppableWorker() noexcept; | ||
| 40 | |||
| 41 | StoppableWorker(const StoppableWorker &) = delete; | ||
| 42 | StoppableWorker &operator=(const StoppableWorker &) = delete; | ||
| 43 | StoppableWorker(StoppableWorker &&) = delete; | ||
| 44 | StoppableWorker &operator=(StoppableWorker &&) = delete; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * @brief Signals the worker to stop cooperatively. | ||
| 48 | * @details Idempotent. Does not block. | ||
| 49 | */ | ||
| 50 | void request_stop() noexcept; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * @brief Returns true once the worker thread has started and before it has been shut down. | ||
| 54 | * @details Reads liveness from atomics, never the std::jthread handle, so it is race-free against a concurrent | ||
| 55 | * shutdown() that joins or detaches the thread. | ||
| 56 | */ | ||
| 57 | [[nodiscard]] bool is_running() const noexcept; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * @brief Returns the worker's descriptive name. | ||
| 61 | */ | ||
| 62 | 2 | [[nodiscard]] const std::string &name() const noexcept { return m_name; } | |
| 63 | |||
| 64 | /** | ||
| 65 | * @brief Requests stop and joins the worker thread. | ||
| 66 | * @details Safe to call multiple times. If invoked under the | ||
| 67 | * Windows loader lock the thread is detached instead of joined (documented trade-off -- the pinned | ||
| 68 | * module keeps code pages alive). | ||
| 69 | */ | ||
| 70 | void shutdown() noexcept; | ||
| 71 | |||
| 72 | private: | ||
| 73 | std::string m_name; | ||
| 74 | std::jthread m_thread; | ||
| 75 | // Stable copy of the jthread stop source. request_stop() signals this shared stop state instead of touching | ||
| 76 | // m_thread while shutdown() may be joining or detaching the handle. | ||
| 77 | std::stop_source m_stop_source; | ||
| 78 | // Liveness snapshot read by is_running()/request_stop(). m_started is set once m_thread and m_stop_source are | ||
| 79 | // both initialized; m_joined is set when shutdown begins (or in the empty-body early return). | ||
| 80 | std::atomic<bool> m_started{false}; | ||
| 81 | std::atomic<bool> m_joined{false}; | ||
| 82 | }; | ||
| 83 | } // namespace DetourModKit | ||
| 84 | |||
| 85 | #endif // DETOURMODKIT_WORKER_HPP | ||
| 86 |