Commit a7bb1d52 authored by antonm@chromium.org's avatar antonm@chromium.org

Introduce one way dependencies into object grouping.

Those are necessary to properly manage relationship between objects in cases
when they don't form proper tree structure.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7202 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e181bd2b
......@@ -2717,6 +2717,17 @@ class V8EXPORT V8 {
size_t length,
RetainedObjectInfo* info = NULL);
/**
* Allows the host application to declare implicit references between
* the objects: if |parent| is alive, all |children| are alive too.
* After each garbage collection, all implicit references
* are removed. It is intended to be used in the before-garbage-collection
* callback function.
*/
static void AddImplicitReferences(Persistent<Object> parent,
Persistent<Value>* children,
size_t length);
/**
* Initializes from snapshot if possible. Otherwise, attempts to
* initialize from scratch. This function is called implicitly if
......
......@@ -4126,11 +4126,22 @@ void V8::AddObjectGroup(Persistent<Value>* objects,
RetainedObjectInfo* info) {
if (IsDeadCheck("v8::V8::AddObjectGroup()")) return;
STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**));
i::GlobalHandles::AddGroup(
i::GlobalHandles::AddObjectGroup(
reinterpret_cast<i::Object***>(objects), length, info);
}
void V8::AddImplicitReferences(Persistent<Object> parent,
Persistent<Value>* children,
size_t length) {
if (IsDeadCheck("v8::V8::AddImplicitReferences()")) return;
STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**));
i::GlobalHandles::AddImplicitReferences(
*Utils::OpenHandle(*parent),
reinterpret_cast<i::Object***>(children), length);
}
int V8::AdjustAmountOfExternalAllocatedMemory(int change_in_bytes) {
if (IsDeadCheck("v8::V8::AdjustAmountOfExternalAllocatedMemory()")) return 0;
return i::Heap::AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
......
......@@ -541,16 +541,36 @@ List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
return &groups;
}
void GlobalHandles::AddGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info) {
void GlobalHandles::AddObjectGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info) {
ObjectGroup* new_entry = new ObjectGroup(length, info);
for (size_t i = 0; i < length; ++i)
for (size_t i = 0; i < length; ++i) {
new_entry->objects_.Add(handles[i]);
}
ObjectGroups()->Add(new_entry);
}
List<ImplicitRefGroup*>* GlobalHandles::ImplicitRefGroups() {
// Lazily initialize the list to avoid startup time static constructors.
static List<ImplicitRefGroup*> groups(4);
return &groups;
}
void GlobalHandles::AddImplicitReferences(HeapObject* parent,
Object*** children,
size_t length) {
ImplicitRefGroup* new_entry = new ImplicitRefGroup(parent, length);
for (size_t i = 0; i < length; ++i) {
new_entry->children_.Add(children[i]);
}
ImplicitRefGroups()->Add(new_entry);
}
void GlobalHandles::RemoveObjectGroups() {
List<ObjectGroup*>* object_groups = ObjectGroups();
for (int i = 0; i< object_groups->length(); i++) {
......@@ -559,4 +579,14 @@ void GlobalHandles::RemoveObjectGroups() {
object_groups->Clear();
}
void GlobalHandles::RemoveImplicitRefGroups() {
List<ImplicitRefGroup*>* ref_groups = ImplicitRefGroups();
for (int i = 0; i< ref_groups->length(); i++) {
delete ref_groups->at(i);
}
ref_groups->Clear();
}
} } // namespace v8::internal
......@@ -39,9 +39,6 @@ namespace internal {
// At GC the destroyed global handles are removed from the free list
// and deallocated.
// Callback function on handling weak global handles.
// typedef bool (*WeakSlotCallback)(Object** pointer);
// An object group is treated like a single JS object: if one of object in
// the group is alive, all objects in the same group are considered alive.
// An object group is used to simulate object relationship in a DOM tree.
......@@ -61,6 +58,24 @@ class ObjectGroup : public Malloced {
};
// An implicit references group consists of two parts: a parent object and
// a list of children objects. If the parent is alive, all the children
// are alive too.
class ImplicitRefGroup : public Malloced {
public:
ImplicitRefGroup() : children_(4) {}
ImplicitRefGroup(HeapObject* parent, size_t capacity)
: parent_(parent),
children_(static_cast<int>(capacity)) { }
HeapObject* parent_;
List<Object**> children_;
private:
DISALLOW_COPY_AND_ASSIGN(ImplicitRefGroup);
};
typedef void (*WeakReferenceGuest)(Object* object, void* parameter);
class GlobalHandles : public AllStatic {
......@@ -128,17 +143,28 @@ class GlobalHandles : public AllStatic {
static void IdentifyWeakHandles(WeakSlotCallback f);
// Add an object group.
// Should only used in GC callback function before a collection.
// Should be only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection.
static void AddGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info);
static void AddObjectGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info);
// Add an implicit references' group.
// Should be only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection.
static void AddImplicitReferences(HeapObject* parent,
Object*** children,
size_t length);
// Returns the object groups.
static List<ObjectGroup*>* ObjectGroups();
// Returns the implicit references' groups.
static List<ImplicitRefGroup*>* ImplicitRefGroups();
// Remove bags, this should only happen after GC.
static void RemoveObjectGroups();
static void RemoveImplicitRefGroups();
// Tear down the global handle structure.
static void TearDown();
......
......@@ -1190,14 +1190,41 @@ void MarkCompactCollector::MarkObjectGroups() {
MarkObject(HeapObject::cast(*objects[j]));
}
}
// Once the entire group has been colored gray, set the object group
// to NULL so it won't be processed again.
delete object_groups->at(i);
delete entry;
object_groups->at(i) = NULL;
}
}
void MarkCompactCollector::MarkImplicitRefGroups() {
List<ImplicitRefGroup*>* ref_groups = GlobalHandles::ImplicitRefGroups();
for (int i = 0; i < ref_groups->length(); i++) {
ImplicitRefGroup* entry = ref_groups->at(i);
if (entry == NULL) continue;
if (!entry->parent_->IsMarked()) continue;
List<Object**>& children = entry->children_;
// A parent object is marked, so mark as gray all child white heap
// objects.
for (int j = 0; j < children.length(); ++j) {
if ((*children[j])->IsHeapObject()) {
MarkObject(HeapObject::cast(*children[j]));
}
}
// Once the entire group has been colored gray, set the group
// to NULL so it won't be processed again.
delete entry;
ref_groups->at(i) = NULL;
}
}
// Mark all objects reachable from the objects on the marking stack.
// Before: the marking stack contains zero or more heap object pointers.
// After: the marking stack is empty, and all objects reachable from the
......@@ -1276,11 +1303,12 @@ void MarkCompactCollector::ProcessMarkingStack() {
}
void MarkCompactCollector::ProcessObjectGroups() {
void MarkCompactCollector::ProcessExternalMarking() {
bool work_to_do = true;
ASSERT(marking_stack.is_empty());
while (work_to_do) {
MarkObjectGroups();
MarkImplicitRefGroups();
work_to_do = !marking_stack.is_empty();
ProcessMarkingStack();
}
......@@ -1311,10 +1339,9 @@ void MarkCompactCollector::MarkLiveObjects() {
MarkRoots(&root_visitor);
// The objects reachable from the roots are marked, yet unreachable
// objects are unmarked. Mark objects reachable from object groups
// containing at least one marked object, and continue until no new
// objects are reachable from the object groups.
ProcessObjectGroups();
// objects are unmarked. Mark objects reachable due to host
// application specific logic.
ProcessExternalMarking();
// The objects reachable from the roots or object groups are marked,
// yet unreachable objects are unmarked. Mark objects reachable
......@@ -1330,9 +1357,9 @@ void MarkCompactCollector::MarkLiveObjects() {
EmptyMarkingStack();
}
// Repeat the object groups to mark unmarked groups reachable from the
// weak roots.
ProcessObjectGroups();
// Repeat host application specific marking to mark unmarked objects
// reachable from the weak roots.
ProcessExternalMarking();
// Prune the symbol table removing all symbols only pointed to by the
// symbol table. Cannot use symbol_table() here because the symbol
......@@ -1350,6 +1377,7 @@ void MarkCompactCollector::MarkLiveObjects() {
// Remove object groups after marking phase.
GlobalHandles::RemoveObjectGroups();
GlobalHandles::RemoveImplicitRefGroups();
// Flush code from collected candidates.
FlushCode::ProcessCandidates();
......
......@@ -220,10 +220,13 @@ class MarkCompactCollector: public AllStatic {
// group marked.
static void MarkObjectGroups();
// Mark all objects in an object group with at least one marked
// object, then all objects reachable from marked objects in object
// groups, and repeat.
static void ProcessObjectGroups();
// Mark objects in implicit references groups if their parent object
// is marked.
static void MarkImplicitRefGroups();
// Mark all objects which are reachable due to host application
// logic like object groups or implicit references' groups.
static void ProcessExternalMarking();
// Mark objects reachable (transitively) from objects in the marking stack
// or overflowed in the heap.
......
......@@ -50,15 +50,19 @@ static bool IsNaN(double x) {
#endif
}
using ::v8::ObjectTemplate;
using ::v8::Value;
using ::v8::AccessorInfo;
using ::v8::Context;
using ::v8::Extension;
using ::v8::Function;
using ::v8::HandleScope;
using ::v8::Local;
using ::v8::String;
using ::v8::Object;
using ::v8::ObjectTemplate;
using ::v8::Persistent;
using ::v8::Script;
using ::v8::Function;
using ::v8::AccessorInfo;
using ::v8::Extension;
using ::v8::String;
using ::v8::Value;
using ::v8::V8;
namespace i = ::i;
......@@ -1789,6 +1793,180 @@ THREADED_TEST(GlobalHandle) {
}
static int NumberOfWeakCalls = 0;
static void WeakPointerCallback(Persistent<Value> handle, void* id) {
CHECK_EQ(reinterpret_cast<void*>(1234), id);
NumberOfWeakCalls++;
handle.Dispose();
}
THREADED_TEST(ApiObjectGroups) {
HandleScope scope;
LocalContext env;
NumberOfWeakCalls = 0;
Persistent<Object> g1s1;
Persistent<Object> g1s2;
Persistent<Object> g1c1;
Persistent<Object> g2s1;
Persistent<Object> g2s2;
Persistent<Object> g2c1;
{
HandleScope scope;
g1s1 = Persistent<Object>::New(Object::New());
g1s2 = Persistent<Object>::New(Object::New());
g1c1 = Persistent<Object>::New(Object::New());
g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g2s1 = Persistent<Object>::New(Object::New());
g2s2 = Persistent<Object>::New(Object::New());
g2c1 = Persistent<Object>::New(Object::New());
g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
}
Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
// Connect group 1 and 2, make a cycle.
CHECK(g1s2->Set(0, g2s2));
CHECK(g2s1->Set(0, g1s1));
{
Persistent<Value> g1_objects[] = { g1s1, g1s2 };
Persistent<Value> g1_children[] = { g1c1 };
Persistent<Value> g2_objects[] = { g2s1, g2s2 };
Persistent<Value> g2_children[] = { g2c1 };
V8::AddObjectGroup(g1_objects, 2);
V8::AddImplicitReferences(g1s1, g1_children, 1);
V8::AddObjectGroup(g2_objects, 2);
V8::AddImplicitReferences(g2s2, g2_children, 1);
}
// Do a full GC
i::Heap::CollectGarbage(i::OLD_POINTER_SPACE);
// All object should be alive.
CHECK_EQ(0, NumberOfWeakCalls);
// Weaken the root.
root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
// But make children strong roots---all the objects (except for children)
// should be collectable now.
g1c1.ClearWeak();
g2c1.ClearWeak();
// Groups are deleted, rebuild groups.
{
Persistent<Value> g1_objects[] = { g1s1, g1s2 };
Persistent<Value> g1_children[] = { g1c1 };
Persistent<Value> g2_objects[] = { g2s1, g2s2 };
Persistent<Value> g2_children[] = { g2c1 };
V8::AddObjectGroup(g1_objects, 2);
V8::AddImplicitReferences(g1s1, g1_children, 1);
V8::AddObjectGroup(g2_objects, 2);
V8::AddImplicitReferences(g2s2, g2_children, 1);
}
i::Heap::CollectGarbage(i::OLD_POINTER_SPACE);
// All objects should be gone. 5 global handles in total.
CHECK_EQ(5, NumberOfWeakCalls);
// And now make children weak again and collect them.
g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
i::Heap::CollectGarbage(i::OLD_POINTER_SPACE);
CHECK_EQ(7, NumberOfWeakCalls);
}
THREADED_TEST(ApiObjectGroupsCycle) {
HandleScope scope;
LocalContext env;
NumberOfWeakCalls = 0;
Persistent<Object> g1s1;
Persistent<Object> g1s2;
Persistent<Object> g2s1;
Persistent<Object> g2s2;
Persistent<Object> g3s1;
Persistent<Object> g3s2;
{
HandleScope scope;
g1s1 = Persistent<Object>::New(Object::New());
g1s2 = Persistent<Object>::New(Object::New());
g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g2s1 = Persistent<Object>::New(Object::New());
g2s2 = Persistent<Object>::New(Object::New());
g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g3s1 = Persistent<Object>::New(Object::New());
g3s2 = Persistent<Object>::New(Object::New());
g3s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
g3s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
}
Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root.
// Connect groups. We're building the following cycle:
// G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other
// groups.
{
Persistent<Value> g1_objects[] = { g1s1, g1s2 };
Persistent<Value> g1_children[] = { g2s1 };
Persistent<Value> g2_objects[] = { g2s1, g2s2 };
Persistent<Value> g2_children[] = { g3s1 };
Persistent<Value> g3_objects[] = { g3s1, g3s2 };
Persistent<Value> g3_children[] = { g1s1 };
V8::AddObjectGroup(g1_objects, 2);
V8::AddImplicitReferences(g1s1, g1_children, 1);
V8::AddObjectGroup(g2_objects, 2);
V8::AddImplicitReferences(g2s1, g2_children, 1);
V8::AddObjectGroup(g3_objects, 2);
V8::AddImplicitReferences(g3s1, g3_children, 1);
}
// Do a full GC
i::Heap::CollectGarbage(i::OLD_POINTER_SPACE);
// All object should be alive.
CHECK_EQ(0, NumberOfWeakCalls);
// Weaken the root.
root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback);
// Groups are deleted, rebuild groups.
{
Persistent<Value> g1_objects[] = { g1s1, g1s2 };
Persistent<Value> g1_children[] = { g2s1 };
Persistent<Value> g2_objects[] = { g2s1, g2s2 };
Persistent<Value> g2_children[] = { g3s1 };
Persistent<Value> g3_objects[] = { g3s1, g3s2 };
Persistent<Value> g3_children[] = { g1s1 };
V8::AddObjectGroup(g1_objects, 2);
V8::AddImplicitReferences(g1s1, g1_children, 1);
V8::AddObjectGroup(g2_objects, 2);
V8::AddImplicitReferences(g2s1, g2_children, 1);
V8::AddObjectGroup(g3_objects, 2);
V8::AddImplicitReferences(g3s1, g3_children, 1);
}
i::Heap::CollectGarbage(i::OLD_POINTER_SPACE);
// All objects should be gone. 7 global handles in total.
CHECK_EQ(7, NumberOfWeakCalls);
}
THREADED_TEST(ScriptException) {
v8::HandleScope scope;
LocalContext env;
......
......@@ -298,6 +298,7 @@ TEST(GCCallback) {
static int NumberOfWeakCalls = 0;
static void WeakPointerCallback(v8::Persistent<v8::Value> handle, void* id) {
ASSERT(id == reinterpret_cast<void*>(1234));
NumberOfWeakCalls++;
handle.Dispose();
}
......@@ -312,23 +313,33 @@ TEST(ObjectGroups) {
GlobalHandles::Create(Heap::AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g1s2 =
GlobalHandles::Create(Heap::AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g1c1 =
GlobalHandles::Create(Heap::AllocateFixedArray(1)->ToObjectChecked());
GlobalHandles::MakeWeak(g1s1.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
GlobalHandles::MakeWeak(g1s2.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
GlobalHandles::MakeWeak(g1c1.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
Handle<Object> g2s1 =
GlobalHandles::Create(Heap::AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g2s2 =
GlobalHandles::Create(Heap::AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g2c1 =
GlobalHandles::Create(Heap::AllocateFixedArray(1)->ToObjectChecked());
GlobalHandles::MakeWeak(g2s1.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
GlobalHandles::MakeWeak(g2s2.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
GlobalHandles::MakeWeak(g2c1.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
Handle<Object> root = GlobalHandles::Create(*g1s1); // make a root.
......@@ -338,9 +349,15 @@ TEST(ObjectGroups) {
{
Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g1_children[] = { g1c1.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
GlobalHandles::AddGroup(g1_objects, 2, NULL);
GlobalHandles::AddGroup(g2_objects, 2, NULL);
Object** g2_children[] = { g2c1.location() };
GlobalHandles::AddObjectGroup(g1_objects, 2, NULL);
GlobalHandles::AddImplicitReferences(HeapObject::cast(*g1s1),
g1_children, 1);
GlobalHandles::AddObjectGroup(g2_objects, 2, NULL);
GlobalHandles::AddImplicitReferences(HeapObject::cast(*g2s2),
g2_children, 1);
}
// Do a full GC
Heap::CollectGarbage(OLD_POINTER_SPACE);
......@@ -352,17 +369,38 @@ TEST(ObjectGroups) {
GlobalHandles::MakeWeak(root.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
// But make children strong roots---all the objects (except for children)
// should be collectable now.
GlobalHandles::ClearWeakness(g1c1.location());
GlobalHandles::ClearWeakness(g2c1.location());
// Groups are deleted, rebuild groups.
{
Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g1_children[] = { g1c1.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
GlobalHandles::AddGroup(g1_objects, 2, NULL);
GlobalHandles::AddGroup(g2_objects, 2, NULL);
Object** g2_children[] = { g2c1.location() };
GlobalHandles::AddObjectGroup(g1_objects, 2, NULL);
GlobalHandles::AddImplicitReferences(HeapObject::cast(*g1s1),
g1_children, 1);
GlobalHandles::AddObjectGroup(g2_objects, 2, NULL);
GlobalHandles::AddImplicitReferences(HeapObject::cast(*g2s2),
g2_children, 1);
}
Heap::CollectGarbage(OLD_POINTER_SPACE);
// All objects should be gone. 5 global handles in total.
CHECK_EQ(5, NumberOfWeakCalls);
// And now make children weak again and collect them.
GlobalHandles::MakeWeak(g1c1.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
GlobalHandles::MakeWeak(g2c1.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
Heap::CollectGarbage(OLD_POINTER_SPACE);
CHECK_EQ(7, NumberOfWeakCalls);
}
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