Communication Library for Autonomous Systems v1.0
Reliable and secure communication library for autonomous vehicle systems
Loading...
Searching...
No Matches
periodicThread.h
Go to the documentation of this file.
1#ifndef PERIODIC_THREAD
2#define PERIODIC_THREAD
3
4#include <pthread.h>
5#include <chrono>
6#include <atomic>
7#include <thread>
8#include <functional>
9#include <cstdint>
10#include <stdexcept>
11#include <signal.h>
12
13#ifndef _GNU_SOURCE
14#define _GNU_SOURCE
15#endif
16
17#include <cstring>
18#include <unistd.h>
19#include <sys/syscall.h>
20#include <linux/sched.h>
21
22// Definition of sched_attr structure (only if not already defined)
23// Robust __has_include detection for cross-platform compatibility
24#ifdef __has_include
25 // Compiler supports __has_include builtin
26 #if __has_include(<linux/sched/types.h>)
27 #include <linux/sched/types.h>
28 #define __SCHED_ATTR_AVAILABLE__ 1
29 #endif
30#else
31 // Compiler doesn't support __has_include, define fallback
32 #define __has_include(x) 0
33#endif
34
35#if !defined(__SCHED_ATTR_AVAILABLE__) && !defined(__SCHED_ATTR_SIZE__)
47#else
48#include <linux/sched/types.h>
49#endif
50
51// Signal handler for thread interruption
52// Use a single static handler for the entire component system
53extern "C" inline void component_signal_handler(int sig) {
54 // Simply wake up the thread to check its running state
55 if (sig == SIGUSR1) {
56 // No action needed, just unblock from system calls
57 }
58}
59
60// Wrapper for the sched_setattr system call (only if not already available)
61// #ifndef SYS_sched_setattr
62// #define SYS_sched_setattr 314
63// #endif
64
65#if !defined(HAVE_SCHED_SETATTR)
66int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags) {
68}
69#endif
70
71template <typename Owner>
73 public:
74 Periodic_Thread() = default;
75 template <typename ...Tn>
78
79 void start(std::int64_t period);
80 void join();
81
82 void adjust_period(std::int64_t period);
83 // Directly overwrite current period (no GCD)
84 void set_period(std::int64_t period);
85 std::int64_t period() const;
86
87 static void* run(void* arg);
88 bool running();
89
92
93 private:
94 std::int64_t mdc(std::int64_t a, std::int64_t b);
95
96 std::atomic<std::int64_t> _period;
97 std::atomic<bool> _running;
98 pthread_t _thread;
99 std::function<void()> _task;
100};
101
102/***** Periodic Thread Implementation *****/
103template <typename Owner>
104template <typename ...Tn>
105Periodic_Thread<Owner>::Periodic_Thread(Owner* owner, void (Owner::*task)(Tn...), Tn...an) : _running(false), _thread(0) {
106 _task = std::bind(task, owner, std::forward<Tn>(an)...);
107 _period.store(0, std::memory_order_release);
108}
109
110template <typename Owner>
114
115template <typename Owner>
117 if (running()) {
118 _running.store(false, std::memory_order_release);
119 // Send a signal to interrupt any blocked thread (critical for proper thread termination)
120 if (_thread != 0) {
121 pthread_kill(_thread, SIGUSR1);
122 // Actually join the thread
123 pthread_join(_thread, nullptr);
124 _thread = 0;
125 }
126 }
127}
128
129template <typename Owner>
130void Periodic_Thread<Owner>::start(std::int64_t period) {
131 if (!running()) {
132 // Install the global signal handler for thread interruption (only once)
133 static std::atomic<bool> signal_handler_installed{false};
134 if (!signal_handler_installed.exchange(true)) {
135 struct sigaction sa;
136 std::memset(&sa, 0, sizeof(sa));
137 sa.sa_handler = component_signal_handler;
138 sigemptyset(&sa.sa_mask);
139 sa.sa_flags = 0;
140 sigaction(SIGUSR1, &sa, nullptr);
141 }
142
143 _period.store(period, std::memory_order_release);
144 _running.store(true, std::memory_order_release);
145 // Fix: pthread_create returns error code, not thread ID
146 int result = pthread_create(&_thread, nullptr, Periodic_Thread::run, this);
147 if (result != 0) {
148 _running.store(false, std::memory_order_release);
149 throw std::runtime_error("Failed to create periodic thread");
150 }
151 }
152}
153
154template <typename Owner>
155void Periodic_Thread<Owner>::adjust_period(std::int64_t period) {
156 _period.store(mdc(_period.load(std::memory_order_acquire), period), std::memory_order_release);
157}
158
159template <typename Owner>
160void Periodic_Thread<Owner>::set_period(std::int64_t period) {
161 _period.store(period, std::memory_order_release);
162 // Wake the thread so the new period takes effect promptly
163 if (running()) {
164 pthread_kill(_thread, SIGUSR1);
165 }
166}
167
168template <typename Owner>
169std::int64_t Periodic_Thread<Owner>::period() const {
170 return _period.load(std::memory_order_acquire);
171}
172
173template <typename Owner>
175 Periodic_Thread* thread = static_cast<Periodic_Thread*>(arg);
176
177 // SCHED_DEADLINE limits (conservative estimates for portability)
178 static constexpr uint64_t MAX_DEADLINE_PERIOD_US = 1000000ULL; // 1 second in microseconds
179
180 bool use_deadline_scheduling = false;
181 struct sched_attr attr_dl;
182
183 while (thread->running()) {
185
186 // Determine if we should use SCHED_DEADLINE based on period length
188
189 // Only attempt SCHED_DEADLINE for periods <= 1 second
191 memset(&attr_dl, 0, sizeof(attr_dl));
192 attr_dl.size = sizeof(attr_dl);
193 attr_dl.sched_policy = SCHED_DEADLINE;
194 attr_dl.sched_flags = 0;
195
196 // Convert microseconds to nanoseconds for SCHED_DEADLINE
198 attr_dl.sched_runtime = period_ns / 2; // 50% of period for execution
199 attr_dl.sched_deadline = period_ns; // Deadline equals period
200 attr_dl.sched_period = period_ns; // Period in nanoseconds
201
202 int result = sched_setattr(0, &attr_dl, 0);
203 if (result == 0) {
205 } else {
207 }
209 // Switch back to regular scheduling for long periods
210 struct sched_param param;
211 param.sched_priority = 0;
213 if (result == 0) {
215 }
216 }
217
218 // Double-check running status before calling task
219 if (thread->running()) {
220 thread->_task();
221 }
222
223 // Sleep for the specified period (convert microseconds to milliseconds)
224 std::this_thread::sleep_for(std::chrono::microseconds(current_period_us));
225 }
226
227 return nullptr;
228}
229
230template <typename Owner>
232 return _running.load(std::memory_order_acquire);
233}
234
235template <typename Owner>
236std::int64_t Periodic_Thread<Owner>::mdc(std::int64_t a, std::int64_t b) {
237 while (b != 0) {
238 std::int64_t temp = b;
239 b = a % b;
240 a = temp;
241 }
242 return a;
243}
244
245#endif // PERIODIC_THREAD
Definition periodicThread.h:72
Periodic_Thread(Owner *owner, void(Owner::*task)(Tn...), Tn...an)
Definition periodicThread.h:105
void adjust_period(std::int64_t period)
Definition periodicThread.h:155
Periodic_Thread()=default
void start(std::int64_t period)
Definition periodicThread.h:130
std::int64_t period() const
Definition periodicThread.h:169
Periodic_Thread(const Periodic_Thread &)=delete
void join()
Definition periodicThread.h:116
~Periodic_Thread()
Definition periodicThread.h:111
void set_period(std::int64_t period)
Definition periodicThread.h:160
static void * run(void *arg)
Definition periodicThread.h:174
Periodic_Thread & operator=(const Periodic_Thread &)=delete
bool running()
Definition periodicThread.h:231
Select_Debug<(Traits< T >::debugged &&Traits< Debug >::error)> db(Debug_Error l)
Definition debug.h:166
void component_signal_handler(int sig)
Definition periodicThread.h:53
int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
Definition periodicThread.h:66
Definition periodicThread.h:36
uint64_t sched_deadline
Definition periodicThread.h:44
int32_t sched_nice
Definition periodicThread.h:40
uint64_t sched_period
Definition periodicThread.h:45
uint32_t size
Definition periodicThread.h:37
uint64_t sched_flags
Definition periodicThread.h:39
uint64_t sched_runtime
Definition periodicThread.h:43
uint32_t sched_priority
Definition periodicThread.h:41
uint32_t sched_policy
Definition periodicThread.h:38