[pypy-commit] stmgc copy-over-original2: In-progress: refactor gcpage.visit() and related code
arigo
noreply at buildbot.pypy.org
Thu Jul 25 17:24:24 CEST 2013
Author: Armin Rigo <arigo at tunes.org>
Branch: copy-over-original2
Changeset: r435:d86ab3aa636d
Date: 2013-07-25 17:24 +0200
http://bitbucket.org/pypy/stmgc/changeset/d86ab3aa636d/
Log: In-progress: refactor gcpage.visit() and related code
diff --git a/c4/et.h b/c4/et.h
--- a/c4/et.h
+++ b/c4/et.h
@@ -67,7 +67,7 @@
static const revision_t GCFLAG_PREBUILT_ORIGINAL = STM_FIRST_GCFLAG << 3;
static const revision_t GCFLAG_PUBLIC_TO_PRIVATE = STM_FIRST_GCFLAG << 4;
// in stmgc.h: GCFLAG_WRITE_BARRIER = STM_FIRST_GCFLAG << 5;
-static const revision_t GCFLAG_MOVED = STM_FIRST_GCFLAG << 6;
+static const revision_t GCFLAG_MOVED = STM_FIRST_GCFLAG << 6;
static const revision_t GCFLAG_BACKUP_COPY /*debug*/ = STM_FIRST_GCFLAG << 7;
static const revision_t GCFLAG_STUB /*debug*/ = STM_FIRST_GCFLAG << 8;
static const revision_t GCFLAG_PRIVATE_FROM_PROTECTED = STM_FIRST_GCFLAG << 9;
diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -212,35 +212,160 @@
static struct GcPtrList objects_to_trace;
-static void keep_original_alive(gcptr obj)
+static gcptr copy_over_original(gcptr obj, gcptr id_copy)
{
- /* keep alive the original of a visited object */
- gcptr id_copy = (gcptr)obj->h_original;
- /* prebuilt original objects may have a predifined
- hash in h_original */
- if (id_copy && !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
- assert(id_copy->h_tid & GCFLAG_PUBLIC);
- if (!(id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
- id_copy->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;
- /* see fix_outdated() */
- if (!(id_copy->h_tid & GCFLAG_VISITED)) {
- id_copy->h_tid |= GCFLAG_VISITED;
- assert(!(id_copy->h_tid & GCFLAG_MOVED));
+ assert(obj != id_copy);
+ assert(!(id_copy->h_revision & 1)); /* not head-revision itself */
- /* XXX: may not always need tracing? */
- if (!(id_copy->h_tid & GCFLAG_STUB))
- gcptrlist_insert(&objects_to_trace, id_copy);
+ /* check a few flags */
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+ assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
+ assert(!(obj->h_tid & GCFLAG_BACKUP_COPY));
+
+ assert(id_copy->h_tid & GCFLAG_PUBLIC);
+ assert(!(id_copy->h_tid & GCFLAG_BACKUP_COPY));
+
+ /* id_copy may be a stub, but in this case, as the original, it
+ should have been allocated with a big enough chunk of memory.
+ Also, obj itself might be a stub. */
+ assert(!(id_copy->h_tid & GCFLAG_SMALLSTUB));
+ if (!(id_copy->h_tid & GCFLAG_STUB) && !(obj->h_tid & GCFLAG_STUB)) {
+ assert(stmgc_size(id_copy) == stmgc_size(obj));
+ }
+
+ /* add the MOVED flag to 'obj' */
+ obj->h_tid |= GCFLAG_MOVED;
+
+ /* copy the object's content */
+ dprintf(("copy %p over %p\n", obj, id_copy));
+ memcpy(id_copy + 1, obj + 1, stmgc_size(obj) - sizeof(struct stm_object_s));
+
+ /* copy the object's h_revision number */
+ id_copy->h_revision = obj->h_revision;
+
+ /* copy the STUB flag */
+ id_copy->h_tid &= ~GCFLAG_STUB;
+ id_copy->h_tid |= (obj->h_tid & GCFLAG_STUB);
+
+ return id_copy;
+}
+
+static void visit_nonpublic(gcptr obj)
+{
+ assert(!(obj->h_tid & GCFLAG_PUBLIC));
+ assert(!(obj->h_tid & GCFLAG_STUB));
+ assert(!(obj->h_tid & GCFLAG_HAS_ID));
+ assert(!(obj->h_tid & GCFLAG_SMALLSTUB));
+ assert(!(obj->h_tid & GCFLAG_MOVED));
+
+ if (obj->h_tid & GCFLAG_VISITED)
+ return; /* already visited */
+
+ obj->h_tid |= GCFLAG_VISITED;
+ gcptrlist_insert(&objects_to_trace, obj);
+}
+
+static gcptr visit_public(gcptr obj)
+{
+ /* The goal is to walk to the most recent copy, then copy its
+ content back into the h_original, and finally returns this
+ h_original.
+ */
+ gcptr original;
+ if (obj->h_original != 0 &&
+ !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL))
+ original = (gcptr)obj->h_original;
+ else
+ original = obj;
+
+ /* the original object must also be a public object, and cannot
+ be a small stub. */
+ assert(original->h_tid & GCFLAG_PUBLIC);
+ assert(!(original->h_tid & GCFLAG_SMALLSTUB));
+
+ assert(!(obj->h_tid & GCFLAG_BACKUP_COPY));
+ assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+ assert(!(original->h_tid & GCFLAG_BACKUP_COPY));
+ assert(!(original->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+
+ /* if 'original' was already visited, we are done */
+ if (original->h_tid & GCFLAG_VISITED)
+ return original;
+
+ /* walk to the head of the chained list */
+ while (IS_POINTER(obj->h_revision)) {
+ if (!(obj->h_revision & 2)) {
+ obj = (gcptr)obj->h_revision;
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+ continue;
+ }
+
+ /* it's a stub: check the current stealing status */
+ assert(obj->h_tid & GCFLAG_STUB);
+ gcptr obj2 = (gcptr)(obj->h_revision - 2);
+
+ if (obj2->h_tid & GCFLAG_PUBLIC) {
+ /* the stub target itself was stolen, so is public now.
+ Continue looping from there. */
+ obj = obj2;
+ continue;
+ }
+
+ if (obj2->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) {
+ /* the stub target is a private_from_protected. */
+ gcptr obj3 = (gcptr)obj2->h_revision;
+ if (obj3->h_tid & GCFLAG_PUBLIC) {
+ assert(!(obj3->h_tid & GCFLAG_BACKUP_COPY));
+ /* the backup copy was stolen and is now a regular
+ public object. */
+ obj = obj3;
+ continue;
}
- }
+ else {
+ /* the backup copy was not stolen. Ignore this pair
+ obj2/obj3, and the head of the public chain is obj.
+ The pair obj2/obj3 was or will be handled by
+ mark_all_stack_roots(). */
+ assert(obj3->h_tid & GCFLAG_BACKUP_COPY);
+ break;
+ }
+ }
else {
- /* prebuilt originals won't get collected anyway
- and if they are not reachable in any other way,
- we only ever need their location, not their content */
+ /* the stub target is just a protected object.
+ The head of the public chain is obj. */
+ assert(!IS_POINTER(obj2->h_revision));
+ break;
}
}
+
+ /* copy obj over original */
+ if (obj != original)
+ copy_over_original(obj, original);
+
+ /* return this original */
+ original->h_tid |= GCFLAG_VISITED;
+ gcptrlist_insert(&objects_to_trace, original);
+ return original;
}
-static void visit(gcptr *pobj);
+static void visit(gcptr *pobj)
+{
+ /* Visits '*pobj', marking it as surviving and possibly adding it to
+ objects_to_trace. Fixes *pobj to point to the exact copy that
+ survived.
+ */
+ gcptr obj = *pobj;
+ if (obj == NULL);
+ return;
+
+ if (!(obj->h_tid & GCFLAG_PUBLIC)) {
+ /* 'obj' is a private or protected copy. */
+ visit_nonpublic(obj);
+ }
+ else {
+ *pobj = visit_public(obj);
+ }
+}
gcptr stmgcpage_visit(gcptr obj)
{
@@ -248,203 +373,6 @@
return obj;
}
-static gcptr copy_over_original(gcptr obj)
-{
- assert(!(obj->h_tid & GCFLAG_VISITED));
- assert(!(obj->h_tid & GCFLAG_STUB));
-
- if (obj->h_tid & GCFLAG_PUBLIC /* XXX: required? */
- && !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)
- && obj->h_original) {
-
- gcptr id_copy = (gcptr)obj->h_original;
- assert(!(id_copy->h_revision & 1)); /* not head-revision itself */
- if (!(id_copy->h_tid & GCFLAG_PUBLIC))
- assert(0);
- /* return NULL; */ /* could be priv_from_protected with
- where backup is stolen and its h-original
- points to it. */
-
- /* id_copy may be a stub, but in this case, as the original, it
- should have been allocated with a big enough chunk of memory */
- assert(!(id_copy->h_tid & GCFLAG_SMALLSTUB));
- assert((id_copy->h_tid & GCFLAG_STUB) ||
- stmgc_size(id_copy) == stmgc_size(obj));
- /* prehash may be specific hash value for prebuilts, or 0 */
- revision_t prehash = id_copy->h_original;
- assert(IMPLIES(prehash, id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL));
- /* old_tid may have prebuilt_original flags that should not be lost */
- revision_t old_tid = id_copy->h_tid;
-
- memcpy(id_copy, obj, stmgc_size(obj));
- assert(!((id_copy->h_tid ^ old_tid)
- & (GCFLAG_BACKUP_COPY //| GCFLAG_STUB, id_copy may be stub
- | GCFLAG_PUBLIC | GCFLAG_HAS_ID | GCFLAG_SMALLSTUB
- | GCFLAG_PRIVATE_FROM_PROTECTED)));
- id_copy->h_original = prehash;
- id_copy->h_tid = old_tid & ~(GCFLAG_VISITED |/* will be visited next */
- GCFLAG_STUB); /* no longer a stub */
-
- dprintf(("copy %p over %p\n", obj, id_copy));
-
- /* for those visiting later: */
- obj->h_revision = (revision_t)id_copy;
-
- /* mark as MOVED for transactions to fix their
- public_to_private. Otherwise, inevitable transactions
- would think their public obj was modified (also for
- other transactions, but they can abort) */
- obj->h_tid |= GCFLAG_MOVED;
-
- return id_copy;
- }
-
- return NULL;
-}
-
-static void visit(gcptr *pobj)
-{
- gcptr obj = *pobj;
- if (obj == NULL)
- return;
-
- restart:
- if (obj->h_revision & 1) {
- assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
- assert(!(obj->h_tid & GCFLAG_STUB));
- if (!(obj->h_tid & GCFLAG_VISITED)) {
- obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see fix_outdated() */
-
- gcptr next = copy_over_original(obj);
- if (next) {
- revision_t loc = (revision_t)pobj - offsetof(struct stm_object_s,
- h_revision);
- if ((gcptr)loc != next)
- /* we don't want to set h_revision of 'next' to
- 'next' itself, it was already set by
- copy_over_original to a global head revision */
- *pobj = next;
- obj = next;
-
- assert(obj->h_revision & 1);
- assert(!(obj->h_tid & GCFLAG_VISITED));
- goto restart;
- }
-
- obj->h_tid |= GCFLAG_VISITED;
- assert(!(obj->h_tid & GCFLAG_MOVED));
-
- gcptrlist_insert(&objects_to_trace, obj);
-
- keep_original_alive(obj);
- }
- }
- else if (obj->h_tid & GCFLAG_PUBLIC) {
- /* h_revision is a ptr: we have a more recent version */
- gcptr prev_obj = obj;
-
- if (!(obj->h_revision & 2)) {
- /* go visit the more recent version */
- obj = (gcptr)obj->h_revision;
- }
- else {
- /* it's a stub: keep it if it points to a protected version,
- because we need to keep the effect of stealing if it is
- later accessed by the wrong thread. If it points to a
- public object (possibly outdated), we can ignore the stub.
- */
- assert(obj->h_tid & GCFLAG_STUB);
- obj = (gcptr)(obj->h_revision - 2);
- if (!(obj->h_tid & GCFLAG_PUBLIC)) {
- prev_obj->h_tid |= GCFLAG_VISITED;
- assert(!(prev_obj->h_tid & GCFLAG_MOVED));
-
- keep_original_alive(prev_obj);
-
- assert(*pobj == prev_obj);
- /* recursion, but should be only once */
- obj = stmgcpage_visit(obj);
- assert(prev_obj->h_tid & GCFLAG_STUB);
- prev_obj->h_revision = ((revision_t)obj) | 2;
- return;
- }
- }
-
- if (!(obj->h_revision & 3)) {
- /* obj is neither a stub nor a most recent revision:
- completely ignore obj->h_revision */
-
- obj = (gcptr)obj->h_revision;
- assert(obj->h_tid & GCFLAG_PUBLIC);
- prev_obj->h_revision = (revision_t)obj;
- }
- *pobj = obj;
- goto restart;
- }
- else if (obj->h_tid & GCFLAG_VISITED) {
- dprintf(("[already visited: %p]\n", obj));
- assert(obj == *pobj);
- assert((obj->h_revision & 3) || /* either odd, or stub */
- (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
- return; /* already seen */
- }
- 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 (obj->h_original && (gcptr)obj->h_original != B) {
- /* if B is original, it will be visited anyway */
- assert(obj->h_original == B->h_original);
- assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
- keep_original_alive(obj);
- }
-
- obj->h_tid |= GCFLAG_VISITED;
- assert(!(obj->h_tid & GCFLAG_MOVED));
- assert(!(obj->h_tid & GCFLAG_STUB));
-
- if (!(B->h_tid & GCFLAG_MOVED)) {
- B->h_tid |= GCFLAG_VISITED;
- assert(!(B->h_tid & GCFLAG_STUB));
- gcptrlist_insert2(&objects_to_trace, obj, B);
- }
- else {
- /* B was copied over its h_original */
- pobj = (gcptr *)&obj->h_revision;
- obj = *pobj;
- goto restart;
- }
-
- if (IS_POINTER(B->h_revision)) {
- assert(B->h_tid & GCFLAG_PUBLIC);
- assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
- assert(!(B->h_revision & 2));
-
- pobj = (gcptr *)&B->h_revision;
- obj = *pobj;
- goto restart;
- }
- }
-}
-
-
-static void visit_keep(gcptr obj)
-{
- if (!(obj->h_tid & GCFLAG_VISITED)) {
- obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see fix_outdated() */
- obj->h_tid |= GCFLAG_VISITED;
- assert(!(obj->h_tid & GCFLAG_MOVED));
- gcptrlist_insert(&objects_to_trace, obj);
-
- if (IS_POINTER(obj->h_revision)) {
- assert(!(obj->h_revision & 2));
- visit((gcptr *)&obj->h_revision);
- }
- keep_original_alive(obj);
- }
-}
-
static void visit_all_objects(void)
{
while (gcptrlist_size(&objects_to_trace) > 0) {
@@ -458,20 +386,18 @@
/* Note about prebuilt roots: 'stm_prebuilt_gcroots' is a list that
contains all the ones that have been modified. Because they are
themselves not in any page managed by this file, their
- GCFLAG_VISITED will not be removed at the end of the current
- collection. This is fine because the base object cannot contain
- references to the heap. So we decided to systematically set
- GCFLAG_VISITED on prebuilt objects. */
+ GCFLAG_VISITED is not removed at the end of the current
+ collection. That's why we remove it here. */
gcptr *pobj = stm_prebuilt_gcroots.items;
gcptr *pend = stm_prebuilt_gcroots.items + stm_prebuilt_gcroots.size;
- gcptr obj;
+ gcptr obj, obj2;
for (; pobj != pend; pobj++) {
obj = *pobj;
obj->h_tid &= ~GCFLAG_VISITED;
assert(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL);
- /* assert(IS_POINTER(obj->h_revision)); */
- visit_keep(obj);
+ obj2 = visit_public(obj);
+ assert(obj2 == obj); /* it is its own original */
}
}
@@ -498,8 +424,8 @@
static void mark_all_stack_roots(void)
{
struct tx_descriptor *d;
- struct G2L new_public_to_private;
- memset(&new_public_to_private, 0, sizeof(struct G2L));
+ struct GcPtrList new_public_to_private;
+ memset(&new_public_to_private, 0, sizeof(new_public_to_private));
for (d = stm_tx_head; d; d = d->tx_next) {
assert(!stm_has_got_any_lock(d));
@@ -513,64 +439,49 @@
/* the current transaction's private copies of public objects */
wlog_t *item;
-
- /* transactions need to have their pub_to_priv fixed. Otherwise,
- they'll think their objects got outdated. Only absolutely
- necessary for inevitable transactions (XXX check performance?). */
- dprintf(("start fixup (%p):\n", d));
- G2L_LOOP_FORWARD(d->public_to_private, item) {
- gcptr R = item->addr;
- gcptr L = item->val;
- if (R->h_tid & GCFLAG_MOVED) {
- /* R was copied over its original */
- gcptr new_R = (gcptr)R->h_original;
- /* gcptrlist_insert(&objects_to_trace, new_R); */
-
- g2l_insert(&new_public_to_private, new_R, L);
- G2L_LOOP_DELETE(item);
-
- if (L && L->h_revision == (revision_t)R) {
- L->h_revision = (revision_t)new_R;
- dprintf((" fixup %p to %p <-> %p\n", R, new_R, L));
- }
- else {
- dprintf((" fixup %p to %p -> %p\n", R, new_R, L));
- }
- }
- } G2L_LOOP_END;
-
- /* reinsert to real pub_to_priv */
- G2L_LOOP_FORWARD(new_public_to_private, item) {
- g2l_insert(&d->public_to_private, item->addr, item->val);
- } G2L_LOOP_END;
- g2l_clear(&new_public_to_private);
-
- /* now visit them */
G2L_LOOP_FORWARD(d->public_to_private, item) {
/* note that 'item->addr' is also in the read set, so if it was
outdated, it will be found at that time */
gcptr R = item->addr;
gcptr L = item->val;
- visit_keep(R);
+ /* we visit the public object R */
+ gcptr new_R = visit_public(R);
+
+ if (new_R != R) {
+ /* we have to update the key in public_to_private, which
+ can only be done by deleting the existing key and
+ (after the loop) re-inserting the new key. */
+ G2L_LOOP_DELETE(item);
+ gcptrlist_insert2(&new_public_to_private, new_R, L);
+ }
+
+ /* we visit the private copy L --- which at this point
+ should be private, possibly private_from_protected,
+ so visit() should return the same private copy */
if (L != NULL) {
- /* minor collection found R->L in public_to_young
- and R was modified. It then sets item->val to NULL and wants
- to abort later. */
- revision_t v = L->h_revision;
- visit_keep(L);
- /* a bit of custom logic here: if L->h_revision used to
- point exactly to R, as set by stealing, then we must
- keep this property, even though visit_keep(L) might
- decide it would be better to make it point to a more
- recent copy. */
- if (v == (revision_t)R) {
- assert(L->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
- L->h_revision = v; /* restore */
- }
+ visit_nonpublic(L);
}
+
} G2L_LOOP_END;
+ /* reinsert to real pub_to_priv */
+ long i, size = new_public_to_private.size;
+ gcptr *items = new_public_to_private.items;
+ for (i = 0; i < size; i += 2) {
+ g2l_insert(&d->public_to_private, items[i], items[i + 1]);
+ }
+ gcptrlist_clear(&new_public_to_private);
+
+ /* the current transaction's private copies of protected objects */
+ items = d->private_from_protected.items;
+ for (i = d->private_from_protected.size - 1; i >= 0; i--) {
+ gcptr obj = items[i];
+ assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
+ visit_nonpublic(obj);
+ visit((gcptr *)&obj->h_revision);
+ }
+
/* make sure that the other lists are empty */
assert(gcptrlist_size(&d->public_with_young_copy) == 0);
assert(gcptrlist_size(&d->public_descriptor->stolen_objects) == 0);
@@ -587,37 +498,13 @@
d->num_private_from_protected_known_old);
}
- if (new_public_to_private.raw_start)
- g2l_delete_not_used_any_more(&new_public_to_private);
+ gcptrlist_delete(&new_public_to_private);
}
static void cleanup_for_thread(struct tx_descriptor *d)
{
long i;
gcptr *items;
-
- /* It can occur that 'private_from_protected' contains an object that
- * has not been visited at all (maybe only in inevitable
- * transactions).
- */
- items = d->private_from_protected.items;
- for (i = d->private_from_protected.size - 1; i >= 0; i--) {
- gcptr obj = items[i];
- assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
- /* we don't copy private / protected objects over prebuilts (yet) */
- assert(!(obj->h_tid & GCFLAG_MOVED));
-
- if (!(obj->h_tid & GCFLAG_VISITED)) {
- /* forget 'obj' */
- dprintf(("private_from_protected: %p UNLISTED\n", obj));
- items[i] = items[--d->private_from_protected.size];
- }
- else {
- dprintf(("private_from_protected: %p\n", obj));
- assert(((gcptr)obj->h_revision)->h_tid & GCFLAG_VISITED);
- }
- }
-
assert(d->old_objects_to_trace.size == 0);
/* If we're aborting this transaction anyway, we don't need to do
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -130,7 +130,7 @@
#define GCFLAG_BACKUP_COPY ...
#define GCFLAG_PUBLIC_TO_PRIVATE ...
#define GCFLAG_WRITE_BARRIER ...
- #define GCFLAG_MOVED ...
+ #define GCFLAG_MOVED ...
#define GCFLAG_STUB ...
#define GCFLAG_PRIVATE_FROM_PROTECTED ...
#define GCFLAG_HAS_ID ...
More information about the pypy-commit
mailing list