| Index: src/heap/slot-set.h
|
| diff --git a/src/heap/slot-set.h b/src/heap/slot-set.h
|
| index 4c5d5d7b3726df48756640d16b34b57c83508c89..6ba3174e3c42f71198bb851faa2de13cf1bd65ee 100644
|
| --- a/src/heap/slot-set.h
|
| +++ b/src/heap/slot-set.h
|
| @@ -247,7 +247,7 @@ enum SlotType {
|
| CODE_TARGET_SLOT,
|
| CODE_ENTRY_SLOT,
|
| DEBUG_TARGET_SLOT,
|
| - NUMBER_OF_SLOT_TYPES
|
| + CLEARED_SLOT
|
| };
|
|
|
| // Data structure for maintaining a multiset of typed slots in a page.
|
| @@ -259,51 +259,78 @@ enum SlotType {
|
| // typed slots contain V8 internal pointers that are not directly exposed to JS.
|
| class TypedSlotSet {
|
| public:
|
| + typedef std::pair<SlotType, uint32_t> TypeAndOffset;
|
| +
|
| 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)
|
| - : type_and_offset_(TypeField::encode(type) |
|
| - OffsetField::encode(offset)),
|
| - host_offset_(host_offset) {}
|
| + TypedSlot(SlotType type, uint32_t host_offset, uint32_t offset) {
|
| + type_and_offset_.SetValue(TypeField::encode(type) |
|
| + OffsetField::encode(offset));
|
| + host_offset_.SetValue(host_offset);
|
| + }
|
|
|
| bool operator==(const TypedSlot other) {
|
| - return type_and_offset_ == other.type_and_offset_ &&
|
| - host_offset_ == other.host_offset_;
|
| + return type_and_offset_.Value() == other.type_and_offset_.Value() &&
|
| + host_offset_.Value() == other.host_offset_.Value();
|
| }
|
|
|
| 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_;
|
| - uint32_t host_offset_;
|
| + base::AtomicValue<uint32_t> type_and_offset_;
|
| + base::AtomicValue<uint32_t> host_offset_;
|
| };
|
| static const int kMaxOffset = 1 << 29;
|
|
|
| explicit TypedSlotSet(Address page_start) : page_start_(page_start) {
|
| - chunk_ = new Chunk(nullptr, kInitialBufferSize);
|
| + chunk_.SetValue(new Chunk(nullptr, kInitialBufferSize));
|
| }
|
|
|
| ~TypedSlotSet() {
|
| - Chunk* chunk = chunk_;
|
| + Chunk* chunk = chunk_.Value();
|
| while (chunk != nullptr) {
|
| - Chunk* next = chunk->next;
|
| + Chunk* next = chunk->next.Value();
|
| delete chunk;
|
| chunk = next;
|
| }
|
| }
|
|
|
| // 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) {
|
| TypedSlot slot(type, host_offset, offset);
|
| - if (!chunk_->AddSlot(slot)) {
|
| - chunk_ = new Chunk(chunk_, NextCapacity(chunk_->capacity));
|
| - bool added = chunk_->AddSlot(slot);
|
| + Chunk* top_chunk = chunk_.Value();
|
| + if (!top_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);
|
| USE(added);
|
| }
|
| @@ -320,27 +347,28 @@ class TypedSlotSet {
|
| // });
|
| template <typename Callback>
|
| int Iterate(Callback callback) {
|
| - STATIC_ASSERT(NUMBER_OF_SLOT_TYPES < 8);
|
| - const TypedSlot kRemovedSlot(NUMBER_OF_SLOT_TYPES, 0, 0);
|
| - Chunk* chunk = chunk_;
|
| + STATIC_ASSERT(CLEARED_SLOT < 8);
|
| + Chunk* chunk = chunk_.Value();
|
| int new_count = 0;
|
| while (chunk != nullptr) {
|
| - TypedSlot* buffer = chunk->buffer;
|
| - int count = chunk->count;
|
| + TypedSlot* buffer = chunk->buffer.Value();
|
| + int count = chunk->count.Value();
|
| for (int i = 0; i < count; i++) {
|
| - TypedSlot slot = buffer[i];
|
| - if (slot != kRemovedSlot) {
|
| - SlotType type = slot.type();
|
| - Address addr = page_start_ + slot.offset();
|
| - Address host_addr = page_start_ + slot.host_offset();
|
| + // Order is important here. We have to read out the slot type last to
|
| + // observe the concurrent removal case consistently.
|
| + Address host_addr = page_start_ + buffer[i].host_offset();
|
| + TypeAndOffset type_and_offset = buffer[i].GetTypeAndOffset();
|
| + 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) {
|
| new_count++;
|
| } else {
|
| - buffer[i] = kRemovedSlot;
|
| + buffer[i].Clear();
|
| }
|
| }
|
| }
|
| - chunk = chunk->next;
|
| + chunk = chunk->next.Value();
|
| }
|
| return new_count;
|
| }
|
| @@ -357,24 +385,32 @@ class TypedSlotSet {
|
| class TypeField : public BitField<SlotType, 29, 3> {};
|
|
|
| struct Chunk : Malloced {
|
| - explicit Chunk(Chunk* next_chunk, int capacity)
|
| - : next(next_chunk), count(0), capacity(capacity) {
|
| - buffer = NewArray<TypedSlot>(capacity);
|
| + explicit Chunk(Chunk* next_chunk, int chunk_capacity) {
|
| + count.SetValue(0);
|
| + capacity.SetValue(chunk_capacity);
|
| + buffer.SetValue(NewArray<TypedSlot>(chunk_capacity));
|
| + next.SetValue(next_chunk);
|
| }
|
| bool AddSlot(TypedSlot slot) {
|
| - if (count == capacity) return false;
|
| - buffer[count++] = slot;
|
| + int current_count = count.Value();
|
| + 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;
|
| }
|
| - ~Chunk() { DeleteArray(buffer); }
|
| - Chunk* next;
|
| - int count;
|
| - int capacity;
|
| - TypedSlot* buffer;
|
| + ~Chunk() { DeleteArray(buffer.Value()); }
|
| + base::AtomicValue<Chunk*> next;
|
| + base::AtomicValue<int> count;
|
| + base::AtomicValue<int> capacity;
|
| + base::AtomicValue<TypedSlot*> buffer;
|
| };
|
|
|
| Address page_start_;
|
| - Chunk* chunk_;
|
| + base::AtomicValue<Chunk*> chunk_;
|
| };
|
|
|
| } // namespace internal
|
|
|