3#include "../../thread.hpp"
4#include "../../thread_config.hpp"
5#include "../../thread_registry.hpp"
6#include "../cpu_topology.hpp"
11#include <condition_variable>
35 template <
typename Predicate>
38 if (config_.
interval <= std::chrono::milliseconds::zero())
39 throw std::invalid_argument(
"chaos interval must be positive");
41 throw std::invalid_argument(
"chaos nice jitter must not be negative");
43 std::promise<thread_id> started;
44 auto ready = started.get_future();
46 [
this, predicate = std::move(predicate), started = std::move(started)]()
mutable
48 running_.store(
true, std::memory_order_release);
57 std::lock_guard<std::mutex> lock(failure_mutex_);
58 failure_ = std::current_exception();
60 running_.store(
false, std::memory_order_release);
62 worker_id_ = ready.get();
63 (void)worker_.
set_name(
"ts_chaos_ctl");
69 std::lock_guard<std::mutex> lock(wait_mutex_);
70 stop_.store(
true, std::memory_order_release);
85 if (!worker_.
joinable() || !worker_id_ || !running_.load(std::memory_order_acquire))
94 if (!worker_.
joinable() || !running_.load(std::memory_order_acquire))
95 return unexpected(std::make_error_code(std::errc::no_such_process));
103 std::lock_guard<std::mutex> lock(failure_mutex_);
108 [[nodiscard]]
static auto
109 make_random_engine() noexcept ->
std::mt19937
111 auto seed =
static_cast<std::uint32_t
>(std::chrono::steady_clock::now().time_since_epoch().count());
112 seed ^=
static_cast<std::uint32_t
>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
115 std::random_device device;
121 return std::mt19937(seed);
124 template <
typename Predicate>
126 run_loop(Predicate& predicate)
128 auto random = make_random_engine();
129 while (!stop_.load(std::memory_order_acquire))
133 perturb(*entries, predicate, random);
135 std::unique_lock<std::mutex> lock(wait_mutex_);
136 wakeup_.wait_for(lock, config_.
interval, [
this] { return stop_.load(std::memory_order_acquire); });
140 template <
typename Predicate>
142 perturb(std::vector<registered_thread>
const& entries, Predicate& predicate, std::mt19937& random)
146 std::size_t selected_index = 0;
147 for (
auto const& entry : entries)
149 if (!entry.alive || !std::invoke(predicate, entry))
154 auto const node = topology.numa_nodes == 0 ? 0 : selected_index % topology.numa_nodes;
155 thread_config affinity;
156 affinity.set_affinity(
157 affinity_for_node(topology,
static_cast<int>(node),
static_cast<int>(selected_index)));
175 chaos_config config_;
176 std::atomic<bool> stop_{
false };
177 std::atomic<bool> running_{
false };
178 mutable std::mutex failure_mutex_;
179 std::exception_ptr failure_;
180 std::mutex wait_mutex_;
181 std::condition_variable wakeup_;
183 std::optional<thread_id> worker_id_;
auto operator=(chaos_controller const &) -> chaos_controller &=delete
chaos_controller(chaos_controller &&)=delete
auto thread_info() const -> std::optional< registered_thread >
chaos_controller(chaos_controller const &)=delete
chaos_controller(chaos_config config, Predicate predicate)
auto failure() const -> std::exception_ptr
Return an exception that stopped the controller, if any.
auto configure_thread(thread_config const &config) -> result< void >
auto operator=(chaos_controller &&) -> chaos_controller &=delete
static constexpr int minimum
Lowest accepted nice value (highest priority).
static constexpr int maximum
Highest accepted nice value (lowest priority).
Portable thread configuration bundle.
Owning thread wrapper with result-based lifecycle/configuration API.
auto set_name(std::string const &name) -> result< void >
Set thread name.
auto configure(thread_config const &config) -> result< void >
Apply a full portable thread configuration to the running thread.
auto get_name() const -> result< std::string >
Query thread name if supported by platform/backend.
auto get_id() const noexcept -> std::thread::id
Return std::thread id of the owned thread.
auto join() -> result< void >
Join the thread.
auto joinable() const noexcept -> bool
Return whether the thread object owns a running thread.
auto affinity_for_node(cpu_topology const &topo, int node_index, int thread_index, int threads_per_node=1) -> thread_affinity
Build a thread_affinity for the given NUMA node using a pre-read topology.
auto read_topology() -> cpu_topology
Discover basic topology. Linux: reads /sys for NUMA nodes. Windows: single node, processor-group-awar...
auto current_native_thread_id() noexcept -> native_thread_id
auto global_registry() -> thread_registry &
std::chrono::milliseconds interval
static constexpr auto make(std::uint64_t value) -> thread_id
Snapshot entry returned by thread_registry::snapshot.