| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CallbackStack_h |
| #define CallbackStack_h |
| |
| #include "platform/heap/BlinkGC.h" |
| #include "wtf/Allocator.h" |
| #include "wtf/Assertions.h" |
| #include "wtf/Threading.h" |
| #include "wtf/ThreadingPrimitives.h" |
| |
| namespace blink { |
| |
| // The CallbackStack contains all the visitor callbacks used to trace and mark |
| // objects. A specific CallbackStack instance contains at most bufferSize |
| // elements. |
| // If more space is needed a new CallbackStack instance is created and chained |
| // together with the former instance. I.e. a logical CallbackStack can be made |
| // of multiple chained CallbackStack object instances. |
| class CallbackStack final { |
| USING_FAST_MALLOC(CallbackStack); |
| |
| public: |
| class Item { |
| DISALLOW_NEW(); |
| |
| public: |
| Item() {} |
| Item(void* object, VisitorCallback callback) |
| : m_object(object), m_callback(callback) {} |
| void* object() { return m_object; } |
| VisitorCallback callback() { return m_callback; } |
| void call(Visitor* visitor) { m_callback(visitor, m_object); } |
| |
| private: |
| void* m_object; |
| VisitorCallback m_callback; |
| }; |
| |
| static std::unique_ptr<CallbackStack> create(); |
| ~CallbackStack(); |
| |
| void commit(); |
| void decommit(); |
| |
| Item* allocateEntry(); |
| Item* pop(); |
| |
| bool isEmpty() const; |
| |
| void invokeEphemeronCallbacks(Visitor*); |
| |
| #if ENABLE(ASSERT) |
| bool hasCallbackForObject(const void*); |
| #endif |
| bool hasJustOneBlock() const; |
| |
| static const size_t kMinimalBlockSize; |
| static const size_t kDefaultBlockSize = (1 << 13); |
| |
| private: |
| class Block { |
| USING_FAST_MALLOC(Block); |
| |
| public: |
| explicit Block(Block* next); |
| ~Block(); |
| |
| #if ENABLE(ASSERT) |
| void clear(); |
| #endif |
| Block* next() const { return m_next; } |
| void setNext(Block* next) { m_next = next; } |
| |
| bool isEmptyBlock() const { return m_current == &(m_buffer[0]); } |
| |
| size_t blockSize() const { return m_blockSize; } |
| |
| Item* allocateEntry() { |
| if (LIKELY(m_current < m_limit)) |
| return m_current++; |
| return nullptr; |
| } |
| |
| Item* pop() { |
| if (UNLIKELY(isEmptyBlock())) |
| return nullptr; |
| return --m_current; |
| } |
| |
| void invokeEphemeronCallbacks(Visitor*); |
| |
| #if ENABLE(ASSERT) |
| bool hasCallbackForObject(const void*); |
| #endif |
| |
| private: |
| size_t m_blockSize; |
| |
| Item* m_buffer; |
| Item* m_limit; |
| Item* m_current; |
| Block* m_next; |
| }; |
| |
| CallbackStack(); |
| Item* popSlow(); |
| Item* allocateEntrySlow(); |
| void invokeOldestCallbacks(Block*, Block*, Visitor*); |
| |
| Block* m_first; |
| Block* m_last; |
| }; |
| |
| class CallbackStackMemoryPool final { |
| USING_FAST_MALLOC(CallbackStackMemoryPool); |
| |
| public: |
| // 2048 * 8 * sizeof(Item) = 256 KB (64bit) is pre-allocated for the |
| // underlying buffer of CallbackStacks. |
| static const size_t kBlockSize = 2048; |
| static const size_t kPooledBlockCount = 8; |
| static const size_t kBlockBytes = kBlockSize * sizeof(CallbackStack::Item); |
| |
| static CallbackStackMemoryPool& instance(); |
| |
| void initialize(); |
| void shutdown(); |
| CallbackStack::Item* allocate(); |
| void free(CallbackStack::Item*); |
| |
| private: |
| Mutex m_mutex; |
| int m_freeListFirst; |
| int m_freeListNext[kPooledBlockCount]; |
| CallbackStack::Item* m_pooledMemory; |
| }; |
| |
| ALWAYS_INLINE CallbackStack::Item* CallbackStack::allocateEntry() { |
| DCHECK(m_first); |
| Item* item = m_first->allocateEntry(); |
| if (LIKELY(!!item)) |
| return item; |
| |
| return allocateEntrySlow(); |
| } |
| |
| ALWAYS_INLINE CallbackStack::Item* CallbackStack::pop() { |
| Item* item = m_first->pop(); |
| if (LIKELY(!!item)) |
| return item; |
| |
| return popSlow(); |
| } |
| |
| } // namespace blink |
| |
| #endif // CallbackStack_h |