- When a job needs to go dependent on another job, we no longer use recursion
  to manage the execution stack. Instead the job is simply marked as waiting
  and we execute other jobs with no dependencies in the meantime. When a job
  completes, all dependents are moved back onto the unscheduled list and
  executed as needed.

- Adjustments to ResolveValueNodeJob to handle the now asynchronous nature
  of waiting.
This commit is contained in:
Rene Gollent 2012-07-19 20:21:06 -04:00
parent 0237895604
commit 1f80f2eec3
3 changed files with 28 additions and 25 deletions

View File

@ -1,4 +1,5 @@
/* /*
* Copyright 2012, Rene Gollent, rene@gollent.com.
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@ -523,6 +524,9 @@ ResolveValueNodeValueJob::_ResolveNodeValue()
fValueNode, fValueNode->Name().String(), parentNode); fValueNode, fValueNode->Name().String(), parentNode);
return error; return error;
} }
if (State() == JOB_STATE_WAITING)
return B_OK;
} }
// resolve the node child location, if necessary // resolve the node child location, if necessary
@ -629,6 +633,8 @@ ResolveValueNodeValueJob::_ResolveParentNodeValue(ValueNode* parentNode)
// "Not found" can happen due to a race condition between // "Not found" can happen due to a race condition between
// unlocking the worker and starting to wait. // unlocking the worker and starting to wait.
break; break;
case JOB_DEPENDENCY_ACTIVE:
return B_OK;
case JOB_DEPENDENCY_FAILED: case JOB_DEPENDENCY_FAILED:
case JOB_DEPENDENCY_ABORTED: case JOB_DEPENDENCY_ABORTED:
default: default:

View File

@ -1,4 +1,5 @@
/* /*
* Copyright 2012, Rene Gollent, rene@gollent.com.
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@ -138,6 +139,15 @@ void
Job::SetWaitStatus(job_wait_status status) Job::SetWaitStatus(job_wait_status status)
{ {
fWaitStatus = status; fWaitStatus = status;
switch (fWaitStatus) {
case JOB_DEPENDENCY_ACTIVE:
fState = JOB_STATE_WAITING;
break;
default:
fState = JOB_STATE_ACTIVE;
break;
}
} }
@ -346,18 +356,6 @@ Worker::WaitForJob(Job* waitingJob, const JobKey& key)
waitingJob->SetDependency(job); waitingJob->SetDependency(job);
job->DependentJobs().Add(waitingJob); job->DependentJobs().Add(waitingJob);
// TODO: Continuations would be nice. For the time being we have to use
// recursion. Disadvantages are that we'll use more stack and that aborting
// a job waiting for a dependency won't abort the job before the dependency
// is done.
locker.Unlock();
_ProcessJobs(waitingJob);
locker.Lock();
// ignore the actual wait status when the game is over anyway
if (fTerminating || waitingJob->State() == JOB_STATE_ABORTED)
return JOB_DEPENDENCY_ABORTED;
return waitingJob->WaitStatus(); return waitingJob->WaitStatus();
} }
@ -372,7 +370,7 @@ Worker::_WorkerLoopEntry(void* data)
status_t status_t
Worker::_WorkerLoop() Worker::_WorkerLoop()
{ {
_ProcessJobs(NULL); _ProcessJobs();
// clean up aborted jobs // clean up aborted jobs
AutoLocker<Worker> locker(this); AutoLocker<Worker> locker(this);
@ -384,7 +382,7 @@ Worker::_WorkerLoop()
void void
Worker::_ProcessJobs(Job* waitingJob) Worker::_ProcessJobs()
{ {
while (true) { while (true) {
AutoLocker<Worker> locker(this); AutoLocker<Worker> locker(this);
@ -395,8 +393,10 @@ Worker::_ProcessJobs(Job* waitingJob)
status_t error = acquire_sem(fWorkToDoSem); status_t error = acquire_sem(fWorkToDoSem);
if (error != B_OK) { if (error != B_OK) {
if (error == B_INTERRUPTED) if (error == B_INTERRUPTED) {
locker.Lock();
continue; continue;
}
break; break;
} }
@ -404,13 +404,9 @@ Worker::_ProcessJobs(Job* waitingJob)
} }
// clean up aborted jobs // clean up aborted jobs
while (Job* job = fAbortedJobs.RemoveHead()) { while (Job* job = fAbortedJobs.RemoveHead())
_FinishJob(job); _FinishJob(job);
if (waitingJob != NULL && waitingJob->State() != JOB_STATE_WAITING)
break;
}
// process the next job // process the next job
if (Job* job = fUnscheduledJobs.RemoveHead()) { if (Job* job = fUnscheduledJobs.RemoveHead()) {
job->SetState(JOB_STATE_ACTIVE); job->SetState(JOB_STATE_ACTIVE);
@ -422,12 +418,10 @@ Worker::_ProcessJobs(Job* waitingJob)
if (job->State() == JOB_STATE_ACTIVE) { if (job->State() == JOB_STATE_ACTIVE) {
job->SetState( job->SetState(
error == B_OK ? JOB_STATE_SUCCEEDED : JOB_STATE_FAILED); error == B_OK ? JOB_STATE_SUCCEEDED : JOB_STATE_FAILED);
} } else if (job->State() == JOB_STATE_WAITING)
continue;
_FinishJob(job); _FinishJob(job);
if (waitingJob != NULL && waitingJob->State() != JOB_STATE_WAITING)
break;
} }
} }
} }
@ -492,7 +486,10 @@ Worker::_FinishJob(Job* job)
while (Job* dependentJob = job->DependentJobs().RemoveHead()) { while (Job* dependentJob = job->DependentJobs().RemoveHead()) {
dependentJob->SetDependency(NULL); dependentJob->SetDependency(NULL);
dependentJob->SetWaitStatus(waitStatus); dependentJob->SetWaitStatus(waitStatus);
fUnscheduledJobs.Add(dependentJob);
} }
release_sem(fWorkToDoSem);
} }
if (job->State() != JOB_STATE_ABORTED) if (job->State() != JOB_STATE_ABORTED)

View File

@ -182,7 +182,7 @@ private:
static status_t _WorkerLoopEntry(void* data); static status_t _WorkerLoopEntry(void* data);
status_t _WorkerLoop(); status_t _WorkerLoop();
void _ProcessJobs(Job* waitingJob); void _ProcessJobs();
void _AbortJob(Job* job, bool removeFromTable); void _AbortJob(Job* job, bool removeFromTable);
void _FinishJob(Job* job); void _FinishJob(Job* job);