c++ - Calling external program using boost::process causes caller to hang (Linux) -
i using boost::process call external program - external program reads input via stdin, , writes stdout , stderr. external program follows (expects single argument - path file debugging)
#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 2 arguments"); } std::ofstream ofs(argv[1]); std::vector<std::string> sometestinput; ofs << "starting program..." << std::endl; // read cin { ofs << "reading cin..." << std::endl; std::string input; while (std::getline(std::cin, input)) { ofs << "received cin: " << input << std::endl; sometestinput.emplace_back(input); } ofs << "finished receiving cin..." << std::endl; } // error if nothing has been input if (sometestinput.empty()) { throw std::logic_error("expected input , received nothing..."); } ofs << "writing cout..." << std::endl; // write cout (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; }
the caller expects 2+ arguments, 1 of path external program, , rest passed on arguments external program.
it hangs while waiting process exit, , seems external program waiting 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 @ least 2 arguments..."); } std::vector<std::string> args; (int = 1; < argc; ++i) { args.emplace_back(argv[i]); } std::cout << "creating stdout, stderr pipes...\n"; // create pipes stdout, stderr boost::process::pipe pstdout = boost::process::create_pipe(); boost::process::pipe pstderr = boost::process::create_pipe(); std::cout << "mapping pipes sources...\n"; // map pipe source stdout , stderr 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 streams sources...\n"; // , set streams 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 check process result, need ensure stdin handle closed properly, // place in separate scope { std::cout << "mapping pipes sinks...\n"; // map pipe sink stdout , stderr 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 source , sink...\n"; boost::process::pipe pstdin = boost::process::create_pipe(); // stdin, map pipe source , sink before - want close on exiting 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 test data cin - comment out below test 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 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 output callee...\n"; // gather 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; } }
i under impression placing stdin pipe , related sources/sinks within scope guarantee they're closed, , therefore send eof.
the same code works under windows (vs2013, boost_1_53).
i using boost_1_53, boost-process 0.5, gcc 4.8.2.
that not happen, because there's still pipe handle open in child process; closed on posix if set explicitly (on windows done automatically). you'd need add that:
#if defined (boost_posix_api) fcntl(pstdout.sink, f_setfd, fd_cloexec); fcntl(pstderr.sink, f_setfd, fd_cloexec); #endif
i recommend use boost.asio , wait asynchronously exit of subprocess , close pipes there.
just fyi: i've worked on boost-process 0.6 has different interface makes asio stuff easier. in review in october/november, might become official boost library soon. it's in beta might want check 1 out.
Comments
Post a Comment