Вызов внешней программы с использованием boost :: process вызывает зависание вызывающей стороны (Linux)

Я использую boost :: process для вызова внешней программы — внешняя программа читает ввод через stdin и записывает в stdout и stderr. Внешняя программа работает следующим образом (ожидает один аргумент — путь к файлу для отладки)

#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

int main(int argc, char** argv)
{
try
{
if (argc != 2)
{
throw std::logic_error("Expected two arguments");
}

std::ofstream ofs(argv[1]);

std::vector<std::string> someTestInput;

ofs << "Starting program..." << std::endl;

// Read from cin
{
ofs << "Reading from cin..." << std::endl;
std::string input;
while (std::getline(std::cin, input))
{
ofs << "Received from cin: " << input << std::endl;
someTestInput.emplace_back(input);
}

ofs << "Finished receiving from cin..." << std::endl;
}

// Error if nothing has been input
if (someTestInput.empty())
{
throw std::logic_error("Expected some input and received nothing...");
}

ofs << "Writing to cout..." << std::endl;

// Write to cout
for (const auto& output : someTestInput)
{
std::cout << output << '\n';
}

ofs << "Finished!\n";
}
catch (std::exception& e)
{
std::cerr << "Error caught: " << e.what() << '\n';
return 1;
}

return 0;
}

Вызывающая сторона ожидает более 2 аргументов, один из которых — путь к внешней программе, а остальные передаются в качестве аргументов внешней программе.

Он зависает, ожидая завершения процесса, и кажется, что внешняя программа ожидает EOF от stdin.

#include <memory>
#include <vector>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/process.hpp>

int main(int argc, char** argv)
{
try
{
if (argc < 2)
{
throw std::logic_error("Expecting at least 2 arguments...");
}

std::vector<std::string> args;

for (int i = 1; i < argc; ++i)
{
args.emplace_back(argv[i]);
}

std::cout << "Creating stdout, stderr pipes...\n";

// Create pipes for stdout, stderr
boost::process::pipe pstdout = boost::process::create_pipe();
boost::process::pipe pstderr = boost::process::create_pipe();

std::cout << "Mapping pipes to sources...\n";

// Map pipe source from stdout and stderr to sources
boost::iostreams::file_descriptor_source sourcestdout(pstdout.source, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_source sourcestderr(pstderr.source, boost::iostreams::close_handle);

std::cout << "Setting up streams for the sources...\n";

// And set up streams for the sources
boost::iostreams::stream<boost::iostreams::file_descriptor_source> istdout(sourcestdout);
boost::iostreams::stream<boost::iostreams::file_descriptor_source> istderr(sourcestderr);

std::unique_ptr<boost::process::child> p;

// Want to check for process result, but also need to ensure stdin handle is closed properly,
// so place everything in separate scope
{
std::cout << "Mapping pipes to sinks...\n";

// Map pipe sink from stdout and stderr to sinks
boost::iostreams::file_descriptor_sink sinkstdout(pstdout.sink, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_sink sinkstderr(pstderr.sink, boost::iostreams::close_handle);

std::cout << "Creating stdin pipe, mapping to source and sink...\n";

boost::process::pipe pstdin = boost::process::create_pipe();

// For stdin, map pipe to source and sink as before - want it to close on exiting this scope
boost::iostreams::file_descriptor_sink sinkstdin(pstdin.sink, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_source sourcestdin(pstdin.source, boost::iostreams::close_handle);
boost::iostreams::stream<boost::iostreams::file_descriptor_sink> ostdin(sinkstdin);

std::cout << "Calling process... \n";

// Call process
p = std::unique_ptr<boost::process::child>(new boost::process::child(boost::process::execute(
boost::process::initializers::set_args(args),
boost::process::initializers::throw_on_error(),
boost::process::initializers::bind_stdout(sinkstdout),
boost::process::initializers::bind_stderr(sinkstderr),
boost::process::initializers::bind_stdin(sourcestdin)
)));

std::cout << "Sending test data...\n";

// Send some test data to cin - comment out the below to test for error case
ostdin << "Test Input 1\n";
ostdin << "Some\n";
ostdin << "Useful\n";
ostdin << "Data\n";

std::cout << "Test data sent, exiting scope...\n";
}

std::cout << "Check if process has exited...\n";

// Check if process has exited OK - if not, report errors
if (boost::process::wait_for_exit(*p))
{
std::cout << "Has not exited OK, reporting problems...\n";

// Gather output from stderr
std::string error;
while (std::getline(istderr, error))
{
std::cout << "Error: " << error << '\n';
}

throw std::logic_error("Problem executing TestProgram...");
}

std::cout << "Exited OK, here is output from the callee...\n";

// Gather the output
std::string output;
while (std::getline(istdout, output))
{
std::cout << output << '\n';
}
}
catch (std::exception& e)
{
std::cerr << "Error: " << e.what() << '\n';
return 1;
}
}

У меня сложилось впечатление, что размещение моей трубы stdin и связанных с ней источников / приемников в области действия гарантирует, что они закрыты, и, следовательно, отправляет EOF.

Тот же код отлично работает под Windows (VS2013, boost_1_53).

Я использую boost_1_53, boost-process 0.5, gcc 4.8.2.

0

Решение

Этого не происходит, потому что в дочернем процессе все еще открыт дескриптор канала; это закрыто только в posix, если вы установите его явно (в windows это делается автоматически). Так что вам нужно добавить что-то вроде этого:

#if defined (BOOST_POSIX_API)
fcntl(pstdout.sink, F_SETFD, FD_CLOEXEC);
fcntl(pstderr.sink, F_SETFD, FD_CLOEXEC);
#endif

Однако я бы порекомендовал использовать boost.asio и асинхронно ждать выхода из подпроцесса и закрыть там каналы.

Просто к сведению: я работал над boost-process 0.6, который имеет другой интерфейс, но значительно облегчает работу с asio. Мы надеемся, что этот вопрос будет рассмотрен в октябре / ноябре, поэтому вскоре он может стать официальной библиотекой поддержки. В настоящее время он находится в бета-версии, так что вы можете проверить это.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]