| /* |
| * 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 |