| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef RUNTIME_VM_HEAP_WEAK_TABLE_H_ |
| #define RUNTIME_VM_HEAP_WEAK_TABLE_H_ |
| |
| #include "vm/globals.h" |
| |
| #include "platform/assert.h" |
| #include "vm/lockers.h" |
| #include "vm/raw_object.h" |
| |
| namespace dart { |
| |
| class WeakTable { |
| public: |
| static constexpr intptr_t kNoValue = 0; |
| |
| WeakTable() : WeakTable(kMinSize) {} |
| explicit WeakTable(intptr_t size) : used_(0), count_(0) { |
| ASSERT(size >= 0); |
| ASSERT(Utils::IsPowerOfTwo(kMinSize)); |
| if (size < kMinSize) { |
| size = kMinSize; |
| } |
| // Get a max size that avoids overflows. |
| const intptr_t kMaxSize = |
| (kIntptrOne << (kBitsPerWord - 2)) / (kEntrySize * kWordSize); |
| ASSERT(Utils::IsPowerOfTwo(kMaxSize)); |
| if (size > kMaxSize) { |
| size = kMaxSize; |
| } |
| size_ = size; |
| ASSERT(Utils::IsPowerOfTwo(size_)); |
| data_ = reinterpret_cast<intptr_t*>(malloc(size_ * kEntrySize * kWordSize)); |
| for (intptr_t i = 0; i < size_; i++) { |
| data_[ObjectIndex(i)] = kNoEntry; |
| data_[ValueIndex(i)] = kNoValue; |
| } |
| } |
| |
| ~WeakTable() { free(data_); } |
| |
| static WeakTable* NewFrom(WeakTable* original) { |
| return new WeakTable(SizeFor(original->count(), original->size())); |
| } |
| |
| intptr_t size() const { return size_; } |
| intptr_t used() const { return used_; } |
| intptr_t count() const { return count_; } |
| |
| // The following methods can be called concurrently and are guarded by a lock. |
| |
| intptr_t GetValue(ObjectPtr key) { |
| MutexLocker ml(&mutex_); |
| return GetValueExclusive(key); |
| } |
| |
| void SetValue(ObjectPtr key, intptr_t val) { |
| MutexLocker ml(&mutex_); |
| return SetValueExclusive(key, val); |
| } |
| |
| intptr_t SetValueIfNonExistent(ObjectPtr key, intptr_t val) { |
| MutexLocker ml(&mutex_); |
| const auto old_value = GetValueExclusive(key); |
| if (old_value == kNoValue) { |
| SetValueExclusive(key, val); |
| return val; |
| } |
| return old_value; |
| } |
| |
| // The following "exclusive" methods must only be called from call sites |
| // which are known to have exclusive access to the weak table. |
| // |
| // This is mostly limited to GC related code (e.g. scavenger, marker, ...) |
| |
| bool IsValidEntryAtExclusive(intptr_t i) const { |
| ASSERT((ValueAtExclusive(i) == 0 && |
| (data_[ObjectIndex(i)] == kNoEntry || |
| data_[ObjectIndex(i)] == kDeletedEntry)) || |
| (ValueAtExclusive(i) != 0 && data_[ObjectIndex(i)] != kNoEntry && |
| data_[ObjectIndex(i)] != kDeletedEntry)); |
| return (data_[ValueIndex(i)] != 0); |
| } |
| |
| void InvalidateAtExclusive(intptr_t i) { |
| ASSERT(IsValidEntryAtExclusive(i)); |
| SetValueAt(i, 0); |
| } |
| |
| ObjectPtr ObjectAtExclusive(intptr_t i) const { |
| ASSERT(i >= 0); |
| ASSERT(i < size()); |
| return static_cast<ObjectPtr>(data_[ObjectIndex(i)]); |
| } |
| |
| intptr_t ValueAtExclusive(intptr_t i) const { |
| ASSERT(i >= 0); |
| ASSERT(i < size()); |
| return data_[ValueIndex(i)]; |
| } |
| |
| void SetValueExclusive(ObjectPtr key, intptr_t val); |
| bool MarkValueExclusive(ObjectPtr key, intptr_t val); |
| |
| intptr_t GetValueExclusive(ObjectPtr key) const { |
| intptr_t mask = size() - 1; |
| intptr_t idx = Hash(key) & mask; |
| ObjectPtr obj = ObjectAtExclusive(idx); |
| while (obj != static_cast<ObjectPtr>(kNoEntry)) { |
| if (obj == key) { |
| return ValueAtExclusive(idx); |
| } |
| idx = (idx + 1) & mask; |
| obj = ObjectAtExclusive(idx); |
| } |
| ASSERT(ValueAtExclusive(idx) == 0); |
| return kNoValue; |
| } |
| |
| // Removes and returns the value associated with |key|. Returns 0 if there is |
| // no value associated with |key|. |
| intptr_t RemoveValueExclusive(ObjectPtr key) { |
| intptr_t mask = size() - 1; |
| intptr_t idx = Hash(key) & mask; |
| ObjectPtr obj = ObjectAtExclusive(idx); |
| while (obj != static_cast<ObjectPtr>(kNoEntry)) { |
| if (obj == key) { |
| intptr_t result = ValueAtExclusive(idx); |
| InvalidateAtExclusive(idx); |
| return result; |
| } |
| idx = (idx + 1) & mask; |
| obj = ObjectAtExclusive(idx); |
| } |
| ASSERT(ValueAtExclusive(idx) == 0); |
| return kNoValue; |
| } |
| |
| void Forward(ObjectPointerVisitor* visitor); |
| |
| void Reset(); |
| |
| private: |
| enum { |
| kObjectOffset = 0, |
| kValueOffset, |
| kEntrySize, |
| }; |
| |
| static const intptr_t kNoEntry = 1; // Not a valid OOP. |
| static const intptr_t kDeletedEntry = 3; // Not a valid OOP. |
| static const intptr_t kMinSize = 8; |
| |
| static intptr_t SizeFor(intptr_t count, intptr_t size); |
| static intptr_t LimitFor(intptr_t size) { |
| // Maintain a maximum of 75% fill rate. |
| return 3 * (size / 4); |
| } |
| intptr_t limit() const { return LimitFor(size()); } |
| |
| intptr_t index(intptr_t i) const { return i * kEntrySize; } |
| |
| void set_used(intptr_t val) { |
| ASSERT(val <= limit()); |
| used_ = val; |
| } |
| |
| void set_count(intptr_t val) { |
| ASSERT(val <= limit()); |
| ASSERT(val <= used()); |
| count_ = val; |
| } |
| |
| intptr_t ObjectIndex(intptr_t i) const { return index(i) + kObjectOffset; } |
| |
| intptr_t ValueIndex(intptr_t i) const { return index(i) + kValueOffset; } |
| |
| ObjectPtr* ObjectPointerAt(intptr_t i) const { |
| ASSERT(i >= 0); |
| ASSERT(i < size()); |
| return reinterpret_cast<ObjectPtr*>(&data_[ObjectIndex(i)]); |
| } |
| |
| void SetObjectAt(intptr_t i, ObjectPtr key) { |
| ASSERT(i >= 0); |
| ASSERT(i < size()); |
| data_[ObjectIndex(i)] = static_cast<intptr_t>(key); |
| } |
| |
| void SetValueAt(intptr_t i, intptr_t val) { |
| ASSERT(i >= 0); |
| ASSERT(i < size()); |
| // Setting a value of 0 is equivalent to invalidating the entry. |
| if (val == 0) { |
| data_[ObjectIndex(i)] = kDeletedEntry; |
| set_count(count() - 1); |
| } |
| data_[ValueIndex(i)] = val; |
| } |
| |
| void Rehash(); |
| |
| static uword Hash(ObjectPtr key) { |
| return (static_cast<uword>(key) * 92821) ^ (static_cast<uword>(key) >> 8); |
| } |
| |
| Mutex mutex_; |
| |
| // data_ contains size_ tuples of key/value. |
| intptr_t* data_; |
| // size_ keeps the number of entries in data_. used_ maintains the number of |
| // non-NULL entries and will trigger rehashing if needed. count_ stores the |
| // number valid entries, and will determine the size_ after rehashing. |
| intptr_t size_; |
| intptr_t used_; |
| intptr_t count_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WeakTable); |
| }; |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_HEAP_WEAK_TABLE_H_ |