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