| /* |
| * 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 |