ros2_control - kilted
Loading...
Searching...
No Matches
realtime_publisher.hpp
1// Copyright (c) 2008, Willow Garage, Inc.
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8//
9// * Redistributions in binary form must reproduce the above copyright
10// notice, this list of conditions and the following disclaimer in the
11// documentation and/or other materials provided with the distribution.
12//
13// * Neither the name of the Willow Garage, Inc. nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28
29/*
30 * Publishing ROS messages is difficult, as the publish function is
31 * not realtime safe. This class provides the proper locking so that
32 * you can call publish in realtime and a separate (non-realtime)
33 * thread will ensure that the message gets published over ROS.
34 *
35 * Author: Stuart Glaser
36 */
37#ifndef REALTIME_TOOLS__REALTIME_PUBLISHER_HPP_
38#define REALTIME_TOOLS__REALTIME_PUBLISHER_HPP_
39
40#include <atomic>
41#include <chrono>
42#include <condition_variable>
43#include <memory>
44#include <mutex>
45#include <string>
46#include <thread>
47#include <utility>
48
49#include "rclcpp/create_publisher.hpp"
50#include "rclcpp/publisher.hpp"
51
52namespace realtime_tools
53{
54template <class MessageT>
56{
57public:
59 using PublisherType = rclcpp::Publisher<MessageT>;
60 using PublisherSharedPtr = typename rclcpp::Publisher<MessageT>::SharedPtr;
61
62 using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
63 using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
64
65 RCLCPP_SMART_PTR_DEFINITIONS(RealtimePublisher<MessageT>)
66
67 [[deprecated(
68 "This variable is deprecated, it is recommended to use the try_publish() method instead.")]]
69 MessageT msg_;
70
71#ifdef _MSC_VER
72#pragma warning(push)
73#pragma warning(disable : 4996)
74#else
75#pragma GCC diagnostic push
76#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
77#endif
90 template <typename NodeT>
92 NodeT && node, const std::string & topic_name, const rclcpp::QoS & qos,
93 const rclcpp::PublisherOptions & options = rclcpp::PublisherOptions())
94 {
95 initialize([&]() {
96 return rclcpp::create_publisher<MessageT>(
97 std::forward<NodeT>(node), topic_name, qos, options);
98 });
99 }
100
110 explicit RealtimePublisher(PublisherSharedPtr publisher)
111 {
112 initialize([&]() { return publisher; });
113 }
114
117 {
118 RCLCPP_DEBUG(rclcpp::get_logger("realtime_tools"), "Waiting for publishing thread to stop....");
119 stop();
120 while (is_running()) {
121 std::this_thread::sleep_for(std::chrono::microseconds(100));
122 }
123 RCLCPP_DEBUG(
124 rclcpp::get_logger("realtime_tools"), "Publishing thread stopped, joining thread....");
125 if (thread_.joinable()) {
126 thread_.join();
127 }
128 }
129#ifdef _MSC_VER
130#pragma warning(pop)
131#else
132#pragma GCC diagnostic pop
133#endif
134
142 void stop()
143 {
144 {
145 std::unique_lock<std::mutex> lock(msg_mutex_);
146 keep_running_ = false;
147 }
148 updated_cond_.notify_one(); // So the publishing loop can exit
149 }
150
159 [[deprecated(
160 "Use try_publish() method instead of this method. This method may be removed in future "
161 "versions.")]]
162 bool trylock()
163 {
164 return turn_.load(std::memory_order_acquire) == State::REALTIME && msg_mutex_.try_lock();
165 }
166
171 bool can_publish() const
172 {
173 std::unique_lock<std::mutex> lock(msg_mutex_, std::try_to_lock);
174 return can_publish(lock);
175 }
176
186 bool try_publish(const MessageT & msg)
187 {
188 std::unique_lock<std::mutex> lock(msg_mutex_, std::try_to_lock);
189 if (can_publish(lock)) {
190 {
191 std::unique_lock<std::mutex> scoped_lock(std::move(lock));
192#ifdef _MSC_VER
193#pragma warning(push)
194#pragma warning(disable : 4996)
195#else
196#pragma GCC diagnostic push
197#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
198#endif
199 msg_ = msg;
200#ifdef _MSC_VER
201#pragma warning(pop)
202#else
203#pragma GCC diagnostic pop
204#endif
205 turn_.store(State::NON_REALTIME, std::memory_order_release);
206 }
207 updated_cond_.notify_one(); // Notify the publishing thread
208 return true;
209 }
210 return false;
211 }
212
224 [[deprecated(
225 "Use try_publish() method instead of this method. This method may be removed in future "
226 "versions.")]]
227 bool tryPublish(const MessageT & msg)
228 {
229 return try_publish(msg);
230 }
231
239 [[deprecated(
240 "Use the try_publish() method to publish the message instead of using this method. This method "
241 "may be removed in future versions.")]]
243 {
244 turn_.store(State::NON_REALTIME, std::memory_order_release);
245#pragma GCC diagnostic push
246#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
247 unlock();
248#pragma GCC diagnostic pop
249 }
250
257 [[deprecated(
258 "Use the try_publish() method to publish the message instead of using this method. This method "
259 "may be removed in future versions.")]]
260 void lock()
261 {
262 msg_mutex_.lock();
263 }
264
269 [[deprecated(
270 "Use the try_publish() method to publish the message instead of using this method. This method "
271 "may be removed in future versions.")]]
272 void unlock()
273 {
274 msg_mutex_.unlock();
275 updated_cond_.notify_one();
276 }
277
283 std::thread & get_thread() { return thread_; }
284
290 const std::thread & get_thread() const { return thread_; }
291
292 [[deprecated(
293 "This getter method will be removed. It is recommended to use the try_publish() instead of "
294 "accessing the msg_ variable.")]]
295 const MessageT & get_msg() const
296 {
297#ifdef _MSC_VER
298#pragma warning(push)
299#pragma warning(disable : 4996)
300#else
301#pragma GCC diagnostic push
302#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
303#endif
304 return msg_;
305#ifdef _MSC_VER
306#pragma warning(pop)
307#else
308#pragma GCC diagnostic pop
309#endif
310 }
311
315 std::mutex & get_mutex() { return msg_mutex_; }
316
320 const std::mutex & get_mutex() const { return msg_mutex_; }
321
322private:
323 template <typename PublisherCreator>
324 void initialize(PublisherCreator && creator)
325 {
326 publisher_ = creator();
327 is_running_ = false;
328 keep_running_ = true;
329 turn_ = State::LOOP_NOT_STARTED;
330
331 thread_ = std::thread(&RealtimePublisher::publishingLoop, this);
332
333 // Wait for the thread to be ready before proceeding
334 // This is important to ensure that the thread is properly initialized and ready to handle
335 // messages before any other operations are performed on the RealtimePublisher instance.
336 while (!thread_.joinable() ||
337 turn_.load(std::memory_order_acquire) == State::LOOP_NOT_STARTED) {
338 std::this_thread::sleep_for(std::chrono::microseconds(100));
339 }
340 }
341
347 bool can_publish(std::unique_lock<std::mutex> & lock) const
348 {
349 return turn_.load(std::memory_order_acquire) == State::REALTIME && lock.owns_lock();
350 }
351
352 // non-copyable
353 RealtimePublisher(const RealtimePublisher &) = delete;
354 RealtimePublisher & operator=(const RealtimePublisher &) = delete;
355
356 bool is_running() const { return is_running_; }
357
369 void publishingLoop()
370 {
371 is_running_ = true;
372
373 while (keep_running_) {
374 MessageT outgoing;
375
376 {
377 turn_.store(State::REALTIME, std::memory_order_release);
378 // Locks msg_ and copies it to outgoing
379 std::unique_lock<std::mutex> lock_(msg_mutex_);
380 updated_cond_.wait(lock_, [&] { return turn_ == State::NON_REALTIME || !keep_running_; });
381#ifdef _MSC_VER
382#pragma warning(push)
383#pragma warning(disable : 4996)
384#else
385#pragma GCC diagnostic push
386#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
387#endif
388 outgoing = msg_;
389#ifdef _MSC_VER
390#pragma warning(pop)
391#else
392#pragma GCC diagnostic pop
393#endif
394 }
395
396 // Sends the outgoing message
397 if (keep_running_) {
398 publisher_->publish(outgoing);
399 }
400 }
401 is_running_ = false;
402 }
403
404 PublisherSharedPtr publisher_;
405 std::atomic<bool> is_running_;
406 std::atomic<bool> keep_running_;
407
408 std::thread thread_;
409
410 mutable std::mutex msg_mutex_; // Protects msg_
411 std::condition_variable updated_cond_;
412
413 enum class State : int { REALTIME, NON_REALTIME, LOOP_NOT_STARTED };
414 std::atomic<State> turn_; // Who's turn is it to use msg_?
415};
416
417template <class MessageT>
418using RealtimePublisherSharedPtr = std::shared_ptr<RealtimePublisher<MessageT>>;
419
420} // namespace realtime_tools
421#endif // REALTIME_TOOLS__REALTIME_PUBLISHER_HPP_
Definition realtime_publisher.hpp:56
std::mutex & get_mutex()
Get the mutex protecting the stored message.
Definition realtime_publisher.hpp:315
void stop()
Stop the realtime publisher.
Definition realtime_publisher.hpp:142
RealtimePublisher(NodeT &&node, const std::string &topic_name, const rclcpp::QoS &qos, const rclcpp::PublisherOptions &options=rclcpp::PublisherOptions())
Constructor for the realtime publisher that creates the publisher internally.
Definition realtime_publisher.hpp:91
bool try_publish(const MessageT &msg)
Try to publish the given message.
Definition realtime_publisher.hpp:186
RealtimePublisher(PublisherSharedPtr publisher)
Constructor for the realtime publisher.
Definition realtime_publisher.hpp:110
bool tryPublish(const MessageT &msg)
Try to publish the given message (deprecated)
Definition realtime_publisher.hpp:227
bool trylock()
Try to acquire the data lock for non-realtime message publishing.
Definition realtime_publisher.hpp:162
std::thread & get_thread()
Get the thread object for the publishing thread.
Definition realtime_publisher.hpp:283
~RealtimePublisher()
Destructor.
Definition realtime_publisher.hpp:116
void unlock()
Unlocks the data without publishing anything.
Definition realtime_publisher.hpp:272
void unlockAndPublish()
Unlock the msg_ variable for the non-realtime thread to start publishing.
Definition realtime_publisher.hpp:242
rclcpp::Publisher< MessageT > PublisherType
Provide various typedefs to resemble the rclcpp::Publisher type.
Definition realtime_publisher.hpp:59
bool can_publish() const
Check if the realtime publisher is in a state to publish messages.
Definition realtime_publisher.hpp:171
const std::mutex & get_mutex() const
Get the mutex protecting the stored message.
Definition realtime_publisher.hpp:320
void lock()
Acquire the data lock.
Definition realtime_publisher.hpp:260
const std::thread & get_thread() const
Get the thread object for the publishing thread.
Definition realtime_publisher.hpp:290
A pthread mutex wrapper that provides a mutex with the priority inheritance protocol and a priority c...
Definition async_function_handler.hpp:42