| /* |
| * Copyright 2020 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/sksl/SkSLPool.h" |
| |
| #include "src/sksl/ir/SkSLIRNode.h" |
| |
| #define VLOG(...) // printf(__VA_ARGS__) |
| |
| namespace SkSL { |
| |
| #if defined(SK_BUILD_FOR_IOS) && \ |
| (!defined(__IPHONE_9_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) |
| |
| // iOS did not support for C++11 `thread_local` variables until iOS 9. |
| // Pooling is not supported here; we allocate all nodes directly. |
| struct PoolData {}; |
| |
| Pool::~Pool() {} |
| void Pool* Pool::CreatePoolOnThread(int nodesInPool) { return new Pool; } |
| void Pool::detachFromThread() {} |
| void Pool::attachToThread() {} |
| void* Pool::AllocIRNode() { return ::operator new(sizeof(IRNode)); } |
| void Pool::FreeIRNode(void* node) { ::operator delete(node); } |
| |
| #else // !defined(SK_BUILD_FOR_IOS)... |
| |
| namespace { struct IRNodeData { |
| union { |
| uint8_t fBuffer[sizeof(IRNode)]; |
| IRNodeData* fFreeListNext; |
| }; |
| }; } |
| |
| struct PoolData { |
| // This holds the first free node in the pool. It will be null when the pool is exhausted. |
| IRNodeData* fFreeListHead = fNodes; |
| |
| // This points to end of our pooled data, and implies the number of nodes. |
| IRNodeData* fNodesEnd = nullptr; |
| |
| // Our pooled data lives here. (We allocate lots of nodes here, not just one.) |
| IRNodeData fNodes[1]; |
| |
| // Accessors. |
| ptrdiff_t nodeCount() { return fNodesEnd - fNodes; } |
| |
| ptrdiff_t nodeIndex(IRNodeData* node) { |
| SkASSERT(node >= fNodes); |
| SkASSERT(node < fNodesEnd); |
| return node - fNodes; |
| } |
| }; |
| |
| static thread_local PoolData* sPoolData = nullptr; |
| |
| static PoolData* create_pool_data(int nodesInPool) { |
| // Create a PoolData structure with extra space at the end for additional IRNode data. |
| int numExtraIRNodes = nodesInPool - 1; |
| PoolData* poolData = static_cast<PoolData*>(malloc(sizeof(PoolData) + |
| (sizeof(IRNodeData) * numExtraIRNodes))); |
| |
| // Initialize each pool node as a free node. The free nodes form a singly-linked list, each |
| // pointing to the next free node in sequence. |
| for (int index = 0; index < nodesInPool - 1; ++index) { |
| poolData->fNodes[index].fFreeListNext = &poolData->fNodes[index + 1]; |
| } |
| poolData->fNodes[nodesInPool - 1].fFreeListNext = nullptr; |
| poolData->fNodesEnd = &poolData->fNodes[nodesInPool]; |
| |
| return poolData; |
| } |
| |
| |
| Pool::~Pool() { |
| if (sPoolData == fData) { |
| SkDEBUGFAIL("SkSL pool is being destroyed while it is still attached to the thread"); |
| sPoolData = nullptr; |
| } |
| |
| // In debug mode, report any leaked nodes. |
| #ifdef SK_DEBUG |
| ptrdiff_t nodeCount = fData->nodeCount(); |
| std::vector<bool> freed(nodeCount); |
| for (IRNodeData* node = fData->fFreeListHead; node; node = node->fFreeListNext) { |
| ptrdiff_t nodeIndex = fData->nodeIndex(node); |
| freed[nodeIndex] = true; |
| } |
| bool foundLeaks = false; |
| for (int index = 0; index < nodeCount; ++index) { |
| if (!freed[index]) { |
| IRNode* leak = reinterpret_cast<IRNode*>(fData->fNodes[index].fBuffer); |
| SkDebugf("Node %d leaked: %s\n", index, leak->description().c_str()); |
| foundLeaks = true; |
| } |
| } |
| if (foundLeaks) { |
| SkDEBUGFAIL("leaking SkSL pool nodes; if they are later freed, this will likely be fatal"); |
| } |
| #endif |
| |
| VLOG("DELETE Pool:0x%016llX\n", (uint64_t)fData); |
| free(fData); |
| } |
| |
| std::unique_ptr<Pool> Pool::CreatePoolOnThread(int nodesInPool) { |
| auto pool = std::unique_ptr<Pool>(new Pool); |
| pool->fData = create_pool_data(nodesInPool); |
| pool->fData->fFreeListHead = &pool->fData->fNodes[0]; |
| VLOG("CREATE Pool:0x%016llX\n", (uint64_t)pool->fData); |
| pool->attachToThread(); |
| return pool; |
| } |
| |
| void Pool::detachFromThread() { |
| VLOG("DETACH Pool:0x%016llX\n", (uint64_t)sPoolData); |
| SkASSERT(sPoolData != nullptr); |
| sPoolData = nullptr; |
| } |
| |
| void Pool::attachToThread() { |
| VLOG("ATTACH Pool:0x%016llX\n", (uint64_t)fData); |
| SkASSERT(sPoolData == nullptr); |
| sPoolData = fData; |
| } |
| |
| void* Pool::AllocIRNode() { |
| // Is a pool attached? |
| if (sPoolData) { |
| // Does the pool contain a free node? |
| IRNodeData* node = sPoolData->fFreeListHead; |
| if (node) { |
| // Yes. Take a node from the freelist. |
| sPoolData->fFreeListHead = node->fFreeListNext; |
| VLOG("ALLOC Pool:0x%016llX Index:%04d 0x%016llX\n", |
| (uint64_t)sPoolData, (int)(node - &sPoolData->fNodes[0]), (uint64_t)node); |
| return node->fBuffer; |
| } |
| } |
| |
| // The pool is detached or full; allocate nodes using malloc. |
| void* ptr = ::operator new(sizeof(IRNode)); |
| VLOG("ALLOC Pool:0x%016llX Index:____ malloc 0x%016llX\n", |
| (uint64_t)sPoolData, (uint64_t)ptr); |
| return ptr; |
| } |
| |
| void Pool::FreeIRNode(void* node_v) { |
| // Is a pool attached? |
| if (sPoolData) { |
| // Did this node come from our pool? |
| auto* node = static_cast<IRNodeData*>(node_v); |
| if (node >= &sPoolData->fNodes[0] && node < sPoolData->fNodesEnd) { |
| // Yes. Push it back onto the freelist. |
| VLOG("FREE Pool:0x%016llX Index:%04d 0x%016llX\n", |
| (uint64_t)sPoolData, (int)(node - &sPoolData->fNodes[0]), (uint64_t)node); |
| node->fFreeListNext = sPoolData->fFreeListHead; |
| sPoolData->fFreeListHead = node; |
| return; |
| } |
| } |
| |
| // No pool is attached or the node was malloced; it must be freed. |
| VLOG("FREE Pool:0x%016llX Index:____ free 0x%016llX\n", |
| (uint64_t)sPoolData, (uint64_t)node_v); |
| ::operator delete(node_v); |
| } |
| |
| #endif // !defined(SK_BUILD_FOR_IOS)... |
| |
| } // namespace SkSL |