[pypy-commit] stm default: Add GCFLAG_OLD. It's very easy to maintain it, and it simplifies things.
arigo
noreply at buildbot.pypy.org
Sat May 25 18:01:25 CEST 2013
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r5:b62e0a5f9940
Date: 2013-05-25 18:01 +0200
http://bitbucket.org/pypy/stm/changeset/b62e0a5f9940/
Log: Add GCFLAG_OLD. It's very easy to maintain it, and it simplifies
things.
diff --git a/c3/et.c b/c3/et.c
--- a/c3/et.c
+++ b/c3/et.c
@@ -81,7 +81,7 @@
{
old_to_young:
v &= ~2;
- if (UNLIKELY(!stmgc_is_young(d, (gcptr)v)))
+ if (UNLIKELY(!stmgc_is_young_in(d, (gcptr)v)))
{
stmgc_public_to_foreign_protected(R);
goto retry;
@@ -183,7 +183,7 @@
if (v & 2)
{
v &= ~2;
- if (!stmgc_is_young(thread_descriptor, (gcptr)v))
+ if (!is_young((gcptr)v))
{
stmgc_follow_foreign(R);
goto retry;
@@ -317,7 +317,7 @@
/* XXX optimize me based on common patterns */
R = HeadOfRevisionChainList(d, P);
- switch (stmgc_classify(d, R)) {
+ switch (stmgc_classify(R)) {
case K_PRIVATE: W = R; break;
case K_PROTECTED: W = LocalizeProtected(d, R); break;
case K_PUBLIC: W = LocalizePublic(d, R); break;
@@ -358,8 +358,8 @@
abort();//XXX
if (R->h_tid & GCFLAG_NURSERY_MOVED)
{
- assert(stmgc_is_young(d, R));
- assert(!stmgc_is_young(d, (gcptr)v));
+ assert(is_young(R));
+ assert(!is_young((gcptr)v));
R = (gcptr)v;
goto retry;
}
@@ -651,6 +651,15 @@
L->h_tid &= ~GCFLAG_PRIVATE_COPY;
L->h_revision = new_revision;
+ if (is_young(L))
+ {
+ item->val = (gcptr)(((revision_t)L) | 2);
+#ifdef DUMP_EXTRA
+ fprintf(stderr, "PUBLIC-TO-PROTECTED:\n");
+ /*mark*/
+#endif
+ }
+
} G2L_LOOP_END;
smp_wmb(); /* a memory barrier: make sure the new L->h_revisions are visible
@@ -659,31 +668,19 @@
G2L_LOOP_FORWARD(d->public_to_private, item)
{
gcptr R = item->addr;
- gcptr L = item->val;
- revision_t v = (revision_t)L;
+ revision_t v = (revision_t)item->val;
assert(!(R->h_tid & GCFLAG_PRIVATE_COPY));
assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
- assert(!stmgc_is_young(d, R));
+ assert(!is_young(R));
assert(R->h_revision != localrev);
- if (stmgc_is_young(d, L))
- {
- v |= 2;
#ifdef DUMP_EXTRA
- fprintf(stderr, "%p->h_revision = %p (UpdateChainHeads2) "
- "<=== PUBLIC-TO-PROTECTED\n", R, (gcptr)v);
- /*mark*/
+ fprintf(stderr, "%p->h_revision = %p (UpdateChainHeads2)\n",
+ R, (gcptr)v);
+ /*mark*/
#endif
- }
- else
- {
-#ifdef DUMP_EXTRA
- fprintf(stderr, "%p->h_revision = %p (UpdateChainHeads2)\n", R, L);
- /*mark*/
-#endif
- }
ACCESS_ONCE(R->h_revision) = v;
if (R->h_tid & GCFLAG_PREBUILT_ORIGINAL)
diff --git a/c3/et.h b/c3/et.h
--- a/c3/et.h
+++ b/c3/et.h
@@ -52,6 +52,8 @@
* set again at the next minor collection.
*
* GCFLAG_NURSERY_MOVED is used temporarily during minor collections.
+ *
+ * GCFLAG_OLD is set on old objects.
*/
#define GCFLAG_PRIVATE_COPY (STM_FIRST_GCFLAG << 0)
#define GCFLAG_VISITED (STM_FIRST_GCFLAG << 1)
@@ -59,10 +61,12 @@
#define GCFLAG_PREBUILT_ORIGINAL (STM_FIRST_GCFLAG << 3)
#define GCFLAG_WRITE_BARRIER (STM_FIRST_GCFLAG << 4)
#define GCFLAG_NURSERY_MOVED (STM_FIRST_GCFLAG << 5)
+#define GCFLAG_OLD (STM_FIRST_GCFLAG << 6)
/* this value must be reflected in PREBUILT_FLAGS in stmgc.h */
#define GCFLAG_PREBUILT (GCFLAG_VISITED | \
- GCFLAG_PREBUILT_ORIGINAL)
+ GCFLAG_PREBUILT_ORIGINAL | \
+ GCFLAG_OLD)
#define GC_FLAG_NAMES { "PRIVATE_COPY", \
"VISITED", \
@@ -70,6 +74,7 @@
"PREBUILT_ORIGINAL", \
"WRITE_BARRIER", \
"NURSERY_MOVED", \
+ "OLD", \
NULL }
/************************************************************/
diff --git a/c3/gcpage.c b/c3/gcpage.c
--- a/c3/gcpage.c
+++ b/c3/gcpage.c
@@ -119,7 +119,7 @@
struct tx_descriptor *d;
for (d = tx_head; d; d = d->tx_next) {
- if (stmgc_is_young(d, L))
+ if (stmgc_is_young_in(d, L))
goto found;
}
assert(0); /* L is not a young pointer anywhere! */
@@ -396,8 +396,8 @@
G2L_LOOP_FORWARD(d->public_to_private, item) {
- assert(stmgc_classify(d, item->addr) == K_PUBLIC);
- assert(stmgc_classify(d, item->val) == K_PRIVATE);
+ assert(stmgc_classify(item->addr) == K_PUBLIC);
+ assert(stmgc_classify(item->val) == K_PRIVATE);
item->addr->h_tid |= GCFLAG_PUBLIC_TO_PRIVATE;
} G2L_LOOP_END;
@@ -689,7 +689,8 @@
/* ^^^ write this line even if the following segfault */
switch (stm_dbgmem_is_active(obj, 1)) {
case 1:
- if (thread_descriptor && stmgc_is_young(thread_descriptor, obj)) {
+ if (thread_descriptor &&
+ stmgc_is_young_in(thread_descriptor, obj)) {
if (g2l_contains(
&thread_descriptor->young_objects_outside_nursery, obj))
fprintf(stderr, " (young but outside nursery)");
@@ -738,7 +739,7 @@
other thread */
if (!(obj->h_revision & 2) ||
(thread_descriptor &&
- stmgc_is_young(thread_descriptor, p))) {
+ stmgc_is_young_in(thread_descriptor, p))) {
gcptrlist_insert(&pending, p);
}
else {
diff --git a/c3/nursery.c b/c3/nursery.c
--- a/c3/nursery.c
+++ b/c3/nursery.c
@@ -9,18 +9,29 @@
return (d->nursery <= (char*)obj && ((char*)obj) < d->nursery_end);
}
-int stmgc_is_young(struct tx_descriptor *d, gcptr obj)
+int stmgc_is_young_in(struct tx_descriptor *d, gcptr obj)
{
+ /* Check if 'obj' is a young object for 'd'. (So if it's a young
+ object for another thread, returns False.) */
return is_in_nursery(d, obj) ||
g2l_contains(&d->young_objects_outside_nursery, obj);
}
-enum protection_class_t stmgc_classify(struct tx_descriptor *d, gcptr obj)
+#ifdef _GC_DEBUG
+int is_young(gcptr obj)
+{
+ int result = (obj->h_tid & GCFLAG_OLD) == 0;
+ assert(result == stmgc_is_young_in(thread_descriptor, obj));
+ return result;
+}
+#endif
+
+enum protection_class_t stmgc_classify(gcptr obj)
{
/* note that this function never returns K_OLD_PRIVATE. */
if (obj->h_revision == stm_local_revision)
return K_PRIVATE;
- if (stmgc_is_young(d, obj))
+ if (is_young(obj))
return K_PROTECTED;
else
return K_PUBLIC;
@@ -42,16 +53,21 @@
G2L_FIND(d->young_objects_outside_nursery, obj, entry,
goto not_found);
- if (obj->h_tid & GCFLAG_VISITED)
+ if (obj->h_tid & GCFLAG_OLD)
e = private ? K_OLD_PRIVATE : K_PUBLIC;
else
e = private ? K_PRIVATE : K_PROTECTED;
}
assert(stm_dbgmem_is_active(obj, 0));
+ if (e == K_PRIVATE || e == K_PROTECTED)
+ assert((obj->h_tid & GCFLAG_OLD) == 0);
+ else
+ assert((obj->h_tid & GCFLAG_OLD) == GCFLAG_OLD);
return e;
not_found:
assert(stm_dbgmem_is_active(obj, 1));
+ assert(obj->h_tid & GCFLAG_OLD);
return private ? K_OLD_PRIVATE : K_PUBLIC;
}
@@ -91,6 +107,7 @@
gcptr p = stmgcpage_malloc(size);
memset(p, 0, size);
p->h_revision = stm_local_revision;
+ p->h_tid = GCFLAG_OLD;
return p;
}
@@ -145,7 +162,8 @@
localobj->h_tid &= ~(GCFLAG_VISITED |
GCFLAG_PUBLIC_TO_PRIVATE |
GCFLAG_PREBUILT_ORIGINAL |
- GCFLAG_WRITE_BARRIER);
+ GCFLAG_WRITE_BARRIER |
+ GCFLAG_OLD);
localobj->h_tid |= GCFLAG_PRIVATE_COPY;
localobj->h_revision = stm_local_revision;
return localobj;
@@ -243,11 +261,13 @@
assert(!(obj->h_tid & GCFLAG_WRITE_BARRIER));
assert(!(obj->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
+ assert(!(obj->h_tid & GCFLAG_OLD));
assert(obj->h_revision & 1); /* odd value so far */
size_t size = stmcb_size(obj);
gcptr fresh_old_copy = stmgcpage_malloc(size);
memcpy(fresh_old_copy, obj, size);
+ fresh_old_copy->h_tid |= GCFLAG_OLD;
obj->h_tid |= GCFLAG_NURSERY_MOVED;
obj->h_revision = (revision_t)fresh_old_copy;
@@ -274,6 +294,11 @@
struct tx_descriptor *d = thread_descriptor;
recdump1(reason, obj);
+
+ /* Note: it's a good idea here to avoid reading any field of 'obj'
+ before we know it's a young object. This avoids a lot of cache
+ misses and cache pollution.
+ */
retry:
if (!is_in_nursery(d, obj)) {
/* 'obj' is not from the nursery (or 'obj == NULL') */
@@ -289,11 +314,11 @@
goto ignore_and_try_again_with_next;
}
/* was it already marked? */
- if (obj->h_tid & GCFLAG_VISITED) {
+ if (obj->h_tid & GCFLAG_OLD) {
return; /* yes, and no '*root' to fix, as it doesn't move */
}
- /* otherwise, add GCFLAG_VISITED, and continue below */
- obj->h_tid |= GCFLAG_VISITED;
+ /* otherwise, add GCFLAG_OLD, and continue below */
+ obj->h_tid |= GCFLAG_OLD;
fresh_old_copy = obj;
}
else {
@@ -336,7 +361,7 @@
previous_obj = NULL;
}
obj = (gcptr)obj->h_revision;
- assert(stmgc_classify(d, obj) != K_PRIVATE);
+ assert(stmgc_classify(obj) != K_PRIVATE);
PATCH_ROOT_WITH(obj);
goto retry;
}
@@ -345,12 +370,13 @@
static gcptr young_object_becomes_old(struct tx_descriptor *d, gcptr obj
_REASON(char *reason))
{
- assert(stmgc_is_young(d, obj));
+ assert(stmgc_is_young_in(d, obj));
assert(!(obj->h_tid & GCFLAG_NURSERY_MOVED));
assert(!(obj->h_tid & GCFLAG_VISITED));
assert(!(obj->h_tid & GCFLAG_WRITE_BARRIER));
assert(!(obj->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
+ assert(!(obj->h_tid & GCFLAG_OLD));
assert(obj->h_revision & 1); /* odd value so far */
visit_if_young(&obj _REASON(reason));
@@ -544,8 +570,10 @@
if (!is_in_nursery(d, obj)) {
if (!g2l_contains(&d->young_objects_outside_nursery, obj) ||
- (obj->h_tid & GCFLAG_VISITED)) {
- /* non-young or visited young objects are kept */
+ (obj->h_tid & GCFLAG_OLD)) {
+ /* non-young or visited young objects are kept (the
+ first line of this check could be removed, but it is
+ better this way to avoid cache pollution) */
continue;
}
}
@@ -605,7 +633,7 @@
return;
struct tx_descriptor *d = thread_descriptor;
- if (!stmgc_is_young(d, obj))
+ if (!stmgc_is_young_in(d, obj))
return;
/* xxx try to avoid duplicate stubs for the same object */
@@ -635,11 +663,7 @@
G2L_LOOP_FORWARD(d->young_objects_outside_nursery, item) {
gcptr obj = item->addr;
- if (obj->h_tid & GCFLAG_VISITED) {
- /* survives */
- obj->h_tid &= ~GCFLAG_VISITED;
- }
- else {
+ if (!(obj->h_tid & GCFLAG_OLD)) {
/* dies */
stmgcpage_free(obj);
}
@@ -670,16 +694,12 @@
/* now all surviving nursery objects have been moved out, and all
surviving young-but-outside-the-nursery objects have been flagged
- with GCFLAG_VISITED */
+ with GCFLAG_OLD */
+ finish_public_to_young(d);
+
if (g2l_any_entry(&d->young_objects_outside_nursery))
free_unvisited_young_objects_outside_nursery(d);
- /* now all previously young objects won't be changed any more and are
- considered old (this is why we have to do this after
- free_unvisited_young_objects_outside_nursery(), which clears
- the VISITED flags and clears 'd->young_objects_outside_nursery') */
- finish_public_to_young(d);
-
teardown_minor_collect(d);
/* clear the nursery */
@@ -787,18 +807,121 @@
void stmgc_public_to_foreign_protected(gcptr R)
{
- abort(); //XXX
+ abort();//XXX
+#if 0
+ /* R is a public object, which contains in h_revision a pointer to a
+ protected object --- but it is protectd by another thread,
+ i.e. it likely lives in a foreign nursery. We have to copy the
+ object out ourselves. This is necessary: we can't simply wait
+ for the other thread to do a minor collection, because it might
+ be blocked in a system call or whatever. */
+ struct tx_descriptor *my_d = thread_descriptor;
+
+ /* repeat the checks in the caller, to avoid passing more than one
+ argument here */
+ revision_t v = ACCESS_ONCE(R->h_revision);
+ assert(!(v & 1)); /* "is a pointer" */
+ if (!(v & 2))
+ return; /* changed already, retry */
+
+ gcptr L = (gcptr)(v & ~2);
+
+ /* We need to look up which thread it belongs to and lock this
+ thread's minor collection lock. This also prevents several
+ threads from getting on each other's toes trying to extract
+ objects from the same nursery */
+ struct tx_descriptor *source_d = stm_find_thread_containing_pointer(L);
+ assert(source_d != my_d);
+
+ spinlock_acquire(source_d->collection_lock);
+
+ /* now that we have the lock, check again that R->h_revision was not
+ modified in the meantime */
+ if (ACCESS_ONCE(R->h_revision) != v) {
+ spinlock_release(source_d->collection_lock);
+ return; /* changed already, retry */
+ }
+
+ /* debugging support: "activate" the foreign nursery */
+ int was_active = stm_dbgmem_is_active(source_d->nursery, 0);
+ if (!was_active) assert(stmgc_nursery_hiding(0));
+
+ /* walk to the head of the chain in the foreign nursery */
+ while (1) {
+ ...
+
+ if (!is_in_nursery(d, obj)) {
+ /* 'obj' is not from the nursery (or 'obj == NULL') */
+ if (obj == NULL || !g2l_contains(
+ &d->young_objects_outside_nursery, obj)) {
+ return; /* then it's an old object or NULL, nothing to do */
+ }
+ /* it's a young object outside the nursery */
+
+ /* is it a protected object with a more recent revision?
+ (this test fails automatically if it's a private object) */
+ if (!(obj->h_revision & 1)) {
+ goto ignore_and_try_again_with_next;
+ }
+ /* was it already marked? */
+ if (obj->h_tid & GCFLAG_VISITED) {
+ return; /* yes, and no '*root' to fix, as it doesn't move */
+ }
+ /* otherwise, add GCFLAG_VISITED, and continue below */
+ obj->h_tid |= GCFLAG_VISITED;
+ fresh_old_copy = obj;
+ }
+ else {
+ /* it's a nursery object. Is it:
+ A. an already-moved nursery object?
+ B. a protected object with a more recent revision?
+ C. common case: first visit to an object to copy outside
+ */
+ if (!(obj->h_revision & 1)) {
+
+ if (obj->h_tid & GCFLAG_NURSERY_MOVED) {
+ /* case A: just fix the ref. */
+ PATCH_ROOT_WITH((gcptr)obj->h_revision);
+ return;
+ }
+ else {
+ /* case B */
+ goto ignore_and_try_again_with_next;
+ }
+ }
+ /* case C */
+ fresh_old_copy = copy_outside_nursery(d, obj _REASON("visit"));
+
+ /* fix the original reference */
+ PATCH_ROOT_WITH(fresh_old_copy);
+ }
+
+ /* add 'fresh_old_copy' to the list of objects to trace */
+ assert(!(fresh_old_copy->h_tid & GCFLAG_WRITE_BARRIER));
+ gcptrlist_insert(&d->old_objects_to_trace, fresh_old_copy);
+ recdump1("MOVED TO", fresh_old_copy);
+ return;
+
+ ignore_and_try_again_with_next:
+ if (previous_obj == NULL) {
+ previous_obj = obj;
+ }
+ else {
+ previous_obj->h_revision = obj->h_revision; /* compress chain */
+ previous_obj = NULL;
+ }
+ obj = (gcptr)obj->h_revision;
+ assert(stmgc_classify(d, obj) != K_PRIVATE);
+ PATCH_ROOT_WITH(obj);
+ goto retry;
+
+ ...;
+#endif
}
#if 0
void stmgc_follow_foreign(gcptr R)
{
- /* R is a global old object, which contains in h_revision a pointer
- to a global *young* object --- but it is young for another
- thread, i.e. it likely lives in a foreign nursery. We have to
- copy the object out ourselves. This is necessary: we can't
- simply wait for the other thread to do a minor collection,
- because it might be blocked in a system call or whatever. */
struct tx_descriptor *d = thread_descriptor;
/* repeat the checks in the caller, to avoid passing more than one
diff --git a/c3/nursery.h b/c3/nursery.h
--- a/c3/nursery.h
+++ b/c3/nursery.h
@@ -42,9 +42,15 @@
void stmgc_minor_collect_no_abort(void);
int stmgc_minor_collect_anything_to_do(struct tx_descriptor *);
void stmgc_write_barrier(gcptr);
-enum protection_class_t stmgc_classify(struct tx_descriptor *, gcptr);
-int stmgc_is_young(struct tx_descriptor *, gcptr);
+enum protection_class_t stmgc_classify(gcptr);
+int stmgc_is_young_in(struct tx_descriptor *, gcptr);
void stmgc_public_to_foreign_protected(gcptr);
int stmgc_nursery_hiding(int);
+#ifdef _GC_DEBUG
+int is_young(gcptr);
+#else
+# define is_young(o) (((o)->h_tid & GCFLAG_OLD) == 0)
#endif
+
+#endif
diff --git a/c3/stmgc.h b/c3/stmgc.h
--- a/c3/stmgc.h
+++ b/c3/stmgc.h
@@ -21,7 +21,7 @@
#define STM_SIZE_OF_USER_TID (sizeof(revision_t) / 2) /* in bytes */
#define STM_FIRST_GCFLAG (1L << (8 * STM_SIZE_OF_USER_TID))
#define STM_USER_TID_MASK (STM_FIRST_GCFLAG - 1)
-#define PREBUILT_FLAGS (STM_FIRST_GCFLAG * (2 + 8))
+#define PREBUILT_FLAGS (STM_FIRST_GCFLAG * (2 + 8 + 64))
#define PREBUILT_REVISION 1
diff --git a/c3/stmsync.c b/c3/stmsync.c
--- a/c3/stmsync.c
+++ b/c3/stmsync.c
@@ -104,7 +104,7 @@
{
gcptr result = _stm_allocate_object_of_size_old(size);
assert(tid == (tid & STM_USER_TID_MASK));
- result->h_tid = tid;
+ result->h_tid = tid | GCFLAG_OLD;
return result;
}
diff --git a/c3/test/support.py b/c3/test/support.py
--- a/c3/test/support.py
+++ b/c3/test/support.py
@@ -96,6 +96,7 @@
#define GCFLAG_PREBUILT_ORIGINAL ...
#define GCFLAG_WRITE_BARRIER ...
#define GCFLAG_NURSERY_MOVED ...
+ #define GCFLAG_OLD ...
#define ABRT_MANUAL ...
typedef struct { ...; } page_header_t;
''')
@@ -372,7 +373,7 @@
def oalloc(size):
"Allocate an 'old' object, i.e. outside any nursery"
p = lib.stmgcpage_malloc(size)
- p.h_tid = GCFLAG_WRITE_BARRIER
+ p.h_tid = GCFLAG_WRITE_BARRIER | GCFLAG_OLD
p.h_revision = lib.get_local_revision()
lib.settid(p, 42 + size)
return p
@@ -382,7 +383,7 @@
def oalloc_refs(nrefs):
"Allocate an 'old' object, i.e. outside any nursery, with nrefs pointers"
p = lib.stmgcpage_malloc(HDR + WORD * nrefs)
- p.h_tid = GCFLAG_WRITE_BARRIER
+ p.h_tid = GCFLAG_WRITE_BARRIER | GCFLAG_OLD
p.h_revision = lib.get_local_revision()
lib.settid(p, 421 + nrefs)
for i in range(nrefs):
diff --git a/c3/test/test_gcpage.py b/c3/test/test_gcpage.py
--- a/c3/test/test_gcpage.py
+++ b/c3/test/test_gcpage.py
@@ -162,6 +162,7 @@
def test_local_copy_from_global_obj():
p1 = oalloc(HDR)
+ assert p1.h_tid & GCFLAG_OLD
assert lib.stm_write_barrier(p1) == p1
make_global(p1)
p2n = lib.stm_write_barrier(p1)
More information about the pypy-commit
mailing list