def dispatch(self):

        # Kill any jobs on the batch system queue from the last time.
        assert len(self.batchSystem.getIssuedBatchJobIDs()) == 0 #Batch system must start with no active jobs!
        logger.info("Checked batch system has no running jobs and no updated jobs")
    
        logger.info("Found %s jobs to start and %i jobs with successors to run",
                    len(self.toilState.updatedJobs), len(self.toilState.successorCounts))
    
        # The main loop in which jobs are scheduled/processed
        
        # Sets up the timing of the jobWrapper rescuing method
        timeSinceJobsLastRescued = time.time()
        # Number of jobs that can not be completed successful after exhausting retries
        totalFailedJobs = 0
        logger.info("Starting the main loop")
        while True:
            # Process jobs that are ready to be scheduled/have successors to schedule
            
            if len(self.toilState.updatedJobs) > 0:
                logger.debug("Built the jobs list, currently have %i jobs to update and %i jobs issued",
                             len(self.toilState.updatedJobs), self.getNumberOfJobsIssued())
    
                for jobWrapper, resultStatus in self.toilState.updatedJobs:
                    #If the jobWrapper has a command it must be run before any successors
                    #Similarly, if the job previously failed we rerun it, even if it doesn't have a command to
                    #run, to eliminate any parts of the stack now completed.
                    if jobWrapper.command != None or resultStatus != 0:
                        if jobWrapper.remainingRetryCount > 0:
                            iJ = IssuedJob(jobWrapper.jobStoreID, jobWrapper.memory, 
                                           jobWrapper.cores, jobWrapper.disk, jobWrapper.preemptable)
                            self.issueJob(iJ)
                        else:
                            totalFailedJobs += 1
                            logger.warn("Job: %s is completely failed", jobWrapper.jobStoreID)
    
                    #There exist successors to run
                    elif len(jobWrapper.stack) > 0:
                        assert len(jobWrapper.stack[-1]) > 0
                        logger.debug("Job: %s has %i successors to schedule",
                                     jobWrapper.jobStoreID, len(jobWrapper.stack[-1]))
                        #Record the number of successors that must be completed before
                        #the jobWrapper can be considered again
                        assert jobWrapper not in self.toilState.successorCounts
                        self.toilState.successorCounts[jobWrapper] = len(jobWrapper.stack[-1])
                        #List of successors to schedule
                        successors = []
                        #For each successor schedule if all predecessors have been completed
                        for successorJobStoreID, memory, cores, disk, preemptable, predecessorID in jobWrapper.stack.pop():
                            #Build map from successor to predecessors.
                            if successorJobStoreID not in self.toilState.successorJobStoreIDToPredecessorJobs:
                                self.toilState.successorJobStoreIDToPredecessorJobs[successorJobStoreID] = []
                            self.toilState.successorJobStoreIDToPredecessorJobs[successorJobStoreID].append(jobWrapper)
                            #Case that the jobWrapper has multiple predecessors
                            if predecessorID != None:
                                #Load the wrapped jobWrapper
                                job2 = self.jobStore.load(successorJobStoreID)
                                #Remove the predecessor from the list of predecessors
                                job2.predecessorsFinished.add(predecessorID)
                                #Checkpoint
                                self.jobStore.update(job2)
                                #If the jobs predecessors have all not all completed then
                                #ignore the jobWrapper
                                assert len(job2.predecessorsFinished) >= 1
                                assert len(job2.predecessorsFinished) <= job2.predecessorNumber
                                if len(job2.predecessorsFinished) < job2.predecessorNumber:
                                    continue
                            successors.append(IssuedJob(successorJobStoreID, memory, cores, disk, preemptable))
                        map(self.issueJob, successors)
    
                    # There are no remaining tasks to schedule within the jobWrapper, but
                    # we schedule it anyway to allow it to be deleted.
    
                    # TODO: An alternative would be simple delete it here and add it to the
                    # list of jobs to process, or (better) to create an asynchronous
                    # process that deletes jobs and then feeds them back into the set
                    # of jobs to be processed
                    else:
                        if jobWrapper.remainingRetryCount > 0:
                            iJ = IssuedJob(jobWrapper.jobStoreID, 
                                           self.config.defaultMemory, self.config.defaultCores,
                                           self.config.defaultDisk, True)
                            self.issueJob(iJ) #We allow this cleanup to potentially occur on a preemptable instance
                            logger.debug("Job: %s is empty, we are scheduling to clean it up", jobWrapper.jobStoreID)
                        else:
                            totalFailedJobs += 1
                            logger.warn("Job: %s is empty but completely failed - something is very wrong", jobWrapper.jobStoreID)
    
                self.toilState.updatedJobs = set() #We've considered them all, so reset
    
            # The exit criterion
    
            if self.getNumberOfJobsIssued() == 0:
                logger.info("Only failed jobs and their dependents (%i total) are remaining, so exiting.", totalFailedJobs)
                return totalFailedJobs
    
            # Gather any new, updated jobs from the batch system
        
            if self.processAnyUpdatedJob(10) == None:  # Asks the batch system to 
                # process a job that has been completed,
                # In the case that there is nothing happening
                # (no updated jobWrapper to gather for 10 seconds)
                # check if their are any jobs that have run too long
                # (see JobBatcher.reissueOverLongJobs) or which
                # have gone missing from the batch system (see JobBatcher.reissueMissingJobs)
                if (time.time() - timeSinceJobsLastRescued >=
                    self.config.rescueJobsFrequency): #We only
                    #rescue jobs every N seconds, and when we have
                    #apparently exhausted the current jobWrapper supply
                    self.reissueOverLongJobs()
                    logger.info("Reissued any over long jobs")
    
                    hasNoMissingJobs = self.reissueMissingJobs()
                    if hasNoMissingJobs:
                        timeSinceJobsLastRescued = time.time()
                    else:
                        timeSinceJobsLastRescued += 60 #This means we'll try again
                        #in a minute, providing things are quiet
                    logger.info("Rescued any (long) missing jobs")
Starts a loop with the batch system to run the Toil workflow
dataset/ETHPy150Open BD2KGenomics/toil/src/toil/batchSystems/jobDispatcher.py/JobDispatcher.dispatch