mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 16:00:45 +00:00
- Add a WeakTable to the VM. This is used to remember the
native peers registered through the Dart C API as well as assigning identity hashcodes to objects when needed. - Use the hashcode to lookup entries in the Expando. R=asiva@google.com Review URL: https://codereview.chromium.org//17992002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@24563 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
c326893f6a
commit
9c9989d2f6
|
@ -3,61 +3,135 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
patch class Expando<T> {
|
||||
/* patch */ Expando([this.name]) : _data = new List();
|
||||
/* patch */ Expando([this.name])
|
||||
: _data = new List(_minSize),
|
||||
_used = 0;
|
||||
|
||||
static const _minSize = 8;
|
||||
static final _deletedEntry = new _WeakProperty(null, null);
|
||||
|
||||
/* patch */ T operator[](Object object) {
|
||||
_checkType(object);
|
||||
var doCompact = false;
|
||||
var result = null;
|
||||
for (int i = 0; i < _data.length; ++i) {
|
||||
var key = _data[i].key;
|
||||
if (identical(key, object)) {
|
||||
result = _data[i].value;
|
||||
break;
|
||||
}
|
||||
if (key == null) {
|
||||
doCompact = true;
|
||||
_data[i] = null;
|
||||
|
||||
var mask = _size - 1;
|
||||
var idx = object.hashCode & mask;
|
||||
var wp = _data[idx];
|
||||
|
||||
while (wp != null) {
|
||||
if (identical(wp.key, object)) {
|
||||
return wp.value;
|
||||
} else if (wp.key == null) {
|
||||
// This entry has been cleared by the GC.
|
||||
_data[idx] = _deletedEntry;
|
||||
}
|
||||
idx = (idx + 1) & mask;
|
||||
wp = _data[idx];
|
||||
}
|
||||
if (doCompact) {
|
||||
_data = _data.where((e) => (e != null)).toList();
|
||||
}
|
||||
return result;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/* patch */ void operator[]=(Object object, T value) {
|
||||
_checkType(object);
|
||||
var doCompact = false;
|
||||
int i = 0;
|
||||
for (; i < _data.length; ++i) {
|
||||
var key = _data[i].key;
|
||||
if (identical(key, object)) {
|
||||
break;
|
||||
|
||||
var mask = _size - 1;
|
||||
var idx = object.hashCode & mask;
|
||||
var empty_idx = -1;
|
||||
var wp = _data[idx];
|
||||
|
||||
while (wp != null) {
|
||||
if (identical(wp.key, object)) {
|
||||
if (value != null) {
|
||||
// Update the associated value.
|
||||
wp.value = value;
|
||||
} else {
|
||||
// Mark the entry as deleted.
|
||||
_data[idx] = _deletedEntry;
|
||||
}
|
||||
return;
|
||||
} else if ((empty_idx < 0) && identical(wp, _deletedEntry)) {
|
||||
empty_idx = idx; // Insert at this location if not found.
|
||||
} else if (wp.key == null) {
|
||||
// This entry has been cleared by the GC.
|
||||
_data[idx] = _deletedEntry;
|
||||
if (empty_idx < 0) {
|
||||
empty_idx = idx; // Insert at this location if not found.
|
||||
}
|
||||
}
|
||||
if (key == null) {
|
||||
doCompact = true;
|
||||
_data[i] = null;
|
||||
idx = (idx + 1) & mask;
|
||||
wp = _data[idx];
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
// Not entering a null value. We just needed to make sure to clear an
|
||||
// existing value if it existed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty_idx >= 0) {
|
||||
// We will be reusing the empty slot below.
|
||||
_used--;
|
||||
idx = empty_idx;
|
||||
}
|
||||
|
||||
if (_used < _limit) {
|
||||
_data[idx] = new _WeakProperty(object, value);
|
||||
_used++;
|
||||
return;
|
||||
}
|
||||
|
||||
// Grow/reallocate if too many slots have been used.
|
||||
_rehash();
|
||||
this[object] = value; // Recursively add the value.
|
||||
}
|
||||
|
||||
_rehash() {
|
||||
// Determine the population count of the map to allocate an appropriately
|
||||
// sized map below.
|
||||
var count = 0;
|
||||
var old_data = _data;
|
||||
var len = old_data.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var entry = old_data[i];
|
||||
if ((entry != null) && (entry.key != null)) {
|
||||
// Only count non-cleared entries.
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (i != _data.length && value == null) {
|
||||
doCompact = true;
|
||||
_data[i] = null;
|
||||
} else if (i != _data.length) {
|
||||
_data[i].value = value;
|
||||
} else {
|
||||
_data.add(new _WeakProperty(object, value));
|
||||
|
||||
var new_size = _size;
|
||||
if (count <= (new_size >> 2)) {
|
||||
new_size = new_size >> 1;
|
||||
} else if (count > (new_size >> 1)) {
|
||||
new_size = new_size << 1;
|
||||
}
|
||||
if (doCompact) {
|
||||
_data = _data.where((e) => (e != null)).toList();
|
||||
new_size = (new_size < _minSize) ? _minSize : new_size;
|
||||
|
||||
// Reset the mappings to empty so that we can just add the existing
|
||||
// valid entries.
|
||||
_data = new List(new_size);
|
||||
_used = 0;
|
||||
|
||||
for (var i = 0; i < old_data.length; i++) {
|
||||
var entry = old_data[i];
|
||||
if ((entry != null) && (entry.key != null)) {
|
||||
this[entry.key] = entry.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static _checkType(object) {
|
||||
if (object == null || object is bool || object is num || object is String) {
|
||||
if ((object == null) ||
|
||||
(object is bool) ||
|
||||
(object is num) ||
|
||||
(object is String)) {
|
||||
throw new ArgumentError(object);
|
||||
}
|
||||
}
|
||||
|
||||
get _size => _data.length;
|
||||
get _limit => (3 * (_size ~/ 4));
|
||||
|
||||
List _data;
|
||||
int _used; // Number of used (active and deleted) slots.
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "vm/bootstrap_natives.h"
|
||||
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/heap.h"
|
||||
#include "vm/native_entry.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/stack_frame.h"
|
||||
|
@ -21,6 +22,22 @@ DEFINE_NATIVE_ENTRY(Object_cid, 1) {
|
|||
}
|
||||
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Object_getHash, 1) {
|
||||
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
||||
Heap* heap = isolate->heap();
|
||||
return Smi::New(heap->GetHash(instance.raw()));
|
||||
}
|
||||
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Object_setHash, 2) {
|
||||
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Smi, hash, arguments->NativeArgAt(1));
|
||||
Heap* heap = isolate->heap();
|
||||
heap->SetHash(instance.raw(), hash.Value());
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Object_toString, 1) {
|
||||
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
||||
const char* c_str = instance.ToCString();
|
||||
|
|
|
@ -4,20 +4,26 @@
|
|||
|
||||
patch class Object {
|
||||
|
||||
// Helpers used to implement hashCode. If a hashCode is used we remember it
|
||||
// using an Expando object. A new hashCode value is calculated using a Random
|
||||
// Helpers used to implement hashCode. If a hashCode is used, we remember it
|
||||
// in a weak table in the VM. A new hashCode value is calculated using a
|
||||
// number generator.
|
||||
static Expando _hashCodeExp = new Expando("Object.hashCode");
|
||||
static Random _hashCodeRnd = new Random();
|
||||
static final _hashCodeRnd = new Random();
|
||||
|
||||
static _getHash(obj) native "Object_getHash";
|
||||
static _setHash(obj, hash) native "Object_setHash";
|
||||
|
||||
/* patch */ int get hashCode {
|
||||
if (this == null) {
|
||||
return 2011; // The year Dart was announced and a prime.
|
||||
}
|
||||
var result = _hashCodeExp[this];
|
||||
if (result == null) {
|
||||
result = _hashCodeRnd.nextInt(0x40000000); // Stay in Smi range.
|
||||
_hashCodeExp[this] = result;
|
||||
var result = _getHash(this);
|
||||
if (result == 0) {
|
||||
// We want the hash to be a Smi value greater than 0.
|
||||
result = _hashCodeRnd.nextInt(0x40000000);
|
||||
while (result == 0) {
|
||||
result = _hashCodeRnd.nextInt(0x40000000);
|
||||
}
|
||||
_setHash(this, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace dart {
|
|||
|
||||
// List of bootstrap native entry points used in the core dart library.
|
||||
#define BOOTSTRAP_NATIVE_LIST(V) \
|
||||
V(Object_getHash, 1) \
|
||||
V(Object_setHash, 2) \
|
||||
V(Object_toString, 1) \
|
||||
V(Object_noSuchMethod, 6) \
|
||||
V(Object_runtimeType, 1) \
|
||||
|
|
|
@ -379,18 +379,20 @@ void GCMarker::ProcessWeakProperty(RawWeakProperty* raw_weak,
|
|||
}
|
||||
|
||||
|
||||
void GCMarker::ProcessPeerReferents(PageSpace* page_space) {
|
||||
PageSpace::PeerTable* peer_table = page_space->GetPeerTable();
|
||||
PageSpace::PeerTable::iterator it = peer_table->begin();
|
||||
while (it != peer_table->end()) {
|
||||
RawObject* raw_obj = it->first;
|
||||
ASSERT(raw_obj->IsHeapObject());
|
||||
if (raw_obj->IsMarked()) {
|
||||
// The object has survived. Do nothing.
|
||||
++it;
|
||||
} else {
|
||||
// The object has become garbage. Remove its record.
|
||||
peer_table->erase(it++);
|
||||
void GCMarker::ProcessWeakTables(PageSpace* page_space) {
|
||||
for (Heap::WeakSelector sel = static_cast<Heap::WeakSelector>(0);
|
||||
sel < Heap::kNumWeakSelectors;
|
||||
sel++) {
|
||||
WeakTable* table = heap_->GetWeakTable(Heap::kOld, sel);
|
||||
intptr_t size = table->size();
|
||||
for (intptr_t i = 0; i < size; i++) {
|
||||
if (table->IsValidEntryAt(i)) {
|
||||
RawObject* raw_obj = table->ObjectAt(i);
|
||||
ASSERT(raw_obj->IsHeapObject());
|
||||
if (!raw_obj->IsMarked()) {
|
||||
table->InvalidateAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +410,7 @@ void GCMarker::MarkObjects(Isolate* isolate,
|
|||
MarkingWeakVisitor mark_weak;
|
||||
IterateWeakRoots(isolate, &mark_weak, invoke_api_callbacks);
|
||||
mark.Finalize();
|
||||
ProcessPeerReferents(page_space);
|
||||
ProcessWeakTables(page_space);
|
||||
Epilogue(isolate, invoke_api_callbacks);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class GCMarker : public ValueObject {
|
|||
void IterateWeakReferences(Isolate* isolate, MarkingVisitor* visitor);
|
||||
void DrainMarkingStack(Isolate* isolate, MarkingVisitor* visitor);
|
||||
void ProcessWeakProperty(RawWeakProperty* raw_weak, MarkingVisitor* visitor);
|
||||
void ProcessPeerReferents(PageSpace* page_space);
|
||||
void ProcessWeakTables(PageSpace* page_space);
|
||||
|
||||
Heap* heap_;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "vm/stack_frame.h"
|
||||
#include "vm/verifier.h"
|
||||
#include "vm/virtual_memory.h"
|
||||
#include "vm/weak_table.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
|
@ -35,7 +36,13 @@ DEFINE_FLAG(int, old_gen_heap_size, Heap::kHeapSizeInMB,
|
|||
"old gen heap size in MB,"
|
||||
"e.g: --old_gen_heap_size=1024 allocates a 1024MB old gen heap");
|
||||
|
||||
Heap::Heap() : read_only_(false), gc_in_progress_(false) {
|
||||
Heap::Heap() : read_only_(false), gc_in_progress_(false) {
|
||||
for (WeakSelector sel = static_cast<WeakSelector>(0);
|
||||
sel < kNumWeakSelectors;
|
||||
sel++) {
|
||||
new_weak_tables_[sel] = new WeakTable(0);
|
||||
old_weak_tables_[sel] = new WeakTable(0);
|
||||
}
|
||||
new_space_ = new Scavenger(this,
|
||||
(FLAG_new_gen_heap_size * MB),
|
||||
kNewObjectAlignmentOffset);
|
||||
|
@ -360,27 +367,33 @@ const char* Heap::GCReasonToString(GCReason gc_reason) {
|
|||
}
|
||||
|
||||
|
||||
void Heap::SetPeer(RawObject* raw_obj, void* peer) {
|
||||
if (raw_obj->IsNewObject()) {
|
||||
new_space_->SetPeer(raw_obj, peer);
|
||||
} else {
|
||||
ASSERT(raw_obj->IsOldObject());
|
||||
old_space_->SetPeer(raw_obj, peer);
|
||||
}
|
||||
int64_t Heap::PeerCount() const {
|
||||
return new_weak_tables_[kPeers]->count() + old_weak_tables_[kPeers]->count();
|
||||
}
|
||||
|
||||
|
||||
void* Heap::GetPeer(RawObject* raw_obj) {
|
||||
int64_t Heap::HashCount() const {
|
||||
return
|
||||
new_weak_tables_[kHashes]->count() + old_weak_tables_[kHashes]->count();
|
||||
}
|
||||
|
||||
|
||||
intptr_t Heap::GetWeakEntry(RawObject* raw_obj, WeakSelector sel) const {
|
||||
if (raw_obj->IsNewObject()) {
|
||||
return new_space_->GetPeer(raw_obj);
|
||||
return new_weak_tables_[sel]->GetValue(raw_obj);
|
||||
}
|
||||
ASSERT(raw_obj->IsOldObject());
|
||||
return old_space_->GetPeer(raw_obj);
|
||||
return old_weak_tables_[sel]->GetValue(raw_obj);
|
||||
}
|
||||
|
||||
|
||||
int64_t Heap::PeerCount() const {
|
||||
return new_space_->PeerCount() + old_space_->PeerCount();
|
||||
void Heap::SetWeakEntry(RawObject* raw_obj, WeakSelector sel, intptr_t val) {
|
||||
if (raw_obj->IsNewObject()) {
|
||||
new_weak_tables_[sel] = new_weak_tables_[sel]->SetValue(raw_obj, val);
|
||||
} else {
|
||||
ASSERT(raw_obj->IsOldObject());
|
||||
old_weak_tables_[sel] = old_weak_tables_[sel]->SetValue(raw_obj, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "vm/globals.h"
|
||||
#include "vm/pages.h"
|
||||
#include "vm/scavenger.h"
|
||||
#include "vm/weak_table.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
|
@ -33,6 +34,12 @@ class Heap {
|
|||
kCode,
|
||||
};
|
||||
|
||||
enum WeakSelector {
|
||||
kPeers = 0,
|
||||
kHashes,
|
||||
kNumWeakSelectors
|
||||
};
|
||||
|
||||
enum ApiCallbacks {
|
||||
kIgnoreApiCallbacks,
|
||||
kInvokeApiCallbacks
|
||||
|
@ -168,17 +175,45 @@ class Heap {
|
|||
|
||||
static const char* GCReasonToString(GCReason gc_reason);
|
||||
|
||||
// Associates a peer with an object. If an object has a peer, it is
|
||||
// replaced. A value of NULL disassociate an object from its peer.
|
||||
void SetPeer(RawObject* raw_obj, void* peer);
|
||||
|
||||
// Retrieves the peer associated with an object. Returns NULL if
|
||||
// there is no association.
|
||||
void* GetPeer(RawObject* raw_obj);
|
||||
|
||||
// Returns the number of objects with a peer.
|
||||
// Associate a peer with an object. A non-existent peer is equal to NULL.
|
||||
void SetPeer(RawObject* raw_obj, void* peer) {
|
||||
SetWeakEntry(raw_obj, kPeers, reinterpret_cast<intptr_t>(peer));
|
||||
}
|
||||
void* GetPeer(RawObject* raw_obj) const {
|
||||
return reinterpret_cast<void*>(GetWeakEntry(raw_obj, kPeers));
|
||||
}
|
||||
int64_t PeerCount() const;
|
||||
|
||||
// Associate an identity hashCode with an object. An non-existent hashCode
|
||||
// is equal to 0.
|
||||
void SetHash(RawObject* raw_obj, intptr_t hash) {
|
||||
SetWeakEntry(raw_obj, kHashes, hash);
|
||||
}
|
||||
intptr_t GetHash(RawObject* raw_obj) const {
|
||||
return GetWeakEntry(raw_obj, kHashes);
|
||||
}
|
||||
int64_t HashCount() const;
|
||||
|
||||
// Used by the GC algorithms to propagate weak entries.
|
||||
intptr_t GetWeakEntry(RawObject* raw_obj, WeakSelector sel) const;
|
||||
void SetWeakEntry(RawObject* raw_obj, WeakSelector sel, intptr_t val);
|
||||
|
||||
WeakTable* GetWeakTable(Space space, WeakSelector selector) const {
|
||||
if (space == kNew) {
|
||||
return new_weak_tables_[selector];
|
||||
}
|
||||
ASSERT(space ==kOld);
|
||||
return old_weak_tables_[selector];
|
||||
}
|
||||
void SetWeakTable(Space space, WeakSelector selector, WeakTable* value) {
|
||||
if (space == kNew) {
|
||||
new_weak_tables_[selector] = value;
|
||||
} else {
|
||||
ASSERT(space == kOld);
|
||||
old_weak_tables_[selector] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Stats collection.
|
||||
void RecordTime(int id, int64_t micros) {
|
||||
ASSERT((id >= 0) && (id < GCStats::kDataEntries));
|
||||
|
@ -246,6 +281,9 @@ class Heap {
|
|||
Scavenger* new_space_;
|
||||
PageSpace* old_space_;
|
||||
|
||||
WeakTable* new_weak_tables_[kNumWeakSelectors];
|
||||
WeakTable* old_weak_tables_[kNumWeakSelectors];
|
||||
|
||||
// GC stats collection.
|
||||
GCStats stats_;
|
||||
|
||||
|
|
|
@ -321,26 +321,6 @@ void PageSpace::VisitObjects(ObjectVisitor* visitor) const {
|
|||
}
|
||||
|
||||
|
||||
void PageSpace::SetPeer(RawObject* raw_obj, void* peer) {
|
||||
if (peer == NULL) {
|
||||
peer_table_.erase(raw_obj);
|
||||
} else {
|
||||
peer_table_[raw_obj] = peer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void* PageSpace::GetPeer(RawObject* raw_obj) {
|
||||
PeerTable::iterator it = peer_table_.find(raw_obj);
|
||||
return (it == peer_table_.end()) ? NULL : it->second;
|
||||
}
|
||||
|
||||
|
||||
int64_t PageSpace::PeerCount() const {
|
||||
return static_cast<int64_t>(peer_table_.size());
|
||||
}
|
||||
|
||||
|
||||
void PageSpace::VisitObjectPointers(ObjectPointerVisitor* visitor) const {
|
||||
HeapPage* page = pages_;
|
||||
while (page != NULL) {
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#ifndef VM_PAGES_H_
|
||||
#define VM_PAGES_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "vm/freelist.h"
|
||||
#include "vm/globals.h"
|
||||
#include "vm/virtual_memory.h"
|
||||
|
@ -205,16 +203,6 @@ class PageSpace {
|
|||
|
||||
void WriteProtect(bool read_only);
|
||||
|
||||
typedef std::map<RawObject*, void*> PeerTable;
|
||||
|
||||
void SetPeer(RawObject* raw_obj, void* peer);
|
||||
|
||||
void* GetPeer(RawObject* raw_obj);
|
||||
|
||||
int64_t PeerCount() const;
|
||||
|
||||
PeerTable* GetPeerTable() { return &peer_table_; }
|
||||
|
||||
private:
|
||||
// Ids for time and data records in Heap::GCStats.
|
||||
enum {
|
||||
|
@ -253,8 +241,6 @@ class PageSpace {
|
|||
HeapPage* pages_tail_;
|
||||
HeapPage* large_pages_;
|
||||
|
||||
PeerTable peer_table_;
|
||||
|
||||
// Various sizes being tracked for this generation.
|
||||
intptr_t max_capacity_;
|
||||
intptr_t capacity_;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "vm/store_buffer.h"
|
||||
#include "vm/verifier.h"
|
||||
#include "vm/visitor.h"
|
||||
#include "vm/weak_table.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
|
@ -556,19 +557,26 @@ uword Scavenger::ProcessWeakProperty(RawWeakProperty* raw_weak,
|
|||
}
|
||||
|
||||
|
||||
void Scavenger::ProcessPeerReferents() {
|
||||
PeerTable prev;
|
||||
std::swap(prev, peer_table_);
|
||||
for (PeerTable::iterator it = prev.begin(); it != prev.end(); ++it) {
|
||||
RawObject* raw_obj = it->first;
|
||||
ASSERT(raw_obj->IsHeapObject());
|
||||
uword raw_addr = RawObject::ToAddr(raw_obj);
|
||||
uword header = *reinterpret_cast<uword*>(raw_addr);
|
||||
if (IsForwarding(header)) {
|
||||
// The object has survived. Preserve its record.
|
||||
uword new_addr = ForwardedAddr(header);
|
||||
raw_obj = RawObject::FromAddr(new_addr);
|
||||
heap_->SetPeer(raw_obj, it->second);
|
||||
void Scavenger::ProcessWeakTables() {
|
||||
for (Heap::WeakSelector sel = static_cast<Heap::WeakSelector>(0);
|
||||
sel < Heap::kNumWeakSelectors;
|
||||
sel++) {
|
||||
WeakTable* table = heap_->GetWeakTable(Heap::kNew, sel);
|
||||
intptr_t size = table->size();
|
||||
heap_->SetWeakTable(Heap::kNew, sel, WeakTable::NewFrom(table));
|
||||
for (intptr_t i = 0; i < size; i++) {
|
||||
if (table->IsValidEntryAt(i)) {
|
||||
RawObject* raw_obj = table->ObjectAt(i);
|
||||
ASSERT(raw_obj->IsHeapObject());
|
||||
uword raw_addr = RawObject::ToAddr(raw_obj);
|
||||
uword header = *reinterpret_cast<uword*>(raw_addr);
|
||||
if (IsForwarding(header)) {
|
||||
// The object has survived. Preserve its record.
|
||||
uword new_addr = ForwardedAddr(header);
|
||||
raw_obj = RawObject::FromAddr(new_addr);
|
||||
heap_->SetWeakEntry(raw_obj, sel, table->ValueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +633,7 @@ void Scavenger::Scavenge(bool invoke_api_callbacks) {
|
|||
ScavengerWeakVisitor weak_visitor(this);
|
||||
IterateWeakRoots(isolate, &weak_visitor, invoke_api_callbacks);
|
||||
visitor.Finalize();
|
||||
ProcessPeerReferents();
|
||||
ProcessWeakTables();
|
||||
int64_t end = OS::GetCurrentTimeMicros();
|
||||
heap_->RecordTime(kProcessToSpace, middle - start);
|
||||
heap_->RecordTime(kIterateWeaks, end - middle);
|
||||
|
@ -649,23 +657,4 @@ void Scavenger::WriteProtect(bool read_only) {
|
|||
}
|
||||
|
||||
|
||||
void Scavenger::SetPeer(RawObject* raw_obj, void* peer) {
|
||||
if (peer == NULL) {
|
||||
peer_table_.erase(raw_obj);
|
||||
} else {
|
||||
peer_table_[raw_obj] = peer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void* Scavenger::GetPeer(RawObject* raw_obj) {
|
||||
PeerTable::iterator it = peer_table_.find(raw_obj);
|
||||
return (it == peer_table_.end()) ? NULL : it->second;
|
||||
}
|
||||
|
||||
|
||||
int64_t Scavenger::PeerCount() const {
|
||||
return static_cast<int64_t>(peer_table_.size());
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#ifndef VM_SCAVENGER_H_
|
||||
#define VM_SCAVENGER_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "platform/utils.h"
|
||||
#include "vm/flags.h"
|
||||
|
@ -89,12 +87,6 @@ class Scavenger {
|
|||
|
||||
void WriteProtect(bool read_only);
|
||||
|
||||
void SetPeer(RawObject* raw_obj, void* peer);
|
||||
|
||||
void* GetPeer(RawObject* raw_obj);
|
||||
|
||||
int64_t PeerCount() const;
|
||||
|
||||
private:
|
||||
// Ids for time and data records in Heap::GCStats.
|
||||
enum {
|
||||
|
@ -145,7 +137,7 @@ class Scavenger {
|
|||
return end_ < to_->end();
|
||||
}
|
||||
|
||||
void ProcessPeerReferents();
|
||||
void ProcessWeakTables();
|
||||
|
||||
VirtualMemory* space_;
|
||||
MemoryRegion* to_;
|
||||
|
@ -153,9 +145,6 @@ class Scavenger {
|
|||
|
||||
Heap* heap_;
|
||||
|
||||
typedef std::map<RawObject*, void*> PeerTable;
|
||||
PeerTable peer_table_;
|
||||
|
||||
// Current allocation top and end. These values are being accessed directly
|
||||
// from generated code.
|
||||
uword top_;
|
||||
|
|
|
@ -335,6 +335,8 @@
|
|||
'visitor.h',
|
||||
'vtune.cc',
|
||||
'vtune.h',
|
||||
'weak_table.cc',
|
||||
'weak_table.h',
|
||||
'zone.cc',
|
||||
'zone.h',
|
||||
'zone_test.cc',
|
||||
|
|
72
runtime/vm/weak_table.cc
Normal file
72
runtime/vm/weak_table.cc
Normal file
|
@ -0,0 +1,72 @@
|
|||
// 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.
|
||||
|
||||
#include "vm/weak_table.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "vm/raw_object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
WeakTable* WeakTable::SetValue(RawObject* key, intptr_t val) {
|
||||
intptr_t sz = size();
|
||||
intptr_t idx = Hash(key) % sz;
|
||||
intptr_t empty_idx = -1;
|
||||
RawObject* obj = ObjectAt(idx);
|
||||
|
||||
while (obj != NULL) {
|
||||
if (obj == key) {
|
||||
SetValueAt(idx, val);
|
||||
return this;
|
||||
} else if ((empty_idx < 0) &&
|
||||
(reinterpret_cast<intptr_t>(obj) == kDeletedEntry)) {
|
||||
empty_idx = idx; // Insert at this location if not found.
|
||||
}
|
||||
idx = (idx + 1) % sz;
|
||||
obj = ObjectAt(idx);
|
||||
}
|
||||
|
||||
if (val == 0) {
|
||||
// Do not enter an invalid value. Associating 0 with a key deletes it from
|
||||
// this weak table above in SetValueAt. If the key was not present in the
|
||||
// weak table we are done.
|
||||
return this;
|
||||
}
|
||||
|
||||
if (empty_idx >= 0) {
|
||||
// We will be reusing a slot below.
|
||||
set_used(used() - 1);
|
||||
idx = empty_idx;
|
||||
}
|
||||
|
||||
ASSERT(!IsValidEntryAt(idx));
|
||||
// Set the key and value.
|
||||
SetObjectAt(idx, key);
|
||||
SetValueAt(idx, val);
|
||||
// Update the counts.
|
||||
set_used(used() + 1);
|
||||
set_count(count() + 1);
|
||||
|
||||
// Rehash if needed to ensure that there are empty slots available.
|
||||
if (used_ >= limit()) {
|
||||
return Rehash();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
WeakTable* WeakTable::Rehash() {
|
||||
intptr_t sz = size();
|
||||
WeakTable* result = NewFrom(this);
|
||||
|
||||
for (intptr_t i = 0; i < sz; i++) {
|
||||
if (IsValidEntryAt(i)) {
|
||||
WeakTable* temp = result->SetValue(ObjectAt(i), ValueAt(i));
|
||||
ASSERT(temp == result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace dart
|
163
runtime/vm/weak_table.h
Normal file
163
runtime/vm/weak_table.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
// 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 VM_WEAK_TABLE_H_
|
||||
#define VM_WEAK_TABLE_H_
|
||||
|
||||
#include "vm/globals.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "vm/raw_object.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
class WeakTable {
|
||||
public:
|
||||
explicit WeakTable(intptr_t size) : used_(0), count_(0) {
|
||||
ASSERT(size >= 0);
|
||||
if (size < kMinSize) {
|
||||
size = kMinSize;
|
||||
}
|
||||
data_ = reinterpret_cast<intptr_t*>(calloc(size, kEntrySize * kWordSize));
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
static WeakTable* NewFrom(WeakTable* original) {
|
||||
intptr_t cnt = original->count();
|
||||
intptr_t sz = original->size();
|
||||
intptr_t new_sz = sz;
|
||||
|
||||
if (cnt <= (sz / 4)) {
|
||||
// Reduce the capacity.
|
||||
new_sz = sz / 2;
|
||||
} else if (cnt > (sz / 2)) {
|
||||
// Increase the capacity.
|
||||
new_sz = sz * 2;
|
||||
if (new_sz < sz) {
|
||||
FATAL("Reached impossible state of having more weak table entries"
|
||||
" than memory available for heap objects.");
|
||||
}
|
||||
}
|
||||
return new WeakTable(new_sz);
|
||||
}
|
||||
|
||||
intptr_t size() const { return size_; }
|
||||
intptr_t used() const { return used_; }
|
||||
intptr_t count() const { return count_; }
|
||||
|
||||
bool IsValidEntryAt(intptr_t i) const {
|
||||
ASSERT(((ValueAt(i) == 0) &&
|
||||
((ObjectAt(i) == NULL) ||
|
||||
(data_[ObjectIndex(i)] == kDeletedEntry))) ||
|
||||
((ValueAt(i) != 0) &&
|
||||
(ObjectAt(i) != NULL) &&
|
||||
(data_[ObjectIndex(i)] != kDeletedEntry)));
|
||||
return (data_[ValueIndex(i)] != 0);
|
||||
}
|
||||
|
||||
void InvalidateAt(intptr_t i) {
|
||||
ASSERT(IsValidEntryAt(i));
|
||||
SetValueAt(i, 0);
|
||||
}
|
||||
|
||||
RawObject* ObjectAt(intptr_t i) const {
|
||||
return reinterpret_cast<RawObject*>(data_[ObjectIndex(i)]);
|
||||
}
|
||||
|
||||
intptr_t ValueAt(intptr_t i) const {
|
||||
return data_[ValueIndex(i)];
|
||||
}
|
||||
|
||||
WeakTable* SetValue(RawObject* key, intptr_t val);
|
||||
|
||||
intptr_t GetValue(RawObject* key) const {
|
||||
intptr_t sz = size();
|
||||
intptr_t idx = Hash(key) % sz;
|
||||
RawObject* obj = ObjectAt(idx);
|
||||
while (obj != NULL) {
|
||||
if (obj == key) {
|
||||
return ValueAt(idx);
|
||||
}
|
||||
idx = (idx + 1) % sz;
|
||||
obj = ObjectAt(idx);
|
||||
}
|
||||
ASSERT(ValueAt(idx) == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
enum {
|
||||
kObjectOffset = 0,
|
||||
kValueOffset,
|
||||
kEntrySize,
|
||||
};
|
||||
|
||||
static const intptr_t kDeletedEntry = 1; // Equivalent to a tagged NULL.
|
||||
static const intptr_t kMinSize = 8;
|
||||
|
||||
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 {
|
||||
ASSERT(i >= 0);
|
||||
ASSERT(i < size());
|
||||
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;
|
||||
}
|
||||
|
||||
void SetObjectAt(intptr_t i, RawObject* key) {
|
||||
data_[ObjectIndex(i)] = reinterpret_cast<intptr_t>(key);
|
||||
}
|
||||
|
||||
void SetValueAt(intptr_t i, intptr_t val) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
WeakTable* Rehash();
|
||||
|
||||
static intptr_t Hash(RawObject* key) {
|
||||
return reinterpret_cast<intptr_t>(key) >> kObjectAlignmentLog2;
|
||||
}
|
||||
|
||||
// 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_IMPLICIT_CONSTRUCTORS(WeakTable);
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // VM_WEAK_TABLE_H_
|
Loading…
Reference in a new issue