./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
1 / 1
100.0%
Branches:
-
-%
Functions:
1 / 1
100.0%

include/DetourModKit/detail/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 and explicit lifecycle state.
7 * @note Lives in detail/ for compile visibility but declares the public utility at the DetourModKit root.
8 */
9
10 #include <atomic>
11 #include <cstdint>
12 #include <functional>
13 #include <memory>
14 #include <stop_token>
15 #include <string>
16 #include <string_view>
17 #include <thread>
18
19 namespace DetourModKit
20 {
21 /** @brief Named background worker built on std::jthread. The destructor runs @ref shutdown. */
22 class StoppableWorker
23 {
24 public:
25 /**
26 * @brief Starts a worker thread that runs @p body.
27 * @param name Name for log records. The worker copies it.
28 * @param body Must poll its std::stop_token cooperatively and return promptly once stop is requested. An
29 * empty body logs an error and starts no thread.
30 * @throws std::system_error if the module reference cannot be taken or the thread cannot be created.
31 * @throws std::bad_alloc if owned setup state cannot be allocated.
32 * @note After a throw, the worker owns no thread and no module reference. If the cleanup join throws, the
33 * constructor hands the thread and the reference to the reaper instead.
34 */
35 StoppableWorker(std::string_view name, std::function<void(std::stop_token)> body);
36
37 ~StoppableWorker() noexcept;
38
39 StoppableWorker(const StoppableWorker &) = delete;
40 StoppableWorker &operator=(const StoppableWorker &) = delete;
41 StoppableWorker(StoppableWorker &&) = delete;
42 StoppableWorker &operator=(StoppableWorker &&) = delete;
43
44 /// Requests a stop. Registered stop callbacks run synchronously on the calling thread and can block.
45 void request_stop() noexcept;
46
47 /**
48 * @brief Returns true while the body starts or runs, and false once it returns or shutdown begins.
49 * @note Race-free against a concurrent shutdown().
50 */
51 [[nodiscard]] bool is_running() const noexcept;
52
53 /// Returns the worker name.
54 2 [[nodiscard]] const std::string &name() const noexcept { return m_name; }
55
56 /**
57 * @brief Retires the worker thread. Idempotent.
58 * @details Without blocking-teardown authorization, for example under the loader lock, it detaches with no stop
59 * request and leaks the module reference. Otherwise it requests stop, then joins and releases the
60 * reference. A failed join detaches the thread and keeps the reference. On the worker's own thread, it
61 * hands the thread and the reference to the reaper instead.
62 * @note Setup/control-plane only.
63 * @warning The join has no timeout. A body that ignores its std::stop_token hangs this call.
64 */
65 void shutdown() noexcept;
66
67 private:
68 enum class State : std::uint8_t
69 {
70 Starting,
71 Running,
72 Exited,
73 Stopping,
74 Stopped
75 };
76
77 std::string m_name;
78 // Heap ownership lets a failed detach retain the still-joinable jthread, so its destructor does not run.
79 std::unique_ptr<std::jthread> m_thread;
80 // Copy of the jthread stop source, so request_stop() never touches m_thread during a shutdown().
81 std::stop_source m_stop_source;
82 // Shared with the body, so its late state CAS touches live storage after a detach or a reaper hand-off.
83 std::shared_ptr<std::atomic<State>> m_state;
84 // Counted HMODULE reference on this module, taken before thread creation. shutdown() documents its release.
85 // void* keeps <windows.h> out of this installed header. See detail::acquire_module_ref.
86 void *m_self_ref{nullptr};
87 };
88 } // namespace DetourModKit
89
90 #endif // DETOURMODKIT_WORKER_HPP
91