I\'m having some trouble grasping how to correctly handle creating a child process from a multithreaded program that uses Boost Asio in a multithreaded fashion.
If I
Consider the following:
fork() creates only one thread in the child process. You would need to recreate the other threads.fork(). Callbacks registered with pthread_atfork() could release the mutexes, but majority of libraries never bother using pthread_atfork(). In other words, you child process could hang forever when calling malloc() or new because the standard heap allocator does use mutexes.In the light of the above, the only robust option in a multi-threaded process is to call fork() and then exec().
Note that your parent process is not affected by fork() as long as pthread_atfork() handlers are not used.
Regarding forking and boost::asio, there is io_service::notify_fork() function that needs to be called before forking in the parent and after forking in both parent and child. What it does ultimately depends on the reactor being used. For Linux/UNIX reactors select_reactor, epoll_reactor, dev_poll_reactor, kqueue_reactor this function does not do anything to the parent before of after fork, but in the child it recreates the reactor state and re-registers the file descriptors. I am not sure what it does on Windows, though.
An example of its usage can be found in process_per_connection.cpp, you can just copy it:
void handle_accept(const boost::system::error_code& ec)
{
if (!ec)
{
// Inform the io_service that we are about to fork. The io_service cleans
// up any internal resources, such as threads, that may interfere with
// forking.
io_service_.notify_fork(boost::asio::io_service::fork_prepare);
if (fork() == 0)
{
// Inform the io_service that the fork is finished and that this is the
// child process. The io_service uses this opportunity to create any
// internal file descriptors that must be private to the new process.
io_service_.notify_fork(boost::asio::io_service::fork_child);
// The child won't be accepting new connections, so we can close the
// acceptor. It remains open in the parent.
acceptor_.close();
// The child process is not interested in processing the SIGCHLD signal.
signal_.cancel();
start_read();
}
else
{
// Inform the io_service that the fork is finished (or failed) and that
// this is the parent process. The io_service uses this opportunity to
// recreate any internal resources that were cleaned up during
// preparation for the fork.
io_service_.notify_fork(boost::asio::io_service::fork_parent);
socket_.close();
start_accept();
}
}
else
{
std::cerr << "Accept error: " << ec.message() << std::endl;
start_accept();
}
}