| // Copyright 2020 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_IMPL_COLLECTION_SUPPORT_HEAP_VECTOR_BACKING_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_IMPL_COLLECTION_SUPPORT_HEAP_VECTOR_BACKING_H_ |
| |
| #include "base/check_op.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/heap/impl/finalizer_traits.h" |
| #include "third_party/blink/renderer/platform/heap/impl/gc_info.h" |
| #include "third_party/blink/renderer/platform/heap/impl/threading_traits.h" |
| #include "third_party/blink/renderer/platform/heap/thread_state.h" |
| #include "third_party/blink/renderer/platform/heap/trace_traits.h" |
| #include "third_party/blink/renderer/platform/wtf/conditional_destructor.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| |
| namespace blink { |
| |
| template <typename T, typename Traits = WTF::VectorTraits<T>> |
| class HeapVectorBacking final |
| : public GarbageCollected<HeapVectorBacking<T, Traits>>, |
| public WTF::ConditionalDestructor<HeapVectorBacking<T, Traits>, |
| !Traits::kNeedsDestruction> { |
| public: |
| template <typename Backing> |
| static void* AllocateObject(size_t); |
| |
| // Conditionally invoked via destructor. |
| void Finalize(); |
| }; |
| |
| // static |
| template <typename T, typename Traits> |
| template <typename Backing> |
| void* HeapVectorBacking<T, Traits>::AllocateObject(size_t size) { |
| ThreadState* state = ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState(); |
| DCHECK(state->IsAllocationAllowed()); |
| return state->Heap().AllocateOnArenaIndex( |
| state, size, BlinkGC::kVectorArenaIndex, GCInfoTrait<Backing>::Index(), |
| WTF_HEAP_PROFILER_TYPE_NAME(Backing)); |
| } |
| |
| template <typename T, typename Traits> |
| void HeapVectorBacking<T, Traits>::Finalize() { |
| static_assert(Traits::kNeedsDestruction, |
| "Only vector buffers with items requiring destruction should " |
| "be finalized"); |
| static_assert( |
| Traits::kCanClearUnusedSlotsWithMemset || std::is_polymorphic<T>::value, |
| "HeapVectorBacking doesn't support objects that cannot be cleared as " |
| "unused with memset or don't have a vtable"); |
| |
| static_assert( |
| !std::is_trivially_destructible<T>::value, |
| "Finalization of trivially destructible classes should not happen."); |
| HeapObjectHeader* header = HeapObjectHeader::FromPayload(this); |
| // Use the payload size as recorded by the heap to determine how many |
| // elements to finalize. |
| size_t length = header->PayloadSize() / sizeof(T); |
| Address payload = header->Payload(); |
| #ifdef ANNOTATE_CONTIGUOUS_CONTAINER |
| ANNOTATE_CHANGE_SIZE(payload, length * sizeof(T), 0, length * sizeof(T)); |
| #endif |
| // As commented above, HeapVectorBacking calls finalizers for unused slots |
| // (which are already zeroed out). |
| if (std::is_polymorphic<T>::value) { |
| for (unsigned i = 0; i < length; ++i) { |
| Address element = payload + i * sizeof(T); |
| if (blink::VTableInitialized(element)) |
| reinterpret_cast<T*>(element)->~T(); |
| } |
| } else { |
| T* buffer = reinterpret_cast<T*>(payload); |
| for (unsigned i = 0; i < length; ++i) |
| buffer[i].~T(); |
| } |
| } |
| |
| template <typename T> |
| struct MakeGarbageCollectedTrait<HeapVectorBacking<T>> { |
| static HeapVectorBacking<T>* Call(size_t num_elements) { |
| static_assert(!std::is_polymorphic<HeapVectorBacking<T>>::value, |
| "HeapVectorBacking must not be polymorphic as it is " |
| "converted to a raw array of buckets for certain operation"); |
| CHECK_GT(num_elements, 0u); |
| void* memory = |
| HeapVectorBacking<T>::template AllocateObject<HeapVectorBacking<T>>( |
| num_elements * sizeof(T)); |
| HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); |
| // Placement new as regular operator new() is deleted. |
| HeapVectorBacking<T>* object = ::new (memory) HeapVectorBacking<T>(); |
| header->MarkFullyConstructed<HeapObjectHeader::AccessMode::kAtomic>(); |
| return object; |
| } |
| }; |
| |
| template <typename T, typename Traits> |
| struct ThreadingTrait<HeapVectorBacking<T, Traits>> { |
| STATIC_ONLY(ThreadingTrait); |
| static const ThreadAffinity kAffinity = ThreadingTrait<T>::Affinity; |
| }; |
| |
| template <typename T, typename Traits> |
| struct TraceTrait<HeapVectorBacking<T, Traits>> { |
| STATIC_ONLY(TraceTrait); |
| using Backing = HeapVectorBacking<T, Traits>; |
| |
| public: |
| static TraceDescriptor GetTraceDescriptor(const void* self) { |
| return {self, TraceTrait<Backing>::Trace}; |
| } |
| |
| static void Trace(Visitor* visitor, const void* self) { |
| if (!Traits::kCanTraceConcurrently && self) { |
| if (visitor->DeferredTraceIfConcurrent({self, &Trace}, |
| GetBackingStoreSize(self))) |
| return; |
| } |
| |
| static_assert(!WTF::IsWeak<T>::value, |
| "Weakness is not supported in HeapVector and HeapDeque"); |
| if (WTF::IsTraceableInCollectionTrait<Traits>::value) { |
| WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, |
| HeapVectorBacking<T, Traits>, |
| void>::Trace(visitor, self); |
| } |
| } |
| |
| private: |
| static size_t GetBackingStoreSize(const void* backing_store) { |
| const HeapObjectHeader* header = |
| HeapObjectHeader::FromPayload(backing_store); |
| return header->IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>() |
| ? static_cast<LargeObjectPage*>(PageFromObject(header)) |
| ->ObjectSize() |
| : header->size<HeapObjectHeader::AccessMode::kAtomic>(); |
| } |
| }; |
| |
| } // namespace blink |
| |
| namespace WTF { |
| |
| // This trace method is used for all HeapVectorBacking objects. On-stack objects |
| // are found and dispatched using conservative stack scanning. HeapVector (i.e. |
| // Vector) dispatches all regular on-heap backings to this method. |
| template <typename T, typename Traits> |
| struct TraceInCollectionTrait<kNoWeakHandling, |
| blink::HeapVectorBacking<T, Traits>, |
| void> { |
| static void Trace(blink::Visitor* visitor, const void* self) { |
| // HeapVectorBacking does not know the exact size of the vector |
| // and just knows the capacity of the vector. Due to the constraint, |
| // HeapVectorBacking can support only the following objects: |
| // |
| // - An object that has a vtable. In this case, HeapVectorBacking |
| // traces only slots that are not zeroed out. This is because if |
| // the object has a vtable, the zeroed slot means that it is |
| // an unused slot (Remember that the unused slots are guaranteed |
| // to be zeroed out by VectorUnusedSlotClearer). |
| // |
| // - An object that can be initialized with memset. In this case, |
| // HeapVectorBacking traces all slots including unused slots. |
| // This is fine because the fact that the object can be initialized |
| // with memset indicates that it is safe to treat the zerod slot |
| // as a valid object. |
| static_assert(!IsTraceableInCollectionTrait<Traits>::value || |
| Traits::kCanClearUnusedSlotsWithMemset || |
| std::is_polymorphic<T>::value, |
| "HeapVectorBacking doesn't support objects that cannot be " |
| "cleared as unused with memset."); |
| |
| // This trace method is instantiated for vectors where |
| // IsTraceableInCollectionTrait<Traits>::value is false, but the trace |
| // method should not be called. Thus we cannot static-assert |
| // IsTraceableInCollectionTrait<Traits>::value but should runtime-assert it. |
| DCHECK(IsTraceableInCollectionTrait<Traits>::value); |
| |
| const T* array = reinterpret_cast<const T*>(self); |
| blink::HeapObjectHeader* header = |
| blink::HeapObjectHeader::FromPayload(self); |
| // Use the payload size as recorded by the heap to determine how many |
| // elements to trace. |
| size_t length = header->PayloadSize() / sizeof(T); |
| #ifdef ANNOTATE_CONTIGUOUS_CONTAINER |
| // As commented above, HeapVectorBacking can trace unused slots |
| // (which are already zeroed out). |
| ANNOTATE_CHANGE_SIZE(array, length, 0, length); |
| #endif |
| if (std::is_polymorphic<T>::value) { |
| const char* pointer = reinterpret_cast<const char*>(array); |
| for (unsigned i = 0; i < length; ++i) { |
| const char* element = pointer + i * sizeof(T); |
| if (blink::VTableInitialized(element)) { |
| blink::TraceIfNeeded< |
| T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor, |
| array[i]); |
| } |
| } |
| } else { |
| for (size_t i = 0; i < length; ++i) { |
| blink::TraceIfNeeded< |
| T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor, |
| array[i]); |
| } |
| } |
| } |
| }; |
| |
| } // namespace WTF |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_IMPL_COLLECTION_SUPPORT_HEAP_VECTOR_BACKING_H_ |