Commit 6f06c36c authored by hpayer's avatar hpayer Committed by Commit bot

[heap] Make typed slot set state and operations atomic.

BUG=chromium:648568

Review-Url: https://codereview.chromium.org/2360513002
Cr-Commit-Position: refs/heads/master@{#39596}
parent d2c975d2
...@@ -363,7 +363,7 @@ class UpdateTypedSlotHelper { ...@@ -363,7 +363,7 @@ class UpdateTypedSlotHelper {
case OBJECT_SLOT: { case OBJECT_SLOT: {
return callback(reinterpret_cast<Object**>(addr)); return callback(reinterpret_cast<Object**>(addr));
} }
case NUMBER_OF_SLOT_TYPES: case CLEARED_SLOT:
break; break;
} }
UNREACHABLE(); UNREACHABLE();
...@@ -382,7 +382,7 @@ inline SlotType SlotTypeForRelocInfoMode(RelocInfo::Mode rmode) { ...@@ -382,7 +382,7 @@ inline SlotType SlotTypeForRelocInfoMode(RelocInfo::Mode rmode) {
return DEBUG_TARGET_SLOT; return DEBUG_TARGET_SLOT;
} }
UNREACHABLE(); UNREACHABLE();
return NUMBER_OF_SLOT_TYPES; return CLEARED_SLOT;
} }
} // namespace internal } // namespace internal
......
...@@ -247,7 +247,7 @@ enum SlotType { ...@@ -247,7 +247,7 @@ enum SlotType {
CODE_TARGET_SLOT, CODE_TARGET_SLOT,
CODE_ENTRY_SLOT, CODE_ENTRY_SLOT,
DEBUG_TARGET_SLOT, DEBUG_TARGET_SLOT,
NUMBER_OF_SLOT_TYPES CLEARED_SLOT
}; };
// Data structure for maintaining a multiset of typed slots in a page. // Data structure for maintaining a multiset of typed slots in a page.
...@@ -259,51 +259,78 @@ enum SlotType { ...@@ -259,51 +259,78 @@ enum SlotType {
// typed slots contain V8 internal pointers that are not directly exposed to JS. // typed slots contain V8 internal pointers that are not directly exposed to JS.
class TypedSlotSet { class TypedSlotSet {
public: public:
typedef std::pair<SlotType, uint32_t> TypeAndOffset;
struct TypedSlot { struct TypedSlot {
TypedSlot() : type_and_offset_(0), host_offset_(0) {} TypedSlot() {
type_and_offset_.SetValue(0);
host_offset_.SetValue(0);
}
TypedSlot(SlotType type, uint32_t host_offset, uint32_t offset) TypedSlot(SlotType type, uint32_t host_offset, uint32_t offset) {
: type_and_offset_(TypeField::encode(type) | type_and_offset_.SetValue(TypeField::encode(type) |
OffsetField::encode(offset)), OffsetField::encode(offset));
host_offset_(host_offset) {} host_offset_.SetValue(host_offset);
}
bool operator==(const TypedSlot other) { bool operator==(const TypedSlot other) {
return type_and_offset_ == other.type_and_offset_ && return type_and_offset_.Value() == other.type_and_offset_.Value() &&
host_offset_ == other.host_offset_; host_offset_.Value() == other.host_offset_.Value();
} }
bool operator!=(const TypedSlot other) { return !(*this == other); } bool operator!=(const TypedSlot other) { return !(*this == other); }
SlotType type() { return TypeField::decode(type_and_offset_); } SlotType type() { return TypeField::decode(type_and_offset_.Value()); }
uint32_t offset() { return OffsetField::decode(type_and_offset_); } uint32_t offset() { return OffsetField::decode(type_and_offset_.Value()); }
TypeAndOffset GetTypeAndOffset() {
uint32_t type_and_offset = type_and_offset_.Value();
return std::make_pair(TypeField::decode(type_and_offset),
OffsetField::decode(type_and_offset));
}
uint32_t host_offset() { return host_offset_; } uint32_t host_offset() { return host_offset_.Value(); }
void Set(TypedSlot slot) {
type_and_offset_.SetValue(slot.type_and_offset_.Value());
host_offset_.SetValue(slot.host_offset_.Value());
}
void Clear() {
type_and_offset_.SetValue(TypeField::encode(CLEARED_SLOT) |
OffsetField::encode(0));
host_offset_.SetValue(0);
}
uint32_t type_and_offset_; base::AtomicValue<uint32_t> type_and_offset_;
uint32_t host_offset_; base::AtomicValue<uint32_t> host_offset_;
}; };
static const int kMaxOffset = 1 << 29; static const int kMaxOffset = 1 << 29;
explicit TypedSlotSet(Address page_start) : page_start_(page_start) { explicit TypedSlotSet(Address page_start) : page_start_(page_start) {
chunk_ = new Chunk(nullptr, kInitialBufferSize); chunk_.SetValue(new Chunk(nullptr, kInitialBufferSize));
} }
~TypedSlotSet() { ~TypedSlotSet() {
Chunk* chunk = chunk_; Chunk* chunk = chunk_.Value();
while (chunk != nullptr) { while (chunk != nullptr) {
Chunk* next = chunk->next; Chunk* next = chunk->next.Value();
delete chunk; delete chunk;
chunk = next; chunk = next;
} }
} }
// The slot offset specifies a slot at address page_start_ + offset. // The slot offset specifies a slot at address page_start_ + offset.
// This method can only be called on the main thread.
void Insert(SlotType type, uint32_t host_offset, uint32_t offset) { void Insert(SlotType type, uint32_t host_offset, uint32_t offset) {
TypedSlot slot(type, host_offset, offset); TypedSlot slot(type, host_offset, offset);
if (!chunk_->AddSlot(slot)) { Chunk* top_chunk = chunk_.Value();
chunk_ = new Chunk(chunk_, NextCapacity(chunk_->capacity)); if (!top_chunk->AddSlot(slot)) {
bool added = chunk_->AddSlot(slot); Chunk* new_top_chunk =
new Chunk(top_chunk, NextCapacity(top_chunk->capacity.Value()));
bool added = new_top_chunk->AddSlot(slot);
chunk_.SetValue(new_top_chunk);
DCHECK(added); DCHECK(added);
USE(added); USE(added);
} }
...@@ -320,27 +347,28 @@ class TypedSlotSet { ...@@ -320,27 +347,28 @@ class TypedSlotSet {
// }); // });
template <typename Callback> template <typename Callback>
int Iterate(Callback callback) { int Iterate(Callback callback) {
STATIC_ASSERT(NUMBER_OF_SLOT_TYPES < 8); STATIC_ASSERT(CLEARED_SLOT < 8);
const TypedSlot kRemovedSlot(NUMBER_OF_SLOT_TYPES, 0, 0); Chunk* chunk = chunk_.Value();
Chunk* chunk = chunk_;
int new_count = 0; int new_count = 0;
while (chunk != nullptr) { while (chunk != nullptr) {
TypedSlot* buffer = chunk->buffer; TypedSlot* buffer = chunk->buffer.Value();
int count = chunk->count; int count = chunk->count.Value();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
TypedSlot slot = buffer[i]; // Order is important here. We have to read out the slot type last to
if (slot != kRemovedSlot) { // observe the concurrent removal case consistently.
SlotType type = slot.type(); Address host_addr = page_start_ + buffer[i].host_offset();
Address addr = page_start_ + slot.offset(); TypeAndOffset type_and_offset = buffer[i].GetTypeAndOffset();
Address host_addr = page_start_ + slot.host_offset(); SlotType type = type_and_offset.first;
if (type != CLEARED_SLOT) {
Address addr = page_start_ + type_and_offset.second;
if (callback(type, host_addr, addr) == KEEP_SLOT) { if (callback(type, host_addr, addr) == KEEP_SLOT) {
new_count++; new_count++;
} else { } else {
buffer[i] = kRemovedSlot; buffer[i].Clear();
} }
} }
} }
chunk = chunk->next; chunk = chunk->next.Value();
} }
return new_count; return new_count;
} }
...@@ -357,24 +385,32 @@ class TypedSlotSet { ...@@ -357,24 +385,32 @@ class TypedSlotSet {
class TypeField : public BitField<SlotType, 29, 3> {}; class TypeField : public BitField<SlotType, 29, 3> {};
struct Chunk : Malloced { struct Chunk : Malloced {
explicit Chunk(Chunk* next_chunk, int capacity) explicit Chunk(Chunk* next_chunk, int chunk_capacity) {
: next(next_chunk), count(0), capacity(capacity) { count.SetValue(0);
buffer = NewArray<TypedSlot>(capacity); capacity.SetValue(chunk_capacity);
buffer.SetValue(NewArray<TypedSlot>(chunk_capacity));
next.SetValue(next_chunk);
} }
bool AddSlot(TypedSlot slot) { bool AddSlot(TypedSlot slot) {
if (count == capacity) return false; int current_count = count.Value();
buffer[count++] = slot; if (current_count == capacity.Value()) return false;
TypedSlot* current_buffer = buffer.Value();
// Order is important here. We have to write the slot first before
// increasing the counter to guarantee that a consistent state is
// observed by concurrent threads.
current_buffer[current_count].Set(slot);
count.SetValue(current_count + 1);
return true; return true;
} }
~Chunk() { DeleteArray(buffer); } ~Chunk() { DeleteArray(buffer.Value()); }
Chunk* next; base::AtomicValue<Chunk*> next;
int count; base::AtomicValue<int> count;
int capacity; base::AtomicValue<int> capacity;
TypedSlot* buffer; base::AtomicValue<TypedSlot*> buffer;
}; };
Address page_start_; Address page_start_;
Chunk* chunk_; base::AtomicValue<Chunk*> chunk_;
}; };
} // namespace internal } // namespace internal
......
...@@ -147,7 +147,7 @@ TEST(TypedSlotSet, Iterate) { ...@@ -147,7 +147,7 @@ TEST(TypedSlotSet, Iterate) {
uint32_t j = 0; uint32_t j = 0;
for (uint32_t i = 0; i < TypedSlotSet::kMaxOffset; for (uint32_t i = 0; i < TypedSlotSet::kMaxOffset;
i += kDelta, j += kHostDelta) { i += kDelta, j += kHostDelta) {
SlotType type = static_cast<SlotType>(i % NUMBER_OF_SLOT_TYPES); SlotType type = static_cast<SlotType>(i % CLEARED_SLOT);
set.Insert(type, j, i); set.Insert(type, j, i);
++added; ++added;
} }
...@@ -156,7 +156,7 @@ TEST(TypedSlotSet, Iterate) { ...@@ -156,7 +156,7 @@ TEST(TypedSlotSet, Iterate) {
Address addr) { Address addr) {
uint32_t i = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(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)); uint32_t j = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(host_addr));
EXPECT_EQ(i % NUMBER_OF_SLOT_TYPES, static_cast<uint32_t>(type)); EXPECT_EQ(i % CLEARED_SLOT, static_cast<uint32_t>(type));
EXPECT_EQ(0, i % kDelta); EXPECT_EQ(0, i % kDelta);
EXPECT_EQ(0, j % kHostDelta); EXPECT_EQ(0, j % kHostDelta);
++iterated; ++iterated;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment