| // 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/allocation.h" | |
| #include "platform/assert.h" | |
| #include "vm/lockers.h" | |
| #include "vm/raw_object.h" | |
| namespace dart { | |
| class WeakTable : public MallocAllocated { | |
| 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 ReportSurvivingAllocations(Dart_HeapSamplingReportCallback callback, | |
| void* context); | |
| void CleanupValues(Dart_HeapSamplingDeleteCallback cleanup); | |
| void Reset(); | |
| private: | |
| enum { | |
| kObjectOffset = 0, | |
| kValueOffset, | |
| kEntrySize, | |
| }; | |
| static constexpr intptr_t kNoEntry = 1; // Not a valid OOP. | |
| static constexpr intptr_t kDeletedEntry = 3; // Not a valid OOP. | |
| static constexpr 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_ |