| /* | 
 |  * Copyright (C) 2012 The Android Open Source Project | 
 |  * | 
 |  * 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. | 
 |  */ | 
 |  | 
 | // #define LOG_NDEBUG 0 | 
 | #define LOG_TAG "WorkQueue" | 
 |  | 
 | #include <utils/Log.h> | 
 | #include "WorkQueue.h" | 
 |  | 
 | namespace android { | 
 |  | 
 | // --- WorkQueue --- | 
 |  | 
 | WorkQueue::WorkQueue(size_t maxThreads, bool canCallJava) : | 
 |         mMaxThreads(maxThreads), mCanCallJava(canCallJava), | 
 |         mCanceled(false), mFinished(false), mIdleThreads(0) { | 
 | } | 
 |  | 
 | WorkQueue::~WorkQueue() { | 
 |     if (!cancel()) { | 
 |         finish(); | 
 |     } | 
 | } | 
 |  | 
 | status_t WorkQueue::schedule(WorkUnit* workUnit, size_t backlog) { | 
 |     AutoMutex _l(mLock); | 
 |  | 
 |     if (mFinished || mCanceled) { | 
 |         return INVALID_OPERATION; | 
 |     } | 
 |  | 
 |     if (mWorkThreads.size() < mMaxThreads | 
 |             && mIdleThreads < mWorkUnits.size() + 1) { | 
 |         sp<WorkThread> workThread = new WorkThread(this, mCanCallJava); | 
 |         status_t status = workThread->run("WorkQueue::WorkThread"); | 
 |         if (status) { | 
 |             return status; | 
 |         } | 
 |         mWorkThreads.add(workThread); | 
 |         mIdleThreads += 1; | 
 |     } else if (backlog) { | 
 |         while (mWorkUnits.size() >= mMaxThreads * backlog) { | 
 |             mWorkDequeuedCondition.wait(mLock); | 
 |             if (mFinished || mCanceled) { | 
 |                 return INVALID_OPERATION; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     mWorkUnits.add(workUnit); | 
 |     mWorkChangedCondition.broadcast(); | 
 |     return OK; | 
 | } | 
 |  | 
 | status_t WorkQueue::cancel() { | 
 |     AutoMutex _l(mLock); | 
 |  | 
 |     return cancelLocked(); | 
 | } | 
 |  | 
 | status_t WorkQueue::cancelLocked() { | 
 |     if (mFinished) { | 
 |         return INVALID_OPERATION; | 
 |     } | 
 |  | 
 |     if (!mCanceled) { | 
 |         mCanceled = true; | 
 |  | 
 |         size_t count = mWorkUnits.size(); | 
 |         for (size_t i = 0; i < count; i++) { | 
 |             delete mWorkUnits.itemAt(i); | 
 |         } | 
 |         mWorkUnits.clear(); | 
 |         mWorkChangedCondition.broadcast(); | 
 |         mWorkDequeuedCondition.broadcast(); | 
 |     } | 
 |     return OK; | 
 | } | 
 |  | 
 | status_t WorkQueue::finish() { | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         if (mFinished) { | 
 |             return INVALID_OPERATION; | 
 |         } | 
 |  | 
 |         mFinished = true; | 
 |         mWorkChangedCondition.broadcast(); | 
 |     } // release lock | 
 |  | 
 |     // It is not possible for the list of work threads to change once the mFinished | 
 |     // flag has been set, so we can access mWorkThreads outside of the lock here. | 
 |     size_t count = mWorkThreads.size(); | 
 |     for (size_t i = 0; i < count; i++) { | 
 |         mWorkThreads.itemAt(i)->join(); | 
 |     } | 
 |     mWorkThreads.clear(); | 
 |     return OK; | 
 | } | 
 |  | 
 | bool WorkQueue::threadLoop() { | 
 |     WorkUnit* workUnit; | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         for (;;) { | 
 |             if (mCanceled) { | 
 |                 return false; | 
 |             } | 
 |  | 
 |             if (!mWorkUnits.isEmpty()) { | 
 |                 workUnit = mWorkUnits.itemAt(0); | 
 |                 mWorkUnits.removeAt(0); | 
 |                 mIdleThreads -= 1; | 
 |                 mWorkDequeuedCondition.broadcast(); | 
 |                 break; | 
 |             } | 
 |  | 
 |             if (mFinished) { | 
 |                 return false; | 
 |             } | 
 |  | 
 |             mWorkChangedCondition.wait(mLock); | 
 |         } | 
 |     } // release lock | 
 |  | 
 |     bool shouldContinue = workUnit->run(); | 
 |     delete workUnit; | 
 |  | 
 |     { // acquire lock | 
 |         AutoMutex _l(mLock); | 
 |  | 
 |         mIdleThreads += 1; | 
 |  | 
 |         if (!shouldContinue) { | 
 |             cancelLocked(); | 
 |             return false; | 
 |         } | 
 |     } // release lock | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | // --- WorkQueue::WorkThread --- | 
 |  | 
 | WorkQueue::WorkThread::WorkThread(WorkQueue* workQueue, bool canCallJava) : | 
 |         Thread(canCallJava), mWorkQueue(workQueue) { | 
 | } | 
 |  | 
 | WorkQueue::WorkThread::~WorkThread() { | 
 | } | 
 |  | 
 | bool WorkQueue::WorkThread::threadLoop() { | 
 |     return mWorkQueue->threadLoop(); | 
 | } | 
 |  | 
 | };  // namespace android |