blob: cb47bcf10b39766b61a1f4f99d3d733d0381e78f [file] [log] [blame]
// Copyright 2015 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.
#include "platform/heap/PersistentNode.h"
#include "platform/heap/Handle.h"
namespace blink {
namespace {
class DummyGCBase final : public GarbageCollected<DummyGCBase> {
public:
DEFINE_INLINE_TRACE() {}
};
}
PersistentRegion::~PersistentRegion() {
PersistentNodeSlots* slots = m_slots;
while (slots) {
PersistentNodeSlots* deadSlots = slots;
slots = slots->m_next;
delete deadSlots;
}
}
int PersistentRegion::numberOfPersistents() {
int persistentCount = 0;
for (PersistentNodeSlots* slots = m_slots; slots; slots = slots->m_next) {
for (int i = 0; i < PersistentNodeSlots::slotCount; ++i) {
if (!slots->m_slot[i].isUnused())
++persistentCount;
}
}
ASSERT(persistentCount == m_persistentCount);
return persistentCount;
}
void PersistentRegion::ensurePersistentNodeSlots(void* self,
TraceCallback trace) {
ASSERT(!m_freeListHead);
PersistentNodeSlots* slots = new PersistentNodeSlots;
for (int i = 0; i < PersistentNodeSlots::slotCount; ++i) {
PersistentNode* node = &slots->m_slot[i];
node->setFreeListNext(m_freeListHead);
m_freeListHead = node;
ASSERT(node->isUnused());
}
slots->m_next = m_slots;
m_slots = slots;
}
void PersistentRegion::releasePersistentNode(
PersistentNode* persistentNode,
ThreadState::PersistentClearCallback callback) {
ASSERT(!persistentNode->isUnused());
// 'self' is in use, containing the persistent wrapper object.
void* self = persistentNode->self();
if (callback) {
(*callback)(self);
ASSERT(persistentNode->isUnused());
return;
}
Persistent<DummyGCBase>* persistent =
reinterpret_cast<Persistent<DummyGCBase>*>(self);
persistent->clear();
ASSERT(persistentNode->isUnused());
}
// This function traces all PersistentNodes. If we encounter
// a PersistentNodeSlot that contains only freed PersistentNodes,
// we delete the PersistentNodeSlot. This function rebuilds the free
// list of PersistentNodes.
void PersistentRegion::tracePersistentNodes(Visitor* visitor,
ShouldTraceCallback shouldTrace) {
size_t debugMarkedObjectSize = ProcessHeap::totalMarkedObjectSize();
base::debug::Alias(&debugMarkedObjectSize);
m_freeListHead = nullptr;
int persistentCount = 0;
PersistentNodeSlots** prevNext = &m_slots;
PersistentNodeSlots* slots = m_slots;
while (slots) {
PersistentNode* freeListNext = nullptr;
PersistentNode* freeListLast = nullptr;
int freeCount = 0;
for (int i = 0; i < PersistentNodeSlots::slotCount; ++i) {
PersistentNode* node = &slots->m_slot[i];
if (node->isUnused()) {
if (!freeListNext)
freeListLast = node;
node->setFreeListNext(freeListNext);
freeListNext = node;
++freeCount;
} else {
++persistentCount;
if (!shouldTrace(visitor, node))
continue;
node->tracePersistentNode(visitor);
debugMarkedObjectSize = ProcessHeap::totalMarkedObjectSize();
}
}
if (freeCount == PersistentNodeSlots::slotCount) {
PersistentNodeSlots* deadSlots = slots;
*prevNext = slots->m_next;
slots = slots->m_next;
delete deadSlots;
} else {
if (freeListLast) {
ASSERT(freeListNext);
ASSERT(!freeListLast->freeListNext());
freeListLast->setFreeListNext(m_freeListHead);
m_freeListHead = freeListNext;
}
prevNext = &slots->m_next;
slots = slots->m_next;
}
}
ASSERT(persistentCount == m_persistentCount);
}
bool CrossThreadPersistentRegion::shouldTracePersistentNode(
Visitor* visitor,
PersistentNode* node) {
CrossThreadPersistent<DummyGCBase>* persistent =
reinterpret_cast<CrossThreadPersistent<DummyGCBase>*>(node->self());
DCHECK(persistent);
DCHECK(!persistent->isHashTableDeletedValue());
Address rawObject = reinterpret_cast<Address>(persistent->get());
if (!rawObject)
return false;
return &visitor->heap() == &ThreadState::fromObject(rawObject)->heap();
}
void CrossThreadPersistentRegion::prepareForThreadStateTermination(
ThreadState* threadState) {
// For heaps belonging to a thread that's detaching, any cross-thread
// persistents pointing into them needs to be disabled. Do that by clearing
// out the underlying heap reference.
MutexLocker lock(m_mutex);
// TODO(sof): consider ways of reducing overhead. (e.g., tracking number of
// active CrossThreadPersistent<>s pointing into the heaps of each ThreadState
// and use that count to bail out early.)
PersistentNodeSlots* slots = m_persistentRegion->m_slots;
while (slots) {
for (int i = 0; i < PersistentNodeSlots::slotCount; ++i) {
if (slots->m_slot[i].isUnused())
continue;
// 'self' is in use, containing the cross-thread persistent wrapper
// object.
CrossThreadPersistent<DummyGCBase>* persistent =
reinterpret_cast<CrossThreadPersistent<DummyGCBase>*>(
slots->m_slot[i].self());
ASSERT(persistent);
void* rawObject = persistent->atomicGet();
if (!rawObject)
continue;
BasePage* page = pageFromObject(rawObject);
ASSERT(page);
// The main thread will upon detach just mark its heap pages as orphaned,
// but not invalidate its CrossThreadPersistent<>s.
if (page->orphaned())
continue;
if (page->arena()->getThreadState() == threadState) {
persistent->clear();
ASSERT(slots->m_slot[i].isUnused());
}
}
slots = slots->m_next;
}
}
} // namespace blink