drm_hwcomposer: introduce QueueWorker
Current method to queue work such as display compositions relies on
spinning the CPU until there is space in the queue. This is inefficient
for the time in which the queue happens to fill up.
Introduce a new QueueWorker class to simplify queueing work and handle
blocking for available space more efficiently.
Change-Id: Ida7aa612931700a56ecae3efc7ddd1c86efec699
diff --git a/queue_worker.h b/queue_worker.h
new file mode 100644
index 0000000..7e96eec
--- /dev/null
+++ b/queue_worker.h
@@ -0,0 +1,157 @@
+/*
+ * 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