def communicate(input=nil, timeout_s=nil)
      raise ArgumentError if !input.nil? && @stdin.nil?

      stdout, stderr = "", ""
      stdout_encoding = @stdout.external_encoding if @stdout
      stderr_encoding = @stderr.external_encoding if @stderr

      input = input.dup unless input.nil?

      @stdin.close if (input.nil? || input.empty?) && !@stdin.nil?

      timeout_at = Time.now + timeout_s if timeout_s

      self.class.catching_sigchld(pid) do |global_read, self_read|
        wait_r = [@stdout, @stderr, self_read, global_read].compact
        wait_w = [input && @stdin].compact
        done = false
        while !done
          # If the process has exited, we want to drain any remaining output before returning
          if poll
            ready_r = wait_r - [self_read, global_read]
            ready_w = []
            done = true
          else
            ready_r, ready_w = select_until(wait_r, wait_w, [], timeout_at)
            raise CommunicateTimeout.new(@command, stdout, stderr) if ready_r.nil?
          end

          if ready_r.include?(@stdout)
            if drain_fd(@stdout, stdout)
              wait_r.delete(@stdout)
            end
          end

          if ready_r.include?(@stderr)
            if drain_fd(@stderr, stderr)
              wait_r.delete(@stderr)
            end
          end

          if ready_r.include?(global_read)
            if drain_fd(global_read)
              raise "Unexpected internal error -- someone closed the global self-pipe!"
            end
            self.class.wakeup_sigchld
          end

          if ready_r.include?(self_read)
            if drain_fd(self_read)
              raise "Unexpected internal error -- someone closed our self-pipe!"
            end
          end

          if ready_w.include?(@stdin)
            written = 0
            begin
              written = @stdin.write_nonblock(input)
            rescue EOFError # Maybe I shouldn't catch this...
            rescue Errno::EINTR
            rescue IO::WaitWritable
              # On OS X, a pipe can raise EAGAIN even after select indicates
              # that it is writable. Once the process consumes from the pipe,
              # the next write should succeed and we should make forward progress.
              # Until then, treat this as not writing any bytes and continue looping.
              # For details see: https://github.com/stripe/subprocess/pull/22
              nil
            rescue Errno::EPIPE
              # The other side of the pipe closed before we could
              # write all of our input. This can happen if the
              # process exits prematurely.
              @stdin.close
              wait_w.delete(@stdin)
            end
            input[0...written] = ''
            if input.empty?
              @stdin.close
              wait_w.delete(@stdin)
            end
          end

          stdout.force_encoding(stdout_encoding) if stdout_encoding
          stderr.force_encoding(stderr_encoding) if stderr_encoding

          if block_given? && !(stderr.empty? && stdout.empty?)
            yield stdout, stderr
            stdout, stderr = "", ""
          end
        end
      end

      wait

      if block_given?
        nil
      else
        [stdout, stderr]
      end
    end
def communicate(input=nil, timeout_s=nil)
      raise ArgumentError if !input.nil? && @stdin.nil?

      stdout, stderr = "", ""
      stdout_encoding = @stdout.external_encoding if @stdout
      stderr_encoding = @stderr.external_encoding if @stderr

      input = input.dup unless input.nil?

      @stdin.close if (input.nil? || input.empty?) && !@stdin.nil?

      timeout_at = Time.now + timeout_s if timeout_s

      self.class.catching_sigchld(pid) do |global_read, self_read|
        wait_r = [@stdout, @stderr, self_read, global_read].compact
        wait_w = [input && @stdin].compact
        done = false
        while !done
          # If the process has exited, we want to drain any remaining output before returning
          if poll
            ready_r = wait_r - [self_read, global_read]
            ready_w = []
            done = true
          else
            ready_r, ready_w = select_until(wait_r, wait_w, [], timeout_at)
            raise CommunicateTimeout.new(@command, stdout, stderr) if ready_r.nil?
          end

          if ready_r.include?(@stdout)
            if drain_fd(@stdout, stdout)
              wait_r.delete(@stdout)
            end
          end

          if ready_r.include?(@stderr)
            if drain_fd(@stderr, stderr)
              wait_r.delete(@stderr)
            end
          end

          if ready_r.include?(global_read)
            if drain_fd(global_read)
              raise "Unexpected internal error -- someone closed the global self-pipe!"
            end
            self.class.wakeup_sigchld
          end

          if ready_r.include?(self_read)
            if drain_fd(self_read)
              raise "Unexpected internal error -- someone closed our self-pipe!"
            end
          end

          if ready_w.include?(@stdin)
            written = 0
            begin
              written = @stdin.write_nonblock(input)
            rescue EOFError # Maybe I shouldn't catch this...
            rescue Errno::EINTR
            rescue IO::WaitWritable
              # On OS X, a pipe can raise EAGAIN even after select indicates
              # that it is writable. Once the process consumes from the pipe,
              # the next write should succeed and we should make forward progress.
              # Until then, treat this as not writing any bytes and continue looping.
              # For details see: https://github.com/stripe/subprocess/pull/22
              nil
            rescue Errno::EPIPE
              # The other side of the pipe closed before we could
              # write all of our input. This can happen if the
              # process exits prematurely.
              @stdin.close
              wait_w.delete(@stdin)
            end
            input[0...written] = ''
            if input.empty?
              @stdin.close
              wait_w.delete(@stdin)
            end
          end

          stdout.force_encoding(stdout_encoding) if stdout_encoding
          stderr.force_encoding(stderr_encoding) if stderr_encoding

          if block_given? && !(stderr.empty? && stdout.empty?)
            yield stdout, stderr
            stdout, stderr = "", ""
          end
        end
      end

      wait

      if block_given?
        nil
      else
        [stdout, stderr]
      end
    end
Write the (optional) input to the process's `stdin` and read the contents of
 `stdout` and `stderr`. If a block is provided, stdout and stderr are yielded as they
 are read. Otherwise they are buffered in memory and returned when the process
 exits. Do this all using `IO::select`, so we don't deadlock due to full pipe
 buffers.

 This is only really useful if you set some of `:stdin`, `:stdout`, and
 `:stderr` to {Subprocess::PIPE}.

 @param [String] input A string to feed to the child's standard input.
 @param [Numeric] timeout_s Raise {Subprocess::CommunicateTimeout} if communicate
   does not finish after timeout_s seconds.
 @yieldparam [String] stdout Data read from stdout since the last yield
 @yieldparam [String] stderr Data read from stderr since the last yield
 @return [Array(String, String), nil] An array of two elements: the data read from the
   child's standard output and standard error, respectively.
   Returns nil if a block is provided.