class_docstr: Execute workflow with a distribution engine
  
  Combinations of ``proc_done`` and ``proc_pending``:
  
  +------------+---------------+--------------------------------+
  | proc_done  | proc_pending  | outcome                        |
  +============+===============+================================+
  | True       | False         | Process is finished            |
  +------------+---------------+--------------------------------+
  | True       | True          | Process is currently being run |
  +------------+---------------+--------------------------------+
  | False      | False         | Process is queued              |
  +------------+---------------+--------------------------------+
  | False      | True          | INVALID COMBINATION            |
  +------------+---------------+--------------------------------+
  
  Attributes
  ----------
  procs : :obj:`list`
      list (N) of underlying interface elements to be processed
  proc_done : :obj:`numpy.ndarray`
      a boolean numpy array (N,) signifying whether a process has been
      submitted for execution
  proc_pending : :obj:`numpy.ndarray`
      a boolean numpy array (N,) signifying whether a
      process is currently running.
  depidx : :obj:`numpy.matrix`
      a boolean matrix (NxN) storing the dependency structure across
      processes. Process dependencies are derived from each column.
class_docstr_tok: ["Execute","workflow","with","a","distribution","engine","Combinations","of","``","proc_done","``","and","``","proc_pending","``",":","+","--","--","--","--","--","--","+","--","--","--","--","--","--","--","-+","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","+","|","proc_done","|","proc_pending","|","outcome","|","+============+===============+================================+","|","True","|","False","|","Process","is","finished","|","+","--","--","--","--","--","--","+","--","--","--","--","--","--","--","-+","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","+","|","True","|","True","|","Process","is","currently","being","run","|","+","--","--","--","--","--","--","+","--","--","--","--","--","--","--","-+","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","+","|","False","|","False","|","Process","is","queued","|","+","--","--","--","--","--","--","+","--","--","--","--","--","--","--","-+","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","+","|","False","|","True","|","INVALID","COMBINATION","|","+","--","--","--","--","--","--","+","--","--","--","--","--","--","--","-+","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","--","+","Attributes","--","--","--","--","--","procs",":",":","obj",":","`","list","`","list","(","N",")","of","underlying","interface","elements","to","be","processed","proc_done",":",":","obj",":","`","numpy.ndarray","`","a","boolean","numpy","array","(","N",",",")","signifying","whether","a","process","has","been","submitted","for","execution","proc_pending",":",":","obj",":","`","numpy.ndarray","`","a","boolean","numpy","array","(","N",",",")","signifying","whether","a","process","is","currently","running",".","depidx",":",":","obj",":","`","numpy.matrix","`","a","boolean","matrix","(","NxN",")","storing","the","dependency","structure","across","processes",".","Process","dependencies","are","derived","from","each","column","."]
docstr: Sends jobs to workers
docstr_tok: ["Sends","jobs","to","workers"]
code: def _send_procs_to_workers(self, updatehash=False, graph=None):
      """
      Sends jobs to workers
      """
  
      while not np.all(self.proc_done):
          num_jobs = len(self.pending_tasks)
          if np.isinf(self.max_jobs):
              slots = None
          else:
              slots = max(0, self.max_jobs - num_jobs)
          logger.debug("Slots available: %s", slots)
          if (num_jobs >= self.max_jobs) or (slots == 0):
              break
  
          # Check if a job is available (jobs with all dependencies run)
          # https://github.com/nipy/nipype/pull/2200#discussion_r141605722
          jobids = np.nonzero(
              ~self.proc_done & (self.depidx.sum(0) == 0)
          )[1]
  
          if len(jobids) > 0:
              # send all available jobs
              logger.info(
                  "Pending[%d] Submitting[%d] jobs Slots[%s]",
                  num_jobs,
                  len(jobids[:slots]),
                  slots or "inf",
              )
  
              for jobid in jobids[:slots]:
                  if isinstance(self.procs[jobid], MapNode):
                      try:
                          num_subnodes = self.procs[
                              jobid
                          ].num_subnodes()
                      except Exception:
                          self._clean_queue(jobid, graph)
                          self.proc_pending[jobid] = False
                          continue
                      if num_subnodes > 1:
                          submit = self._submit_mapnode(jobid)
                          if not submit:
                              continue
                  # change job status in appropriate queues
                  self.proc_done[jobid] = True
                  self.proc_pending[jobid] = True
                  # Send job to task manager and add to pending tasks
                  logger.info(
                      "Submitting: %s ID: %d", self.procs[jobid], jobid
                  )
                  if self._status_callback:
                      self._status_callback(self.procs[jobid], "start")
  
                  if not self._local_hash_check(jobid, graph):
                      if self.procs[jobid].run_without_submitting:
                          logger.debug(
                              "Running node %s on master thread",
                              self.procs[jobid],
                          )
                          try:
                              self.procs[jobid].run()
                          except Exception:
                              self._clean_queue(jobid, graph)
                          self._task_finished_cb(jobid)
                          self._remove_node_dirs()
                      else:
                          tid = self._submit_job(
                              deepcopy(self.procs[jobid]),
                              updatehash=updatehash,
                          )
                          if tid is None:
                              self.proc_done[jobid] = False
                              self.proc_pending[jobid] = False
                          else:
                              self.pending_tasks.insert(0, (tid, jobid))
                  logger.info(
                      "Finished submitting: %s ID: %d",
                      self.procs[jobid],
                      jobid,
                  )
          else:
              break
  
imports: import sys
  from copy import deepcopy
  from glob import glob
  import os
  import shutil
  from time import sleep, time
  from traceback import format_exception
  import numpy
  from ...None import logging
  from ...utils.misc import str2bool
  from ..engine.utils import topological_sort, load_resultfile
  from ..engine import MapNode
  from .tools import report_crash, report_nodes_not_run, create_pyscript
label_desc: Use image node_id 11 for calling the DistributedPluginBase obj's underlying member method code with example usage: obj._send_procs_to_workers(updatehash, graph) without return types