| /* |
| * 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 { |
| |
| 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; } |
| |
| int nodeIndex(IRNodeData* node) { |
| SkASSERT(node >= fNodes); |
| SkASSERT(node < fNodesEnd); |
| return SkToInt(node - fNodes); |
| } |
| }; |
| |
| #if defined(SK_BUILD_FOR_IOS) && \ |
| (!defined(__IPHONE_9_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) |
| |
| #include <pthread.h> |
| |
| static pthread_key_t get_pthread_key() { |
| static pthread_key_t sKey = []{ |
| pthread_key_t key; |
| int result = pthread_key_create(&key, /*destructor=*/nullptr); |
| if (result != 0) { |
| SK_ABORT("pthread_key_create failure: %d", result); |
| } |
| return key; |
| }(); |
| return sKey; |
| } |
| |
| static PoolData* get_thread_local_pool_data() { |
| return static_cast<PoolData*>(pthread_getspecific(get_pthread_key())); |
| } |
| |
| static void set_thread_local_pool_data(PoolData* poolData) { |
| pthread_setspecific(get_pthread_key(), poolData); |
| } |
| |
| #else |
| |
| static thread_local PoolData* sPoolData = nullptr; |
| |
| static PoolData* get_thread_local_pool_data() { |
| return sPoolData; |
| } |
| |
| static void set_thread_local_pool_data(PoolData* poolData) { |
| sPoolData = poolData; |
| } |
| |
| #endif |
| |
| 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 (get_thread_local_pool_data() == fData) { |
| SkDEBUGFAIL("SkSL pool is being destroyed while it is still attached to the thread"); |
| set_thread_local_pool_data(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)get_thread_local_pool_data()); |
| SkASSERT(get_thread_local_pool_data() != nullptr); |
| set_thread_local_pool_data(nullptr); |
| } |
| |
| void Pool::attachToThread() { |
| VLOG("ATTACH Pool:0x%016llX\n", (uint64_t)fData); |
| SkASSERT(get_thread_local_pool_data() == nullptr); |
| set_thread_local_pool_data(fData); |
| } |
| |
| void* Pool::AllocIRNode() { |
| // Is a pool attached? |
| PoolData* poolData = get_thread_local_pool_data(); |
| if (poolData) { |
| // Does the pool contain a free node? |
| IRNodeData* node = poolData->fFreeListHead; |
| if (node) { |
| // Yes. Take a node from the freelist. |
| poolData->fFreeListHead = node->fFreeListNext; |
| VLOG("ALLOC Pool:0x%016llX Index:%04d 0x%016llX\n", |
| (uint64_t)poolData, poolData->nodeIndex(node), (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)poolData, (uint64_t)ptr); |
| return ptr; |
| } |
| |
| void Pool::FreeIRNode(void* node_v) { |
| // Is a pool attached? |
| PoolData* poolData = get_thread_local_pool_data(); |
| if (poolData) { |
| // Did this node come from our pool? |
| auto* node = static_cast<IRNodeData*>(node_v); |
| if (node >= &poolData->fNodes[0] && node < poolData->fNodesEnd) { |
| // Yes. Push it back onto the freelist. |
| VLOG("FREE Pool:0x%016llX Index:%04d 0x%016llX\n", |
| (uint64_t)poolData, poolData->nodeIndex(node), (uint64_t)node); |
| node->fFreeListNext = poolData->fFreeListHead; |
| poolData->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)poolData, (uint64_t)node_v); |
| ::operator delete(node_v); |
| } |
| |
| } // namespace SkSL |