Author: Armin Rigo <[email protected]>
Branch: weakref
Changeset: r407:69291ba64476
Date: 2013-07-17 19:49 +0200
http://bitbucket.org/pypy/stmgc/changeset/69291ba64476/
Log: Weakrefs in major collections. Tests are a bit light here given
that there are a lot of possible corner cases.
diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -222,11 +222,13 @@
if (!(id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
id_copy->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;
/* see fix_outdated() */
- id_copy->h_tid |= GCFLAG_VISITED;
+ if (!(id_copy->h_tid & GCFLAG_VISITED)) {
+ id_copy->h_tid |= GCFLAG_VISITED;
- /* XXX: may not always need tracing? */
- if (!(id_copy->h_tid & GCFLAG_STUB))
- gcptrlist_insert(&objects_to_trace, id_copy);
+ /* XXX: may not always need tracing? */
+ if (!(id_copy->h_tid & GCFLAG_STUB))
+ gcptrlist_insert(&objects_to_trace, id_copy);
+ }
}
else {
/* prebuilt originals won't get collected anyway
@@ -236,6 +238,14 @@
}
}
+static void visit(gcptr *pobj);
+
+gcptr stmgcpage_visit(gcptr obj)
+{
+ visit(&obj);
+ return obj;
+}
+
static void visit(gcptr *pobj)
{
gcptr obj = *pobj;
@@ -275,10 +285,10 @@
keep_original_alive(prev_obj);
assert(*pobj == prev_obj);
- gcptr obj1 = obj;
- visit(&obj1); /* recursion, but should be only once */
+ /* recursion, but should be only once */
+ obj = stmgcpage_visit(obj);
assert(prev_obj->h_tid & GCFLAG_STUB);
- prev_obj->h_revision = ((revision_t)obj1) | 2;
+ prev_obj->h_revision = ((revision_t)obj) | 2;
return;
}
}
@@ -649,8 +659,6 @@
int i;
wlog_t *item;
- stm_invalidate_old_weakrefs(gcp);
-
for (i = 1; i < GC_SMALL_REQUESTS; i++) {
sweep_pages(gcp, i);
}
@@ -777,9 +785,13 @@
assert(gcptrlist_size(&objects_to_trace) == 0);
mark_prebuilt_roots();
mark_all_stack_roots();
- visit_all_objects();
+ do {
+ visit_all_objects();
+ stm_visit_old_weakrefs();
+ } while (gcptrlist_size(&objects_to_trace) != 0);
gcptrlist_delete(&objects_to_trace);
clean_up_lists_of_read_objects_and_fix_outdated_flags();
+ stm_clean_old_weakrefs();
mc_total_in_use = mc_total_reserved = 0;
free_all_unused_local_pages();
diff --git a/c4/gcpage.h b/c4/gcpage.h
--- a/c4/gcpage.h
+++ b/c4/gcpage.h
@@ -84,6 +84,7 @@
void stmgcpage_add_prebuilt_root(gcptr obj);
void stmgcpage_possibly_major_collect(int force);
long stmgcpage_count(int quantity);
+gcptr stmgcpage_visit(gcptr);
extern struct GcPtrList stm_prebuilt_gcroots;
diff --git a/c4/test/test_weakref.py b/c4/test/test_weakref.py
--- a/c4/test/test_weakref.py
+++ b/c4/test/test_weakref.py
@@ -115,6 +115,6 @@
major_collect()
p2b = lib.stm_pop_root()
p1 = lib.stm_pop_root()
- assert lib.rawgetptr(p1, 0) == p2
+ assert lib.rawgetptr(p1, 0) == p2b
assert p2b != p2
- assert lib.rawgetlong(p2b, 0) == 912809218
+ assert lib.getlong(p2b, 0) == 912809218
diff --git a/c4/weakref.c b/c4/weakref.c
--- a/c4/weakref.c
+++ b/c4/weakref.c
@@ -64,29 +64,140 @@
/***** Major collection *****/
-void stm_invalidate_old_weakrefs(struct tx_public_descriptor *gcp)
+static _Bool is_partially_visited(gcptr obj)
{
- /* walk over list of objects that contain weakrefs. If the
- object it references does not survive, invalidate the weakref */
- long i;
+ /* Based on gcpage.c:visit(). Check the code here if we simplify
+ visit(). Returns True or False depending on whether we find any
+ version of 'obj' to be VISITED or not.
+ */
+ restart:
+ if (obj->h_tid & GCFLAG_VISITED)
+ return 1;
+
+ if (obj->h_revision & 1) {
+ assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+ assert(!(obj->h_tid & GCFLAG_STUB));
+ return 0;
+ }
+ else if (obj->h_tid & GCFLAG_PUBLIC) {
+ /* h_revision is a ptr: we have a more recent version */
+ if (!(obj->h_revision & 2)) {
+ /* go visit the more recent version */
+ obj = (gcptr)obj->h_revision;
+ }
+ else {
+ /* it's a stub */
+ assert(obj->h_tid & GCFLAG_STUB);
+ obj = (gcptr)(obj->h_revision - 2);
+ }
+ goto restart;
+ }
+ else {
+ assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
+ gcptr B = (gcptr)obj->h_revision;
+ assert(B->h_tid & (GCFLAG_PUBLIC | GCFLAG_BACKUP_COPY));
+ if (B->h_tid & GCFLAG_VISITED)
+ return 1;
+ assert(!(obj->h_tid & GCFLAG_STUB));
+ assert(!(B->h_tid & GCFLAG_STUB));
+
+ if (IS_POINTER(B->h_revision)) {
+ assert(B->h_tid & GCFLAG_PUBLIC);
+ assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
+ assert(!(B->h_revision & 2));
+
+ obj = (gcptr)B->h_revision;
+ goto restart;
+ }
+ }
+ return 0;
+}
+
+static void visit_old_weakrefs(struct tx_public_descriptor *gcp)
+{
+ /* Note: it's possible that a weakref points to a public stub to a
+ protected object, and only the protected object was marked as
+ VISITED so far. In this case, this function needs to mark the
+ public stub as VISITED too.
+ */
+ long i, size = gcp->old_weakrefs.size;
gcptr *items = gcp->old_weakrefs.items;
- for (i = gcp->old_weakrefs.size - 1; i >= 0; i--) {
+ for (i = 0; i < size; i++) {
gcptr weakref = items[i];
+ /* weakrefs are immutable: during a major collection, they
+ cannot be in the nursery, and so there should be only one
+ version of each weakref object. XXX relying on this is
+ a bit fragile, but simplifies things a lot... */
+ assert(weakref->h_revision & 1);
+
if (!(weakref->h_tid & GCFLAG_VISITED)) {
- /* weakref itself dies */
+ /* the weakref itself dies */
}
else {
size_t size = stmgc_size(weakref);
gcptr pointing_to = WEAKREF_PTR(weakref, size);
- //...;
- abort();
+ assert(pointing_to != NULL);
+ if (is_partially_visited(pointing_to)) {
+ pointing_to = stmgcpage_visit(pointing_to);
+ assert(pointing_to->h_tid & GCFLAG_VISITED);
+ WEAKREF_PTR(weakref, size) = pointing_to;
+ }
+ else {
+ /* the weakref appears to be pointing to a dying object,
+ but we don't know for sure now. Clearing it is left
+ to clean_old_weakrefs(). */
+ }
}
+ }
+}
+static void clean_old_weakrefs(struct tx_public_descriptor *gcp)
+{
+ long i, size = gcp->old_weakrefs.size;
+ gcptr *items = gcp->old_weakrefs.items;
+
+ for (i = size - 1; i >= 0; i--) {
+ gcptr weakref = items[i];
+ assert(weakref->h_revision & 1);
+ if (weakref->h_tid & GCFLAG_VISITED) {
+ size_t size = stmgc_size(weakref);
+ gcptr pointing_to = WEAKREF_PTR(weakref, size);
+ if (pointing_to->h_tid & GCFLAG_VISITED) {
+ continue; /* the target stays alive, the weakref remains */
+ }
+ WEAKREF_PTR(weakref, size) = NULL; /* the target dies */
+ }
/* remove this weakref from the list */
items[i] = items[--gcp->old_weakrefs.size];
}
-
gcptrlist_compress(&gcp->old_weakrefs);
}
+
+static void for_each_public_descriptor(
+ void visit(struct tx_public_descriptor *)) {
+ struct tx_descriptor *d;
+ for (d = stm_tx_head; d; d = d->tx_next)
+ visit(d->public_descriptor);
+
+ struct tx_public_descriptor *gcp;
+ revision_t index = -1;
+ while ((gcp = stm_get_free_public_descriptor(&index)) != NULL)
+ visit(gcp);
+}
+
+void stm_visit_old_weakrefs(void)
+{
+ /* Figure out which weakrefs survive, which possibly
+ adds more objects to 'objects_to_trace'.
+ */
+ for_each_public_descriptor(visit_old_weakrefs);
+}
+
+void stm_clean_old_weakrefs(void)
+{
+ /* Clean up the non-surviving weakrefs
+ */
+ for_each_public_descriptor(clean_old_weakrefs);
+}
diff --git a/c4/weakref.h b/c4/weakref.h
--- a/c4/weakref.h
+++ b/c4/weakref.h
@@ -3,7 +3,8 @@
void stm_move_young_weakrefs(struct tx_descriptor *);
-void stm_invalidate_old_weakrefs(struct tx_public_descriptor *);
+void stm_visit_old_weakrefs(void);
+void stm_clean_old_weakrefs(void);
#endif
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit