| /* |
| * Copyright (C) 2016 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. |
| */ |
| |
| #ifndef ANDROID_QUEUE_WORKER_H_ |
| #define ANDROID_QUEUE_WORKER_H_ |
| |
| #include "worker.h" |
| |
| #include <queue> |
| |
| namespace android { |
| |
| template <typename T> |
| class QueueWorker : public Worker { |
| public: |
| static const size_t kDefaultMaxQueueSize = 2; |
| static const int64_t kTimeoutDisabled = -1; |
| |
| QueueWorker(const char *name, int priority) |
| : Worker(name, priority), |
| max_queue_size_(kDefaultMaxQueueSize), |
| queue_timeout_ms_(kTimeoutDisabled), |
| idle_timeout_ms_(kTimeoutDisabled), |
| idled_out_(false) { |
| } |
| |
| int QueueWork(std::unique_ptr<T> workitem); |
| |
| bool IsWorkPending() const { |
| return !queue_.empty(); |
| } |
| bool idle() const { |
| return idled_out_; |
| } |
| |
| int64_t idle_timeout() { |
| return idle_timeout_ms_; |
| } |
| void set_idle_timeout(int64_t timeout_ms) { |
| idle_timeout_ms_ = timeout_ms; |
| } |
| |
| int64_t queue_timeout() { |
| return queue_timeout_ms_; |
| } |
| void set_queue_timeout(int64_t timeout_ms) { |
| queue_timeout_ms_ = timeout_ms; |
| } |
| |
| size_t max_queue_size() const { |
| return max_queue_size_; |
| } |
| void set_max_queue_size(size_t size) { |
| max_queue_size_ = size; |
| } |
| |
| protected: |
| virtual void ProcessWork(std::unique_ptr<T> workitem) = 0; |
| virtual void ProcessIdle(){} |
| virtual void Routine(); |
| |
| template <typename Predicate> |
| int WaitCond(std::unique_lock<std::mutex> &lock, Predicate pred, |
| int64_t max_msecs); |
| |
| private: |
| std::queue<std::unique_ptr<T>> queue_; |
| size_t max_queue_size_; |
| int64_t queue_timeout_ms_; |
| int64_t idle_timeout_ms_; |
| bool idled_out_; |
| }; |
| |
| template <typename T> |
| template <typename Predicate> |
| int QueueWorker<T>::WaitCond(std::unique_lock<std::mutex> &lock, Predicate pred, |
| int64_t max_msecs) { |
| bool ret = true; |
| auto wait_func = [&] { return pred() || should_exit(); }; |
| |
| if (max_msecs < 0) { |
| cond_.wait(lock, wait_func); |
| } else { |
| auto timeout = std::chrono::milliseconds(max_msecs); |
| ret = cond_.wait_for(lock, timeout, wait_func); |
| } |
| |
| if (!ret) |
| return -ETIMEDOUT; |
| else if (should_exit()) |
| return -EINTR; |
| |
| return 0; |
| } |
| |
| template <typename T> |
| void QueueWorker<T>::Routine() { |
| std::unique_lock<std::mutex> lk(mutex_); |
| std::unique_ptr<T> workitem; |
| |
| auto wait_func = [&] { return !queue_.empty(); }; |
| int ret = |
| WaitCond(lk, wait_func, idled_out_ ? kTimeoutDisabled : idle_timeout_ms_); |
| switch (ret) { |
| case 0: |
| break; |
| case -ETIMEDOUT: |
| ProcessIdle(); |
| idled_out_ = true; |
| return; |
| case -EINTR: |
| default: |
| return; |
| } |
| |
| if (!queue_.empty()) { |
| workitem = std::move(queue_.front()); |
| queue_.pop(); |
| } |
| lk.unlock(); |
| cond_.notify_all(); |
| |
| idled_out_ = false; |
| ProcessWork(std::move(workitem)); |
| } |
| |
| template <typename T> |
| int QueueWorker<T>::QueueWork(std::unique_ptr<T> workitem) { |
| std::unique_lock<std::mutex> lk(mutex_); |
| |
| auto wait_func = [&] { return queue_.size() < max_queue_size_; }; |
| int ret = WaitCond(lk, wait_func, queue_timeout_ms_); |
| if (ret) |
| return ret; |
| |
| queue_.push(std::move(workitem)); |
| lk.unlock(); |
| |
| cond_.notify_one(); |
| |
| return 0; |
| } |
| }; |
| #endif |