class_docstr: This controls a job:
  - instantiate each of the tasks
  - sends timeout signal
  - stops all tasks if one of them finishes
  - cleans up the tasks/closes log files when deleted
class_docstr_tok: ["This","controls","a","job",":","-","instantiate","each","of","the","tasks","-","sends","timeout","signal","-","stops","all","tasks","if","one","of","them","finishes","-","cleans","up","the","tasks\/closes","log","files","when","deleted"]
docstr: Waits for all tasks to finish or to time-out.
  
  Returns
  -------
  Sequence[Optional[int]]:
      Exit codes of each task.
      Some tasks might still have not exited, but they will have received the "timed-out" signal.
code: def wait(self, freq: int = 24) -> tp.Sequence[tp.Optional[int]]:
      """Waits for all tasks to finish or to time-out.
  
      Returns
      -------
      Sequence[Optional[int]]:
          Exit codes of each task.
          Some tasks might still have not exited, but they will have received the "timed-out" signal.
      """
      assert self.tasks, "Nothing to do!"
      timeout = freq * self.timeout_s
      almost_timeout = freq * (self.timeout_s - self.signal_delay_s)
  
      # safer to keep a for loop :)
      for step in range(timeout):
          exit_codes = [t.poll() for t in self.tasks]
          if all(e is not None for e in exit_codes):
              return exit_codes
  
          if step == almost_timeout:
              self._forward_signal(LocalJobEnvironment._usr_sig())
  
          time.sleep(1.0 / freq)
      return [t.poll() for t in self.tasks]
  
imports: import os
  import shlex
  import signal
  import subprocess
  import sys
  import time
  import typing
  from pathlib import Path
  from ..core import core, job_environment, logger, utils
  from ..core.core import R
label_desc: Use image node_id 5 for calling the Controller obj's underlying member method code with example usage: obj.wait(freq) and returns: unknown, exit_codes