1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef _UI_INPUT_BLOCKING_QUEUE_H
18 #define _UI_INPUT_BLOCKING_QUEUE_H
19 
20 #include "android-base/thread_annotations.h"
21 #include <condition_variable>
22 #include <mutex>
23 #include <vector>
24 
25 namespace android {
26 
27 /**
28  * A FIFO queue that stores up to <i>capacity</i> objects.
29  * Objects can always be added. Objects are added immediately.
30  * If the queue is full, new objects cannot be added.
31  *
32  * The action of retrieving an object will block until an element is available.
33  */
34 template <class T>
35 class BlockingQueue {
36 public:
BlockingQueue(size_t capacity)37     BlockingQueue(size_t capacity) : mCapacity(capacity) {
38         mQueue.reserve(mCapacity);
39     };
40 
41     /**
42      * Retrieve and remove the oldest object.
43      * Blocks execution while queue is empty.
44      */
pop()45     T pop() {
46         std::unique_lock lock(mLock);
47         android::base::ScopedLockAssertion assumeLock(mLock);
48         mHasElements.wait(lock, [this]{
49                 android::base::ScopedLockAssertion assumeLock(mLock);
50                 return !this->mQueue.empty();
51         });
52         T t = std::move(mQueue.front());
53         mQueue.erase(mQueue.begin());
54         return t;
55     };
56 
57     /**
58      * Add a new object to the queue.
59      * Does not block.
60      * Return true if an element was successfully added.
61      * Return false if the queue is full.
62      */
push(T && t)63     bool push(T&& t) {
64         {
65             std::scoped_lock lock(mLock);
66             if (mQueue.size() == mCapacity) {
67                 return false;
68             }
69             mQueue.push_back(std::move(t));
70         }
71         mHasElements.notify_one();
72         return true;
73     };
74 
erase(const std::function<bool (const T &)> & lambda)75     void erase(const std::function<bool(const T&)>& lambda) {
76         std::scoped_lock lock(mLock);
77         mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
78                 [&lambda](const T& t) { return lambda(t); }), mQueue.end());
79     }
80 
81     /**
82      * Remove all elements.
83      * Does not block.
84      */
clear()85     void clear() {
86         std::scoped_lock lock(mLock);
87         mQueue.clear();
88     };
89 
90     /**
91      * How many elements are currently stored in the queue.
92      * Primary used for debugging.
93      * Does not block.
94      */
size()95     size_t size() {
96         std::scoped_lock lock(mLock);
97         return mQueue.size();
98     }
99 
100 private:
101     const size_t mCapacity;
102     /**
103      * Used to signal that mQueue is non-empty.
104      */
105     std::condition_variable mHasElements;
106     /**
107      * Lock for accessing and waiting on elements.
108      */
109     std::mutex mLock;
110     std::vector<T> mQueue GUARDED_BY(mLock);
111 };
112 
113 
114 } // namespace android
115 #endif
116