code: def output_thread(log, stdout, stderr, timeout_event, is_alive, quit,
          stop_output_event):
      
  
      poller = Poller()
      if stdout is not None:
          poller.register_read(stdout)
      if stderr is not None:
          poller.register_read(stderr)
  
      # this is our poll loop for polling stdout or stderr that is ready to
      # be read and processed.  if one of those streamreaders indicate that it
      # is done altogether being read from, we remove it from our list of
      # things to poll.  when no more things are left to poll, we leave this
      # loop and clean up
      while poller:
          changed = no_interrupt(poller.poll, 0.1)
          for f, events in changed:
              if events & (POLLER_EVENT_READ | POLLER_EVENT_HUP):
                  log.debug("%r ready to be read from", f)
                  done = f.read()
                  if done:
                      poller.unregister(f)
              elif events & POLLER_EVENT_ERROR:
                  # for some reason, we have to just ignore streams that have had an
                  # error.  i'm not exactly sure why, but don't remove this until we
                  # figure that out, and create a test for it
                  pass
  
          if timeout_event and timeout_event.is_set():
              break
  
          if stop_output_event.is_set():
              break
  
      # we need to wait until the process is guaranteed dead before closing our
      # outputs, otherwise SIGPIPE
      alive, _ = is_alive()
      while alive:
          quit.wait(1)
          alive, _ = is_alive()
  
      if stdout:
          stdout.close()
  
      if stderr:
          stderr.close()
docs: this function is run in a separate thread.  it reads from the
      process's stdout stream (a streamreader), and waits for it to claim that
      its done