blob: 005b399f603b79253e1fb5d2b5a0a6159f764ebb [file] [log] [blame]
/*
* 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.
*/
#pragma once
#include "utils/RefBase.h"
#include "utils/Log.h"
#include "utils/Macros.h"
#include <atomic>
#include <stdint.h>
#include <memory>
#include <mutex>
namespace android {
namespace uirenderer {
/*
* Simple thread-safe pool of int64_t arrays of a provided size.
*
* Permits allocating a client-provided max number of buffers.
* If all buffers are in use, refuses to service any more
* acquire requests until buffers are re-released to the pool.
*/
class BufferPool : public VirtualLightRefBase {
public:
class Buffer {
PREVENT_COPY_AND_ASSIGN(Buffer);
public:
int64_t* getBuffer() { return mBuffer.get(); }
size_t getSize() { return mSize; }
void release() {
LOG_ALWAYS_FATAL_IF(mPool.get() == nullptr, "attempt to release unacquired buffer");
mPool->release(this);
}
Buffer* incRef() {
mRefs++;
return this;
}
int decRef() {
int refs = mRefs.fetch_sub(1);
LOG_ALWAYS_FATAL_IF(refs == 0, "buffer reference decremented below 0");
return refs - 1;
}
bool isUniqueRef() {
return mRefs.load() == 1;
}
private:
friend class BufferPool;
Buffer(BufferPool* pool, size_t size) : mRefs(1) {
mSize = size;
mBuffer.reset(new int64_t[size]);
mPool = pool;
}
void setPool(BufferPool* pool) {
mPool = pool;
}
std::unique_ptr<Buffer> mNext;
std::unique_ptr<int64_t[]> mBuffer;
sp<BufferPool> mPool;
size_t mSize;
std::atomic_int mRefs;
};
BufferPool(size_t bufferSize, size_t count)
: mBufferSize(bufferSize), mCount(count) {}
/**
* Acquires a buffer from the buffer pool if available.
*
* Only `mCount` buffers are allowed to be in use at a single
* instance.
*
* If no buffer is available, i.e. `mCount` buffers are in use,
* returns nullptr.
*
* The pointer returned from this method *MUST NOT* be freed, instead
* BufferPool::release() must be called upon it when the client
* is done with it. Failing to release buffers will eventually make the
* BufferPool refuse to service any more BufferPool::acquire() requests.
*/
BufferPool::Buffer* acquire() {
std::lock_guard<std::mutex> lock(mLock);
if (mHead.get() != nullptr) {
BufferPool::Buffer* res = mHead.release();
mHead = std::move(res->mNext);
res->mNext.reset(nullptr);
res->setPool(this);
res->incRef();
return res;
}
if (mAllocatedCount < mCount) {
++mAllocatedCount;
return new BufferPool::Buffer(this, mBufferSize);
}
return nullptr;
}
/**
* Releases a buffer previously acquired by BufferPool::acquire().
*
* The released buffer is not valid after calling this method and
* attempting to use will result in undefined behavior.
*/
void release(BufferPool::Buffer* buffer) {
std::lock_guard<std::mutex> lock(mLock);
if (buffer->decRef() != 0) {
return;
}
buffer->setPool(nullptr);
BufferPool::Buffer* list = mHead.get();
if (list == nullptr) {
mHead.reset(buffer);
mHead->mNext.reset(nullptr);
return;
}
while (list->mNext.get() != nullptr) {
list = list->mNext.get();
}
list->mNext.reset(buffer);
}
/*
* Used for testing.
*/
size_t getAvailableBufferCount() {
size_t remainingToAllocateCount = mCount - mAllocatedCount;
BufferPool::Buffer* list = mHead.get();
if (list == nullptr) return remainingToAllocateCount;
int count = 1;
while (list->mNext.get() != nullptr) {
count++;
list = list->mNext.get();
}
return count + remainingToAllocateCount;
}
private:
mutable std::mutex mLock;
size_t mBufferSize;
size_t mCount;
size_t mAllocatedCount = 0;
std::unique_ptr<BufferPool::Buffer> mHead;
};
}; // namespace uirenderer
}; // namespace android