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 | ||
| 22 | * cooperatively. On destruction the worker requests stop and | ||
| 23 | * joins the thread, unless it detects that the current thread | ||
| 24 | * is executing under the Windows loader lock -- in which case | ||
| 25 | * the thread is detached to avoid deadlock. Callers that need | ||
| 26 | * to preempt the loader-lock case should call request_stop() | ||
| 27 | * and join() from a thread outside DllMain prior to teardown. | ||
| 28 | * | ||
| 29 | * Non-copyable and non-movable: the name, stop state, and | ||
| 30 | * thread handle form a single invariant. Copying would | ||
| 31 | * duplicate the thread handle; moving would race with a | ||
| 32 | * running body. | ||
| 33 | */ | ||
| 34 | class StoppableWorker | ||
| 35 | { | ||
| 36 | public: | ||
| 37 | /** | ||
| 38 | * @brief Starts a new worker thread running the supplied body. | ||
| 39 | * @param name Descriptive name for logging. Copied into the worker. | ||
| 40 | * @param body Invocable receiving a stop_token. Must return promptly | ||
| 41 | * when stop_requested() becomes true. | ||
| 42 | */ | ||
| 43 | StoppableWorker(std::string_view name, | ||
| 44 | std::function<void(std::stop_token)> body); | ||
| 45 | |||
| 46 | ~StoppableWorker() noexcept; | ||
| 47 | |||
| 48 | StoppableWorker(const StoppableWorker &) = delete; | ||
| 49 | StoppableWorker &operator=(const StoppableWorker &) = delete; | ||
| 50 | StoppableWorker(StoppableWorker &&) = delete; | ||
| 51 | StoppableWorker &operator=(StoppableWorker &&) = delete; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @brief Signals the worker to stop cooperatively. | ||
| 55 | * @details Idempotent. Does not block. | ||
| 56 | */ | ||
| 57 | void request_stop() noexcept; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * @brief Returns true while the worker thread is still joinable. | ||
| 61 | */ | ||
| 62 | [[nodiscard]] bool is_running() const noexcept; | ||
| 63 | |||
| 64 | /** | ||
| 65 | * @brief Returns the worker's descriptive name. | ||
| 66 | */ | ||
| 67 | 2 | [[nodiscard]] const std::string &name() const noexcept { return name_; } | |
| 68 | |||
| 69 | /** | ||
| 70 | * @brief Requests stop and joins the worker thread. | ||
| 71 | * @details Safe to call multiple times. If invoked under the | ||
| 72 | * Windows loader lock the thread is detached instead of | ||
| 73 | * joined (documented trade-off -- the pinned module | ||
| 74 | * keeps code pages alive). | ||
| 75 | */ | ||
| 76 | void shutdown() noexcept; | ||
| 77 | |||
| 78 | private: | ||
| 79 | std::string name_; | ||
| 80 | std::jthread thread_; | ||
| 81 | std::atomic<bool> joined_{false}; | ||
| 82 | }; | ||
| 83 | } // namespace DetourModKit | ||
| 84 | |||
| 85 | #endif // DETOURMODKIT_WORKER_HPP | ||
| 86 |