Goby3 3.4.0
2026.04.13
Loading...
Searching...
No Matches
thread.h
Go to the documentation of this file.
1// Copyright 2016-2026:
2// GobySoft, LLC (2013-)
3// Community contributors (see AUTHORS file)
4// File authors:
5// Toby Schneider <toby@gobysoft.org>
6// James D. Turner <james.turner@nrl.navy.mil>
7//
8//
9// This file is part of the Goby Underwater Autonomy Project Libraries
10// ("The Goby Libraries").
11//
12// The Goby Libraries are free software: you can redistribute them and/or modify
13// them under the terms of the GNU Lesser General Public License as published by
14// the Free Software Foundation, either version 2.1 of the License, or
15// (at your option) any later version.
16//
17// The Goby Libraries are distributed in the hope that they will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU Lesser General Public License for more details.
21//
22// You should have received a copy of the GNU Lesser General Public License
23// along with Goby. If not, see <http://www.gnu.org/licenses/>.
24
25#ifndef GOBY_MIDDLEWARE_APPLICATION_THREAD_H
26#define GOBY_MIDDLEWARE_APPLICATION_THREAD_H
27
28#include <atomic>
29#include <chrono>
30#include <memory>
31#include <mutex>
32#include <typeindex>
33
34#include <boost/units/systems/si.hpp>
35
36#include "goby/exception.h"
39
43
44namespace goby
45{
46namespace middleware
47{
49{
50 std::type_index type_i{std::type_index(typeid(void))};
51 int index{-1};
52 bool all_threads{false};
53};
54
60template <typename Config, typename TransporterType> class Thread
61{
62 private:
63 TransporterType* transporter_{nullptr};
64
65 boost::units::quantity<boost::units::si::frequency> loop_frequency_;
66 std::chrono::steady_clock::time_point loop_time_;
67 unsigned long long loop_count_{0};
68 const Config cfg_;
69 int index_;
70 std::atomic<bool>* alive_{nullptr};
71 std::type_index type_i_{std::type_index(typeid(void))};
72
73 // Linux/Apple TID, from gettid()
74#ifdef __APPLE__
75 uint64_t thread_id_;
76#else
77 int thread_id_;
78#endif
79 // demangled class name / index
80 std::string thread_name_;
81
82 // unique id value used by MultiThreadApplication
83 int uid_;
84
85 bool finalize_run_{false};
86
87 public:
88 using Transporter = TransporterType;
89
96 Thread(const Config& cfg, TransporterType* transporter, int index)
97 : Thread(cfg, transporter, 0 * boost::units::si::hertz, index)
98 {
99 }
100
107 Thread(const Config& cfg, TransporterType* transporter, double loop_freq_hertz = 0,
108 int index = -1)
109 : Thread(cfg, transporter, loop_freq_hertz * boost::units::si::hertz, index)
110 {
111 }
112
119 Thread(const Config& cfg, TransporterType* transporter,
120 boost::units::quantity<boost::units::si::frequency> loop_freq, int index = -1)
121 : Thread(cfg, loop_freq, index)
122 {
124 }
125
126 virtual ~Thread() {}
127
131 void run(std::atomic<bool>& alive)
132 {
133 alive_ = &alive;
134 do_subscribe();
135 initialize();
136 while (alive) { run_once(); }
137 if (!finalize_run_)
138 {
139 finalize();
140 finalize_run_ = true;
141 }
142 }
143
145 int index() const { return index_; }
146
147 std::type_index type_index() { return type_i_; }
148 void set_type_index(std::type_index type_i) { type_i_ = type_i; }
149
150 std::string name() { return thread_name_; }
151 void set_name(const std::string& name) { thread_name_ = name; }
152
153 int uid() { return uid_; }
154 void set_uid(int uid) { uid_ = uid; }
155
156 double loop_frequency_hertz() const { return loop_frequency_ / boost::units::si::hertz; }
157 decltype(loop_frequency_) loop_frequency() const { return loop_frequency_; }
158
159 void set_loop_frequency_hertz(double loop_freq_hertz)
160 {
161 set_loop_frequency(loop_freq_hertz * boost::units::si::hertz);
162 }
163
164 void set_loop_frequency(boost::units::quantity<boost::units::si::frequency> loop_freq)
165 {
166 loop_frequency_ = loop_freq;
167 loop_time_ = std::chrono::steady_clock::now();
168
169 if (loop_frequency_hertz() > 0 &&
170 loop_frequency_hertz() != std::numeric_limits<double>::infinity())
171 {
172 unsigned long long microsec_interval =
174
175 unsigned long long ticks_since_epoch =
176 std::chrono::duration_cast<std::chrono::microseconds>(loop_time_.time_since_epoch())
177 .count() /
178 microsec_interval;
179
180 loop_time_ = std::chrono::steady_clock::time_point(
181 std::chrono::microseconds((ticks_since_epoch + 1) * microsec_interval));
182 }
183 }
184
185 static constexpr goby::middleware::Group shutdown_group_{"goby::middleware::Thread::shutdown"};
186 static constexpr goby::middleware::Group joinable_group_{"goby::middleware::Thread::joinable"};
187
188 protected:
189 Thread(const Config& cfg, boost::units::quantity<boost::units::si::frequency> loop_freq,
190 int index = -1)
191 : loop_frequency_(loop_freq),
192 loop_time_(std::chrono::steady_clock::now()),
193 cfg_(cfg),
194 index_(index),
195 thread_id_(goby::middleware::gettid()),
196 thread_name_(std::to_string(thread_id_)),
197 uid_(-1)
198 {
199 set_loop_frequency(loop_freq);
200 }
201
202 void set_transporter(TransporterType* transporter) { transporter_ = transporter; }
203
204 virtual void loop()
205 {
206 throw(std::runtime_error(
207 "void Thread::loop() must be overridden for non-zero loop frequencies"));
208 }
209
210 double loop_max_frequency() const { return std::numeric_limits<double>::infinity(); }
211 void run_once();
212
213 TransporterType& transporter() const { return *transporter_; }
214
215 const Config& cfg() const { return cfg_; }
216
217 // called after alive() is true, but before run()
218 virtual void initialize() {}
219
220 // called after alive() is false
221 virtual void finalize() {}
222
224 {
225 health.set_name(thread_name_);
226#ifdef __APPLE__
227 health.set_thread_id_apple(thread_id_);
228#else
229 health.set_thread_id(thread_id_);
230#endif
231 if (uid_ >= 0)
232 health.set_uid(uid_);
233 this->health(health);
234 }
235
243
245 {
246 (*alive_) = false;
247 if (!finalize_run_)
248 {
249 finalize();
250 finalize_run_ = true;
251 }
252 }
253
254 bool alive() { return alive_ && *alive_; }
255
256 private:
257 void do_subscribe()
258 {
259 if (!transporter_)
260 {
261 throw(goby::Exception(
262 "Thread::transporter_ is null. Must set_transporter() before using"));
263 }
264
266 .innermost()
267 .template subscribe<shutdown_group_, ThreadIdentifier, MarshallingScheme::CXX_OBJECT>(
268 [this](const ThreadIdentifier ti)
269 {
270 if (ti.all_threads ||
271 (ti.type_i == this->type_index() && ti.index == this->index()))
272 {
273 this->thread_quit();
274 }
275 });
276 }
277};
278
279} // namespace middleware
280
281template <typename Config, typename TransporterType>
284
285template <typename Config, typename TransporterType>
288
289template <typename Config, typename TransporterType>
291{
292 if (!transporter_)
293 throw(goby::Exception("Null transporter"));
294
295 if (loop_frequency_hertz() == std::numeric_limits<double>::infinity())
296 {
297 // call loop as fast as possible
298 transporter_->poll(std::chrono::seconds(0));
299 loop();
300 }
301 else if (loop_frequency_hertz() > 0)
302 {
303 int events = transporter_->poll(loop_time_);
304
305 // timeout
306 if (events == 0)
307 {
308 loop();
309 ++loop_count_;
310 loop_time_ += std::chrono::nanoseconds(
311 (unsigned long long)(1000000000ull / (loop_frequency_hertz() *
313 }
314 }
315 else
316 {
317 // don't call loop()
318 transporter_->poll();
319 }
320}
321} // namespace goby
322
323#endif
simple exception class for goby applications
Definition exception.h:35
Class for grouping publications in the Goby middleware. Analogous to "topics" in ROS,...
Definition group.h:60
Represents a thread of execution within the Goby middleware, interleaving periodic events (loop()) wi...
Definition thread.h:61
virtual void finalize()
Definition thread.h:221
Thread(const Config &cfg, boost::units::quantity< boost::units::si::frequency > loop_freq, int index=-1)
Definition thread.h:189
std::string name()
Definition thread.h:150
Thread(const Config &cfg, TransporterType *transporter, double loop_freq_hertz=0, int index=-1)
Construct a thread with all possible metadata (using double to specify frequency in Hertz)
Definition thread.h:107
static constexpr goby::middleware::Group shutdown_group_
Definition thread.h:185
virtual void initialize()
Definition thread.h:218
decltype(loop_frequency_) loop_frequency() const
Definition thread.h:157
void set_loop_frequency(boost::units::quantity< boost::units::si::frequency > loop_freq)
Definition thread.h:164
static constexpr goby::middleware::Group joinable_group_
Definition thread.h:186
Thread(const Config &cfg, TransporterType *transporter, int index)
Construct a thread with a given configuration, underlying transporter, and index (for multiple instan...
Definition thread.h:96
double loop_max_frequency() const
Definition thread.h:210
void set_transporter(TransporterType *transporter)
Definition thread.h:202
void set_type_index(std::type_index type_i)
Definition thread.h:148
TransporterType & transporter() const
Definition thread.h:213
TransporterType Transporter
Definition thread.h:88
std::type_index type_index()
Definition thread.h:147
void set_uid(int uid)
Definition thread.h:154
void set_name(const std::string &name)
Definition thread.h:151
void set_loop_frequency_hertz(double loop_freq_hertz)
Definition thread.h:159
virtual void health(goby::middleware::protobuf::ThreadHealth &health)
Called when HealthRequest is made by goby_coroner.
Definition thread.h:239
Thread(const Config &cfg, TransporterType *transporter, boost::units::quantity< boost::units::si::frequency > loop_freq, int index=-1)
Construct a thread with all possible metadata (using boost::units to specify frequency)
Definition thread.h:119
virtual void loop()
Definition thread.h:204
void thread_health(goby::middleware::protobuf::ThreadHealth &health)
Definition thread.h:223
const Config & cfg() const
Definition thread.h:215
double loop_frequency_hertz() const
Definition thread.h:156
void run(std::atomic< bool > &alive)
Run the thread until the boolean reference passed is set false. This call blocks, and should be run i...
Definition thread.h:131
std::string to_string(goby::middleware::protobuf::Layer layer)
Definition common.h:44
The global namespace for the Goby project.
STL namespace.
static int warp_factor
Warp factor to speed up (or slow time) the time values returned by SteadyClock::now() and SystemClock...
Definition simulation.h:40