[pypy-commit] stmgc implement-id: merge
Raemi
noreply at buildbot.pypy.org
Fri Jun 21 13:46:51 CEST 2013
Author: Remi Meier <meierrem at student.ethz.ch>
Branch: implement-id
Changeset: r217:74a48c97a3f6
Date: 2013-06-20 16:53 +0200
http://bitbucket.org/pypy/stmgc/changeset/74a48c97a3f6/
Log: merge
diff --git a/c4/demo2.c b/c4/demo2.c
--- a/c4/demo2.c
+++ b/c4/demo2.c
@@ -105,7 +105,7 @@
stm_read_barrier((gcptr)r_next))) {
asm volatile ("pause":::"memory"); /* smp_spinloop() */
i++;
- assert(i < 1000);
+ assert(i < 1000000);
}
// for now:
assert(((nodeptr)stm_read_barrier((gcptr)r_prev->next))->value
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -972,7 +972,7 @@
} G2L_LOOP_END;
}
-//static pthread_mutex_t mutex_prebuilt_gcroots = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_prebuilt_gcroots = PTHREAD_MUTEX_INITIALIZER;
static void UpdateChainHeads(struct tx_descriptor *d, revision_t cur_time,
revision_t localrev)
@@ -1028,16 +1028,13 @@
#endif
ACCESS_ONCE(R->h_revision) = v;
-#if 0
if (R->h_tid & GCFLAG_PREBUILT_ORIGINAL)
{
/* cannot possibly get here more than once for a given value of R */
pthread_mutex_lock(&mutex_prebuilt_gcroots);
gcptrlist_insert(&stm_prebuilt_gcroots, R);
pthread_mutex_unlock(&mutex_prebuilt_gcroots);
- /*mark*/
}
-#endif
} G2L_LOOP_END;
@@ -1433,6 +1430,8 @@
/* we are reusing 'pd' */
descriptor_array_free_list = pd->free_list_next;
assert(descriptor_array_free_list >= 0);
+ assert(pd->stolen_objects.size == 0);
+ assert(pd->stolen_young_stubs.size == 0);
assert(pd->collection_lock == 0 || pd->collection_lock == -1);
pd->collection_lock = 0;
}
@@ -1483,8 +1482,12 @@
assert(d->active == 0);
stmgcpage_acquire_global_lock();
- /* our nursery is empty at this point */
+ /* our nursery is empty at this point. The list 'stolen_objects'
+ should have been emptied at the previous minor collection and
+ should remain empty because we don't have any young object. */
+ assert(d->public_descriptor->stolen_objects.size == 0);
assert(d->public_descriptor->stolen_young_stubs.size == 0);
+ gcptrlist_delete(&d->public_descriptor->stolen_objects);
gcptrlist_delete(&d->public_descriptor->stolen_young_stubs);
stmgcpage_done_tls();
diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -199,21 +199,56 @@
return; /* already seen */
if (obj->h_revision & 1) {
+ assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see also fix_outdated() */
}
- else {
+ else if (obj->h_tid & GCFLAG_PUBLIC) {
/* h_revision is a ptr: we have a more recent version */
- assert(!(obj->h_tid & GCFLAG_STUB));
gcptr prev_obj = obj;
- obj = (gcptr)obj->h_revision; /* go visit the more recent version */
- if (!(obj->h_tid & GCFLAG_VISITED) && IS_POINTER(obj->h_revision)) {
+ 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(*pobj == prev_obj);
+ gcptr obj1 = obj;
+ visit(&obj1); /* recursion, but should be only once */
+ prev_obj->h_revision = ((revision_t)obj1) + 2;
+ return;
+ }
+ }
+
+ if (!(obj->h_revision & 3)) {
+ obj = (gcptr)obj->h_revision;
+ assert(obj->h_tid & GCFLAG_PUBLIC);
prev_obj->h_revision = (revision_t)obj;
}
*pobj = obj;
goto restart;
}
+ else {
+ assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
+ gcptr B = (gcptr)obj->h_revision;
+ if (!(B->h_tid & GCFLAG_PUBLIC)) {
+ /* a regular private_from_protected object with a backup copy B */
+ assert(B->h_tid & GCFLAG_BACKUP_COPY);
+ B->h_tid |= GCFLAG_VISITED;
+ }
+ else {
+ assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
+ abort(); // XXX
+ }
+ }
obj->h_tid |= GCFLAG_VISITED;
gcptrlist_insert(&objects_to_trace, obj);
}
@@ -304,6 +339,11 @@
if (d->active < 0)
return; /* already "aborted" during forced minor collection */
+ if (d->active == 2) {
+ /* inevitable transaction: clear the list of read objects */
+ gcptrlist_clear(&d->list_of_read_objects);
+ }
+
for (i = d->list_of_read_objects.size - 1; i >= 0; --i) {
gcptr obj = items[i];
@@ -313,7 +353,8 @@
just removing it is very wrong --- we want 'd' to abort.
*/
revision_t v = obj->h_revision;
- if (IS_POINTER(v)) { /* has a more recent revision. Oups. */
+ if (IS_POINTER(v) && !(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)) {
+ /* has a more recent revision. Oups. */
fprintf(stderr,
"ABRT_COLLECT_MAJOR: %p was read but modified already\n",
obj);
@@ -321,16 +362,13 @@
return;
}
- /* on the other hand, if we see a non-visited object in the read
- list, then we need to remove it --- it's wrong to just abort.
- Consider the following case: the transaction is inevitable,
- and since it started, it popped objects out of its shadow
- stack. Some popped objects might become free even if they
- have been read from. We must not abort such transactions
- (and cannot anyway: they are inevitable!). */
- if (!(obj->h_tid & GCFLAG_VISITED)) {
- items[i] = items[--d->list_of_read_objects.size];
- }
+ /* It should not be possible to see a non-visited object in the
+ read list. I think the only case is: the transaction is
+ inevitable, and since it started, it popped objects out of
+ its shadow stack. Some popped objects might become free even
+ if they have been read from. But for inevitable transactions,
+ we clear the 'list_of_read_objects' above anyway. */
+ assert(obj->h_tid & GCFLAG_VISITED);
}
d->num_read_objects_known_old = d->list_of_read_objects.size;
@@ -483,6 +521,36 @@
}
+/***** Major collections: forcing minor collections *****/
+
+void force_minor_collections(void)
+{
+ struct tx_descriptor *d;
+ struct tx_descriptor *saved = thread_descriptor;
+ revision_t saved_private_rev = stm_private_rev_num;
+ assert(saved_private_rev == *saved->private_revision_ref);
+
+ for (d = stm_tx_head; d; d = d->tx_next) {
+ /* Force a minor collection to run in the thread 'd'.
+ Usually not needed, but it may be the case that this major
+ collection was not preceeded by a minor collection if the
+ thread is busy in a system call for example.
+ */
+ if (stmgc_minor_collect_anything_to_do(d)) {
+ /* Hack: temporarily pretend that we "are" the other thread...
+ */
+ thread_descriptor = d;
+ stm_private_rev_num = *d->private_revision_ref;
+ //assert(stmgc_nursery_hiding(d, 0));
+ stmgc_minor_collect_no_abort();
+ //assert(stmgc_nursery_hiding(d, 1));
+ thread_descriptor = saved;
+ stm_private_rev_num = saved_private_rev;
+ }
+ }
+}
+
+
/***** Major collections: main *****/
void update_next_threshold(void)
@@ -519,11 +587,9 @@
stmgcpage_acquire_global_lock();
fprintf(stderr, ",-----\n| running major collection...\n");
-#if 0
force_minor_collections();
assert(gcptrlist_size(&objects_to_trace) == 0);
-#endif
mark_prebuilt_roots();
mark_all_stack_roots();
visit_all_objects();
diff --git a/c4/nursery.c b/c4/nursery.c
--- a/c4/nursery.c
+++ b/c4/nursery.c
@@ -357,6 +357,11 @@
gcptr *items = d->list_of_read_objects.items;
assert(d->list_of_read_objects.size >= limit);
+ if (d->active == 2) {
+ /* inevitable transaction: clear the list of read objects */
+ gcptrlist_clear(&d->list_of_read_objects);
+ }
+
for (i = d->list_of_read_objects.size - 1; i >= limit; --i) {
gcptr obj = items[i];
@@ -444,6 +449,12 @@
AbortNowIfDelayed();
}
+void stmgc_minor_collect_no_abort(void)
+{
+ struct tx_descriptor *d = thread_descriptor;
+ minor_collect(d);
+}
+
int stmgc_minor_collect_anything_to_do(struct tx_descriptor *d)
{
if (d->nursery_current == d->nursery_base /*&&
diff --git a/c4/nursery.h b/c4/nursery.h
--- a/c4/nursery.h
+++ b/c4/nursery.h
@@ -40,6 +40,7 @@
void stmgc_init_nursery(void);
void stmgc_done_nursery(void);
void stmgc_minor_collect(void);
+void stmgc_minor_collect_no_abort(void);
int stmgc_minor_collect_anything_to_do(struct tx_descriptor *);
gcptr stmgc_duplicate(gcptr);
gcptr stmgc_duplicate_old(gcptr);
diff --git a/c4/test/test_gcpage.py b/c4/test/test_gcpage.py
--- a/c4/test/test_gcpage.py
+++ b/c4/test/test_gcpage.py
@@ -265,3 +265,51 @@
check_prebuilt(p1)
check_free_old(p2)
check_not_free(p3) # XXX replace with p1
+
+def test_prebuilt_version_to_protected():
+ p1 = lib.pseudoprebuilt(HDR, 42 + HDR)
+ p2 = lib.stm_write_barrier(p1)
+ lib.stm_commit_transaction()
+ lib.stm_begin_inevitable_transaction()
+ minor_collect()
+ p2 = lib.stm_read_barrier(p1)
+ assert p2 != p1
+ minor_collect()
+ major_collect()
+ check_prebuilt(p1)
+ check_not_free(p2) # XXX replace with p1
+
+def test_private():
+ p1 = nalloc(HDR)
+ lib.stm_push_root(p1)
+ minor_collect()
+ major_collect()
+ p1 = lib.stm_pop_root()
+ assert not lib.in_nursery(p1)
+ check_not_free(p1)
+
+def test_major_collect_first_does_minor_collect():
+ p1 = nalloc(HDR)
+ lib.stm_push_root(p1)
+ major_collect()
+ p1 = lib.stm_pop_root()
+ assert not lib.in_nursery(p1)
+ check_not_free(p1)
+
+def test_private_from_protected_young():
+ p1 = nalloc(HDR)
+ lib.stm_commit_transaction()
+ lib.stm_begin_inevitable_transaction()
+ p1b = lib.stm_write_barrier(p1)
+ assert p1b == p1
+ check_not_free(follow_revision(p1))
+ assert follow_revision(p1).h_tid & GCFLAG_BACKUP_COPY
+ lib.stm_push_root(p1)
+ major_collect()
+ p1 = lib.stm_pop_root()
+ assert not lib.in_nursery(p1)
+ check_not_free(p1)
+ p1b = lib.stm_write_barrier(p1)
+ assert p1b == p1
+ check_not_free(follow_revision(p1))
+ assert follow_revision(p1).h_tid & GCFLAG_BACKUP_COPY
More information about the pypy-commit
mailing list