How to reproduce deadlock hinted to by Boost process documentation?

会有一股神秘感。 提交于 2021-02-18 22:31:33

问题


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;
}

回答1:


Once the child process exits it closes the write-end of the pipe.

This seems to be the assumption. What program closes what pipe?

If /bin/ls does, what happens for

bp::child c("/bin/bash", bp::args({"-c", "ls; ls"}));

If ls really does close it, then it should be closed twice.

Perhaps bash duplicates the handles under the hood, so the subprocesses close different copies of the same pipe. I'm not sure about the reliability of these semantics¹

So, apparently stdout is well-catered for. However, I can reproduce the deadlock when using a non-standard file-descriptor for output on linux:

#include <boost/process.hpp>
#include <iostream>

namespace bp = ::boost::process;

int main() {
    bp::ipstream is;
    bp::child c("/bin/bash", bp::args({"-c", "exec >&40; ls"}), bp::posix::fd.bind(40, is.rdbuf()->pipe().native_sink()));

    std::string line;
    while (std::getline(is, line)) {
        std::cout << line << "\n";
    }
}

I'm not sure why the "closing stdout" behaviour of sub processes in bash should behave differently when it was redirected to an fd, but there you go.

Another nice way to demonstrate a related deadlock is:

{
    bp::child c("/bin/bash", bp::args({"-c", "ls -R /"}), bp::std_out > is);
    c.wait();
    return c.exit_code();
}

This answer is not conclusive but does observe some points and demonstrate them on linux:

  • not all filedescriptors seem to be covered the same as stdout
  • deadlocks can occur in many scenarios where subprocesses handles their IO asynchronously but the calling process treats them synchronously.

I think the latter was the point in the documentation.


¹ indeed the documentation explicitly suggests difference in these semantics are the problem in Win32:

It is not possible to use automatically pipe-closing in this library, because a pipe might be a file-handle (as for async pipes on windows)



来源:https://stackoverflow.com/questions/45795544/how-to-reproduce-deadlock-hinted-to-by-boost-process-documentation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!