[pypy-commit] stmgc default: Untested, work in progress
arigo
noreply at buildbot.pypy.org
Sun May 26 12:09:45 CEST 2013
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r8:26dca82e7423
Date: 2013-05-26 12:05 +0200
http://bitbucket.org/pypy/stmgc/changeset/26dca82e7423/
Log: Untested, work in progress
diff --git a/c3/atomic_ops.h b/c3/atomic_ops.h
--- a/c3/atomic_ops.h
+++ b/c3/atomic_ops.h
@@ -115,7 +115,7 @@
} while (1)
#define spinlock_release(lock) \
- do { smp_wmb(); assert((lock) == 1); (lock) = 0; } while (0)
+ do { smp_wmb(); assert((lock) != 0); (lock) = 0; } while (0)
#endif /* _SRCSTM_ATOMIC_OPS_ */
diff --git a/c3/et.c b/c3/et.c
--- a/c3/et.c
+++ b/c3/et.c
@@ -314,6 +314,13 @@
struct tx_descriptor *d = thread_descriptor;
assert(d->active >= 1);
+ /* must normalize the situation now, otherwise we risk that
+ LocalizePublic creates a new private version of a public
+ object that has got one, attached to the equivalent stolen
+ protected object */
+ if (gcptrlist_size(&d->stolen_objects) > 0)
+ stmgc_normalize_stolen_objects();
+
/* XXX optimize me based on common patterns */
R = HeadOfRevisionChainList(d, P);
@@ -352,11 +359,9 @@
v = ACCESS_ONCE(R->h_revision);
if (!(v & 1)) // "is a pointer", i.e.
{ // "has a more recent revision"
- /* ... unless it is only a GCFLAG_NURSERY_MOVED, which may
- only come from my thread's nursery; then look at the
- moved-out object instead */
+ /* ... unless it is a GCFLAG_STOLEN object */
abort();//XXX
- if (R->h_tid & GCFLAG_NURSERY_MOVED)
+ if (R->h_tid & GCFLAG_STOLEN)
{
assert(is_young(R));
assert(!is_young((gcptr)v));
@@ -572,6 +577,7 @@
gcptr R = item->addr;
revision_t v;
retry:
+ assert(R->h_tid & GCFLAG_OLD);
v = ACCESS_ONCE(R->h_revision);
if (!(v & 1)) // "is a pointer", i.e.
{ // "has a more recent revision"
@@ -642,6 +648,7 @@
assert(!(L->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
assert(!(L->h_tid & GCFLAG_PREBUILT_ORIGINAL));
assert(!(L->h_tid & GCFLAG_NURSERY_MOVED));
+ assert(!(L->h_tid & GCFLAG_STOLEN));
assert(L->h_revision != localrev); /* modified by AcquireLocks() */
#ifdef DUMP_EXTRA
@@ -673,6 +680,7 @@
assert(!(R->h_tid & GCFLAG_PRIVATE_COPY));
assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
+ assert(!(R->h_tid & GCFLAG_STOLEN));
assert(!is_young(R));
assert(R->h_revision != localrev);
@@ -757,6 +765,7 @@
UpdateChainHeads(d, cur_time, localrev);
+ stmgc_committed_transaction(d);
d->num_commits++;
d->active = 0;
stm_stop_sharedlock();
diff --git a/c3/et.h b/c3/et.h
--- a/c3/et.h
+++ b/c3/et.h
@@ -54,6 +54,9 @@
* GCFLAG_NURSERY_MOVED is used temporarily during minor collections.
*
* GCFLAG_OLD is set on old objects.
+ *
+ * GCFLAG_STOLEN is set of protected objects after we notice that they
+ * have been stolen.
*/
#define GCFLAG_PRIVATE_COPY (STM_FIRST_GCFLAG << 0)
#define GCFLAG_VISITED (STM_FIRST_GCFLAG << 1)
@@ -62,6 +65,7 @@
#define GCFLAG_WRITE_BARRIER (STM_FIRST_GCFLAG << 4)
#define GCFLAG_NURSERY_MOVED (STM_FIRST_GCFLAG << 5)
#define GCFLAG_OLD (STM_FIRST_GCFLAG << 6)
+#define GCFLAG_STOLEN (STM_FIRST_GCFLAG << 7)
/* this value must be reflected in PREBUILT_FLAGS in stmgc.h */
#define GCFLAG_PREBUILT (GCFLAG_VISITED | \
@@ -75,6 +79,7 @@
"WRITE_BARRIER", \
"NURSERY_MOVED", \
"OLD", \
+ "STOLEN", \
NULL }
/************************************************************/
diff --git a/c3/gcpage.c b/c3/gcpage.c
--- a/c3/gcpage.c
+++ b/c3/gcpage.c
@@ -557,9 +557,9 @@
*/
thread_descriptor = d;
stm_local_revision = *d->local_revision_ref;
- assert(stmgc_nursery_hiding(d, 0));
+ assert(stmgc_nursery_hiding(0));
stmgc_minor_collect_no_abort();
- assert(stmgc_nursery_hiding(d, 1));
+ assert(stmgc_nursery_hiding(1));
}
}
thread_descriptor = saved;
diff --git a/c3/nursery.c b/c3/nursery.c
--- a/c3/nursery.c
+++ b/c3/nursery.c
@@ -63,6 +63,8 @@
assert((obj->h_tid & GCFLAG_OLD) == 0);
else
assert((obj->h_tid & GCFLAG_OLD) == GCFLAG_OLD);
+ if (e != K_PROTECTED)
+ assert(!(obj->h_tid & GCFLAG_STOLEN));
return e;
not_found:
@@ -169,6 +171,14 @@
return localobj;
}
+static revision_t fetch_extra_word(gcptr L)
+{
+ size_t size = stmcb_size(L);
+ revision_t extra_word = *(revision_t *)(((char*)L) + size);
+ assert(extra_word & 1); /* "is not a pointer", should be a rev number */
+ return extra_word;
+}
+
void stmgc_start_transaction(struct tx_descriptor *d)
{
assert(!gcptrlist_size(&d->protected_with_private_copy));
@@ -179,6 +189,7 @@
}
static void fix_new_public_to_protected_references(struct tx_descriptor *d);
+static void normalize_stolen_objects(struct tx_descriptor *d);
void stmgc_stop_transaction(struct tx_descriptor *d)
{
@@ -189,13 +200,19 @@
fix_new_public_to_protected_references(d);
spinlock_acquire(d->collection_lock);
+ d->collection_lock = 'C'; /* Committing */
+
if (gcptrlist_size(&d->stolen_objects) > 0)
- abort(); //XXX XXX also is it fine if stolen_objects grows just after
- spinlock_release(d->collection_lock);
+ normalize_stolen_objects(d);
fprintf(stderr, "stop transaction\n");
}
+void stmgc_committed_transaction(struct tx_descriptor *d)
+{
+ spinlock_release(d->collection_lock);
+}
+
void stmgc_abort_transaction(struct tx_descriptor *d)
{
/* cancel the change to the h_revision done in LocalizeProtected() */
@@ -209,20 +226,22 @@
other thread is busy stealing (which includes reading the extra
word immediately after private objects).
*/
- spinlock_acquire(d->collection_lock);
+ if (d->collection_lock != 'C')
+ spinlock_acquire(d->collection_lock);
for (i = 0; i < size; i++) {
gcptr R = items[i];
assert(dclassify(R) == K_PROTECTED);
assert(!(R->h_revision & 1)); /* "is a pointer" */
gcptr L = (gcptr)R->h_revision;
+
+ if (R->h_tid & GCFLAG_STOLEN) { /* ignore stolen objects */
+ assert(dclassify(L) == K_PUBLIC);
+ continue;
+ }
assert(dclassify(L) == K_PRIVATE);
- size_t size = stmcb_size(R);
- revision_t extra_word = *(revision_t *)(((char*)L) + size);
-
- assert(extra_word & 1);
- R->h_revision = extra_word;
+ R->h_revision = fetch_extra_word(L);
abort();//XXX
}
gcptrlist_clear(&d->protected_with_private_copy);
@@ -251,8 +270,7 @@
# define recdump1(msg, obj) /* removed */
#endif
-static inline gcptr create_old_object_copy(struct tx_descriptor *d, gcptr obj
- _REASON(char *reason))
+static inline gcptr create_old_object_copy(gcptr obj _REASON(char *reason))
{
assert(!(obj->h_tid & GCFLAG_NURSERY_MOVED));
assert(!(obj->h_tid & GCFLAG_VISITED));
@@ -335,7 +353,7 @@
}
}
/* case C */
- fresh_old_copy = create_old_object_copy(d, obj _REASON("visit"));
+ fresh_old_copy = create_old_object_copy(obj _REASON("visit"));
obj->h_tid |= GCFLAG_NURSERY_MOVED;
obj->h_revision = (revision_t)fresh_old_copy;
@@ -410,14 +428,15 @@
assert(dclassify(R) == K_PROTECTED);
assert(!(R->h_revision & 1)); /* "is a pointer" */
gcptr L = (gcptr)R->h_revision;
+
+ if (R->h_tid & GCFLAG_STOLEN) { /* ignore stolen objects */
+ assert(dclassify(L) == K_PUBLIC);
+ continue;
+ }
assert(dclassify(L) == K_PRIVATE);
/* we first detach the link, restoring the original R->h_revision */
- size_t size = stmcb_size(R);
- revision_t extra_word = *(revision_t *)(((char*)L) + size);
-
- assert(extra_word & 1);
- R->h_revision = extra_word;
+ R->h_revision = fetch_extra_word(L);
/* then we turn the protected object in a public one */
R = young_object_becomes_old(d, R
@@ -601,7 +620,7 @@
else {
/* second case */
abort();//XXX
- /* ... check
+ /* ABRT_COLLECT_MINOR ... check
for stolen object */
}
}
@@ -686,6 +705,9 @@
/* acquire the "collection lock" first */
setup_minor_collect(d);
+ if (gcptrlist_size(&d->stolen_objects) > 0)
+ normalize_stolen_objects(d);
+
mark_protected_with_private_copy(d);
mark_public_to_young(d);
@@ -775,9 +797,10 @@
gcptrlist_insert(&d->private_old_pointing_to_young, obj);
}
-int stmgc_nursery_hiding(struct tx_descriptor *d, int hide)
+int stmgc_nursery_hiding(int hide)
{
#ifdef _GC_DEBUG
+ struct tx_descriptor *d = thread_descriptor;
if (hide) {
stm_dbgmem_not_used(d->nursery, GC_NURSERY, 1);
}
@@ -808,101 +831,165 @@
/************************************************************/
-static gcptr extract_from_foreign_nursery(struct tx_descriptor *source_d,
- gcptr L)
+static gcptr extract_from_foreign_nursery(gcptr R)
{
- /* "Stealing": this function follows a chain of protected objects
- in the foreign nursery of the thread 'source_d'. It copies the
- last one outside the nursery, and return it. */
- gcptr L2, N;
+ /* "Stealing": this function follows a chain of protected objects in
+ the foreign nursery of the thread temporarily in
+ 'thread_descriptor'. It copies the last one outside the nursery,
+ and return it. */
+ gcptr R2, N;
revision_t source_local_rev, v;
- source_local_rev = ACCESS_ONCE(*source_d->local_revision_ref);
- v = ACCESS_ONCE(L->h_revision);
+ source_local_rev = stm_local_revision;
+ v = ACCESS_ONCE(R->h_revision);
- /* check that L is a protected object */
- assert(!(L->h_tid & GCFLAG_OLD));
+ /* check that R is a protected object */
+ assert(!(R->h_tid & GCFLAG_OLD));
assert(v != source_local_rev);
/* walk to the head of the chain in the foreign nursery
*/
while (!(v & 1)) { /* "is a pointer" */
- L2 = (gcptr)v;
- v = ACCESS_ONCE(L2->h_revision);
+ R2 = (gcptr)v;
+ v = ACCESS_ONCE(R2->h_revision);
if (v == source_local_rev) {
- /* L->h_revision is a pointer, but the target is a private
- object. We ignore private objects, so we stay at L; but
- have to fetch L's real revision off-line from the extra
- word that follows L2 */
- size_t size = stmcb_size(L2);
- v = *(revision_t *)(((char*)L2) + size);
- assert(v & 1); /* "is not a pointer" */
+ /* R->h_revision is a pointer, but the target is a private
+ object. We ignore private objects, so we stay at R; but
+ have to fetch R's real revision off-line from the extra
+ word that follows R2 */
+ v = fetch_extra_word(R2);
break;
}
- else if (L2->h_tid & GCFLAG_OLD) {
+ else if (R2->h_tid & GCFLAG_OLD) {
/* we find a public object again: easy case, just return it */
- return L2;
+ return R2;
}
else {
/* the chain continues with another protected object, go on */
- L = L2;
+ R = R2;
}
}
- /* L is now the protected object to move outside, with revision v. */
- N = create_old_object_copy(source_d, L _REASON("stolen copy"));
+ /* R is now the protected object to move outside, with revision v. */
+ N = create_old_object_copy(R _REASON("stolen copy"));
N->h_revision = v;
- gcptrlist_insert2(&source_d->stolen_objects, L, N);
+ gcptrlist_insert2(&thread_descriptor->stolen_objects, R, N);
- smp_wmb();
+ /* there might be references in N going to protected objects. We
+ must fix them with stubs. */
+ stmcb_trace(N, &create_yo_stubs);
return N;
}
-void stmgc_public_to_foreign_protected(gcptr R)
+void stmgc_public_to_foreign_protected(gcptr P)
{
- /* R is a public object, which contains in h_revision a pointer to a
+ /* P 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;
+ revision_t my_local_rev = stm_local_revision;
/* repeat the checks in the caller, to avoid passing more than one
argument here */
- revision_t v = ACCESS_ONCE(R->h_revision);
+ revision_t v = ACCESS_ONCE(P->h_revision);
assert(!(v & 1)); /* "is a pointer" */
if (!(v & 2))
return; /* changed already, retry */
- gcptr L = (gcptr)(v & ~2);
+ gcptr R = (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);
+ struct tx_descriptor *source_d = stm_find_thread_containing_pointer(R);
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
+ /* now that we have the lock, check again that P->h_revision was not
modified in the meantime. If it did change, we do nothing and will
retry.
*/
- if (R->h_revision == v) {
+ if (P->h_revision == v) {
+ /* temporarily take the identity of source_d */
+ thread_descriptor = source_d;
+ stm_local_revision = *source_d->local_revision_ref;
+
/* debugging support: "activate" the foreign nursery */
int was_active = stm_dbgmem_is_active(source_d->nursery, 0);
- if (!was_active) assert(stmgc_nursery_hiding(source_d, 0));
+ if (!was_active) assert(stmgc_nursery_hiding(0));
- gcptr N = extract_from_foreign_nursery(source_d, L);
- ACCESS_ONCE(R->h_revision) = (revision_t)N;
+ /* copy the protected source object */
+ gcptr N = extract_from_foreign_nursery(R);
+
+ /* make sure the copy N is visible to other threads before we
+ change P->h_revision */
+ smp_wmb();
+
+ /* do the change to P->h_revision */
+ ACCESS_ONCE(P->h_revision) = (revision_t)N;
fprintf(stderr, "STEALING: %p->h_revision changed from %p to %p\n",
- R, L, N);
+ P, R, N);
/* debugging support: "deactivate" the foreign nursery again */
- if (!was_active) assert(stmgc_nursery_hiding(source_d, 1));
+ if (!was_active) assert(stmgc_nursery_hiding(1));
+
+ /* restore my own identity */
+ stm_local_revision = my_local_rev;
+ thread_descriptor = my_d;
}
spinlock_release(source_d->collection_lock);
}
+
+static void normalize_stolen_objects(struct tx_descriptor *d)
+{
+ /* d->stolen_objects lists pairs (R, N) with R being a protected
+ object, and N a public object at the _same_ revision (and with
+ identical content). The protected object R can have a private
+ copy, but it cannot have another already-committed 'h_revision'.
+ */
+ long i, size = d->stolen_objects.size;
+ gcptr *items = d->stolen_objects.items;
+
+ for (i = 0; i < size; i += 2) {
+ gcptr R = items[i];
+ gcptr N = items[i + 1];
+
+ assert(dclassify(R) == K_PROTECTED);
+ assert(dclassify(N) == K_PUBLIC);
+
+ revision_t v = R->h_revision;
+ R->h_tid |= GCFLAG_STOLEN;
+ R->h_revision = (revision_t)N;
+
+ if (!(v & 1)) { /* "is a pointer" */
+ gcptr L = (gcptr)v;
+ assert(dclassify(L) == K_PRIVATE);
+
+ /* R has got a private copy L. This means that R is listed
+ in protected_with_private_copy. Where? We don't know.
+ The other places that scan protected_with_private_copy
+ must carefully ignore GCFLAG_STOLEN entries. */
+
+ /* we re-insert L as a private copy of the public object N */
+ N->h_tid |= GCFLAG_PUBLIC_TO_PRIVATE;
+ g2l_insert(&d->public_to_private, N, L);
+ gcptrlist_insert(&d->public_to_young, N);
+ }
+ }
+ gcptrlist_clear(&d->stolen_objects);
+ abort();//XXX
+}
+
+void stmgc_normalize_stolen_objects(void)
+{
+ struct tx_descriptor *d = thread_descriptor;
+ spinlock_acquire(d->collection_lock);
+ normalize_stolen_objects(d);
+ spinlock_release(d->collection_lock);
+}
diff --git a/c3/nursery.h b/c3/nursery.h
--- a/c3/nursery.h
+++ b/c3/nursery.h
@@ -35,6 +35,7 @@
gcptr stmgc_duplicate(gcptr, revision_t);
void stmgc_start_transaction(struct tx_descriptor *);
void stmgc_stop_transaction(struct tx_descriptor *);
+void stmgc_committed_transaction(struct tx_descriptor *);
void stmgc_abort_transaction(struct tx_descriptor *);
void stmgc_init_tls(void);
void stmgc_done_tls(void);
@@ -45,7 +46,8 @@
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(struct tx_descriptor *, int);
+int stmgc_nursery_hiding(int);
+void stmgc_normalize_stolen_objects(void);
#ifdef _GC_DEBUG
int is_young(gcptr);
diff --git a/c3/stmsync.c b/c3/stmsync.c
--- a/c3/stmsync.c
+++ b/c3/stmsync.c
@@ -236,12 +236,12 @@
{
int err = pthread_rwlock_rdlock(&rwlock_shared);
assert(err == 0);
- assert(stmgc_nursery_hiding(thread_descriptor, 0));
+ assert(stmgc_nursery_hiding(0));
}
void stm_stop_sharedlock(void)
{
- assert(stmgc_nursery_hiding(thread_descriptor, 1));
+ assert(stmgc_nursery_hiding(1));
int err = pthread_rwlock_unlock(&rwlock_shared);
assert(err == 0);
}
More information about the pypy-commit
mailing list