Search code examples
c++boostc++17boost-process

Is there way to detach process from out-stream after some time?


I am using boost::process::child to spawn new process. Start time of process which I am start isn't instant, so I have to wait some time until full initialization of it.

auto is_ptr = std::make_shared<bp::ipstream>();
auto child_pr = std::make_shared<bp::child>(executable, args, bp::std_out > *is_ptr);
m_childs[port] = {child_pr, is_ptr};

std::string line;
while (child_pr->running() && std::getline(*is_ptr, line)) {
    std::cerr <<"SI: \t" << line << std::endl;
    if( 0 == line.compare(0, string_to_find.size(), string_to_find)){
        break;
    }
}
...

After this cycle I don't need to have ipstream anymore. Is any way to detach it from the child process?


Solution

  • Since you asked to provide answer, I'll put some additional information here, although I am not sure it will completely answer your question.

    Assuming the target platform is Linux, once ipstream is destroyed in the parent process, it effectively means that the file descriptor for the associated pipe between the parent and child process is closed in the parent process. Once the child process writes to the pipe after the parent process closed its read end of the pipe, SIGPIPE is generated for the child process, which will cause it to terminate in case no extra measures are taken.

    To prevent this, one option is to ignore SIGPIPE in the child. This will now cause errors in the child process when writing to that pipe. It depends on the implementation of the child process what cause that will have. A solution in your case could be to ignore SIGPIPE, and take measures in the child process once it can no longer successfully write data, to prevent a lot of wasted CPU cycles.

    To experiment with this on a lower level, you can use the following program. It will fork a child process that will keep on writing to some output as long as that succeeds. The parent process will close the corresponding pipe as soon as it has read some data from it.

    The behavior of the program differs depending on how SIGPIPE is handled in the child process. In case it is ignored, the write() in the child process will fail, and the child process will exit with a non-zero exit code. In case the SIGPIPE is not ignored, the child process is terminated by the operating system. The parent process will tell you what happened in the child process.

    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main(int argc, char** argv)
    {
        int pipe_fds[2];
        if (pipe(pipe_fds) < 0) {
            perror("pipe");
            exit(1);
        }
    
        pid_t pid;
        if ((pid = fork()) < 0) {
            perror("fork");
            exit(1);
        }
    
        if (pid == 0)
        {
            close(pipe_fds[0]); /* close read-end in the child */
    
            /* Uncomment the following line, and the child will terminate as soon
               as the parent closes the read end of the pipe...This is here merely
               for illustrative purposes, production code should use either
               sigaction() or pthreads related signal functionality in case of a
               multi-threaded program. */
    
            /* signal(SIGPIPE, SIG_IGN); */
    
            /* Child process, start writing to the write-end of the pipe. */
            const char message[] = "Hello world!\n";
            while (write(pipe_fds[1], message, strlen(message)) >= 0);
    
            exit(1);
        }
    
        close(pipe_fds[1]);
        char buf[256];
        ssize_t count;
        while ((count = read(pipe_fds[0], buf, sizeof(buf) - 1)) == 0);
        if (count < 0) {
            perror("read");
            exit(1);
        }
    
        buf[count] = '\0';
        printf("%s", buf);
    
        /* Close read-end in the parent, this will trigger SIGPIPE in the child
           once the child writes to the pipe. */
        close(pipe_fds[0]);
    
        int stat;
        if (waitpid(pid, &stat, 0) < 0) {
            perror("waitpid");
            exit(1);
        }
    
        if (WIFSIGNALED(stat) && WTERMSIG(stat) == SIGPIPE) {
            printf("\nChild terminated by SIGPIPE\n");
        }
        if (WIFEXITED(stat)) {
            printf("\nChild exited with exit code %d\n", WEXITSTATUS(stat));
        }
    
        exit(0);
    }