From 02b7e58481c0cbe7a72f706b08bcb6836702faaf Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Fri, 3 Feb 2017 01:14:42 +0000 Subject: [PATCH] Make piping data into shell commands non blocking Fixes #1180 --- src/shell_manager.cc | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/shell_manager.cc b/src/shell_manager.cc index 57b6dad3..63ec22a5 100644 --- a/src/shell_manager.cc +++ b/src/shell_manager.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include extern char **environ; @@ -180,9 +181,6 @@ std::pair ShellManager::eval( child_stdout.close_write_fd(); child_stderr.close_write_fd(); - write(child_stdin.write_fd(), input); - child_stdin.close_write_fd(); - auto wait_time = Clock::now(); struct PipeReader : FDWatcher @@ -206,9 +204,36 @@ std::pair ShellManager::eval( {} }; + struct PipeWriter : FDWatcher + { + PipeWriter(Pipe& pipe, StringView contents) + : FDWatcher(pipe.write_fd(), FdEvents::Write, + [contents, &pipe](FDWatcher& watcher, FdEvents, EventMode) mutable { + while (fd_writable(pipe.write_fd())) + { + ssize_t size = ::write(pipe.write_fd(), contents.begin(), + (size_t)contents.length()); + if (size > 0) + contents = contents.substr(ByteCount{(int)size}); + if (size == -1 and (errno = EAGAIN or errno == EWOULDBLOCK)) + return; + if (size < 0 or contents.empty()) + { + pipe.close_write_fd(); + watcher.disable(); + } + } + }) + { + int flags = fcntl(pipe.write_fd(), F_GETFL, 0); + fcntl(pipe.write_fd(), F_SETFL, flags | O_NONBLOCK); + } + }; + String stdout_contents, stderr_contents; PipeReader stdout_reader{child_stdout, stdout_contents}; PipeReader stderr_reader{child_stderr, stderr_contents}; + PipeWriter stdin_writer{child_stdin, input}; // block SIGCHLD to make sure we wont receive it before // our call to pselect, that will end up blocking indefinitly. @@ -235,7 +260,7 @@ std::pair ShellManager::eval( wait_notified = true; }, EventMode::Urgent}; - while (not terminated or + while (not terminated or child_stdin.write_fd() != -1 or ((flags & Flags::WaitForStdout) and (child_stdout.read_fd() != -1 or child_stderr.read_fd() != -1))) {