Home | History | Annotate | Download | only in src
      1 /*
      2  * workqueue.cpp, workqueue class
      3  *
      4  * Copyright (c) 2009-2010 Wind River Systems, Inc.
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  * http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 
     19 #include <workqueue.h>
     20 #include <sys/prctl.h>
     21 
     22 WorkQueue::WorkQueue()
     23 {
     24     stop = false;
     25     executing = true;
     26     wait_for_works = false;
     27     works = NULL;
     28 
     29     pthread_mutex_init(&wlock, NULL);
     30     pthread_cond_init(&wcond, NULL);
     31 
     32     pthread_mutex_init(&executing_lock, NULL);
     33     pthread_cond_init(&executing_wait, NULL);
     34     pthread_cond_init(&paused_wait, NULL);
     35 }
     36 
     37 WorkQueue::~WorkQueue()
     38 {
     39     StopWork();
     40 
     41     pthread_cond_destroy(&wcond);
     42     pthread_mutex_destroy(&wlock);
     43 
     44     pthread_cond_destroy(&paused_wait);
     45     pthread_cond_destroy(&executing_wait);
     46     pthread_mutex_destroy(&executing_lock);
     47 }
     48 
     49 int WorkQueue::StartWork(bool executing)
     50 {
     51     this->executing = executing;
     52 
     53     pthread_mutex_lock(&wlock);
     54     stop = false;
     55     pthread_mutex_unlock(&wlock);
     56     return Start();
     57 }
     58 
     59 void WorkQueue::StopWork(void)
     60 {
     61     /* discard all scheduled works */
     62     pthread_mutex_lock(&wlock);
     63     while (works)
     64         works = __list_delete(works, works);
     65     pthread_mutex_unlock(&wlock);
     66 
     67     /*  wakeup DoWork() if it's sleeping */
     68     /*
     69      * FIXME
     70      *
     71      * if DoWork() is sleeping, Work()'s called one more time at this moment.
     72      * if DoWork()::wi->Work() called ScheduleWork() (self-rescheduling),
     73      * this function would be sleeping forever at Join() because works list
     74      * never be empty.
     75      */
     76     ResumeWork();
     77 
     78     pthread_mutex_lock(&wlock);
     79     stop = true;
     80     pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
     81     pthread_mutex_unlock(&wlock);
     82 
     83     Join();
     84 }
     85 
     86 /* it returns when Run() is sleeping at executing_wait or at wcond */
     87 void WorkQueue::PauseWork(void)
     88 {
     89     pthread_mutex_lock(&executing_lock);
     90     executing = false;
     91     /* this prevents deadlock if Run() is sleeping with locking wcond */
     92     if (!wait_for_works)
     93         pthread_cond_wait(&paused_wait, &executing_lock); /* wokeup by Run() */
     94     pthread_mutex_unlock(&executing_lock);
     95 }
     96 
     97 void WorkQueue::ResumeWork(void)
     98 {
     99     pthread_mutex_lock(&executing_lock);
    100     executing = true;
    101     pthread_cond_signal(&executing_wait);
    102     pthread_mutex_unlock(&executing_lock);
    103 }
    104 
    105 void WorkQueue::Run(void)
    106 {
    107     prctl(PR_SET_NAME, (unsigned long)"IntelHwCodec", 0, 0, 0);
    108 
    109     while (true) {
    110         pthread_mutex_lock(&wlock);
    111         if (stop) {
    112             pthread_mutex_unlock(&wlock);
    113             break;
    114         }
    115 
    116         if (!works) {
    117             pthread_mutex_lock(&executing_lock);
    118             wait_for_works = true;
    119             /* wake up PauseWork() if it's sleeping */
    120             pthread_cond_signal(&paused_wait);
    121             pthread_mutex_unlock(&executing_lock);
    122 
    123             /*
    124              * sleeps until works're available.
    125              * wokeup by ScheduleWork() or FlushWork() or ~WorkQueue()
    126              */
    127             pthread_cond_wait(&wcond, &wlock);
    128 
    129             pthread_mutex_lock(&executing_lock);
    130             wait_for_works = false;
    131             pthread_mutex_unlock(&executing_lock);
    132         }
    133 
    134         while (works) {
    135             struct list *entry = works;
    136             WorkableInterface *wi =
    137                 static_cast<WorkableInterface *>(entry->data);
    138 
    139             works = __list_delete(works, entry);
    140             pthread_mutex_unlock(&wlock);
    141 
    142             /*
    143              * 1. if PauseWork() locks executing_lock right before Run() locks
    144              *    the lock, Run() sends the paused signal and go to sleep.
    145              * 2. if Run() locks executing_lock first, DoWork() is called and
    146              *    PausedWork() waits for paused_wait signal. Run() sends the
    147              *    signal during next loop processing or at the end of loop
    148              *    in case of works're not available.
    149              */
    150             pthread_mutex_lock(&executing_lock);
    151             if (!executing) {
    152                 pthread_cond_signal(&paused_wait);
    153                 pthread_cond_wait(&executing_wait, &executing_lock);
    154             }
    155             pthread_mutex_unlock(&executing_lock);
    156 
    157             DoWork(wi);
    158 
    159             pthread_mutex_lock(&wlock);
    160         }
    161 
    162         pthread_mutex_unlock(&wlock);
    163     }
    164 }
    165 
    166 void WorkQueue::DoWork(WorkableInterface *wi)
    167 {
    168     if (wi)
    169         wi->Work();
    170 }
    171 
    172 void WorkQueue::Work(void)
    173 {
    174     return;
    175 }
    176 
    177 void WorkQueue::ScheduleWork(void)
    178 {
    179     pthread_mutex_lock(&wlock);
    180     works = list_add_tail(works, static_cast<WorkableInterface *>(this));
    181     pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
    182     pthread_mutex_unlock(&wlock);
    183 }
    184 
    185 void WorkQueue::ScheduleWork(WorkableInterface *wi)
    186 {
    187     pthread_mutex_lock(&wlock);
    188     if (wi)
    189         works = list_add_tail(works, wi);
    190     else
    191         works = list_add_tail(works, static_cast<WorkableInterface *>(this));
    192     pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
    193     pthread_mutex_unlock(&wlock);
    194 }
    195 
    196 void WorkQueue::CancelScheduledWork(WorkableInterface *wi)
    197 {
    198     pthread_mutex_lock(&wlock);
    199     works = list_delete_all(works, wi);
    200     pthread_mutex_unlock(&wlock);
    201 }
    202 
    203 void WorkQueue::FlushWork(void)
    204 {
    205     FlushBarrier fb;
    206     bool needtowait = false;
    207 
    208     pthread_mutex_lock(&wlock);
    209     if (works) {
    210         list_add_tail(works, &fb);
    211         pthread_cond_signal(&wcond); /* wakeup Run() if it's sleeping */
    212 
    213         needtowait = true;
    214     }
    215     pthread_mutex_unlock(&wlock);
    216 
    217     if (needtowait)
    218         fb.WaitCompletion(); /* wokeup by FlushWork::Work() */
    219 }
    220 
    221 WorkQueue::FlushBarrier::FlushBarrier()
    222 {
    223     pthread_mutex_init(&cplock, NULL);
    224     pthread_cond_init(&complete, NULL);
    225 }
    226 
    227 WorkQueue::FlushBarrier::~FlushBarrier()
    228 {
    229     pthread_cond_destroy(&complete);
    230     pthread_mutex_destroy(&cplock);
    231 }
    232 
    233 void WorkQueue::FlushBarrier::WaitCompletion(void)
    234 {
    235     pthread_mutex_lock(&cplock);
    236     pthread_cond_wait(&complete, &cplock);
    237     pthread_mutex_unlock(&cplock);
    238 }
    239 
    240 void WorkQueue::FlushBarrier::Work(void)
    241 {
    242     pthread_mutex_lock(&cplock);
    243     pthread_cond_signal(&complete); /* wakeup WaitCompletion() */
    244     pthread_mutex_unlock(&cplock);
    245 }
    246