docs: def _start(self):
          """
          Start a task, which may involve starting zero or more processes.
  
          This is indicated as an internal method because tasks are really
          only ever marked as startable by the configuration.  Any task
          that should be running and is not will be started during regular
          manage() calls.  A task set to run only once will be started only
          if the _stopped attribute is None.
  
          If a task requires another task, it won't be started until the
          required task has started, except if the required task has 'once'
          control, then it won't be started until the 'once' task has
          stopped.
  
          Currently, processes are started via direct fork/exec, with
          stdin/stdout/stderr all redirected from /dev/null.  In future,
          will probably add options to redirect stdout/stderr to syslog
          or files.
  
          Note that processes are intentionally not detached or put in
          separate process groups or terminal groups.  The presumption is
          that "async" and "adopt" tasks will handle this themselves, and
          we need "wait" tasks to not be detached.
  
          Returns True to request a shorter period before the next call,
          False if nothing special is needed.
      """
          log = self._params.get('log', self._discard)
          if self._stopping:
              log.debug("%s task is stopping", self._name)
              return True
          now = time.time()
          conf = self._config_running
          control = self._get(conf.get('control'))
          once = (control in self._legion.once_controls)
  
          #  Tasks with "event" control are immediately marked stopped as if they
          #  ran at start.  This is the only difference between "event" and "once"
          #  controls.
          #
          if control == 'event' and not self._stopped:
              self._stopped = now
          if self._stopped:
              if self._dnr:
                  log.info("Task '%s' stopped and will now be deleted", self._name)
                  self.close()
                  return False
              elif once:
                  log.debug("'%s' task %s exited %s ago", control, self._name, deltafmt(time.time() - self._stopped))
                  return False
              else:
                  log.debug("Restarting %s, task was stopped %s ago",
                              self._name, deltafmt(time.time() - self._stopped))
                  self._reset_state()
  
          start_delay = self._get(conf.get('start_delay'))
          if start_delay:
              try:
                  start_delay = int(start_delay)
              except Exception as e:
                  log.error("Task '%s' has invalid start_delay '%s'", self._name, start_delay)
                  start_delay = 0
          else:
              start_delay = 0
          if self._starting and not self._started:
              if now > self._starting + start_delay:
                  log.info("%s task marked started after %s", self._name, deltafmt(now - self._starting))
                  self._mark_started()
                  return False
              log.debug("%s task has been starting for %s of %s",
                      self._name, deltafmt(now - self._starting), deltafmt(start_delay))
              return True
  
          #  Check the required state to ensure dependencies have been started.  In the case of
          #  'once' controls, the dependency must have already stopped, otherwise it must have
          #  started.
          #
          if self._started and control != 'suspend':
              log.debug("Task '%s' already started, skipping requires-check", self._name)
          else:
              for req in self.get_requires():
                  if req._config_running.get('control') == 'once':
                      if not req._stopped:
                          if self._last_message + repetition_limit < time.time():
                              log.info("Task '%s' is waiting on '%s' to complete", self._name, req._name)
                              self._last_message = now
                          return True
                  else:
                      if not req._started:
                          if self._last_message + repetition_limit < time.time():
                              log.info("Task '%s' is waiting on '%s' to start", self._name, req._name)
                              self._last_message = now
                          return True
  
          self._last_message = 0
          if once:
              #  "once" processes are immediately marked as stopping.
              #
              self._stopping = now
  
          try:
              start_command = None
              if 'commands' in conf:
                  start_command = self._get_list(conf['commands'].get('start'))
              if not start_command:
                  raise TaskError(self._name, "No 'start' command in task configuration")
              if not isinstance(start_command, list):
                  start_command = list(start_command)
  
              if control != 'suspend':
                  needed = self._get(conf.get('count'), default=1)
                  running = len(self.get_pids())
                  if needed < running:
                      self._shrink(needed, running)
                      return False
                  elif needed == running:
                      log.debug("all %d needed process%s running", running, ses(running, 'es'))
                      return False
  
              self._starting = now
              if not start_delay:
                  self._mark_started()
  
              if control == 'suspend':
                  if not self._suspended:
                      log.debug("%s just moved to %r", self._name, control)
                      running = len(self.get_pids())
                      if running > 0:
                          log.debug("%s now %r, stopping running processes", self._name, control)
                          self._shrink(0, running)
                      else:
                          log.debug("%s is %r control, skipping process startup", self._name, control)
                          self._suspended = now
                  return False
              else:
                  if self._suspended:
                      log.debug("%s just moved to %r", self._name, control)
                  self._suspended = None
  
              log.debug("Found %d running, %d needed, starting %d", running, needed, needed-running)
              started = 0
              for instance in range(needed):
                  if instance < len(self._proc_state):
                      proc = self._proc_state[instance]
                      if proc.pid is not None:
                          log.debug("%s instance %d already started", self._name, instance)
                          continue
                      if proc.started is None:
                          proc.started = now
                      last_start_delta = now - proc.started
                      if last_start_delta < 0:
                          #  This can happen when the system clock is manually set.  As one
                          #  of the goals here is to restart ntpd when it dies due to exceeding
                          #  the panic threshold (1000 seconds), go ahead and mark the time
                          #  as now so the task restart will only be delayed slightly longer
                          #  than normal.
                          #
                          log.warning("Time flowed backwards, resetting %s instance %d start time",
                                  self._name, instance)
                          proc.started = now
                          continue
                      if last_start_delta < reexec_delay:
                          log.debug("%s instance %d restart skipped, last attempt %s ago",
                                  self._name, instance, deltafmt(last_start_delta))
                          continue
                  else:
                      log.debug("%s growing instance %d", self._name, instance)
                      self._proc_state.append(ProcessState())
                      proc = self._proc_state[instance]
  
                  pid = _exec_process(start_command, self._context, instance=instance, log=log)
                  log.debug("Forked pid %d for '%s', %d of %d now running",
                              pid, self._name, len(self.get_pids()), needed)
                  self._legion.proc_add(event_target(self, 'proc_exit', key=pid, log=log))
                  proc.pid = pid
                  proc.started = now
                  started += 1
  
              log.info("Task %s: %d process%s scheduled to start%s",
                      self._name, started, ses(started, 'es'),
                      (' with time limit %s' % (deltafmt(self._limit - now),)) if self._limit else '')
          except Exception as e:
              log.error("Failed to start task '%s' -- %s", self._name, e, exc_info=log.isEnabledFor(logging.DEBUG))
          return False