如何处理`boost :: process`与`boost :: asio`相结合的错误?

时间:2019-04-30 07:58:34

标签: c++ boost boost-asio boost-process

boost::process允许以可移植的方式将程序输出重定向到管道,因此不需要使用预处理器条件并调用OS特定的API来运行进程,创建和读取管道。

boost::process支持同步和异步IO。同步会产生高的死锁风险;根据我的经验,如果应用程序很复杂,并且运行着许多带有不同输出的子代(类似于旧的好CGI协议实现,例如,我们运行未知且可能是第三方的,有问题的子代进程),则< strong>保证,boost::process同步IO迟早会导致死锁。

因此,我们必须使用基于boost::asio的异步IO。它可以帮助我们避免陷入僵局,但是,它会提出更多没有答案的问题。

通常,我们要读取child生成的所有数据。我们也通常不知道子级将生成多少数据,它将不会生成任何数据,是否会崩溃,预期的数据格式等等。我们通常一无所知,并且仍然希望读取所有数据。即使进程错误代码非零,它仍然可以打印我们也要阅读的任何有用的错误消息。

我看到的唯一方法是读取直到出现错误。当我们将boost::asio与TCP套接字一起使用时,当套接字关闭时,它将引发文件结尾错误,因此我们可以知道它是数据结尾,并且套接字已关闭,因此不会再显示任何数据。

但是,对于async_pipe,没有记录错误代码必须表示end of pipepipe is closed by other side的含义。

在实践中,我们可以使用以下代码“读取直到出错”:

std::vector<char> exec(const std::string & command)
{
    using boost::asio::async_read;
    using boost::asio::buffer;
    using boost::asio::io_context;
    using boost::system::error_code;
    using boost::system::system_error;
    using namespace std;
    namespace bp = boost::process;
    io_context async_io_context;
    bp::async_pipe command_stdout(async_io_context);
    bp::child command_process(command, bp::std_in.close(), bp::std_out > command_stdout, bp::std_err > command_stdout, bp::shell);
    static constexpr const size_t kStdOutBufferSize = 65535;
    array<char, kStdOutBufferSize> command_stdout_buffer;
    vector<char> command_output;
    function<void(const boost::system::error_code &ec, std::size_t size)> async_read_handler_function;
    error_code io_error_code;
    async_read_handler_function = [&](const error_code &ec, std::size_t size)
    {
        // Write data
        if(size)
            command_output.insert(command_output.end(), command_stdout_buffer.data(), command_stdout_buffer.data() + size);
        // Parse error code
        if (ec)
        {
            io_error_code = ec;
            async_io_context.stop();
        }
        else
            async_read(command_stdout, buffer(command_stdout_buffer.data(), command_stdout_buffer.size()), async_read_handler_function);
    };
    async_read(command_stdout, buffer(command_stdout_buffer.data(), command_stdout_buffer.size()), async_read_handler_function);
    async_io_context.run();
    if (io_error_code)
    {
        // TODO: so how to make us sure that we really read all data and other side of pipe is gracefully closed by child process?
    }
    return command_output;
}

在Windows上,当一切正常运行时,此代码实际上会读取所有输出并返回带有system_category109整数值的错误代码。在boost错误代码枚举中,109表示boost::system::errc::destination_address_required。我的问题:

  • 是否可以确保如果子进程关闭了远程管道末端并且我们读取了所有未决数据,则错误代码将为boost::system::errc::destination_address_required
  • 此错误代码是否可移植,因此任何操作系统(Windows,Linux,Android,iOS,MacOS)都将为我们提供相同的错误代码?

如果代码不可移植,并且其他操作系统上的错误也会有所不同,那么使代码可移植的方法是什么? boost如果仅创建可移植代码,则具有意义,否则系统特定的API更好,因为它们将提供更好的性能和对系统特定功能的更深入的使用。

0 个答案:

没有答案