/* * 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 #include #include #include 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 mNext; std::unique_ptr mBuffer; sp 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 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 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 mHead; }; }; // namespace uirenderer }; // namespace android