[heap] Concurrently free empty typed slot set chunks.
BUG=chromium:648568
Review-Url: https://codereview.chromium.org/2352423002
Cr-Commit-Position: refs/heads/master@{#39605}
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
index 08472e5..aae6593 100644
--- a/src/heap/mark-compact.cc
+++ b/src/heap/mark-compact.cc
@@ -3865,6 +3865,12 @@
} else {
max_freed = RawSweep(page, REBUILD_FREE_LIST, free_space_mode);
}
+
+ // After finishing sweeping of a page we clean up its remembered set.
+ if (page->typed_old_to_new_slots()) {
+ page->typed_old_to_new_slots()->FreeToBeFreedChunks();
+ }
+
{
base::LockGuard<base::Mutex> guard(&mutex_);
swept_list_[identity].Add(page);
diff --git a/src/heap/remembered-set.cc b/src/heap/remembered-set.cc
index 6575d55..467f725 100644
--- a/src/heap/remembered-set.cc
+++ b/src/heap/remembered-set.cc
@@ -36,7 +36,8 @@
} else {
return REMOVE_SLOT;
}
- });
+ },
+ TypedSlotSet::PREFREE_EMPTY_CHUNKS);
}
}
for (MemoryChunk* chunk : *heap->map_space()) {
diff --git a/src/heap/remembered-set.h b/src/heap/remembered-set.h
index bdfae03..75aa524 100644
--- a/src/heap/remembered-set.h
+++ b/src/heap/remembered-set.h
@@ -149,10 +149,13 @@
static void RemoveRangeTyped(MemoryChunk* page, Address start, Address end) {
TypedSlotSet* slots = GetTypedSlotSet(page);
if (slots != nullptr) {
- slots->Iterate([start, end](SlotType slot_type, Address host_addr,
- Address slot_addr) {
- return start <= slot_addr && slot_addr < end ? REMOVE_SLOT : KEEP_SLOT;
- });
+ slots->Iterate(
+ [start, end](SlotType slot_type, Address host_addr,
+ Address slot_addr) {
+ return start <= slot_addr && slot_addr < end ? REMOVE_SLOT
+ : KEEP_SLOT;
+ },
+ TypedSlotSet::PREFREE_EMPTY_CHUNKS);
}
}
@@ -173,7 +176,7 @@
static void IterateTyped(MemoryChunk* chunk, Callback callback) {
TypedSlotSet* slots = GetTypedSlotSet(chunk);
if (slots != nullptr) {
- int new_count = slots->Iterate(callback);
+ int new_count = slots->Iterate(callback, TypedSlotSet::KEEP_EMPTY_CHUNKS);
if (new_count == 0) {
ReleaseTypedSlotSet(chunk);
}
diff --git a/src/heap/slot-set.h b/src/heap/slot-set.h
index 6ba3174..3690099 100644
--- a/src/heap/slot-set.h
+++ b/src/heap/slot-set.h
@@ -5,6 +5,8 @@
#ifndef V8_SLOT_SET_H
#define V8_SLOT_SET_H
+#include <stack>
+
#include "src/allocation.h"
#include "src/base/atomic-utils.h"
#include "src/base/bits.h"
@@ -259,6 +261,8 @@
// typed slots contain V8 internal pointers that are not directly exposed to JS.
class TypedSlotSet {
public:
+ enum IterationMode { PREFREE_EMPTY_CHUNKS, KEEP_EMPTY_CHUNKS };
+
typedef std::pair<SlotType, uint32_t> TypeAndOffset;
struct TypedSlot {
@@ -326,6 +330,10 @@
void Insert(SlotType type, uint32_t host_offset, uint32_t offset) {
TypedSlot slot(type, host_offset, offset);
Chunk* top_chunk = chunk_.Value();
+ if (!top_chunk) {
+ top_chunk = new Chunk(nullptr, kInitialBufferSize);
+ chunk_.SetValue(top_chunk);
+ }
if (!top_chunk->AddSlot(slot)) {
Chunk* new_top_chunk =
new Chunk(top_chunk, NextCapacity(top_chunk->capacity.Value()));
@@ -346,13 +354,15 @@
// else return REMOVE_SLOT;
// });
template <typename Callback>
- int Iterate(Callback callback) {
+ int Iterate(Callback callback, IterationMode mode) {
STATIC_ASSERT(CLEARED_SLOT < 8);
Chunk* chunk = chunk_.Value();
+ Chunk* previous = nullptr;
int new_count = 0;
while (chunk != nullptr) {
TypedSlot* buffer = chunk->buffer.Value();
int count = chunk->count.Value();
+ bool empty = true;
for (int i = 0; i < count; i++) {
// Order is important here. We have to read out the slot type last to
// observe the concurrent removal case consistently.
@@ -363,16 +373,40 @@
Address addr = page_start_ + type_and_offset.second;
if (callback(type, host_addr, addr) == KEEP_SLOT) {
new_count++;
+ empty = false;
} else {
buffer[i].Clear();
}
}
}
+
+ if (mode == PREFREE_EMPTY_CHUNKS && empty) {
+ // We remove the chunk from the list but let it still point its next
+ // chunk to allow concurrent iteration.
+ if (previous) {
+ previous->next.SetValue(chunk->next.Value());
+ } else {
+ chunk_.SetValue(chunk->next.Value());
+ }
+ base::LockGuard<base::Mutex> guard(&to_be_freed_chunks_mutex_);
+ to_be_freed_chunks_.push(chunk);
+ } else {
+ previous = chunk;
+ }
chunk = chunk->next.Value();
}
return new_count;
}
+ void FreeToBeFreedChunks() {
+ base::LockGuard<base::Mutex> guard(&to_be_freed_chunks_mutex_);
+ while (!to_be_freed_chunks_.empty()) {
+ Chunk* top = to_be_freed_chunks_.top();
+ to_be_freed_chunks_.pop();
+ delete top;
+ }
+ }
+
private:
static const int kInitialBufferSize = 100;
static const int kMaxBufferSize = 16 * KB;
@@ -411,6 +445,8 @@
Address page_start_;
base::AtomicValue<Chunk*> chunk_;
+ base::Mutex to_be_freed_chunks_mutex_;
+ std::stack<Chunk*> to_be_freed_chunks_;
};
} // namespace internal
diff --git a/test/unittests/heap/slot-set-unittest.cc b/test/unittests/heap/slot-set-unittest.cc
index 9bf8507..8db5b1a8 100644
--- a/test/unittests/heap/slot-set-unittest.cc
+++ b/test/unittests/heap/slot-set-unittest.cc
@@ -152,24 +152,29 @@
++added;
}
int iterated = 0;
- set.Iterate([&iterated, kDelta, kHostDelta](SlotType type, Address host_addr,
- Address addr) {
- uint32_t i = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr));
- uint32_t j = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(host_addr));
- EXPECT_EQ(i % CLEARED_SLOT, static_cast<uint32_t>(type));
- EXPECT_EQ(0, i % kDelta);
- EXPECT_EQ(0, j % kHostDelta);
- ++iterated;
- return i % 2 == 0 ? KEEP_SLOT : REMOVE_SLOT;
- });
+ set.Iterate(
+ [&iterated, kDelta, kHostDelta](SlotType type, Address host_addr,
+ Address addr) {
+ uint32_t i = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr));
+ uint32_t j =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(host_addr));
+ EXPECT_EQ(i % CLEARED_SLOT, static_cast<uint32_t>(type));
+ EXPECT_EQ(0, i % kDelta);
+ EXPECT_EQ(0, j % kHostDelta);
+ ++iterated;
+ return i % 2 == 0 ? KEEP_SLOT : REMOVE_SLOT;
+ },
+ TypedSlotSet::KEEP_EMPTY_CHUNKS);
EXPECT_EQ(added, iterated);
iterated = 0;
- set.Iterate([&iterated](SlotType type, Address host_addr, Address addr) {
- uint32_t i = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr));
- EXPECT_EQ(0, i % 2);
- ++iterated;
- return KEEP_SLOT;
- });
+ set.Iterate(
+ [&iterated](SlotType type, Address host_addr, Address addr) {
+ uint32_t i = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr));
+ EXPECT_EQ(0, i % 2);
+ ++iterated;
+ return KEEP_SLOT;
+ },
+ TypedSlotSet::KEEP_EMPTY_CHUNKS);
EXPECT_EQ(added / 2, iterated);
}