dfx 0.1.0
Linux-based dynamic dataflow executor
Loading...
Searching...
No Matches
Thread.hpp
1// SPDX-FileCopyrightText: 2025-2026 Vincent Leroy
2// SPDX-License-Identifier: MIT
3//
4// This file is part of dfx.
5//
6// Licensed under the MIT License. See the LICENSE file in the project root
7// for full license information.
8
9#pragma once
10
11// Standard includes
12#include <cstring>
13#include <future>
14#include <string_view>
15#include <system_error>
16#include <thread>
17#include <utility>
18
19// Project includes
20#include "CompilerSupport.hpp"
21#include "CopyMoveControl.hpp"
22#include "Exception.hpp"
23#include "Log.hpp"
24
25namespace dfx::Utils
26{
29
33template<typename T>
34concept CallWithToken = requires(T t, std::stop_token st)
35{ t.exec(st); };
36
40template<typename T>
41concept CallWithoutToken = requires(T t)
42{ t.exec(); };
43
48template<typename T>
50
52
66class Thread
67{
68private:
69 struct State
70 {
71 std::promise<void> startPromise;
72 std::shared_future<void> startFuture;
73 void * runnable = nullptr;
74 };
75
76public:
88 template<Runnable R, typename ... Args>
89 Thread(std::in_place_type_t<R>, std::string threadName, Args && ... args)
90 : _st { std::make_shared<State>() }
91 {
92 _st->startFuture = _st->startPromise.get_future();
93
94 // Do not capture this in the following lambda since the Thread object can be moved
95 // between the end of this ctor and the thread finishing. Use the State internal struct
96 // to pass information around instead
97 _thread = std::jthread([newName = std::move(threadName), ...args = std::forward<Args>(args), state = _st](std::stop_token stopToken) mutable noexcept
98 {
99 std::error_code ec;
100 setName(newName, ec);
101 if (ec)
102 DFX_UTILITIES_LOG_WARN("Failed to set name '{}' for thread ID {}: {}", newName, gettid(), ::strerror(ec.value()));
103
104 auto logger = DFX_UTILITIES_LOGGER();
105 try
106 {
107 R runnable(std::move(args)...);
108 if (DFX_UNLIKELY(stopToken.stop_requested()))
109 return ;
110
111 state->runnable = static_cast<void *>(&runnable);
112 state->startPromise.set_value();
113 if constexpr (CallWithToken<R>)
114 runnable.exec(std::move(stopToken));
115 else
116 runnable.exec();
117 }
118 catch (Exception const & e)
119 { if (logger != nullptr) Exception::printToLogger(e, logger); }
120 catch (std::exception const & e)
121 { if (logger != nullptr) SPDLOG_LOGGER_ERROR(logger, "An error occured: {}", e.what()); }
122 catch (...)
123 { if (logger != nullptr) SPDLOG_LOGGER_ERROR(logger, "An unknown error occured"); }
124 });
125 }
126
128
129public:
137 { return _st->startFuture.get(); }
138
142 void stop(bool waitForFinished = false) noexcept;
143
145 std::jthread const & nativeThread() const noexcept
146 { return _thread; }
147
152 template<Runnable R>
153 R & getRunnable() const { return *static_cast<R *>(_st->runnable); }
154
155public:
161 template<Runnable R, typename ... Args>
162 static Thread create(std::string threadName, Args && ... args)
163 {
164 return Thread(std::in_place_type<R>, std::move(threadName), std::forward<Args>(args)...);
165 }
166
167public:
175 static void setName(std::string_view name, std::error_code & ec) noexcept;
176
183 static void setName(std::string_view name);
184
192 static std::string getName(std::error_code & ec) noexcept;
193
200 static std::string getName();
201
202private:
203 std::jthread _thread;
204 std::shared_ptr<State> _st;
205};
206} // !namespace dfx::Utils
Convenience macros to explicitly control copy and move semantics.
#define DFX_ENABLE_DEFAULT_MOVE_DISABLE_COPY_NOEXCEPT(ClassName)
Enable default move and disable copy noexcept.
Definition CopyMoveControl.hpp:77
Exception utilities for dfx (source-location aware exceptions, nested stacks, and safe invocation hel...
Runtime error that captures a std::source_location.
Definition Exception.hpp:52
static void printToLogger(Exception const &e, spdlog::level::level_enum lvl=spdlog::level::err, std::string const &loggerName="", std::size_t initialIndentLevel=0, std::string const &logPattern=noFileLogPattern, std::string const &currentLogPattern=defaultLogPattern)
Log an exception (including nested exceptions) using a logger name.
A managed execution wrapper that ensures a Runnable's full lifecycle occurs within the worker thread.
Definition Thread.hpp:67
std::jthread const & nativeThread() const noexcept
Access the underlying jthread instance.
Definition Thread.hpp:145
void stop(bool waitForFinished=false) noexcept
Signals the worker thread to stop.
void waitForStarted()
Blocks until the worker thread has reached the start of the Runnable lifecycle.
Definition Thread.hpp:136
static Thread create(std::string threadName, Args &&... args)
Factory method for creating a Thread.
Definition Thread.hpp:162
R & getRunnable() const
Access the constructed Runnable.
Definition Thread.hpp:153
static void setName(std::string_view name, std::error_code &ec) noexcept
Set the calling thread name.
Thread(std::in_place_type_t< R >, std::string threadName, Args &&... args)
Constructs the Thread and enters the worker loop.
Definition Thread.hpp:89
static void setName(std::string_view name)
Set the calling thread name.
static std::string getName(std::error_code &ec) noexcept
Get the calling thread name.
static std::string getName()
Get the calling thread name.
Concept for a task that can be executed with a stop token.
Definition Thread.hpp:34
Concept for a task that can be executed without a stop token.
Definition Thread.hpp:41
Concept defining a valid Thread worker.
Definition Thread.hpp:49
Definition SystemConfigCommandHandler.hpp:15
STL namespace.