Commit b2b7aa6f authored by vegorov@chromium.org's avatar vegorov@chromium.org

Link all global contexts into a weak list.

Review URL: http://codereview.chromium.org/3764011

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5649 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent af95fa60
...@@ -1814,6 +1814,11 @@ Genesis::Genesis(Handle<Object> global_object, ...@@ -1814,6 +1814,11 @@ Genesis::Genesis(Handle<Object> global_object,
i::Counters::contexts_created_from_scratch.Increment(); i::Counters::contexts_created_from_scratch.Increment();
} }
// Add this context to the weak list of global contexts.
(*global_context_)->set(Context::NEXT_CONTEXT_LINK,
Heap::global_contexts_list());
Heap::set_global_contexts_list(*global_context_);
result_ = global_context_; result_ = global_context_;
} }
......
...@@ -225,7 +225,15 @@ class Context: public FixedArray { ...@@ -225,7 +225,15 @@ class Context: public FixedArray {
OUT_OF_MEMORY_INDEX, OUT_OF_MEMORY_INDEX,
MAP_CACHE_INDEX, MAP_CACHE_INDEX,
CONTEXT_DATA_INDEX, CONTEXT_DATA_INDEX,
GLOBAL_CONTEXT_SLOTS
// Properties from here are treated as weak references by the full GC.
// Scavenge treats them as strong references.
NEXT_CONTEXT_LINK,
// Total number of slots.
GLOBAL_CONTEXT_SLOTS,
FIRST_WEAK_SLOT = NEXT_CONTEXT_LINK
}; };
// Direct slot access. // Direct slot access.
...@@ -333,6 +341,17 @@ class Context: public FixedArray { ...@@ -333,6 +341,17 @@ class Context: public FixedArray {
return kHeaderSize + index * kPointerSize - kHeapObjectTag; return kHeaderSize + index * kPointerSize - kHeapObjectTag;
} }
static const int kSize = kHeaderSize + GLOBAL_CONTEXT_SLOTS * kPointerSize;
// GC support.
typedef FixedBodyDescriptor<
kHeaderSize, kSize, kSize> ScavengeBodyDescriptor;
typedef FixedBodyDescriptor<
kHeaderSize,
kHeaderSize + FIRST_WEAK_SLOT * kPointerSize,
kSize> MarkCompactBodyDescriptor;
private: private:
// Unchecked access to the slots. // Unchecked access to the slots.
Object* unchecked_previous() { return get(PREVIOUS_INDEX); } Object* unchecked_previous() { return get(PREVIOUS_INDEX); }
......
...@@ -54,6 +54,7 @@ namespace internal { ...@@ -54,6 +54,7 @@ namespace internal {
String* Heap::hidden_symbol_; String* Heap::hidden_symbol_;
Object* Heap::roots_[Heap::kRootListLength]; Object* Heap::roots_[Heap::kRootListLength];
Object* Heap::global_contexts_list_;
NewSpace Heap::new_space_; NewSpace Heap::new_space_;
OldSpace* Heap::old_pointer_space_ = NULL; OldSpace* Heap::old_pointer_space_ = NULL;
...@@ -1015,6 +1016,9 @@ void Heap::Scavenge() { ...@@ -1015,6 +1016,9 @@ void Heap::Scavenge() {
} }
} }
// Scavenge object reachable from the global contexts list directly.
scavenge_visitor.VisitPointer(BitCast<Object**>(&global_contexts_list_));
new_space_front = DoScavenge(&scavenge_visitor, new_space_front); new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
UpdateNewSpaceReferencesInExternalStringTable( UpdateNewSpaceReferencesInExternalStringTable(
...@@ -1082,6 +1086,44 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable( ...@@ -1082,6 +1086,44 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable(
} }
void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) {
Object* head = undefined_value();
Context* tail = NULL;
Object* candidate = global_contexts_list_;
while (!candidate->IsUndefined()) {
// Check whether to keep the candidate in the list.
Context* candidate_context = reinterpret_cast<Context*>(candidate);
Object* retain = retainer->RetainAs(candidate);
if (retain != NULL) {
if (head->IsUndefined()) {
// First element in the list.
head = candidate_context;
} else {
// Subsequent elements in the list.
ASSERT(tail != NULL);
tail->set_unchecked(Context::NEXT_CONTEXT_LINK,
candidate_context,
UPDATE_WRITE_BARRIER);
}
// Retained context is new tail.
tail = candidate_context;
}
// Move to next element in the list.
candidate = candidate_context->get(Context::NEXT_CONTEXT_LINK);
}
// Terminate the list if there is one or more elements.
if (tail != NULL) {
tail->set_unchecked(Context::NEXT_CONTEXT_LINK,
Heap::undefined_value(),
UPDATE_WRITE_BARRIER);
}
// Update the head of the list of contexts.
Heap::global_contexts_list_ = head;
}
class NewSpaceScavenger : public StaticNewSpaceVisitor<NewSpaceScavenger> { class NewSpaceScavenger : public StaticNewSpaceVisitor<NewSpaceScavenger> {
public: public:
static inline void VisitPointer(Object** p) { static inline void VisitPointer(Object** p) {
...@@ -1138,6 +1180,9 @@ class ScavengingVisitor : public StaticVisitorBase { ...@@ -1138,6 +1180,9 @@ class ScavengingVisitor : public StaticVisitorBase {
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate); table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
table_.Register(kVisitByteArray, &EvacuateByteArray); table_.Register(kVisitByteArray, &EvacuateByteArray);
table_.Register(kVisitFixedArray, &EvacuateFixedArray); table_.Register(kVisitFixedArray, &EvacuateFixedArray);
table_.Register(kVisitGlobalContext,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
VisitSpecialized<Context::kSize>);
typedef ObjectEvacuationStrategy<POINTER_OBJECT> PointerObject; typedef ObjectEvacuationStrategy<POINTER_OBJECT> PointerObject;
...@@ -1628,7 +1673,9 @@ bool Heap::CreateInitialMaps() { ...@@ -1628,7 +1673,9 @@ bool Heap::CreateInitialMaps() {
obj = AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel); obj = AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
if (obj->IsFailure()) return false; if (obj->IsFailure()) return false;
set_global_context_map(Map::cast(obj)); Map* global_context_map = Map::cast(obj);
global_context_map->set_visitor_id(StaticVisitorBase::kVisitGlobalContext);
set_global_context_map(global_context_map);
obj = AllocateMap(SHARED_FUNCTION_INFO_TYPE, obj = AllocateMap(SHARED_FUNCTION_INFO_TYPE,
SharedFunctionInfo::kAlignedSize); SharedFunctionInfo::kAlignedSize);
...@@ -4217,6 +4264,8 @@ bool Heap::Setup(bool create_heap_objects) { ...@@ -4217,6 +4264,8 @@ bool Heap::Setup(bool create_heap_objects) {
// Create initial objects // Create initial objects
if (!CreateInitialObjects()) return false; if (!CreateInitialObjects()) return false;
global_contexts_list_ = undefined_value();
} }
LOG(IntPtrTEvent("heap-capacity", Capacity())); LOG(IntPtrTEvent("heap-capacity", Capacity()));
......
...@@ -202,9 +202,10 @@ namespace internal { ...@@ -202,9 +202,10 @@ namespace internal {
V(closure_symbol, "(closure)") V(closure_symbol, "(closure)")
// Forward declaration of the GCTracer class. // Forward declarations.
class GCTracer; class GCTracer;
class HeapStats; class HeapStats;
class WeakObjectRetainer;
typedef String* (*ExternalStringTableUpdaterCallback)(Object** pointer); typedef String* (*ExternalStringTableUpdaterCallback)(Object** pointer);
...@@ -766,6 +767,11 @@ class Heap : public AllStatic { ...@@ -766,6 +767,11 @@ class Heap : public AllStatic {
// not match the empty string. // not match the empty string.
static String* hidden_symbol() { return hidden_symbol_; } static String* hidden_symbol() { return hidden_symbol_; }
static void set_global_contexts_list(Object* object) {
global_contexts_list_ = object;
}
static Object* global_contexts_list() { return global_contexts_list_; }
// Iterates over all roots in the heap. // Iterates over all roots in the heap.
static void IterateRoots(ObjectVisitor* v, VisitMode mode); static void IterateRoots(ObjectVisitor* v, VisitMode mode);
// Iterates over all strong roots in the heap. // Iterates over all strong roots in the heap.
...@@ -869,6 +875,11 @@ class Heap : public AllStatic { ...@@ -869,6 +875,11 @@ class Heap : public AllStatic {
// Generated code can embed this address to get access to the roots. // Generated code can embed this address to get access to the roots.
static Object** roots_address() { return roots_; } static Object** roots_address() { return roots_; }
// Get address of global contexts list for serialization support.
static Object** global_contexts_list_address() {
return &global_contexts_list_;
}
#ifdef DEBUG #ifdef DEBUG
static void Print(); static void Print();
static void PrintHandles(); static void PrintHandles();
...@@ -1050,6 +1061,8 @@ class Heap : public AllStatic { ...@@ -1050,6 +1061,8 @@ class Heap : public AllStatic {
static void UpdateNewSpaceReferencesInExternalStringTable( static void UpdateNewSpaceReferencesInExternalStringTable(
ExternalStringTableUpdaterCallback updater_func); ExternalStringTableUpdaterCallback updater_func);
static void ProcessWeakReferences(WeakObjectRetainer* retainer);
// Helper function that governs the promotion policy from new space to // Helper function that governs the promotion policy from new space to
// old. If the object's old address lies below the new space's age // old. If the object's old address lies below the new space's age
// mark or if we've already filled the bottom 1/16th of the to space, // mark or if we've already filled the bottom 1/16th of the to space,
...@@ -1156,6 +1169,8 @@ class Heap : public AllStatic { ...@@ -1156,6 +1169,8 @@ class Heap : public AllStatic {
static Object* roots_[kRootListLength]; static Object* roots_[kRootListLength];
static Object* global_contexts_list_;
struct StringTypeTable { struct StringTypeTable {
InstanceType type; InstanceType type;
int size; int size;
...@@ -2042,6 +2057,19 @@ class ExternalStringTable : public AllStatic { ...@@ -2042,6 +2057,19 @@ class ExternalStringTable : public AllStatic {
static List<Object*> old_space_strings_; static List<Object*> old_space_strings_;
}; };
// Abstract base class for checking whether a weak object should be retained.
class WeakObjectRetainer {
public:
virtual ~WeakObjectRetainer() {}
// Return whether this object should be retained. If NULL is returned the
// object has no references. Otherwise the address of the retained object
// should be returned as in some GC situations the object has been moved.
virtual Object* RetainAs(Object* object) = 0;
};
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_HEAP_H_ #endif // V8_HEAP_H_
...@@ -282,6 +282,11 @@ class StaticMarkingVisitor : public StaticVisitorBase { ...@@ -282,6 +282,11 @@ class StaticMarkingVisitor : public StaticVisitorBase {
FixedArray::BodyDescriptor, FixedArray::BodyDescriptor,
void>::Visit); void>::Visit);
table_.Register(kVisitGlobalContext,
&FixedBodyVisitor<StaticMarkingVisitor,
Context::MarkCompactBodyDescriptor,
void>::Visit);
table_.Register(kVisitSharedFunctionInfo, &VisitSharedFunctionInfo); table_.Register(kVisitSharedFunctionInfo, &VisitSharedFunctionInfo);
table_.Register(kVisitByteArray, &DataObjectVisitor::Visit); table_.Register(kVisitByteArray, &DataObjectVisitor::Visit);
...@@ -578,6 +583,7 @@ class StaticMarkingVisitor : public StaticVisitorBase { ...@@ -578,6 +583,7 @@ class StaticMarkingVisitor : public StaticVisitorBase {
VisitPointers(SLOT_ADDR(object, VisitPointers(SLOT_ADDR(object,
JSFunction::kCodeEntryOffset + kPointerSize), JSFunction::kCodeEntryOffset + kPointerSize),
SLOT_ADDR(object, JSFunction::kSize)); SLOT_ADDR(object, JSFunction::kSize));
#undef SLOT_ADDR #undef SLOT_ADDR
} }
...@@ -738,6 +744,21 @@ class SymbolTableCleaner : public ObjectVisitor { ...@@ -738,6 +744,21 @@ class SymbolTableCleaner : public ObjectVisitor {
}; };
// Implementation of WeakObjectRetainer for mark compact GCs. All marked objects
// are retained.
class MarkCompactWeakObjectRetainer : public WeakObjectRetainer {
public:
virtual Object* RetainAs(Object* object) {
MapWord first_word = HeapObject::cast(object)->map_word();
if (first_word.IsMarked()) {
return object;
} else {
return NULL;
}
}
};
void MarkCompactCollector::MarkUnmarkedObject(HeapObject* object) { void MarkCompactCollector::MarkUnmarkedObject(HeapObject* object) {
ASSERT(!object->IsMarked()); ASSERT(!object->IsMarked());
ASSERT(Heap::Contains(object)); ASSERT(Heap::Contains(object));
...@@ -1069,6 +1090,10 @@ void MarkCompactCollector::MarkLiveObjects() { ...@@ -1069,6 +1090,10 @@ void MarkCompactCollector::MarkLiveObjects() {
ExternalStringTable::Iterate(&v); ExternalStringTable::Iterate(&v);
ExternalStringTable::CleanUp(); ExternalStringTable::CleanUp();
// Process the weak references.
MarkCompactWeakObjectRetainer mark_compact_object_retainer;
Heap::ProcessWeakReferences(&mark_compact_object_retainer);
// Remove object groups after marking phase. // Remove object groups after marking phase.
GlobalHandles::RemoveObjectGroups(); GlobalHandles::RemoveObjectGroups();
} }
...@@ -1639,6 +1664,9 @@ static void SweepNewSpace(NewSpace* space) { ...@@ -1639,6 +1664,9 @@ static void SweepNewSpace(NewSpace* space) {
} }
} }
// Update pointer from the global contexts list.
updating_visitor.VisitPointer(Heap::global_contexts_list_address());
// Update pointers from external string table. // Update pointers from external string table.
Heap::UpdateNewSpaceReferencesInExternalStringTable( Heap::UpdateNewSpaceReferencesInExternalStringTable(
&UpdateNewSpaceReferenceInExternalStringTableEntry); &UpdateNewSpaceReferenceInExternalStringTableEntry);
...@@ -2245,6 +2273,9 @@ void MarkCompactCollector::UpdatePointers() { ...@@ -2245,6 +2273,9 @@ void MarkCompactCollector::UpdatePointers() {
Heap::IterateRoots(&updating_visitor, VISIT_ONLY_STRONG); Heap::IterateRoots(&updating_visitor, VISIT_ONLY_STRONG);
GlobalHandles::IterateWeakRoots(&updating_visitor); GlobalHandles::IterateWeakRoots(&updating_visitor);
// Update the pointer to the head of the weak list of global contexts.
updating_visitor.VisitPointer(&Heap::global_contexts_list_);
int live_maps_size = IterateLiveObjects(Heap::map_space(), int live_maps_size = IterateLiveObjects(Heap::map_space(),
&UpdatePointersInOldObject); &UpdatePointersInOldObject);
int live_pointer_olds_size = IterateLiveObjects(Heap::old_pointer_space(), int live_pointer_olds_size = IterateLiveObjects(Heap::old_pointer_space(),
......
...@@ -1470,6 +1470,15 @@ void FixedArray::set_unchecked(int index, Smi* value) { ...@@ -1470,6 +1470,15 @@ void FixedArray::set_unchecked(int index, Smi* value) {
} }
void FixedArray::set_unchecked(int index,
Object* value,
WriteBarrierMode mode) {
int offset = kHeaderSize + index * kPointerSize;
WRITE_FIELD(this, offset, value);
CONDITIONAL_WRITE_BARRIER(this, offset, mode);
}
void FixedArray::set_null_unchecked(int index) { void FixedArray::set_null_unchecked(int index) {
ASSERT(index >= 0 && index < this->length()); ASSERT(index >= 0 && index < this->length());
ASSERT(!Heap::InNewSpace(Heap::null_value())); ASSERT(!Heap::InNewSpace(Heap::null_value()));
......
...@@ -50,6 +50,7 @@ class StaticVisitorBase : public AllStatic { ...@@ -50,6 +50,7 @@ class StaticVisitorBase : public AllStatic {
kVisitShortcutCandidate, kVisitShortcutCandidate,
kVisitByteArray, kVisitByteArray,
kVisitFixedArray, kVisitFixedArray,
kVisitGlobalContext,
// For data objects, JS objects and structs along with generic visitor which // For data objects, JS objects and structs along with generic visitor which
// can visit object of any size we provide visitors specialized by // can visit object of any size we provide visitors specialized by
...@@ -263,6 +264,11 @@ class StaticNewSpaceVisitor : public StaticVisitorBase { ...@@ -263,6 +264,11 @@ class StaticNewSpaceVisitor : public StaticVisitorBase {
FixedArray::BodyDescriptor, FixedArray::BodyDescriptor,
int>::Visit); int>::Visit);
table_.Register(kVisitGlobalContext,
&FixedBodyVisitor<StaticVisitor,
Context::ScavengeBodyDescriptor,
int>::Visit);
table_.Register(kVisitByteArray, &VisitByteArray); table_.Register(kVisitByteArray, &VisitByteArray);
table_.Register(kVisitSharedFunctionInfo, table_.Register(kVisitSharedFunctionInfo,
......
...@@ -1751,6 +1751,7 @@ class FixedArray: public HeapObject { ...@@ -1751,6 +1751,7 @@ class FixedArray: public HeapObject {
// Setters with less debug checks for the GC to use. // Setters with less debug checks for the GC to use.
inline void set_unchecked(int index, Smi* value); inline void set_unchecked(int index, Smi* value);
inline void set_null_unchecked(int index); inline void set_null_unchecked(int index);
inline void set_unchecked(int index, Object* value, WriteBarrierMode mode);
// Gives access to raw memory which stores the array's data. // Gives access to raw memory which stores the array's data.
inline Object** data_start(); inline Object** data_start();
......
...@@ -619,6 +619,8 @@ void Deserializer::Deserialize() { ...@@ -619,6 +619,8 @@ void Deserializer::Deserialize() {
external_reference_decoder_ = new ExternalReferenceDecoder(); external_reference_decoder_ = new ExternalReferenceDecoder();
Heap::IterateStrongRoots(this, VISIT_ONLY_STRONG); Heap::IterateStrongRoots(this, VISIT_ONLY_STRONG);
Heap::IterateWeakRoots(this, VISIT_ALL); Heap::IterateWeakRoots(this, VISIT_ALL);
Heap::set_global_contexts_list(Heap::undefined_value());
} }
......
...@@ -954,6 +954,7 @@ TEST(Regression39128) { ...@@ -954,6 +954,7 @@ TEST(Regression39128) {
CHECK(page->IsRegionDirty(clone_addr + (object_size - kPointerSize))); CHECK(page->IsRegionDirty(clone_addr + (object_size - kPointerSize)));
} }
TEST(TestCodeFlushing) { TEST(TestCodeFlushing) {
i::FLAG_allow_natives_syntax = true; i::FLAG_allow_natives_syntax = true;
// If we do not flush code this test is invalid. // If we do not flush code this test is invalid.
...@@ -997,3 +998,88 @@ TEST(TestCodeFlushing) { ...@@ -997,3 +998,88 @@ TEST(TestCodeFlushing) {
CHECK(function->shared()->is_compiled()); CHECK(function->shared()->is_compiled());
CHECK(function->is_compiled()); CHECK(function->is_compiled());
} }
// Count the number of global contexts in the weak list of global contexts.
static int CountGlobalContexts() {
int count = 0;
Object* object = Heap::global_contexts_list();
while (!object->IsUndefined()) {
count++;
object = Context::cast(object)->get(Context::NEXT_CONTEXT_LINK);
}
return count;
}
TEST(TestInternalWeakLists) {
static const int kNumTestContexts = 10;
v8::HandleScope scope;
v8::Persistent<v8::Context> ctx[kNumTestContexts];
CHECK_EQ(0, CountGlobalContexts());
// Create a number of global contests which gets linked together.
for (int i = 0; i < kNumTestContexts; i++) {
ctx[i] = v8::Context::New();
CHECK_EQ(i + 1, CountGlobalContexts());
ctx[i]->Enter();
ctx[i]->Exit();
}
// Dispose the global contexts one by one.
for (int i = 0; i < kNumTestContexts; i++) {
ctx[i].Dispose();
ctx[i].Clear();
// Scavenge treats these references as strong.
for (int j = 0; j < 10; j++) {
Heap::PerformScavenge();
CHECK_EQ(kNumTestContexts - i, CountGlobalContexts());
}
// Mark compact handles the weak references.
Heap::CollectAllGarbage(true);
CHECK_EQ(kNumTestContexts - i - 1, CountGlobalContexts());
}
CHECK_EQ(0, CountGlobalContexts());
}
// Count the number of global contexts in the weak list of global contexts
// causing a GC after the specified number of elements.
static int CountGlobalContextsWithGC(int n) {
int count = 0;
Handle<Object> object(Heap::global_contexts_list());
while (!object->IsUndefined()) {
count++;
if (count == n) Heap::CollectAllGarbage(true);
object =
Handle<Object>(Context::cast(*object)->get(Context::NEXT_CONTEXT_LINK));
}
return count;
}
TEST(TestInternalWeakListsTraverseWithGC) {
static const int kNumTestContexts = 10;
v8::HandleScope scope;
v8::Persistent<v8::Context> ctx[kNumTestContexts];
CHECK_EQ(0, CountGlobalContexts());
// Create an number of contexts and check the length of the weak list both
// with and without GCs while iterating the list.
for (int i = 0; i < kNumTestContexts; i++) {
ctx[i] = v8::Context::New();
CHECK_EQ(i + 1, CountGlobalContexts());
CHECK_EQ(i + 1, CountGlobalContextsWithGC(i / 2 + 1));
ctx[i]->Enter();
ctx[i]->Exit();
}
}
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