blob: 005b399f603b79253e1fb5d2b5a0a6159f764ebb [file] [log] [blame]
Andres Morales06f5bc72015-12-15 15:21:31 -08001/*
2 * Copyright (C) 2016 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#pragma once
18
John Reck65182cc2016-01-26 10:50:10 -080019#include "utils/RefBase.h"
20#include "utils/Log.h"
21#include "utils/Macros.h"
Andres Morales06f5bc72015-12-15 15:21:31 -080022
23#include <atomic>
24#include <stdint.h>
25#include <memory>
26#include <mutex>
27
28namespace android {
29namespace uirenderer {
30
31/*
32 * Simple thread-safe pool of int64_t arrays of a provided size.
33 *
34 * Permits allocating a client-provided max number of buffers.
35 * If all buffers are in use, refuses to service any more
36 * acquire requests until buffers are re-released to the pool.
37 */
38class BufferPool : public VirtualLightRefBase {
39public:
40 class Buffer {
John Reck65182cc2016-01-26 10:50:10 -080041 PREVENT_COPY_AND_ASSIGN(Buffer);
Andres Morales06f5bc72015-12-15 15:21:31 -080042 public:
43 int64_t* getBuffer() { return mBuffer.get(); }
44 size_t getSize() { return mSize; }
45
46 void release() {
47 LOG_ALWAYS_FATAL_IF(mPool.get() == nullptr, "attempt to release unacquired buffer");
48 mPool->release(this);
49 }
50
51 Buffer* incRef() {
52 mRefs++;
53 return this;
54 }
55
56 int decRef() {
57 int refs = mRefs.fetch_sub(1);
58 LOG_ALWAYS_FATAL_IF(refs == 0, "buffer reference decremented below 0");
59 return refs - 1;
60 }
61
John Reck65182cc2016-01-26 10:50:10 -080062 bool isUniqueRef() {
63 return mRefs.load() == 1;
64 }
65
Andres Morales06f5bc72015-12-15 15:21:31 -080066 private:
67 friend class BufferPool;
68
John Reck65182cc2016-01-26 10:50:10 -080069 Buffer(BufferPool* pool, size_t size) : mRefs(1) {
Andres Morales06f5bc72015-12-15 15:21:31 -080070 mSize = size;
71 mBuffer.reset(new int64_t[size]);
72 mPool = pool;
Andres Morales06f5bc72015-12-15 15:21:31 -080073 }
74
75 void setPool(BufferPool* pool) {
76 mPool = pool;
77 }
78
79 std::unique_ptr<Buffer> mNext;
80 std::unique_ptr<int64_t[]> mBuffer;
81 sp<BufferPool> mPool;
82 size_t mSize;
83
84 std::atomic_int mRefs;
85 };
86
87 BufferPool(size_t bufferSize, size_t count)
88 : mBufferSize(bufferSize), mCount(count) {}
89
90 /**
91 * Acquires a buffer from the buffer pool if available.
92 *
93 * Only `mCount` buffers are allowed to be in use at a single
94 * instance.
95 *
96 * If no buffer is available, i.e. `mCount` buffers are in use,
97 * returns nullptr.
98 *
99 * The pointer returned from this method *MUST NOT* be freed, instead
100 * BufferPool::release() must be called upon it when the client
101 * is done with it. Failing to release buffers will eventually make the
102 * BufferPool refuse to service any more BufferPool::acquire() requests.
103 */
104 BufferPool::Buffer* acquire() {
105 std::lock_guard<std::mutex> lock(mLock);
106
107 if (mHead.get() != nullptr) {
108 BufferPool::Buffer* res = mHead.release();
109 mHead = std::move(res->mNext);
110 res->mNext.reset(nullptr);
111 res->setPool(this);
112 res->incRef();
113 return res;
114 }
115
116 if (mAllocatedCount < mCount) {
117 ++mAllocatedCount;
118 return new BufferPool::Buffer(this, mBufferSize);
119 }
120
121 return nullptr;
122 }
123
124 /**
125 * Releases a buffer previously acquired by BufferPool::acquire().
126 *
127 * The released buffer is not valid after calling this method and
128 * attempting to use will result in undefined behavior.
129 */
130 void release(BufferPool::Buffer* buffer) {
131 std::lock_guard<std::mutex> lock(mLock);
132
133 if (buffer->decRef() != 0) {
134 return;
135 }
136
137 buffer->setPool(nullptr);
138
139 BufferPool::Buffer* list = mHead.get();
140 if (list == nullptr) {
141 mHead.reset(buffer);
142 mHead->mNext.reset(nullptr);
143 return;
144 }
145
146 while (list->mNext.get() != nullptr) {
147 list = list->mNext.get();
148 }
149
150 list->mNext.reset(buffer);
151 }
152
153 /*
154 * Used for testing.
155 */
156 size_t getAvailableBufferCount() {
157 size_t remainingToAllocateCount = mCount - mAllocatedCount;
158
159 BufferPool::Buffer* list = mHead.get();
160 if (list == nullptr) return remainingToAllocateCount;
161
162 int count = 1;
163 while (list->mNext.get() != nullptr) {
164 count++;
165 list = list->mNext.get();
166 }
167
168 return count + remainingToAllocateCount;
169 }
170
171private:
172 mutable std::mutex mLock;
173
174 size_t mBufferSize;
175 size_t mCount;
176 size_t mAllocatedCount = 0;
177 std::unique_ptr<BufferPool::Buffer> mHead;
178};
179
180}; // namespace uirenderer
181}; // namespace android