According to the Boost documentation (section 'Why does the pipe not close?'), the following code will result in a deadlock:
#include <boost/process.hpp>
#include <iostream>
namespace bp = ::boost::process;
int main(void)
{
bp::ipstream is;
bp::child c("ls", bp::std_out > is);
std::string line;
while (std::getline(is, line))
{
std::cout << line << "\n";
}
return 0;
}
The documentation says:
This will also deadlock, because the pipe does not close when the subprocess exits. So the ipstream will still look for data even though the process has ended.
However, I am not able to reproduce the deadlock (under Linux). Furthermore, I do not understand why the deadlock would occur in the first place. Once the child process exits it closes the write-end of the pipe. The read-end of the pipe will still be available for the parent process to read from, and std::getline()
will fail once no more data is available in the pipe buffer, and the write-end was closed, correct? In case the pipe buffer fills up during execution of the child process, the child process will block waiting for the parent process to read enough data from the pipe so that it can continue.
So in case the above code can deadlock, is there an easy way to reproduce the deadlock scenario?
Update:
Indeed, the following piece of code deadlocks using Boost process:
#include <boost/process.hpp>
#include <iostream>
namespace bp = ::boost::process;
int main()
{
bp::ipstream is;
bp::child c("/bin/bash", bp::args({"-c", "ls >&40"}), bp::posix::fd.bind(40, is.rdbuf()->pipe().native_sink()));
std::string line;
while (std::getline(is, line))
{
std::cout << line << "\n";
}
c.wait();
return 0;
}
I wonder whether this really is some unavoidable property of process spawning under Linux though. Reproducing the above example using Subprocess from Facebook's Folly library at least does not deadlock:
#include <folly/Subprocess.h>
#include <iostream>
int main()
{
std::vector<std::string> arguments = {"/bin/bash", "-c", "ls >&40"};
folly::Subprocess::Options options;
options.fd(40, STDOUT_FILENO);
folly::Subprocess p(arguments, options);
std::cout << p.communicate().first;
p.wait();
return 0;
}
pipe.hpp
at least in the version that I have? Your last example is related to the deadlock I describe in my question; the child process deadlocks since the parent process does not read from is, therefore the internal buffer for the pipe forwarding stdout from the child process to the parent process fills up, and the child process waits for the parent process to process output. – Scribbler