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]() REQUIRES(mLock) { return !this->mQueue.empty(); });
49         T t = std::move(mQueue.front());
50         mQueue.erase(mQueue.begin());
51         return t;
52     };
53 
54     /**
55      * Add a new object to the queue.
56      * Does not block.
57      * Return true if an element was successfully added.
58      * Return false if the queue is full.
59      */
push(T && t)60     bool push(T&& t) {
61         {
62             std::scoped_lock lock(mLock);
63             if (mQueue.size() == mCapacity) {
64                 return false;
65             }
66             mQueue.push_back(std::move(t));
67         }
68         mHasElements.notify_one();
69         return true;
70     };
71 
erase(const std::function<bool (const T &)> & lambda)72     void erase(const std::function<bool(const T&)>& lambda) {
73         std::scoped_lock lock(mLock);
74         mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(),
75                 [&lambda](const T& t) { return lambda(t); }), mQueue.end());
76     }
77 
78     /**
79      * Remove all elements.
80      * Does not block.
81      */
clear()82     void clear() {
83         std::scoped_lock lock(mLock);
84         mQueue.clear();
85     };
86 
87     /**
88      * How many elements are currently stored in the queue.
89      * Primary used for debugging.
90      * Does not block.
91      */
size()92     size_t size() {
93         std::scoped_lock lock(mLock);
94         return mQueue.size();
95     }
96 
97 private:
98     const size_t mCapacity;
99     /**
100      * Used to signal that mQueue is non-empty.
101      */
102     std::condition_variable mHasElements;
103     /**
104      * Lock for accessing and waiting on elements.
105      */
106     std::mutex mLock;
107     std::vector<T> mQueue GUARDED_BY(mLock);
108 };
109 
110 
111 } // namespace android
112 #endif
113