2 * Copyright 2012-2014, Rene Gollent, rene@gollent.com.
3 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
9 #include <AutoDeleter.h>
10 #include <AutoLocker.h>
13 // pragma mark - JobKey
21 // pragma mark - SimpleJobKey
24 SimpleJobKey::SimpleJobKey(const void* object
, uint32 type
)
32 SimpleJobKey::SimpleJobKey(const SimpleJobKey
& other
)
41 SimpleJobKey::HashValue() const
43 return (size_t)(addr_t
)object
^ (size_t)type
;
48 SimpleJobKey::operator==(const JobKey
& other
) const
50 const SimpleJobKey
* otherKey
= dynamic_cast<const SimpleJobKey
*>(&other
);
51 return otherKey
!= NULL
&& object
== otherKey
->object
52 && type
== otherKey
->type
;
57 SimpleJobKey::operator=(const SimpleJobKey
& other
)
59 object
= other
.object
;
65 // #pragma mark - JobListener
68 JobListener::~JobListener()
74 JobListener::JobStarted(Job
* job
)
80 JobListener::JobDone(Job
* job
)
86 JobListener::JobWaitingForInput(Job
* job
)
92 JobListener::JobFailed(Job
* job
)
98 JobListener::JobAborted(Job
* job
)
103 // #pragma mark - Job
109 fState(JOB_STATE_UNSCHEDULED
),
111 fWaitStatus(JOB_DEPENDENCY_NOT_FOUND
),
123 Job::WaitFor(const JobKey
& key
)
125 return fWorker
->WaitForJob(this, key
);
130 Job::WaitForUserInput()
132 return fWorker
->WaitForUserInput(this);
137 Job::SetDescription(const char* format
, ...)
140 va_start(args
, format
);
141 fDescription
.SetToFormatVarArgs(format
, args
);
146 Job::SetWorker(Worker
* worker
)
153 Job::SetState(job_state state
)
160 Job::SetDependency(Job
* job
)
167 Job::SetWaitStatus(job_wait_status status
)
169 fWaitStatus
= status
;
170 switch (fWaitStatus
) {
171 case JOB_DEPENDENCY_ACTIVE
:
172 case JOB_USER_INPUT_WAITING
:
173 fState
= JOB_STATE_WAITING
;
176 fState
= JOB_STATE_ACTIVE
;
183 Job::AddListener(JobListener
* listener
)
185 return fListeners
.AddItem(listener
) ? B_OK
: B_NO_MEMORY
;
190 Job::RemoveListener(JobListener
* listener
)
192 fListeners
.RemoveItem(listener
);
197 Job::NotifyListeners()
199 int32 count
= fListeners
.CountItems();
200 for (int32 i
= count
- 1; i
>= 0; i
--) {
201 JobListener
* listener
= fListeners
.ItemAt(i
);
203 case JOB_STATE_ACTIVE
:
204 listener
->JobStarted(this);
206 case JOB_STATE_WAITING
:
207 if (fWaitStatus
== JOB_USER_INPUT_WAITING
)
208 listener
->JobWaitingForInput(this);
210 case JOB_STATE_SUCCEEDED
:
211 listener
->JobDone(this);
213 case JOB_STATE_FAILED
:
214 listener
->JobFailed(this);
216 case JOB_STATE_ABORTED
:
218 listener
->JobAborted(this);
225 // #pragma mark - Worker
241 if (fWorkerThread
>= 0)
242 wait_for_thread(fWorkerThread
, NULL
);
250 status_t error
= fLock
.InitCheck();
255 error
= fJobs
.Init();
259 // create semaphore for the worker
260 fWorkToDoSem
= create_sem(0, "work to do");
261 if (fWorkToDoSem
< 0)
264 // spawn worker thread
265 fWorkerThread
= spawn_thread(_WorkerLoopEntry
, "worker", B_NORMAL_PRIORITY
,
267 if (fWorkerThread
< 0)
268 return fWorkerThread
;
270 resume_thread(fWorkerThread
);
279 AutoLocker
<Worker
> locker(this);
287 Job
* job
= fJobs
.Clear(true);
288 while (job
!= NULL
) {
289 Job
* nextJob
= job
->fNext
;
290 _AbortJob(job
, false);
295 // let the work thread terminate
296 delete_sem(fWorkToDoSem
);
302 Worker::ScheduleJob(Job
* job
, JobListener
* listener
)
307 BReference
<Job
> jobReference(job
, true);
308 AutoLocker
<Worker
> locker(this);
313 if (listener
!= NULL
) {
314 status_t error
= job
->AddListener(listener
);
319 bool notify
= fUnscheduledJobs
.IsEmpty() && fAbortedJobs
.IsEmpty();
321 job
->SetWorker(this);
322 job
->SetState(JOB_STATE_UNSCHEDULED
);
324 fUnscheduledJobs
.Add(jobReference
.Detach());
327 release_sem(fWorkToDoSem
);
334 Worker::AbortJob(const JobKey
& key
)
336 AutoLocker
<Worker
> locker(this);
338 Job
* job
= fJobs
.Lookup(key
);
342 _AbortJob(job
, true);
347 Worker::GetJob(const JobKey
& key
)
349 AutoLocker
<Worker
> locker(this);
350 return fJobs
.Lookup(key
);
355 Worker::ResumeJob(Job
* job
)
357 AutoLocker
<Worker
> locker(this);
359 for (JobList::Iterator it
= fSuspendedJobs
.GetIterator(); it
.Next();) {
360 if (it
.Current() == job
) {
362 job
->SetState(JOB_STATE_UNSCHEDULED
);
363 fUnscheduledJobs
.Add(job
);
364 release_sem(fWorkToDoSem
);
369 return B_ENTRY_NOT_FOUND
;
374 Worker::HasPendingJobs()
376 AutoLocker
<Worker
> locker(this);
377 return !fJobs
.IsEmpty();
382 Worker::AddListener(const JobKey
& key
, JobListener
* listener
)
384 AutoLocker
<Worker
> locker(this);
386 Job
* job
= fJobs
.Lookup(key
);
388 return B_ENTRY_NOT_FOUND
;
390 return job
->AddListener(listener
);
395 Worker::RemoveListener(const JobKey
& key
, JobListener
* listener
)
397 AutoLocker
<Worker
> locker(this);
399 if (Job
* job
= fJobs
.Lookup(key
))
400 job
->RemoveListener(listener
);
405 Worker::WaitForJob(Job
* waitingJob
, const JobKey
& key
)
407 AutoLocker
<Worker
> locker(this);
409 // don't wait when the game is over anyway
410 if (fTerminating
|| waitingJob
->State() == JOB_STATE_ABORTED
)
411 return JOB_DEPENDENCY_ABORTED
;
413 Job
* job
= fJobs
.Lookup(key
);
415 return JOB_DEPENDENCY_NOT_FOUND
;
417 waitingJob
->SetWaitStatus(JOB_DEPENDENCY_ACTIVE
);
418 waitingJob
->SetDependency(job
);
419 job
->DependentJobs().Add(waitingJob
);
421 return waitingJob
->WaitStatus();
426 Worker::WaitForUserInput(Job
* waitingJob
)
428 AutoLocker
<Worker
> locker(this);
430 if (fTerminating
|| waitingJob
->State() == JOB_STATE_ABORTED
)
431 return B_INTERRUPTED
;
433 waitingJob
->SetWaitStatus(JOB_USER_INPUT_WAITING
);
434 waitingJob
->NotifyListeners();
435 fSuspendedJobs
.Add(waitingJob
);
442 Worker::_WorkerLoopEntry(void* data
)
444 return ((Worker
*)data
)->_WorkerLoop();
449 Worker::_WorkerLoop()
453 // clean up aborted jobs
454 AutoLocker
<Worker
> locker(this);
455 while (Job
* job
= fAbortedJobs
.RemoveHead())
463 Worker::_ProcessJobs()
466 AutoLocker
<Worker
> locker(this);
469 if (fUnscheduledJobs
.IsEmpty() && fAbortedJobs
.IsEmpty()) {
472 status_t error
= acquire_sem(fWorkToDoSem
);
474 if (error
== B_INTERRUPTED
) {
484 // clean up aborted jobs
485 while (Job
* job
= fAbortedJobs
.RemoveHead())
488 // process the next job
489 if (Job
* job
= fUnscheduledJobs
.RemoveHead()) {
490 job
->SetState(JOB_STATE_ACTIVE
);
491 job
->NotifyListeners();
494 status_t error
= job
->Do();
497 if (job
->State() == JOB_STATE_ACTIVE
) {
499 error
== B_OK
? JOB_STATE_SUCCEEDED
: JOB_STATE_FAILED
);
500 } else if (job
->State() == JOB_STATE_WAITING
)
510 Worker::_AbortJob(Job
* job
, bool removeFromTable
)
512 switch (job
->State()) {
513 case JOB_STATE_ABORTED
:
516 case JOB_STATE_UNSCHEDULED
:
517 fUnscheduledJobs
.Remove(job
);
518 fAbortedJobs
.Add(job
);
521 case JOB_STATE_WAITING
:
523 Job
* dependency
= job
->Dependency();
524 if (dependency
!= NULL
)
525 dependency
->DependentJobs().Remove(job
);
526 job
->SetDependency(NULL
);
529 case JOB_STATE_ACTIVE
:
530 case JOB_STATE_FAILED
:
531 case JOB_STATE_SUCCEEDED
:
536 job
->SetState(JOB_STATE_ABORTED
);
543 Worker::_FinishJob(Job
* job
)
545 // wake up dependent jobs
546 if (!job
->DependentJobs().IsEmpty()) {
547 job_wait_status waitStatus
;
548 switch (job
->State()) {
549 case JOB_STATE_ABORTED
:
550 waitStatus
= JOB_DEPENDENCY_ABORTED
;
552 case JOB_STATE_FAILED
:
553 waitStatus
= JOB_DEPENDENCY_FAILED
;
555 case JOB_STATE_SUCCEEDED
:
556 waitStatus
= JOB_DEPENDENCY_SUCCEEDED
;
559 case JOB_STATE_UNSCHEDULED
:
560 case JOB_STATE_WAITING
:
561 case JOB_STATE_ACTIVE
:
563 // should never happen
564 waitStatus
= JOB_DEPENDENCY_NOT_FOUND
;
568 while (Job
* dependentJob
= job
->DependentJobs().RemoveHead()) {
569 dependentJob
->SetDependency(NULL
);
570 dependentJob
->SetWaitStatus(waitStatus
);
571 fUnscheduledJobs
.Add(dependentJob
);
574 release_sem(fWorkToDoSem
);
577 if (job
->State() != JOB_STATE_ABORTED
)
579 job
->NotifyListeners();
580 job
->ReleaseReference();