2017-02-08 12 views
0

У меня есть два потока, которые запускают дочерний процесс каждый. Первое приложение представляет собой двоичный файл, который работает довольно долго. Второй выход довольно быстро.Состояние гонки при запуске подпроцессов вызывает чтение из трубы для зависания

Состояние гонки, которое иногда приводит к сбою. Ниже приведен пример минимального жизнеспособного кода.

В нем используется Boost Process 0.5, который использует стандартную систему fork/execve/dup2. Есть некоторые хаки о том, как работает Boost Process, но в целом он работает достаточно хорошо.

Родительский процесс запускает намного больше процессов, и в целом он работает.

Я не могу легко загружать процессы по одному, тем более, что я не знаю, какие части не могут чередовать.

Любые идеи о том, почему это виснет?

Ожидаемый результат:

/etc/init.d/led restart: Creating child 
Creating child1 
Reading STDOUT 
/etc/init.d/led restart: Waiting for it to exit 
Reading std_err_pipe 


wait_for_exit(pullapp); 
Reading std_out_pipe 
< file list> 

Done 

Однако, часто, но не всегда, он останавливается на std_err_pipe.

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

#include <boost/iostreams/stream.hpp> 
#include <boost/process.hpp> 
#include <boost/thread.hpp> 

void run_sleep() 
{ 
    int  exit_code; 
    std::string str; 

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

    boost::shared_ptr<boost::process::child> child; 

    args.push_back(boost::process::search_path("sleep")); 
    args.push_back("20"); 

    boost::iostreams::stream<boost::iostreams::file_descriptor_source> 
     out_stream; 

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

    { 
     //MUST BE IN SEPARATE SCOPE SO SINK AND SOURCE ARE DESTROYED 
     // See http://stackoverflow.com/a/12469478/5151127 
     boost::iostreams::file_descriptor_sink out_sink 
      (out_pipe.sink, boost::iostreams::close_handle); 
     boost::iostreams::file_descriptor_source out_source 
      (out_pipe.source, boost::iostreams::close_handle); 

     std::cout << "Creating child1" << std::endl; 
     child.reset(new boost::process::child(
      boost::process::execute(
       boost::process::initializers::run_exe(args[0]), 
       boost::process::initializers::set_args(args), 
       boost::process::initializers::bind_stdout(out_sink), 
       boost::process::initializers::bind_stderr(out_sink) 
      ) 
     )); 

     out_stream.open(out_source); 
    } 

    std::cout << "Reading STDOUT" << std::endl; 
    while(out_stream) { 
     std::string line; 

     std::getline(out_stream, line); 

     std::cout << line << std::endl; 
    } 

    std::cout << "wait_for_exit(pullapp);" << std::endl; 
    exit_code = wait_for_exit(*child); 

    child.reset(); 

    return; 
} 


void run_ls() 
{ 
    int  exit_code; 
    std::string str; 

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

    args.push_back(boost::process::search_path("ls")); 
    args.push_back("/lib"); 

    boost::process::pipe std_out_pipe = boost::process::create_pipe(); 
    boost::process::pipe std_err_pipe = boost::process::create_pipe(); 

    std::cout << "/etc/init.d/led restart: Creating child" << std::endl; 

    { 
     boost::process::child child = boost::process::execute(
      boost::process::initializers::set_args(args), 
      boost::process::initializers::bind_stdout(
       boost::iostreams::file_descriptor_sink(
        std_out_pipe.sink, 
        boost::iostreams::close_handle 
       ) 
      ), 
      boost::process::initializers::bind_stderr(
       boost::iostreams::file_descriptor_sink(
        std_err_pipe.sink, 
        boost::iostreams::close_handle 
       ) 
      ), 
      boost::process::initializers::throw_on_error() 
     ); 

     std::cout << "/etc/init.d/led restart: Waiting for it to exit" << std::endl; 
     exit_code = wait_for_exit(child); 
    } 

    { //with std_err_stream, istream 
     boost::iostreams::stream<boost::iostreams::file_descriptor_source> 
      std_err_stream(
       boost::iostreams::file_descriptor_source(
        std_err_pipe.source, boost::iostreams::close_handle 
       ) 
      ); 

     std::cout << "Reading std_err_pipe" << std::endl; 
     std::istream istream(std_err_stream.rdbuf()); 
     while(istream) { 
      getline(istream, str); 
      std::cout << str << std::endl; 
     } 
    } 

    { //with std_out_stream, istream 
     boost::iostreams::stream<boost::iostreams::file_descriptor_source> 
      std_out_stream(
       boost::iostreams::file_descriptor_source(
        std_out_pipe.source, boost::iostreams::close_handle 
       ) 
      ); 

     std::cout << "Reading std_out_pipe" << std::endl; 
     std::istream istream(std_out_stream.rdbuf()); 
     while(istream) { 
      getline(istream, str); 
      std::cout << str << std::endl; 
     } 
    } 

    std::cout << "Done" << std::endl; 
} 

int main() 
{ 
    boost::thread run_sleep_tr(run_sleep); 
    boost::thread run_ls_tr(run_ls); 

    run_sleep_tr.join(); 
    run_ls_tr.join(); 

    return 0; 
} 

(Сохранить как процесс-test.cpp и компилировать с g++ process-test.cpp -o process-test -lboost_iostreams -lboost_filesystem -lboost_thread -lboost_system)

ответ

0

Видимо, это происходит потому, что дескрипторы файлов в конечном итоге в нескольких процессах. Эти процессы не закрывают эти ручки, поэтому родитель остается ждать.

Для Linux исправление относительно просто; труба должна быть создана с O_CLOEXEC в create_pipe. Вызов dup2 в методах bind_* очищает этот флаг, которого достаточно, чтобы труба работала должным образом.

В Windows я еще не нашел решение. Вы должны пометить дескриптор как наследуемый. Возможно, это можно сделать в методе executor(), но, возможно, для этого требуется глобальный мьютекс. У меня не было времени, чтобы должным образом изучить его.

0

Я не уверен, что «использовать boost.process 0.6» считается ответом, но это делает это для вас. После нескольких сообщений об ошибках. На окнах, закрывающих раковину в отцовском процессе, должно быть достаточно.

+1

Очевидно, что процесс Boost, который я использовал, был старой версией. Последнее находится на странице https://github.com/klemens-morgenstern/boost-process. –