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