From: vegorov@chromium.org Date: Mon, 18 Oct 2010 14:59:03 +0000 (+0000) Subject: Link all global contexts into a weak list. X-Git-Tag: upstream/4.7.83~21075 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b2b7aa6f3bbefa79d957e55aa6536773c18e94eb;p=platform%2Fupstream%2Fv8.git 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 --- diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index aa8d8e5..d7491e1 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1814,6 +1814,11 @@ Genesis::Genesis(Handle global_object, 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_; } diff --git a/src/contexts.h b/src/contexts.h index 78dda6a..9722a93 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -225,7 +225,15 @@ class Context: public FixedArray { OUT_OF_MEMORY_INDEX, MAP_CACHE_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. @@ -333,6 +341,17 @@ class Context: public FixedArray { 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: // Unchecked access to the slots. Object* unchecked_previous() { return get(PREVIOUS_INDEX); } diff --git a/src/heap.cc b/src/heap.cc index 87a7554..de0c294 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -54,6 +54,7 @@ namespace internal { String* Heap::hidden_symbol_; Object* Heap::roots_[Heap::kRootListLength]; +Object* Heap::global_contexts_list_; NewSpace Heap::new_space_; OldSpace* Heap::old_pointer_space_ = NULL; @@ -1015,6 +1016,9 @@ void Heap::Scavenge() { } } + // Scavenge object reachable from the global contexts list directly. + scavenge_visitor.VisitPointer(BitCast(&global_contexts_list_)); + new_space_front = DoScavenge(&scavenge_visitor, new_space_front); 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(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 { public: static inline void VisitPointer(Object** p) { @@ -1138,6 +1180,9 @@ class ScavengingVisitor : public StaticVisitorBase { table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate); table_.Register(kVisitByteArray, &EvacuateByteArray); table_.Register(kVisitFixedArray, &EvacuateFixedArray); + table_.Register(kVisitGlobalContext, + &ObjectEvacuationStrategy:: + VisitSpecialized); typedef ObjectEvacuationStrategy PointerObject; @@ -1628,7 +1673,9 @@ bool Heap::CreateInitialMaps() { obj = AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel); 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, SharedFunctionInfo::kAlignedSize); @@ -4217,6 +4264,8 @@ bool Heap::Setup(bool create_heap_objects) { // Create initial objects if (!CreateInitialObjects()) return false; + + global_contexts_list_ = undefined_value(); } LOG(IntPtrTEvent("heap-capacity", Capacity())); diff --git a/src/heap.h b/src/heap.h index f46ece8..6d32a4b 100644 --- a/src/heap.h +++ b/src/heap.h @@ -202,9 +202,10 @@ namespace internal { V(closure_symbol, "(closure)") -// Forward declaration of the GCTracer class. +// Forward declarations. class GCTracer; class HeapStats; +class WeakObjectRetainer; typedef String* (*ExternalStringTableUpdaterCallback)(Object** pointer); @@ -766,6 +767,11 @@ class Heap : public AllStatic { // not match the empty string. 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. static void IterateRoots(ObjectVisitor* v, VisitMode mode); // Iterates over all strong roots in the heap. @@ -869,6 +875,11 @@ class Heap : public AllStatic { // Generated code can embed this address to get access to the 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 static void Print(); static void PrintHandles(); @@ -1050,6 +1061,8 @@ class Heap : public AllStatic { static void UpdateNewSpaceReferencesInExternalStringTable( ExternalStringTableUpdaterCallback updater_func); + static void ProcessWeakReferences(WeakObjectRetainer* retainer); + // 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 // mark or if we've already filled the bottom 1/16th of the to space, @@ -1156,6 +1169,8 @@ class Heap : public AllStatic { static Object* roots_[kRootListLength]; + static Object* global_contexts_list_; + struct StringTypeTable { InstanceType type; int size; @@ -2042,6 +2057,19 @@ class ExternalStringTable : public AllStatic { static List 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 #endif // V8_HEAP_H_ diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 26f88cf..c817251 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -282,6 +282,11 @@ class StaticMarkingVisitor : public StaticVisitorBase { FixedArray::BodyDescriptor, void>::Visit); + table_.Register(kVisitGlobalContext, + &FixedBodyVisitor::Visit); + table_.Register(kVisitSharedFunctionInfo, &VisitSharedFunctionInfo); table_.Register(kVisitByteArray, &DataObjectVisitor::Visit); @@ -578,6 +583,7 @@ class StaticMarkingVisitor : public StaticVisitorBase { VisitPointers(SLOT_ADDR(object, JSFunction::kCodeEntryOffset + kPointerSize), SLOT_ADDR(object, JSFunction::kSize)); + #undef SLOT_ADDR } @@ -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) { ASSERT(!object->IsMarked()); ASSERT(Heap::Contains(object)); @@ -1069,6 +1090,10 @@ void MarkCompactCollector::MarkLiveObjects() { ExternalStringTable::Iterate(&v); ExternalStringTable::CleanUp(); + // Process the weak references. + MarkCompactWeakObjectRetainer mark_compact_object_retainer; + Heap::ProcessWeakReferences(&mark_compact_object_retainer); + // Remove object groups after marking phase. GlobalHandles::RemoveObjectGroups(); } @@ -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. Heap::UpdateNewSpaceReferencesInExternalStringTable( &UpdateNewSpaceReferenceInExternalStringTableEntry); @@ -2245,6 +2273,9 @@ void MarkCompactCollector::UpdatePointers() { Heap::IterateRoots(&updating_visitor, VISIT_ONLY_STRONG); 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(), &UpdatePointersInOldObject); int live_pointer_olds_size = IterateLiveObjects(Heap::old_pointer_space(), diff --git a/src/objects-inl.h b/src/objects-inl.h index 8f1b09b..11f9d34 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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) { ASSERT(index >= 0 && index < this->length()); ASSERT(!Heap::InNewSpace(Heap::null_value())); diff --git a/src/objects-visiting.h b/src/objects-visiting.h index 90f7ce0..ed76cb9 100644 --- a/src/objects-visiting.h +++ b/src/objects-visiting.h @@ -50,6 +50,7 @@ class StaticVisitorBase : public AllStatic { kVisitShortcutCandidate, kVisitByteArray, kVisitFixedArray, + kVisitGlobalContext, // For data objects, JS objects and structs along with generic visitor which // can visit object of any size we provide visitors specialized by @@ -263,6 +264,11 @@ class StaticNewSpaceVisitor : public StaticVisitorBase { FixedArray::BodyDescriptor, int>::Visit); + table_.Register(kVisitGlobalContext, + &FixedBodyVisitor::Visit); + table_.Register(kVisitByteArray, &VisitByteArray); table_.Register(kVisitSharedFunctionInfo, diff --git a/src/objects.h b/src/objects.h index 6e6172f..d917a57 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1751,6 +1751,7 @@ class FixedArray: public HeapObject { // Setters with less debug checks for the GC to use. inline void set_unchecked(int index, Smi* value); 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. inline Object** data_start(); diff --git a/src/serialize.cc b/src/serialize.cc index cde7577..ccba737 100644 --- a/src/serialize.cc +++ b/src/serialize.cc @@ -500,7 +500,7 @@ void ExternalReferenceEncoder::Put(Address key, int index) { ExternalReferenceDecoder::ExternalReferenceDecoder() - : encodings_(NewArray(kTypeCodeCount)) { + : encodings_(NewArray(kTypeCodeCount)) { ExternalReferenceTable* external_references = ExternalReferenceTable::instance(); for (int type = kFirstTypeCode; type < kTypeCodeCount; ++type) { @@ -619,6 +619,8 @@ void Deserializer::Deserialize() { external_reference_decoder_ = new ExternalReferenceDecoder(); Heap::IterateStrongRoots(this, VISIT_ONLY_STRONG); Heap::IterateWeakRoots(this, VISIT_ALL); + + Heap::set_global_contexts_list(Heap::undefined_value()); } diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 80254b0..2b1f61a 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -954,6 +954,7 @@ TEST(Regression39128) { CHECK(page->IsRegionDirty(clone_addr + (object_size - kPointerSize))); } + TEST(TestCodeFlushing) { i::FLAG_allow_natives_syntax = true; // If we do not flush code this test is invalid. @@ -997,3 +998,88 @@ TEST(TestCodeFlushing) { CHECK(function->shared()->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 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(Heap::global_contexts_list()); + while (!object->IsUndefined()) { + count++; + if (count == n) Heap::CollectAllGarbage(true); + object = + Handle(Context::cast(*object)->get(Context::NEXT_CONTEXT_LINK)); + } + return count; +} + + +TEST(TestInternalWeakListsTraverseWithGC) { + static const int kNumTestContexts = 10; + + v8::HandleScope scope; + v8::Persistent 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(); + } +}