summaryrefslogtreecommitdiffstats
path: root/src/scons/Job.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/scons/Job.py')
-rw-r--r--src/scons/Job.py97
1 files changed, 65 insertions, 32 deletions
diff --git a/src/scons/Job.py b/src/scons/Job.py
index 5e542d4..09bfcb8 100644
--- a/src/scons/Job.py
+++ b/src/scons/Job.py
@@ -24,7 +24,7 @@ class Jobs:
if num > 1:
self.jobs = []
for i in range(num):
- self.jobs.append(Parallel(taskmaster))
+ self.jobs.append(Parallel(taskmaster, self))
else:
self.jobs = [Serial(taskmaster)]
@@ -61,28 +61,35 @@ class Serial:
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.
- """
+ 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 successfully executed or failed() will be called if it failed to
+ execute (e.g. execute() raised an exception). 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. """
+ and executing them, and return when there are no more tasks. If a task
+ fails to execute (i.e. execute() raises an exception), then the job will
+ stop."""
while 1:
task = self.taskmaster.next_task()
if task is None:
break
-
- task.execute()
- self.taskmaster.executed(task)
+
+ try:
+ task.execute()
+ except:
+ self.taskmaster.failed(task)
+ return
+ else:
+ self.taskmaster.executed(task)
def stop(self):
"""Serial jobs are always finished when start() returns, so there
@@ -109,20 +116,21 @@ class Parallel:
"""
- 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.
+ def __init__(self, taskmaster, jobs):
+ """Create a new parallel job given a taskmaster, and a Jobs instance.
+ 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.
+ is successfully executed or failed() will be called if the task
+ failed to execute (i.e. execute() raised an exception). 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
@@ -137,6 +145,7 @@ class Parallel:
import threading
self.taskmaster = taskmaster
+ self.jobs = jobs
self.thread = threading.Thread(None, self.__run)
self.stop_running = 0
@@ -148,6 +157,9 @@ class Parallel:
tasks from the task master and executing them. This method returns
immediately and doesn't wait for the jobs to be executed.
+ If a task fails to execute (i.e. execute() raises an exception),
+ all jobs will be stopped.
+
To stop the job, call stop().
To wait for the job to finish, call wait().
"""
@@ -160,8 +172,13 @@ class Parallel:
To wait for the job to finish, call wait().
"""
- self.stop_running = 1
+ cv.acquire()
+ self.stop_running = 1
+ # wake up the sleeping jobs so this job will end as soon as possible:
+ cv.notifyAll()
+ cv.release()
+
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
@@ -181,23 +198,39 @@ class Parallel:
try:
while 1:
- while self.taskmaster.is_blocked():
+ while self.taskmaster.is_blocked() and not self.stop_running:
cv.wait(None)
+ # check this before calling next_task(), because
+ # this job may have been stopped because of a build
+ # failure:
+ if self.stop_running:
+ break
+
task = self.taskmaster.next_task()
- if task == None or self.stop_running:
+ if task == None:
break
cv.release()
- task.execute()
- cv.acquire()
-
- self.taskmaster.executed(task)
-
- if not self.taskmaster.is_blocked():
- cv.notifyAll()
-
+ try:
+ try:
+ task.execute()
+ finally:
+ cv.acquire()
+ except:
+ self.taskmaster.failed(task)
+ # stop all jobs since there was a failure:
+ # (this will wake up any waiting jobs, so
+ # it isn't necessary to explicitly wake them
+ # here)
+ self.jobs.stop()
+ else:
+ self.taskmaster.executed(task)
+
+ if not self.taskmaster.is_blocked():
+ cv.notifyAll()
+
finally:
cv.release()