Skip to content

Reading from boost::asio::readable_pipe used by Boost Process V2 ends with wrong code 109 on Windows (as opposed to expected boost::asio::error::eof) #553

@aliquis162

Description

@aliquis162

When I launch process (V2) and connect boost::asio::readable_pipe to its stdout on Windows, reading the pipe in a loop finishes with boost::system::error_code with value 109 and message "The pipe has been ended". On Linux I get code 2 and message "End of file". I would expect the code on Windows to behave the same way as Linux, as there seems to be a predefined constant for that: boost::asio::error::eof. This issue seems to be present in all functions (sync read, async read, async read with co_await).

What is the suggested workaround around this bug for now? Do I have to compare error codes based on OS, or is there something better and less error-prone?

Complete example:

#include <iostream>
#include <string>
#include <thread>
#include <vector>

#include <boost/asio.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/process/v2/process.hpp>
#include <boost/process/v2/stdio.hpp>

int main()
{
    boost::asio::io_context ctx;
    boost::asio::readable_pipe stdOutPipe(ctx);

#ifdef unix
    std::string processName = "/usr/bin/bash";
    std::vector<std::string> processArgs = { "-c", "ls", "-al"};
#else
    std::string processName = std::format("{}\\system32\\cmd.exe", std::getenv("windir"));
    std::vector<std::string> processArgs = { "/c", "dir"};
#endif

    boost::process::v2::process process(ctx, processName, processArgs, boost::process::v2::process_stdio{ {}, stdOutPipe, {} });

    std::jthread printStdOutThread([&]
        {
            while (true)
            {
                std::string stdOut;
                boost::system::error_code ec;
                size_t stdOutLength = boost::asio::read(stdOutPipe, boost::asio::dynamic_buffer(stdOut), ec);

                if (stdOutLength > 0)
                {
                    if (stdOut.size() != stdOutLength)
                        stdOut.resize(stdOutLength);

                    std::cout << stdOut;
                    std::cout.flush();
                }

                if (ec.value() != boost::system::errc::success)
                {
                    std::cout
                        << "------------------------\n"
                        << "Pipe end.\n"
                        << "eof: " << (ec == boost::asio::error::eof) << "\n"      // Windows: 0, Linux: 1
                        << "error code: " << ec.value() << "\n"       // Windows: 109, Linux: 2
                        << "error message: " << ec.message() << "\n"     // Windows: "The pipe has been ended", Linux: "End of file"
                        << "pipe is still open: " << stdOutPipe.is_open() << "\n"     // Windows: 1, Linux: 1
                        << "process is still running: " << process.running() << "\n";    // Unreliable because the child process may crash or be terminated at any time, but got this: Windows: false, Linux: true
                    std::cout.flush();

                    break;
                }
            }
        });

    ctx.run();

    int resultCode = process.wait();

    return resultCode;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions