class_docstr: A class the contains a naive implementation for concurrent sub-process
  execution. It uses `subprocess.Popen` and threads to read from stdout and
  stderr of the subprocess, and to write to stdin of the subprocess.
  
  All read data and timeouts are passed to a protocol instance, which can
  create the final result.
class_docstr_tok: ["A","class","the","contains","a","naive","implementation","for","concurrent","sub-process","execution",".","It","uses","`","subprocess.Popen","`","and","threads","to","read","from","stdout","and","stderr","of","the","subprocess",",","and","to","write","to","stdin","of","the","subprocess",".","All","read","data","and","timeouts","are","passed","to","a","protocol","instance",",","which","can","create","the","final","result","."]
docstr: Get a single event from the queue or handle a timeout. This method
  might modify the set of active file numbers if a file-closed event
  is read from the output queue, or if a timeout-callback return True.
docstr_tok: ["Get","a","single","event","from","the","queue","or","handle","a","timeout",".","This","method","might","modify","the","set","of","active","file","numbers","if","a","file-closed","event","is","read","from","the","output","queue",",","or","if","a","timeout-callback","return","True","."]
code: def process_queue(self):
      """
      Get a single event from the queue or handle a timeout. This method
      might modify the set of active file numbers if a file-closed event
      is read from the output queue, or if a timeout-callback return True.
      """
      data = None
      while True:
          # We do not need a user provided timeout here. If
          # self.timeout is None, no timeouts are reported anyway.
          # If self.timeout is not None, and any enqueuing (stdin)
          # or de-queuing (stdout, stderr) operation takes longer than
          # self.timeout, we will get a queue entry for that.
          # We still use a "system"-timeout, i.e.
          # `ThreadedRunner.process_check_interval`, to check whether the
          # process is still running.
          try:
              file_number, state, data = self.output_queue.get(
                  timeout=ThreadedRunner.timeout_resolution
              )
              break
          except Empty:
              if self.check_for_stall() is True:
                  return
              if self.process_timeouts():
                  return
              continue
  
      if state == IOState.process_exit:
          self.remove_process()
          return
  
      if self.write_stdin and file_number == self.process_stdin_fileno:
          # The only data-signal we expect from stdin thread
          # is None, indicating that the thread ended
          assert data is None
          self.remove_file_number(self.process_stdin_fileno)
  
      elif self.catch_stderr or self.catch_stdout:
          if data is None:
              # Received an EOF for stdout or stderr.
              self.remove_file_number(file_number)
          else:
              # Call the protocol handler for data
              assert isinstance(data, bytes)
              self.last_touched[file_number] = time.time()
              self.protocol.pipe_data_received(
                  self.fileno_mapping[file_number], data
              )
  
imports: from __future__ import annotations
  import enum
  import logging
  import subprocess
  import threading
  import time
  from collections import deque
  from collections.abc import Generator
  from queue import Empty, Queue
  from subprocess import Popen
  from typing import IO, Any, Optional
  from datalad.utils import on_windows
  from .exception import CommandError
  from .protocol import GeneratorMixIn, WitlessProtocol
  from .runnerthreads import IOState, ReadThread, WaitThread, WriteThread, _try_close
label_desc: Use image node_id 15 for calling the ThreadedRunner obj's underlying member method code with example usage: obj.process_queue() without return types