def __init__(self, strategy): assert isinstance(strategy, Strategy) self.howToSchedule = strategy # timer listeners self.cpu = CPU() # that's what we want to manage self.processManager = ProcessManager() # is responsible for managing processes self.ea_queues = [EAQueue()] * 1 self.ready_queue = list() # hold only processes waiting for the cpu self.init_process = None # you have to initialize the scheduler first self._loop_counter = 0 # count how many time the run-method was called # every process needs the same quantum at beginning of the run try: self.quantum = strategy.__getattribute__("quantum") except AttributeError: self.quantum = 4 self.processManager.setQuantumForEveryProcess( self.quantum) # TODO:move to run-method / assuming here: processes are created before scheduler instantiates try: self.timeslice = strategy.__getattribute__("timeslice") # make timeslice public except AttributeError: self.timeslice = 10 SystemTimer().timeunit = self.timeslice SystemTimer().register(self) # register at system clock
class Scheduler(TimerListener): def __init__(self, strategy): assert isinstance(strategy, Strategy) self.howToSchedule = strategy # timer listeners self.cpu = CPU() # that's what we want to manage self.processManager = ProcessManager() # is responsible for managing processes self.ea_queues = [EAQueue()] * 1 self.ready_queue = list() # hold only processes waiting for the cpu self.init_process = None # you have to initialize the scheduler first self._loop_counter = 0 # count how many time the run-method was called # every process needs the same quantum at beginning of the run try: self.quantum = strategy.__getattribute__("quantum") except AttributeError: self.quantum = 4 self.processManager.setQuantumForEveryProcess( self.quantum) # TODO:move to run-method / assuming here: processes are created before scheduler instantiates try: self.timeslice = strategy.__getattribute__("timeslice") # make timeslice public except AttributeError: self.timeslice = 10 SystemTimer().timeunit = self.timeslice SystemTimer().register(self) # register at system clock def popFromReadyQueue(self): """ get the next process from the ready queue. The state belongs in state ready. I :return: PCB """ try: return self.ready_queue.pop(0) except IndexError: raise IndexError("the schedulers ready queue is empty") def addToEAQueue(self, pcb, i=0): print "add process to EAQueue%i %s" % (i, pcb) self.ea_queues[i].append(pcb) def notify(self, timestamp): """an event occurred. On every event, we have to decide if we dispatch the running process or not""" print "Scheduler notified after a step of", timestamp if self.schedule(): # there is still something to do SystemTimer().tick() else: print "FINISHED" def initialize(self, start_pcb_str): """ initialize the scheduler by defining the first process to run. :param start_pcb_str: name of the initial process """ self.init_process = self.processManager.getProcessByName(start_pcb_str) def run(self): """ start the scheduler begin with initialisation of the ready queue :return: """ if self.init_process is None: raise RuntimeError("No init process found! please initialise first") else: # on the first run, the initial process is pushed to the ready queue self.addToMatchingQueue(self.init_process) # init fill the ready queue self.schedule() # init process is now in CPU, time is 0 SystemTimer().tick() # do the first tick def addToMatchingQueue(self, pcb): """ append pcb to right queue there are two types of queue: ready queues and waiting queues (aka. EAQueues) :param pcb: pcb to push to queue :return: :raise IndexError: if pcb's Workplan is empty """ if isinstance(pcb.process.workplan.head(), Wait): # waiting section means ea queue: self.addToEAQueue(pcb) elif isinstance(pcb.process.workplan.head(), Work): self.addToReadyQueue(pcb) elif isinstance(pcb.process.workplan.head(), Launch): # interesting! now we have a launch. We push the Included process to the queue! print "trying to launch", pcb.process.workplan.head().action self.addToMatchingQueue(pcb.process.workplan.head().action) # the process to launch is stored in the action else: # these sections are not made for queues pass def schedule(self): # check the ea queues if there are finished processes. if so, append them to the queues finished_waiting_processes = self.__check_ea_queues() for p in finished_waiting_processes: self.addToMatchingQueue(p) # active = self.processManager.getActiveProcess() active = self.cpu.running_process try: next_section = active.process.workplan.head() except: pass else: if isinstance(next_section, Wait): # process will enter wait section: dispatch try: next_from_ready_queue = self.ready_queue.pop() except IndexError: # ready queue empyt next_from_ready_queue = None waiting_pcb = self.cpu.dispatch(next_from_ready_queue) self.addToMatchingQueue(waiting_pcb) active = self.cpu.running_process # notwendig, da dispatch ausgefuehrt wurde if isinstance(next_section, Launch): # process will launch another process self.addToMatchingQueue(active) # collect information about resources sum_waiting_pcbs = sum(map(lambda x: len(x), self.ea_queues)) # Sum of all PCBs in EAQueues readyq_empty = True if len(self.ready_queue) == 0 else False # ready queue empty? if not active and readyq_empty: # cpu leer, runq leer if sum_waiting_pcbs > 0: # but there are still waiting processes next_run = True else: next_run = False # and no more processes in ea_queue elif not active and not readyq_empty: # cpu leer, runq voll: dispatchen und nochmal laufen ready = self.ready_queue.pop() ready.refill_quantum() # refill quantum for next process in cpu self.cpu.dispatch(ready) # update system timer work_duration = ready.process.workplan.head().duration SystemTimer().next_tick_in(work_duration) next_run = True elif active and readyq_empty: # cpu voll, runq leer # there is no readyq_empty process... # reschedule: process in cpu belongs in cpu print 'reschedule process' if self.cpu.running_process.quantum == 0: # quantum gets refilled, if process is allowed to run again and the quantum was already consumed self.cpu.running_process.refill_quantum() next_run = True elif active and not readyq_empty: # cpu voll, runq voll next_run = True if SystemTimer().next_temp_timeunit != 0: # we need this distinction because this happens in 2 steps... strategy_result = self.howToSchedule.schedule(self) if strategy_result is not active: # neuer process old = self.cpu.dispatch(strategy_result) active = strategy_result # the new active process is the strategy result self.addToMatchingQueue(old) else: # unefined state raise RuntimeError("undefined scheduling state") self._loop_counter += 1 # count the scheduling-steps return next_run def __check_ea_queues(self): """ check if there are finished processes in the queue :return list: list of all ready pcbs """ finished_waiting_processes = [] for ea_q in self.ea_queues: p = ea_q.pickup_ready_processes() finished_waiting_processes.extend(p) return finished_waiting_processes def addToReadyQueue(self, pcb): """ add process to ready queue :param pcb: pcb to be added """ pcb.setReady() self.__maintain_process_history(pcb) print "add process to ready queue:", pcb # if rescheduled because of time quantum, add at the end, if it's because of preemption, add to the beginning self.howToSchedule.addToReadyQueue(scheduler=self, pcb=pcb) return True def __maintain_process_history(self, pcb): """ usually, the history is maintained in the doWork()-Method in the Process-Class. However the Ready-Section must be maintained here. The Ready-Section is not part of the regular workplan. """ # if len(pcb.process.history.plan) == 0: # ready_time = 0 # else: # ready_time = SystemTimer().next_temp_timeunit # @TODO: fix this ugly method... ready_time = 0 new_history_section = Ready(ready_time) new_history_section.starting_at = SystemTimer().timecounter new_history_section.ending_at = SystemTimer().timecounter + ready_time pcb.process.history.insert(new_history_section, i=len(pcb.process.history.plan))