blob: a75b5cecd25b46cb6e8305d1a646ceb6b06c0119 [file] [log] [blame]
/*
* workqueue.cpp, workqueue class
*
* Copyright (c) 2009-2010 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <workqueue.h>
#include <sys/prctl.h>
WorkQueue::WorkQueue()
{
stop = false;
executing = true;
wait_for_works = false;
works = NULL;
pthread_mutex_init(&wlock, NULL);
pthread_cond_init(&wcond, NULL);
pthread_mutex_init(&executing_lock, NULL);
pthread_cond_init(&executing_wait, NULL);
pthread_cond_init(&paused_wait, NULL);
}
WorkQueue::~WorkQueue()
{
StopWork();
pthread_cond_destroy(&wcond);
pthread_mutex_destroy(&wlock);
pthread_cond_destroy(&paused_wait);
pthread_cond_destroy(&executing_wait);
pthread_mutex_destroy(&executing_lock);
}
int WorkQueue::StartWork(bool executing)
{
this->executing = executing;
pthread_mutex_lock(&wlock);
stop = false;
pthread_mutex_unlock(&wlock);
return Start();
}
void WorkQueue::StopWork(void)
{
/* discard all scheduled works */
pthread_mutex_lock(&wlock);
while (works)
works = __list_delete(works, works);
pthread_mutex_unlock(&wlock);
/* wakeup DoWork() if it's sleeping */
/*
* FIXME
*
* if DoWork() is sleeping, Work()'s called one more time at this moment.
* if DoWork()::wi->Work() called ScheduleWork() (self-rescheduling),
* this function would be sleeping forever at Join() because works list
* never be empty.
*/
ResumeWork();
pthread_mutex_lock(&wlock);
stop = true;
pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
pthread_mutex_unlock(&wlock);
Join();
}
/* it returns when Run() is sleeping at executing_wait or at wcond */
void WorkQueue::PauseWork(void)
{
pthread_mutex_lock(&executing_lock);
executing = false;
/* this prevents deadlock if Run() is sleeping with locking wcond */
if (!wait_for_works)
pthread_cond_wait(&paused_wait, &executing_lock); /* wokeup by Run() */
pthread_mutex_unlock(&executing_lock);
}
void WorkQueue::ResumeWork(void)
{
pthread_mutex_lock(&executing_lock);
executing = true;
pthread_cond_signal(&executing_wait);
pthread_mutex_unlock(&executing_lock);
}
void WorkQueue::Run(void)
{
prctl(PR_SET_NAME, (unsigned long)"IntelHwCodec", 0, 0, 0);
while (true) {
pthread_mutex_lock(&wlock);
if (stop) {
pthread_mutex_unlock(&wlock);
break;
}
if (!works) {
pthread_mutex_lock(&executing_lock);
wait_for_works = true;
/* wake up PauseWork() if it's sleeping */
pthread_cond_signal(&paused_wait);
pthread_mutex_unlock(&executing_lock);
/*
* sleeps until works're available.
* wokeup by ScheduleWork() or FlushWork() or ~WorkQueue()
*/
pthread_cond_wait(&wcond, &wlock);
pthread_mutex_lock(&executing_lock);
wait_for_works = false;
pthread_mutex_unlock(&executing_lock);
}
while (works) {
struct list *entry = works;
WorkableInterface *wi =
static_cast<WorkableInterface *>(entry->data);
works = __list_delete(works, entry);
pthread_mutex_unlock(&wlock);
/*
* 1. if PauseWork() locks executing_lock right before Run() locks
* the lock, Run() sends the paused signal and go to sleep.
* 2. if Run() locks executing_lock first, DoWork() is called and
* PausedWork() waits for paused_wait signal. Run() sends the
* signal during next loop processing or at the end of loop
* in case of works're not available.
*/
pthread_mutex_lock(&executing_lock);
if (!executing) {
pthread_cond_signal(&paused_wait);
pthread_cond_wait(&executing_wait, &executing_lock);
}
pthread_mutex_unlock(&executing_lock);
DoWork(wi);
pthread_mutex_lock(&wlock);
}
pthread_mutex_unlock(&wlock);
}
}
void WorkQueue::DoWork(WorkableInterface *wi)
{
if (wi)
wi->Work();
}
void WorkQueue::Work(void)
{
return;
}
void WorkQueue::ScheduleWork(void)
{
pthread_mutex_lock(&wlock);
works = list_add_tail(works, static_cast<WorkableInterface *>(this));
pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
pthread_mutex_unlock(&wlock);
}
void WorkQueue::ScheduleWork(WorkableInterface *wi)
{
pthread_mutex_lock(&wlock);
if (wi)
works = list_add_tail(works, wi);
else
works = list_add_tail(works, static_cast<WorkableInterface *>(this));
pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
pthread_mutex_unlock(&wlock);
}
void WorkQueue::CancelScheduledWork(WorkableInterface *wi)
{
pthread_mutex_lock(&wlock);
works = list_delete_all(works, wi);
pthread_mutex_unlock(&wlock);
}
void WorkQueue::FlushWork(void)
{
FlushBarrier fb;
bool needtowait = false;
pthread_mutex_lock(&wlock);
if (works) {
list_add_tail(works, &fb);
pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
needtowait = true;
}
pthread_mutex_unlock(&wlock);
if (needtowait)
fb.WaitCompletion(); /* wokeup by FlushWork::Work() */
}
WorkQueue::FlushBarrier::FlushBarrier()
{
pthread_mutex_init(&cplock, NULL);
pthread_cond_init(&complete, NULL);
}
WorkQueue::FlushBarrier::~FlushBarrier()
{
pthread_cond_destroy(&complete);
pthread_mutex_destroy(&cplock);
}
void WorkQueue::FlushBarrier::WaitCompletion(void)
{
pthread_mutex_lock(&cplock);
pthread_cond_wait(&complete, &cplock);
pthread_mutex_unlock(&cplock);
}
void WorkQueue::FlushBarrier::Work(void)
{
pthread_mutex_lock(&cplock);
pthread_cond_signal(&complete); /* wakeup WaitCompletion() */
pthread_mutex_unlock(&cplock);
}