From 8202ed8ff323bd5f2c19e2c77491f1b6df60977b Mon Sep 17 00:00:00 2001 From: Henrik Larsen Date: Wed, 25 Sep 2019 22:08:24 +0200 Subject: [PATCH 1/2] Issue #15: rxros::spin() funtion uses unnecessary many threads --- README.md | 7 ++++++- rxros/include/rxros/rxros.h | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5e35791..6646c67 100644 --- a/README.md +++ b/README.md @@ -159,13 +159,18 @@ The other purpose of the rxros::spin function is to dispatch messages from ROS t Failure to omit the rxros::spin function will cause the program to terminate immediately and observables based on ROS topics will not emit any events. +rxros::spin will per default use one thread for dispatching messages. For most nodes this will be sufficient. +In special cases where a node uses many topics it may be necessary to use more threads for dispatching messages. +This can be achieved by specifying the number of threads the rxros::spin function should use. +A special case is rxros::spin(0) which will allocate a thread for each CPU core. + The Example below shows a minimal RxROS program that creates a ROS node named “my_node”. The program will due to rxros:spin() continue to run until it is either shutdown or terminated. ### Syntax ```cpp -void rxros::spin(); +void rxros::spin(uint32_t thread_count = 1); ``` ### Example diff --git a/rxros/include/rxros/rxros.h b/rxros/include/rxros/rxros.h index 55433ff..9bc0b5a 100644 --- a/rxros/include/rxros/rxros.h +++ b/rxros/include/rxros/rxros.h @@ -48,8 +48,8 @@ namespace rxros ros::init(argc, argv, name); } - static void spin() { - ros::MultiThreadedSpinner spinner(0); // Use a spinner for each available CPU core + static void spin(uint32_t thread_count = 1) { // Use per default 1 thread for spinning + ros::MultiThreadedSpinner spinner(thread_count); spinner.spin(); } From 55327bd6a5994b692b6efed70c82170d1ca894ec Mon Sep 17 00:00:00 2001 From: Henrik Larsen Date: Sun, 27 Oct 2019 00:01:20 +0200 Subject: [PATCH 2/2] Fixed Issue #15: rxros::spin() funtion uses unnecessary many threads. Removed rxros::init, rxros::spin and rxros::ok. --- README.md | 121 +++++++++------------------ rxros/include/rxros/rxros.h | 16 +--- rxros_tf/include/rxros_tf/rxros_tf.h | 2 +- 3 files changed, 40 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 51079bf..fcf26da 100644 --- a/README.md +++ b/README.md @@ -20,40 +20,36 @@ RxROS aspires to the slogan ‘concurrency made easy’. * [Acknowledgements](#acknowledgements) * [Example packages](#example-packages) * [RxROS API](#rxros-api) - * [Initial setup](#initial-setup) - * [Syntax](#syntax) + * [Creating a RxROS Node](#creating-a-rxros-node) * [Example](#example) - * [Spinning](#spinning) - * [Syntax](#syntax-1) - * [Example](#example-1) * [Parameters](#parameters) - * [Syntax](#syntax-2) + * [Syntax](#syntax-1) * [Example](#example-2) * [Logging](#logging) - * [Syntax](#syntax-3) + * [Syntax](#syntax-2) * [Example](#example-3) * [Observables](#observables) * [Observable from a Topic](#observable-from-a-topic) - * [Syntax](#syntax-4) + * [Syntax](#syntax-3) * [Example](#example-4) * [Observable from a transform listener](#observable-from-a-transform-listener) - * [Syntax](#syntax-5) + * [Syntax](#syntax-4) * [Observable from a Linux device](#observable-from-a-linux-device) - * [Syntax](#syntax-6) + * [Syntax](#syntax-5) * [Example](#example-5) * [Observable from a Yaml file](#observable-from-a-yaml-file) - * [Syntax](#syntax-7) + * [Syntax](#syntax-6) * [Example](#example-6) * [Operators](#operators) * [Publish to Topic](#publish-to-topic) - * [Syntax:](#syntax-8) + * [Syntax:](#syntax-7) * [Example:](#example-7) * [Send Transform](#send-transform) - * [Syntax:](#syntax-9) + * [Syntax:](#syntax-8) * [Call Service](#call-service) - * [Syntax:](#syntax-10) + * [Syntax:](#syntax-9) * [Sample with Frequency](#sample-with-frequency) - * [Syntax:](#syntax-11) + * [Syntax:](#syntax-10) * [Example:](#example-8) * [Example 1: A Keyboard Publisher](#example-1-a-keyboard-publisher) * [Example 2: A Velocity Publisher](#example-2-a-velocity-publisher) @@ -146,66 +142,25 @@ Refer to the `README` in that repository for additional setup and installation i ## RxROS API -Now, lets look at the language in more details. +Now, lets look at the RxROS API in more details. RxROS provides simple access to ROS via a set of functions. -These functions provide more precisely an extension to RxCpp that -gives simple access to ROS.
- -### Initial setup +These functions provide more precisely an extension to RxCpp in form of observables and operations that give simple access to ROS.
-The most fundamental RxROS program is the initialization of a ROS node. -This is done simply by calling the rxros::init function. The rxros::init function takes three arguments: -The number of command line arguments the node or program was called with (argc), the actual arguments (argv) -and the name of the node. argc and argv are inherited directly from the main function and all three arguments -are used to initialize a ROS node. Failure to initialize a node with rxros::init will cause all interaction -with ROS to fail. +### Creating a RxROS node -#### Syntax - -```cpp -rxros::init(argc, argv, "Name_of_ROS_node"); -``` +Creating a RxROS node has some commonalities with creating a normal ROS node, as shown in the example below. +A RxROS node starts by calling `ros::init` to initialize the node and ends with a call to `ros::spin` to block the main thread and listen for topic updates. +Failure to include theses statements will either cause a RxROS node to exit immediately or behave in a unpredictable manner. +However, any variant of ros::init and ros::spin may be used to create a RxROS node. #### Example ```cpp #include int main(int argc, char** argv) { - rxros::init(argc, argv, "velocity_publisher"); // Name of this node. + ros::init(argc, argv, "node_name"); // ... -} -``` - -### Spinning - -The other fundamental function of RxROS is rxros::spin. The function blocks the main thread until it is shutdown or terminated. -This means that the rxros::spin function always should be placed at the very bottom of the main function. -The other purpose of the rxros::spin function is to dispatch messages from ROS topics to the appropriate RxROS observables. -Failure to omit the rxros::spin function will cause the program to terminate immediately and observables based on ROS topics -will not emit any events. - -rxros::spin will per default use one thread for dispatching messages. For most nodes this will be sufficient. -In special cases where a node uses many topics it may be necessary to use more threads for dispatching messages. -This can be achieved by specifying the number of threads the rxros::spin function should use. -A special case is rxros::spin(0) which will allocate a thread for each CPU core. - -The Example below shows a minimal RxROS program that creates a ROS node named “my_node”. -The program will due to rxros:spin() continue to run until it is either shutdown or terminated. - -#### Syntax - -```cpp -void rxros::spin(uint32_t thread_count = 1); -``` - -#### Example - -```cpp -#include -int main(int argc, char** argv) { - rxros::init(argc, argv, "my_node"); // Name of this node. - // ... - rxros::spin(); // block the main thread until it is terminated. + ros::spin() } ``` @@ -233,13 +188,13 @@ auto rxros::parameter::get(const std::string& name, const std::string& defaultVa ```cpp #include int main(int argc, char** argv) { - rxros::init(argc, argv, "velocity_publisher"); // Name of this node. + ros::init(argc, argv, "velocity_publisher"); // Name of this node. //... const auto frequency = rxros::parameter::get("/velocity_publisher/frequency", 10.0); // hz const auto minVelLinear = rxros::parameter::get("/velocity_publisher/min_vel_linear", 0.04); // m/s const auto maxVelLinear = rxros::parameter::get("/velocity_publisher/max_vel_linear", 0.10); // m/s //... - rxros::spin(); + ros::spin()(); } ``` @@ -267,7 +222,7 @@ rxros::logging& rxros::logging().fatal(); ```cpp #include int main(int argc, char** argv) { - rxros::init(argc, argv, "velocity_publisher"); // Name of this node. + ros::init(argc, argv, "velocity_publisher"); // Name of this node. //... rxros::logging().debug() << "frequency: " << frequency; rxros::logging().info() << "min_vel_linear: " << minVelLinear << " m/s"; @@ -275,7 +230,7 @@ int main(int argc, char** argv) { rxros::logging().error() << "min_vel_angular: " << minVelAngular << " rad/s"; rxros::logging().fatal() << "max_vel_angular: " << maxVelAngular << " rad/s"; //... - rxros::spin(); + ros::spin()(); } ``` @@ -312,7 +267,7 @@ auto rxros::observable::from_topic(const std::string& topic, const u ```cpp int main(int argc, char** argv) { - rxros::init(argc, argv, "velocity_publisher"); // Name of this node. + ros::init(argc, argv, "velocity_publisher"); // Name of this node. //... auto joy_obsrv = rxros::observable::from_topic("/joystick") | map([](teleop_msgs::Joystick joy) { return joy.event; }); @@ -320,7 +275,7 @@ int main(int argc, char** argv) { | map([](teleop_msgs::Keyboard key) { return key.event; }); auto teleop_obsrv = joyObsrv.merge(keyObsrv); //... - rxros::spin(); + ros::spin()(); } ``` @@ -362,13 +317,13 @@ auto rxros::observable::from_device(const std::string& device_name) ```cpp int main(int argc, char** argv) { - rxros::init(argc, argv, "joystick_publisher"); // Name of this node. + ros::init(argc, argv, "joystick_publisher"); // Name of this node. //... rxros::observable::from_device("/dev/input/js0") | map(joystickEvent2JoystickMsg) | publish_to_topic("/joystick"); //... - rxros::spin(); + ros::spin()(); } ``` @@ -420,7 +375,7 @@ auto rxros::observable::from_yaml(const std::string& namespace); ```cpp #include int main(int argc, char** argv) { - rxros::init(argc, argv, "brickpi3"); // Name of this node. + ros::init(argc, argv, "brickpi3"); // Name of this node. //... rxros::observable::from_yaml("/brickpi3/brickpi3_robot").subscribe( [=](auto config) { // on_next event @@ -428,7 +383,7 @@ int main(int argc, char** argv) { if (device.getType() == "motor") { rxros::logging().debug() << device.getType() << ", " << device.getName() << ", " << device.getPort() << ", " << device.getFrequency(); //... - rxros::spin(); + ros::spin()(); } ``` @@ -496,13 +451,13 @@ auto rxros::operators::publish_to_topic(const std::string &topic, co ```cpp int main(int argc, char** argv) { - rxros::init(argc, argv, "joystick_publisher"); // Name of this node. + ros::init(argc, argv, "joystick_publisher"); // Name of this node. //... rxros::observable::from_device("/dev/input/js0") | map(joystickEvent2JoystickMsg) | publish_to_topic("/joystick"); //... - rxros::spin(); + ros::spin()(); } ``` @@ -564,12 +519,12 @@ auto rxros::operation::sample_with_frequency(const double frequency, Coordinatio ```cpp int main(int argc, char** argv) { - rxros::init(argc, argv, "joystick_publisher"); // Name of this node. + ros::init(argc, argv, "joystick_publisher"); // Name of this node. //... | sample_with_frequency(frequencyInHz) | publish_to_topic("/cmd_vel"); //... - rxros::spin(); + ros::spin()(); } ``` @@ -588,7 +543,7 @@ using namespace rxros::operators; int main(int argc, char** argv) { - rxros::init(argc, argv, "keyboard_publisher"); // Name of this Node. + ros::init(argc, argv, "keyboard_publisher"); // Name of this Node. const auto keyboardDevice = rxros::parameter::get("/keyboard_publisher/device", "/dev/input/event1"); rxros::logging().info() << "Keyboard device: " << keyboardDevice; @@ -618,7 +573,7 @@ int main(int argc, char** argv) | publish_to_topic("/keyboard"); rxros::logging().info() << "Spinning keyboard_publisher..."; - rxros::spin(); + ros::spin()(); } ``` @@ -639,7 +594,7 @@ on the /cmd_vel topic. For more information see [rxros_examples](https://github. int main(int argc, char** argv) { - rxros::init(argc, argv, "velocity_publisher"); // Name of this node. + ros::init(argc, argv, "velocity_publisher"); // Name of this node. const auto frequencyInHz = rxros::Parameter::get("/velocity_publisher/frequency", 10.0); // hz const auto minVelLinear = rxros::Parameter::get("/velocity_publisher/min_vel_linear", 0.04); // m/s @@ -698,6 +653,6 @@ int main(int argc, char** argv) { | publish_to_topic("/cmd_vel"); // publish the Twist messages to the topic "/cmd_vel" rxros::Logging().info() << "Spinning velocity_publisher ..."; - rxros::spin(); + ros::spin()(); } ``` diff --git a/rxros/include/rxros/rxros.h b/rxros/include/rxros/rxros.h index 9bc0b5a..b9f5926 100644 --- a/rxros/include/rxros/rxros.h +++ b/rxros/include/rxros/rxros.h @@ -44,20 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace rxros { - static void init(int argc, char** argv, const std::string& name) { - ros::init(argc, argv, name); - } - - static void spin(uint32_t thread_count = 1) { // Use per default 1 thread for spinning - ros::MultiThreadedSpinner spinner(thread_count); - spinner.spin(); - } - - static bool ok() { - return ros::ok(); - } - - class node: public ros::NodeHandle { private: @@ -225,7 +211,7 @@ namespace rxros FD_SET(fd, &readfds); T event{}; bool errReported = false; - while (rxros::ok()) { + while (ros::ok()) { int rc = select(fd + 1, &readfds, nullptr, nullptr, nullptr); // wait for input on keyboard device if (rc == -1 && errno == EINTR) { // select was interrupted. This is not an error but we exit the loop subscriber.on_completed(); diff --git a/rxros_tf/include/rxros_tf/rxros_tf.h b/rxros_tf/include/rxros_tf/rxros_tf.h index 5de1c23..cdbe376 100644 --- a/rxros_tf/include/rxros_tf/rxros_tf.h +++ b/rxros_tf/include/rxros_tf/rxros_tf.h @@ -47,7 +47,7 @@ namespace rxros tf::TransformListener transform_listener; ros::Rate rate(frequencyInHz); bool errReported = false; - while (rxros::ok()) { + while (ros::ok()) { try { tf::StampedTransform transform; transform_listener.lookupTransform(parent_frameId, child_frameId, ros::Time(0), transform);