headers/bsd: Add sys/queue.h.
[haiku.git] / src / kits / debugger / util / Worker.cpp
blob0a184bdfbca95caf944a986d5176b0bbc0249b79
1 /*
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.
5 */
7 #include "Worker.h"
9 #include <AutoDeleter.h>
10 #include <AutoLocker.h>
13 // pragma mark - JobKey
16 JobKey::~JobKey()
21 // pragma mark - SimpleJobKey
24 SimpleJobKey::SimpleJobKey(const void* object, uint32 type)
26 object(object),
27 type(type)
32 SimpleJobKey::SimpleJobKey(const SimpleJobKey& other)
34 object(other.object),
35 type(other.type)
40 size_t
41 SimpleJobKey::HashValue() const
43 return (size_t)(addr_t)object ^ (size_t)type;
47 bool
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;
56 SimpleJobKey&
57 SimpleJobKey::operator=(const SimpleJobKey& other)
59 object = other.object;
60 type = other.type;
61 return *this;
65 // #pragma mark - JobListener
68 JobListener::~JobListener()
73 void
74 JobListener::JobStarted(Job* job)
79 void
80 JobListener::JobDone(Job* job)
85 void
86 JobListener::JobWaitingForInput(Job* job)
91 void
92 JobListener::JobFailed(Job* job)
97 void
98 JobListener::JobAborted(Job* job)
103 // #pragma mark - Job
106 Job::Job()
108 fWorker(NULL),
109 fState(JOB_STATE_UNSCHEDULED),
110 fDependency(NULL),
111 fWaitStatus(JOB_DEPENDENCY_NOT_FOUND),
112 fListeners(10)
117 Job::~Job()
122 job_wait_status
123 Job::WaitFor(const JobKey& key)
125 return fWorker->WaitForJob(this, key);
129 status_t
130 Job::WaitForUserInput()
132 return fWorker->WaitForUserInput(this);
136 void
137 Job::SetDescription(const char* format, ...)
139 va_list args;
140 va_start(args, format);
141 fDescription.SetToFormatVarArgs(format, args);
145 void
146 Job::SetWorker(Worker* worker)
148 fWorker = worker;
152 void
153 Job::SetState(job_state state)
155 fState = state;
159 void
160 Job::SetDependency(Job* job)
162 fDependency = job;
166 void
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;
174 break;
175 default:
176 fState = JOB_STATE_ACTIVE;
177 break;
182 status_t
183 Job::AddListener(JobListener* listener)
185 return fListeners.AddItem(listener) ? B_OK : B_NO_MEMORY;
189 void
190 Job::RemoveListener(JobListener* listener)
192 fListeners.RemoveItem(listener);
196 void
197 Job::NotifyListeners()
199 int32 count = fListeners.CountItems();
200 for (int32 i = count - 1; i >= 0; i--) {
201 JobListener* listener = fListeners.ItemAt(i);
202 switch (fState) {
203 case JOB_STATE_ACTIVE:
204 listener->JobStarted(this);
205 break;
206 case JOB_STATE_WAITING:
207 if (fWaitStatus == JOB_USER_INPUT_WAITING)
208 listener->JobWaitingForInput(this);
209 break;
210 case JOB_STATE_SUCCEEDED:
211 listener->JobDone(this);
212 break;
213 case JOB_STATE_FAILED:
214 listener->JobFailed(this);
215 break;
216 case JOB_STATE_ABORTED:
217 default:
218 listener->JobAborted(this);
219 break;
225 // #pragma mark - Worker
228 Worker::Worker()
230 fLock("worker"),
231 fWorkerThread(-1),
232 fTerminating(false)
237 Worker::~Worker()
239 ShutDown();
241 if (fWorkerThread >= 0)
242 wait_for_thread(fWorkerThread, NULL);
246 status_t
247 Worker::Init()
249 // check lock
250 status_t error = fLock.InitCheck();
251 if (error != B_OK)
252 return error;
254 // init jobs table
255 error = fJobs.Init();
256 if (error != B_OK)
257 return error;
259 // create semaphore for the worker
260 fWorkToDoSem = create_sem(0, "work to do");
261 if (fWorkToDoSem < 0)
262 return fWorkToDoSem;
264 // spawn worker thread
265 fWorkerThread = spawn_thread(_WorkerLoopEntry, "worker", B_NORMAL_PRIORITY,
266 this);
267 if (fWorkerThread < 0)
268 return fWorkerThread;
270 resume_thread(fWorkerThread);
272 return B_OK;
276 void
277 Worker::ShutDown()
279 AutoLocker<Worker> locker(this);
281 if (fTerminating)
282 return;
284 fTerminating = true;
286 // abort all jobs
287 Job* job = fJobs.Clear(true);
288 while (job != NULL) {
289 Job* nextJob = job->fNext;
290 _AbortJob(job, false);
291 job = nextJob;
295 // let the work thread terminate
296 delete_sem(fWorkToDoSem);
297 fWorkToDoSem = -1;
301 status_t
302 Worker::ScheduleJob(Job* job, JobListener* listener)
304 if (job == NULL)
305 return B_NO_MEMORY;
307 BReference<Job> jobReference(job, true);
308 AutoLocker<Worker> locker(this);
310 if (fTerminating)
311 return B_ERROR;
313 if (listener != NULL) {
314 status_t error = job->AddListener(listener);
315 if (error != B_OK)
316 return error;
319 bool notify = fUnscheduledJobs.IsEmpty() && fAbortedJobs.IsEmpty();
321 job->SetWorker(this);
322 job->SetState(JOB_STATE_UNSCHEDULED);
323 fJobs.Insert(job);
324 fUnscheduledJobs.Add(jobReference.Detach());
326 if (notify)
327 release_sem(fWorkToDoSem);
329 return B_OK;
333 void
334 Worker::AbortJob(const JobKey& key)
336 AutoLocker<Worker> locker(this);
338 Job* job = fJobs.Lookup(key);
339 if (job == NULL)
340 return;
342 _AbortJob(job, true);
346 Job*
347 Worker::GetJob(const JobKey& key)
349 AutoLocker<Worker> locker(this);
350 return fJobs.Lookup(key);
354 status_t
355 Worker::ResumeJob(Job* job)
357 AutoLocker<Worker> locker(this);
359 for (JobList::Iterator it = fSuspendedJobs.GetIterator(); it.Next();) {
360 if (it.Current() == job) {
361 it.Remove();
362 job->SetState(JOB_STATE_UNSCHEDULED);
363 fUnscheduledJobs.Add(job);
364 release_sem(fWorkToDoSem);
365 return B_OK;
369 return B_ENTRY_NOT_FOUND;
373 bool
374 Worker::HasPendingJobs()
376 AutoLocker<Worker> locker(this);
377 return !fJobs.IsEmpty();
381 status_t
382 Worker::AddListener(const JobKey& key, JobListener* listener)
384 AutoLocker<Worker> locker(this);
386 Job* job = fJobs.Lookup(key);
387 if (job == NULL)
388 return B_ENTRY_NOT_FOUND;
390 return job->AddListener(listener);
394 void
395 Worker::RemoveListener(const JobKey& key, JobListener* listener)
397 AutoLocker<Worker> locker(this);
399 if (Job* job = fJobs.Lookup(key))
400 job->RemoveListener(listener);
404 job_wait_status
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);
414 if (job == NULL)
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();
425 status_t
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);
437 return B_OK;
441 /*static*/ status_t
442 Worker::_WorkerLoopEntry(void* data)
444 return ((Worker*)data)->_WorkerLoop();
448 status_t
449 Worker::_WorkerLoop()
451 _ProcessJobs();
453 // clean up aborted jobs
454 AutoLocker<Worker> locker(this);
455 while (Job* job = fAbortedJobs.RemoveHead())
456 _FinishJob(job);
458 return B_OK;
462 void
463 Worker::_ProcessJobs()
465 while (true) {
466 AutoLocker<Worker> locker(this);
468 // wait for next job
469 if (fUnscheduledJobs.IsEmpty() && fAbortedJobs.IsEmpty()) {
470 locker.Unlock();
472 status_t error = acquire_sem(fWorkToDoSem);
473 if (error != B_OK) {
474 if (error == B_INTERRUPTED) {
475 locker.Lock();
476 continue;
478 break;
481 locker.Lock();
484 // clean up aborted jobs
485 while (Job* job = fAbortedJobs.RemoveHead())
486 _FinishJob(job);
488 // process the next job
489 if (Job* job = fUnscheduledJobs.RemoveHead()) {
490 job->SetState(JOB_STATE_ACTIVE);
491 job->NotifyListeners();
493 locker.Unlock();
494 status_t error = job->Do();
495 locker.Lock();
497 if (job->State() == JOB_STATE_ACTIVE) {
498 job->SetState(
499 error == B_OK ? JOB_STATE_SUCCEEDED : JOB_STATE_FAILED);
500 } else if (job->State() == JOB_STATE_WAITING)
501 continue;
503 _FinishJob(job);
509 void
510 Worker::_AbortJob(Job* job, bool removeFromTable)
512 switch (job->State()) {
513 case JOB_STATE_ABORTED:
514 return;
516 case JOB_STATE_UNSCHEDULED:
517 fUnscheduledJobs.Remove(job);
518 fAbortedJobs.Add(job);
519 break;
521 case JOB_STATE_WAITING:
523 Job* dependency = job->Dependency();
524 if (dependency != NULL)
525 dependency->DependentJobs().Remove(job);
526 job->SetDependency(NULL);
527 break;
529 case JOB_STATE_ACTIVE:
530 case JOB_STATE_FAILED:
531 case JOB_STATE_SUCCEEDED:
532 default:
533 break;
536 job->SetState(JOB_STATE_ABORTED);
537 if (removeFromTable)
538 fJobs.Remove(job);
542 void
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;
551 break;
552 case JOB_STATE_FAILED:
553 waitStatus = JOB_DEPENDENCY_FAILED;
554 break;
555 case JOB_STATE_SUCCEEDED:
556 waitStatus = JOB_DEPENDENCY_SUCCEEDED;
557 break;
559 case JOB_STATE_UNSCHEDULED:
560 case JOB_STATE_WAITING:
561 case JOB_STATE_ACTIVE:
562 default:
563 // should never happen
564 waitStatus = JOB_DEPENDENCY_NOT_FOUND;
565 break;
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)
578 fJobs.Remove(job);
579 job->NotifyListeners();
580 job->ReleaseReference();