[pypy-commit] stm default: First draft of stealing.
arigo
noreply at buildbot.pypy.org
Sat May 25 19:57:50 CEST 2013
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r6:118aee6f0236
Date: 2013-05-25 19:57 +0200
http://bitbucket.org/pypy/stm/changeset/118aee6f0236/
Log: First draft of stealing.
diff --git a/c3/et.c b/c3/et.c
--- a/c3/et.c
+++ b/c3/et.c
@@ -751,8 +751,9 @@
(long)cur_time);
revision_t localrev = stm_local_revision;
- stm_local_revision = -(cur_time + 1);
- assert(stm_local_revision & 1);
+ revision_t newrev = -(cur_time + 1);
+ assert(newrev & 1);
+ ACCESS_ONCE(stm_local_revision) = newrev;
UpdateChainHeads(d, cur_time, localrev);
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(0));
+ assert(stmgc_nursery_hiding(d, 0));
stmgc_minor_collect_no_abort();
- assert(stmgc_nursery_hiding(1));
+ assert(stmgc_nursery_hiding(d, 1));
}
}
thread_descriptor = saved;
diff --git a/c3/nursery.c b/c3/nursery.c
--- a/c3/nursery.c
+++ b/c3/nursery.c
@@ -174,7 +174,6 @@
assert(!gcptrlist_size(&d->protected_with_private_copy));
assert(!g2l_any_entry(&d->public_to_private));
assert(!gcptrlist_size(&d->private_old_pointing_to_young));
- assert(!gcptrlist_size(&d->stolen_objects));
d->num_read_objects_known_old = 0;
d->num_public_to_protected = gcptrlist_size(&d->public_to_young);
}
@@ -252,24 +251,20 @@
# define recdump1(msg, obj) /* removed */
#endif
-static inline gcptr copy_outside_nursery(struct tx_descriptor *d, gcptr obj
- _REASON(char *reason))
+static inline gcptr create_old_object_copy(struct tx_descriptor *d, gcptr obj
+ _REASON(char *reason))
{
- assert(is_in_nursery(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 */
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;
#ifdef DUMP_EXTRA
fprintf(stderr, "%s: %p is copied to %p\n", reason, obj, fresh_old_copy);
@@ -340,7 +335,9 @@
}
}
/* case C */
- fresh_old_copy = copy_outside_nursery(d, obj _REASON("visit"));
+ fresh_old_copy = create_old_object_copy(d, obj _REASON("visit"));
+ obj->h_tid |= GCFLAG_NURSERY_MOVED;
+ obj->h_revision = (revision_t)fresh_old_copy;
/* fix the original reference */
PATCH_ROOT_WITH(fresh_old_copy);
@@ -770,11 +767,9 @@
gcptrlist_insert(&d->private_old_pointing_to_young, obj);
}
-int stmgc_nursery_hiding(int hide)
+int stmgc_nursery_hiding(struct tx_descriptor *d, int hide)
{
#ifdef _GC_DEBUG
- struct tx_descriptor *d = thread_descriptor;
-
if (hide) {
stm_dbgmem_not_used(d->nursery, GC_NURSERY, 1);
}
@@ -805,10 +800,59 @@
/************************************************************/
+static gcptr extract_from_foreign_nursery(struct tx_descriptor *source_d,
+ gcptr L)
+{
+ /* "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;
+ revision_t source_local_rev, v;
+
+ source_local_rev = ACCESS_ONCE(*source_d->local_revision_ref);
+ v = ACCESS_ONCE(L->h_revision);
+
+ /* check that L is a protected object */
+ assert(!(L->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);
+ 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" */
+ break;
+ }
+ else if (L2->h_tid & GCFLAG_OLD) {
+ /* we find a public object again: easy case, just return it */
+ return L2;
+ }
+ else {
+ /* the chain continues with another protected object, go on */
+ L = L2;
+ }
+ }
+
+ /* L is now the protected object to move outside, with revision v. */
+ N = create_old_object_copy(source_d, L _REASON("stolen copy"));
+ N->h_revision = v;
+ gcptrlist_insert2(&source_d->stolen_objects, L, N);
+
+ smp_wmb();
+
+ return N;
+}
+
void stmgc_public_to_foreign_protected(gcptr R)
{
- 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
@@ -836,155 +880,21 @@
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 */
+ modified in the meantime. If it did change, we do nothing and will
+ retry.
+ */
+ if (R->h_revision == v) {
+ /* 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));
+
+ gcptr N = extract_from_foreign_nursery(source_d, L);
+ ACCESS_ONCE(R->h_revision) = (revision_t)N;
+ fprintf(stderr, "STEALING: %p->h_revision changed from %p to %p\n",
+ R, L, N);
+
+ /* debugging support: "deactivate" the foreign nursery again */
+ if (!was_active) assert(stmgc_nursery_hiding(source_d, 1));
}
-
- /* 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
+ spinlock_release(source_d->collection_lock);
}
-
-#if 0
-void stmgc_follow_foreign(gcptr R)
-{
- struct tx_descriptor *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);
-
- setup_minor_collect(source_d, 0);
-
- /* check again that R->h_revision was not modified in the meantime */
- if (ACCESS_ONCE(R->h_revision) != v) {
- teardown_minor_collect(source_d);
- return; /* changed already, retry */
- }
-
- /* temporarily take the identity of the other thread to run a very
- partial minor collection */
- thread_descriptor = source_d;
- assert(stmgc_is_young(source_d, L));
-
- /* debugging support */
- int was_active = stm_dbgmem_is_active(source_d->nursery, 0);
- if (!was_active) assert(stmgc_nursery_hiding(0));
-
- /* force the object L out of the nursery, and follow references */
-
- /*XXX <<<young_objects_outside_nursery, race>>>*/
- /*XXX <<<stmgcpage_malloc, race>>>*/
- /*XXX <<<object's tid???, race>>>*/
-
- gcptr L2 = L;
- assert(L->h_revision != *source_d->local_revision_ref);
- visit_nursery(&L2 _REASON("FORCE OUT FROM OTHER THREAD"));
- assert(!is_in_nursery(source_d, L2));
- if (L2 != L)
- assert(L->h_tid & GCFLAG_NURSERY_MOVED);
- else
- assert(L->h_tid & GCFLAG_VISITED);
-
- visit_all_outside_objects(source_d);
-
- mark_visited_young_objects_outside_nursery_as_old(source_d);
-
- /* debugging support */
- if (!was_active) assert(stmgc_nursery_hiding(0));
-
- teardown_minor_collect(source_d);
- /* the smp_wmb() done above forces all writes from above to be
- committed; then afterward we fix the field R->h_revision, making
- it a pointer to the now-old object */
- R->h_revision = (revision_t)L2;
-
- /* done taking the identity of the other thread, take mine again */
- thread_descriptor = d;
-}
-#endif
diff --git a/c3/nursery.h b/c3/nursery.h
--- a/c3/nursery.h
+++ b/c3/nursery.h
@@ -45,7 +45,7 @@
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);
+int stmgc_nursery_hiding(struct tx_descriptor *, int);
#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(0));
+ assert(stmgc_nursery_hiding(thread_descriptor, 0));
}
void stm_stop_sharedlock(void)
{
- assert(stmgc_nursery_hiding(1));
+ assert(stmgc_nursery_hiding(thread_descriptor, 1));
int err = pthread_rwlock_unlock(&rwlock_shared);
assert(err == 0);
}
More information about the pypy-commit
mailing list