JobQueue: fixed leak, notification, added Pop() variant.

* Was leaking fQueuedJobs on destruction.
* fHaveRunnableJobSem implementation was not completed; it was never
  released.
* Added Pop() variant that is a bit more flexible, and allows for a
  timeout as well as waiting even when the queue is empty, and can
  return a status code.
This commit is contained in:
Axel Dörfler 2015-05-31 19:03:34 +02:00
parent 3375ee66bc
commit 6ff95509c2
2 changed files with 51 additions and 18 deletions

View File

@ -30,8 +30,12 @@ public:
// gives up ownership // gives up ownership
BJob* Pop(); BJob* Pop();
status_t Pop(bigtime_t timeout, bool returnWhenEmpty,
BJob** _job);
// caller owns job // caller owns job
size_t CountJobs() const;
void Close(); void Close();
private: private:
@ -49,7 +53,7 @@ private:
void _RequeueDependantJobsOf(BJob* job); void _RequeueDependantJobsOf(BJob* job);
void _RemoveDependantJobsOf(BJob* job); void _RemoveDependantJobsOf(BJob* job);
BLocker fLock; mutable BLocker fLock;
uint32 fNextTicketNumber; uint32 fNextTicketNumber;
JobPriorityQueue* fQueuedJobs; JobPriorityQueue* fQueuedJobs;
sem_id fHaveRunnableJobSem; sem_id fHaveRunnableJobSem;

View File

@ -3,6 +3,7 @@
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* Axel Dörfler <axeld@pinc-software.de>
* Oliver Tappe <zooey@hirschkaefer.de> * Oliver Tappe <zooey@hirschkaefer.de>
*/ */
@ -50,6 +51,9 @@ class JobQueue::JobPriorityQueue
}; };
// #pragma mark -
JobQueue::JobQueue() JobQueue::JobQueue()
: :
fLock("job queue"), fLock("job queue"),
@ -61,6 +65,8 @@ JobQueue::JobQueue()
JobQueue::~JobQueue() JobQueue::~JobQueue()
{ {
Close();
delete fQueuedJobs;
} }
@ -89,6 +95,8 @@ JobQueue::AddJob(BJob* job)
} }
BJob::Private(*job).SetTicketNumber(fNextTicketNumber++); BJob::Private(*job).SetTicketNumber(fNextTicketNumber++);
job->AddStateListener(this); job->AddStateListener(this);
if (job->IsRunnable())
release_sem(fHaveRunnableJobSem);
} }
return B_OK; return B_OK;
@ -137,35 +145,54 @@ JobQueue::JobFailed(BJob* job)
BJob* BJob*
JobQueue::Pop() JobQueue::Pop()
{
BJob* job;
if (Pop(B_INFINITE_TIMEOUT, true, &job) == B_OK)
return job;
return NULL;
}
status_t
JobQueue::Pop(bigtime_t timeout, bool returnWhenEmpty, BJob** _job)
{ {
BAutolock lock(&fLock); BAutolock lock(&fLock);
if (lock.IsLocked()) { if (lock.IsLocked()) {
JobPriorityQueue::iterator head = fQueuedJobs->begin(); while (true) {
if (head == fQueuedJobs->end()) JobPriorityQueue::iterator head = fQueuedJobs->begin();
return NULL; if (head != fQueuedJobs->end()) {
while (!(*head)->IsRunnable()) { if ((*head)->IsRunnable()) {
// we need to wait until a job becomes runnable *_job = *head;
fQueuedJobs->erase(head);
return B_OK;
}
} else if (returnWhenEmpty)
return B_ENTRY_NOT_FOUND;
// we need to wait until a job becomes available/runnable
status_t result; status_t result;
do { do {
lock.Unlock(); lock.Unlock();
result = acquire_sem(fHaveRunnableJobSem); result = acquire_sem_etc(fHaveRunnableJobSem, 1,
B_RELATIVE_TIMEOUT, timeout);
if (!lock.Lock()) if (!lock.Lock())
return NULL; return B_ERROR;
} while (result == B_INTERRUPTED); } while (result == B_INTERRUPTED);
if (result != B_OK) if (result != B_OK)
return NULL; return result;
// fetch current head, it must be runnable now
head = fQueuedJobs->begin();
if (head == fQueuedJobs->end())
return NULL;
} }
BJob* job = *head;
fQueuedJobs->erase(head);
return job;
} }
return NULL; return B_ERROR;
}
size_t
JobQueue::CountJobs() const
{
BAutolock locker(fLock);
return fQueuedJobs->size();
} }
@ -222,6 +249,8 @@ JobQueue::_RequeueDependantJobsOf(BJob* job)
dependantJob->RemoveDependency(job); dependantJob->RemoveDependency(job);
try { try {
fQueuedJobs->insert(dependantJob); fQueuedJobs->insert(dependantJob);
if (dependantJob->IsRunnable())
release_sem(fHaveRunnableJobSem);
} catch (...) { } catch (...) {
} }
} }