diff options
Diffstat (limited to 'src/scons/Job.py')
-rw-r--r-- | src/scons/Job.py | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/src/scons/Job.py b/src/scons/Job.py new file mode 100644 index 0000000..6da7e40 --- /dev/null +++ b/src/scons/Job.py @@ -0,0 +1,163 @@ +"""scons.Job + +This module defines the Serial and Parallel classes that execute tasks to +complete a build. + +""" + +__revision__ = "Job.py __REVISION__ __DATE__ __DEVELOPER__" + +class Serial: + """This class is used to execute tasks in series, and is more efficient + than Parallel, but is only appropriate for non-parallel builds. Only + one instance of this class should be in existence at a time. + + This class is not thread safe. + """ + + def __init__(self, taskmaster): + """Create a new serial job given a taskmaster. + + The taskmaster's next_task() method should return the next task that + needs to be executed, or None if there are no more tasks. The + taskmaster's executed() method will be called for each task when it is + finished being executed. The taskmaster's is_blocked() method will not + be called. + """ + + self.taskmaster = taskmaster + + def start(self): + + """Start the job. This will begin pulling tasks from the taskmaster + and executing them, and return when there are no more tasks. """ + + while 1: + task = self.taskmaster.next_task() + + if task is None: + break + + task.execute() + self.taskmaster.executed(task) + + def stop(self): + """Serial jobs are always finished when start() returns, so there + is nothing to do here""" + + pass + + def wait(self): + """Serial jobs are always finished when start() returns, so there + is nothing to do here""" + pass + + +# The will hold a condition variable once the first parallel task +# is created. +cv = None + +class Parallel: + """This class is used to execute tasks in parallel, and is less + efficient than Serial, but is appropriate for parallel builds. Create + an instance of this class for each job or thread you want. + + This class is thread safe. + """ + + + def __init__(self, taskmaster): + + """Create a new parallel job given a taskmaster. Multiple jobs will + be using the taskmaster in parallel, but all method calls to taskmaster + methods are serialized by the jobs themselves. + + The taskmaster's next_task() method should return the next task + that needs to be executed, or None if there are no more tasks. The + taskmaster's executed() method will be called for each task when it + is finished being executed. The taskmaster's is_blocked() method + should return true iff there are more tasks, but they can't be + executed until one or more other tasks have been + executed. next_task() will be called iff is_blocked() returned + false. + + Note: calls to taskmaster are serialized, but calls to execute() on + distinct tasks are not serialized, because that is the whole point + of parallel jobs: they can execute multiple tasks + simultaneously. """ + + global cv + + # import threading here so that everything in the Job module + # but the Parallel class will work if the interpreter doesn't + # support threads + import threading + + self.taskmaster = taskmaster + self.thread = threading.Thread(None, self.__run) + self.stop_running = 0 + + if cv is None: + cv = threading.Condition() + + def start(self): + """Start the job. This will spawn a thread that will begin pulling + tasks from the task master and executing them. This method returns + immediately and doesn't wait for the jobs to be executed. + + To stop the job, call stop(). + To wait for the job to finish, call wait(). + """ + self.thread.start() + + def stop(self): + """Stop the job. This will cause the job to finish after the + currently executing task is done. A job that has been stopped can + not be restarted. + + To wait for the job to finish, call wait(). + """ + self.stop_running = 1 + + def wait(self): + """Wait for the job to finish. A job is finished when either there + are no more tasks or the job has been stopped and it is no longer + executing a task. + + This method should only be called after start() has been called. + + To stop the job, call stop(). + """ + self.thread.join() + + def __run(self): + """private method that actually executes the tasks""" + + cv.acquire() + + try: + + while 1: + while self.taskmaster.is_blocked(): + cv.wait(None) + + task = self.taskmaster.next_task() + + if task == None or self.stop_running: + break + + cv.release() + task.execute() + cv.acquire() + + self.taskmaster.executed(task) + + if not self.taskmaster.is_blocked(): + cv.notifyAll() + + finally: + cv.release() + + + + |