ROS2 quietly crashes when running as a forked process
Summary
I've created a simple process that forks, then launches one of two ROS2 nodes depending on which branch it is. However, the program simply quits upon instantiating a node. There is no error output. The only indication that something was wrong was when I echo the process exit code (echo $? -> 245
)
Technical Details
- Operating System: Ubuntu 19.10 Eoan
- ROS Distribution: Eloquent
Notes
I have made sure that I can not calling any ROS2 init/shutdown code before I fork. It is only done after. Seeing as they should appear as independent processes to ROS2, I don't see why this causes a crash to happen.
Minimal Reproducible Example
I have a minimal example here that exhibits the behavior I have described in the summary:
#include <chrono>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/num.hpp"
extern "C" {
#include <sched.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <sys/fcntl.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <sys/wait.h>
}
using namespace std::chrono_literals;
using std::placeholders::_1;
/*
*******************************************************************************
* Class A Definition *
*******************************************************************************
*/
class A : public rclcpp::Node {
public:
A() : Node("A")
{
// Initialize publisher
pub = this->create_publisher<tutorial_interfaces::msg::Num>("x", 10);
// Create timer
timer = this->create_wall_timer(100ms, std::bind(&A::dispatch_all, this));
}
void dispatch_all ()
{
auto msg = tutorial_interfaces::msg::Num();
msg.num = 1;
// Send a single high QoS message
pub->publish(msg);
// Print
RCLCPP_INFO(this->get_logger(), "Done");
}
private:
// Publisher
rclcpp::Publisher<tutorial_interfaces::msg::Num>::SharedPtr pub;
// Timer
rclcpp::TimerBase::SharedPtr timer;
};
/*
*******************************************************************************
* Class X Definition *
*******************************************************************************
*/
class X : public rclcpp::Node {
public:
X(): Node("X") {
sub = this->create_subscription<tutorial_interfaces::msg::Num>("x", 10, std::bind(&X::cb, this, _1));
}
private:
// Subscription
rclcpp::Subscription<tutorial_interfaces::msg::Num>::SharedPtr sub;
// Callback
void cb (const tutorial_interfaces::msg::Num::SharedPtr msg) const {
RCLCPP_INFO(this->get_logger(), "X: Receivied %d", msg->num);
}
};
/*
*******************************************************************************
* Main *
*******************************************************************************
*/
int main (int argc, char *argv[])
{
int set_result;
int pid;
int process_id = 0;
// Fork a child process
if ((pid = fork()) == -1) {
fprintf(stderr, "Err: Bad fork: %s\n", strerror(errno));
}
// Identify child process
if (pid == 0) {
process_id++;
}
// Configure the core
fprintf(stdout, "Fork #%d\n", process_id);
// Init and launch ROS
rclcpp::init(argc, argv);
switch (process_id) {
case 0: { // Process 0
auto node_a = std::make_shared<A>();
printf("Launching A on Fork #%d\n!", process_id);
rclcpp::spin(node_a);
}
break;
case 1: { // Process 1
auto node_x = std::make_shared<X>();
printf("Launching X on Fork #%d\n!", process_id);
rclcpp::spin(std::make_shared<X>());
}
break;
default: {
fprintf(stderr, "Err: Unexpected case (%d)!\n", process_id);
return -1;
}
}
rclcpp::shutdown();
printf("Shutting down ... \n");
// If parent - collect children
if (process_id == 0) {
wait(NULL);
}
return 0;
}