Author: Armin Rigo <[email protected]>
Branch:
Changeset: r133:87748cb8c1c5
Date: 2013-06-15 11:28 +0200
http://bitbucket.org/pypy/stmgc/changeset/87748cb8c1c5/
Log: in-progress
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -82,12 +82,12 @@
restart_all:
if (P->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)
{
- assert(!(P->h_revision & 1)); /* pointer to the backup copy */
+ assert(IS_POINTER(P->h_revision)); /* pointer to the backup copy */
/* check P->h_revision->h_revision: if a pointer, then it means
the backup copy has been stolen into a public object and then
modified by some other thread. Abort. */
- if (!(((gcptr)P->h_revision)->h_revision & 1))
+ if (IS_POINTER(((gcptr)P->h_revision)->h_revision))
AbortTransaction(ABRT_STOLEN_MODIFIED);
goto add_in_recent_reads_cache;
@@ -104,8 +104,8 @@
restart_all_public:
assert(P->h_tid & GCFLAG_PUBLIC);
v = ACCESS_ONCE(P->h_revision);
- if (!(v & 1)) // "is a pointer", i.e.
- { // "has a more recent revision"
+ if (IS_POINTER(v)) /* "is a pointer", "has a more recent revision" */
+ {
retry:
if (v & 2)
goto follow_stub;
@@ -116,8 +116,8 @@
v = ACCESS_ONCE(P->h_revision);
- if (!(v & 1)) // "is a pointer", i.e.
- { // "has a more recent revision"
+ if (IS_POINTER(v))
+ {
if (v & 2)
goto follow_stub;
@@ -127,7 +127,7 @@
P_prev->h_revision = v;
P = (gcptr)v;
v = ACCESS_ONCE(P->h_revision);
- if (!(v & 1)) // "is a pointer", i.e. "has a more recent rev"
+ if (IS_POINTER(v))
goto retry;
}
@@ -302,7 +302,7 @@
if (P->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)
{
/* private too, with a backup copy */
- assert(!(P->h_revision & 1));
+ assert(IS_POINTER(P->h_revision));
fprintf(stderr, "private_from_protected\n");
return P;
}
@@ -311,7 +311,7 @@
{
fprintf(stderr, "public ");
- while (v = P->h_revision, !(v & 1))
+ while (v = P->h_revision, IS_POINTER(v))
{
if (v & 2)
{
@@ -553,8 +553,8 @@
revision_t v;
retry:
v = ACCESS_ONCE(R->h_revision);
- if (!(v & 1)) // "is a pointer", i.e.
- { // "has a more recent revision"
+ if (IS_POINTER(v)) /* "is a pointer", i.e. has a more recent revision */
+ {
if (R->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)
{
/* such an object R might be listed in list_of_read_objects
@@ -815,8 +815,8 @@
retry:
assert(R->h_tid & GCFLAG_PUBLIC);
v = ACCESS_ONCE(R->h_revision);
- if (!(v & 1)) // "is a pointer", i.e.
- { // "has a more recent revision"
+ if (IS_POINTER(v)) /* "has a more recent revision" */
+ {
assert(v != 0);
AbortTransaction(ABRT_COMMIT);
}
@@ -907,7 +907,7 @@
L->h_revision = new_revision;
gcptr stub = stm_stub_malloc(d->public_descriptor);
- stub->h_tid = GCFLAG_PUBLIC | GCFLAG_STUB;
+ stub->h_tid = GCFLAG_PUBLIC | GCFLAG_STUB | GCFLAG_OLD;
stub->h_revision = ((revision_t)L) | 2;
item->val = stub;
@@ -962,7 +962,7 @@
assert(P->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
P->h_tid &= ~GCFLAG_PRIVATE_FROM_PROTECTED;
- if (P->h_revision & 1) // "is not a pointer"
+ if (!IS_POINTER(P->h_revision))
{
/* This case occurs when a GCFLAG_PRIVATE_FROM_PROTECTED object
is stolen: it ends up as a value in 'public_to_private'.
@@ -980,7 +980,7 @@
while (1)
{
revision_t v = ACCESS_ONCE(B->h_revision);
- if (!(v & 1)) // "is a pointer", i.e. "was modified"
+ if (IS_POINTER(v)) /* "was modified" */
AbortTransaction(ABRT_STOLEN_MODIFIED);
if (bool_cas(&B->h_revision, v, (revision_t)P))
@@ -1004,14 +1004,14 @@
{
gcptr P = items[i];
assert(P->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
- assert(!(P->h_revision & 1)); // "is a pointer"
+ assert(IS_POINTER(P->h_revision));
gcptr B = (gcptr)P->h_revision;
if (B->h_tid & GCFLAG_PUBLIC)
{
assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
P->h_tid &= ~GCFLAG_PRIVATE_FROM_PROTECTED;
- P->h_tid |= GCFLAG_PUBLIC;
+ P->h_tid |= GCFLAG_PUBLIC; abort();
/* P becomes a public outdated object */
}
else
diff --git a/c4/et.h b/c4/et.h
--- a/c4/et.h
+++ b/c4/et.h
@@ -83,6 +83,8 @@
"PRIVATE_FROM_PROTECTED", \
NULL }
+#define IS_POINTER(v) (!((v) & 1)) /* even-valued number */
+
/************************************************************/
#define ABRT_MANUAL 0
diff --git a/c4/nursery.c b/c4/nursery.c
--- a/c4/nursery.c
+++ b/c4/nursery.c
@@ -139,7 +139,7 @@
/* found P in 'public_to_private' */
- if (!(v & 1)) { // "is a pointer"
+ if (IS_POINTER(v)) {
/* P is both a key in public_to_private and an outdated copy.
We are in a case where we know the transaction will not
be able to commit successfully.
@@ -157,7 +157,7 @@
continue;
not_in_public_to_private:
- if (v & 1) { // "is not a pointer"
+ if (!IS_POINTER(v)) {
/* P is neither a key in public_to_private nor outdated.
It must come from an older transaction that aborted.
Nothing to do now.
diff --git a/c4/steal.c b/c4/steal.c
--- a/c4/steal.c
+++ b/c4/steal.c
@@ -74,7 +74,8 @@
not_found:
stub = stm_stub_malloc(sd->foreign_pd);
stub->h_tid = (obj->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC
- | GCFLAG_STUB;
+ | GCFLAG_STUB
+ | GCFLAG_OLD;
stub->h_revision = ((revision_t)obj) | 2;
g2l_insert(&sd->all_stubs, obj, stub);
@@ -156,6 +157,17 @@
odd number that is also valid on a public up-to-date object.
*/
+ /* Move the object out of the other thread's nursery, if needed */
+ if (!(L->h_tid & GCFLAG_OLD)) {
+ size_t size = stmcb_size(L);
+ gcptr O = stm_malloc(size);
+ memcpy(O, L, size);
+ L->h_revision = (revision_t)O;
+ L->h_tid |= GCFLAG_NURSERY_MOVED;
+ O->h_tid |= GCFLAG_OLD;
+ L = O;
+ }
+
/* Fix the content of the object: we need to change all pointers
that reference protected copies into pointers that reference
stub copies.
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -188,8 +188,18 @@
int in_nursery(gcptr obj)
{
struct tx_descriptor *d = thread_descriptor;
- return (d->nursery_base <= (char*)obj &&
- ((char*)obj) < d->nursery_end);
+ int result1 = (d->nursery_base <= (char*)obj &&
+ ((char*)obj) < d->nursery_end);
+ if (obj->h_tid & GCFLAG_OLD) {
+ assert(result1 == 0);
+ }
+ else {
+ /* this assert() also fails if "obj" is in another nursery than
+ the one of the current thread. This is ok, because we
+ should not see such pointers. */
+ assert(result1 == 1);
+ }
+ return result1;
}
gcptr pseudoprebuilt(size_t size, int tid)
@@ -515,8 +525,15 @@
return "private"
if public:
if stub:
+ assert not lib.in_nursery(p)
return "stub"
- return "public"
+ else:
+ # public objects usually never live in the nursery, but
+ # if stealing makes one, it has GCFLAG_NURSERY_MOVED.
+ if lib.in_nursery(p):
+ assert p.h_tid & GCFLAG_NURSERY_MOVED
+ assert not (p.h_revision & 1) # "is a pointer"
+ return "public"
if backup:
return "backup"
else:
diff --git a/c4/test/test_et.py b/c4/test/test_et.py
--- a/c4/test/test_et.py
+++ b/c4/test/test_et.py
@@ -198,7 +198,7 @@
assert p4 == p2
assert list_of_read_objects() == [p2]
-def test_stealing():
+def test_stealing_old():
p = palloc(HDR + WORD)
plist = [p]
def f1(r):
@@ -207,6 +207,10 @@
assert p1 != p
assert classify(p) == "public"
assert classify(p1) == "private"
+ minor_collect()
+ check_nursery_free(p1)
+ p1 = lib.stm_read_barrier(p)
+ assert p1.h_tid & GCFLAG_OLD
assert p.h_tid & GCFLAG_PUBLIC_TO_PRIVATE
lib.rawsetlong(p1, 0, 2782172)
lib.stm_commit_transaction()
@@ -235,6 +239,51 @@
r.set(3)
run_parallel(f1, f2)
+def test_stealing_young():
+ p = palloc(HDR + WORD)
+ plist = [p]
+ def f1(r):
+ assert (p.h_tid & GCFLAG_PUBLIC_TO_PRIVATE) == 0
+ p1 = lib.stm_write_barrier(p) # private copy
+ assert p1 != p
+ assert classify(p) == "public"
+ assert classify(p1) == "private"
+ assert p.h_tid & GCFLAG_PUBLIC_TO_PRIVATE
+ lib.rawsetlong(p1, 0, 2782172)
+ lib.stm_commit_transaction()
+ lib.stm_begin_inevitable_transaction()
+ assert classify(p) == "public"
+ assert classify(p1) == "protected"
+ plist.append(p1) # now p's most recent revision is protected
+ assert classify(follow_revision(p)) == "stub"
+ assert p1.h_revision & 1
+ r.set(2)
+ r.wait(3)
+ assert classify(p1) == "public"
+ assert (p1.h_revision & 1) == 0 # outdated
+ p2 = ffi.cast("gcptr", p1.h_revision)
+ assert lib.in_nursery(p1)
+ assert not lib.in_nursery(p2)
+ assert lib.stm_read_barrier(p) == p2
+ assert lib.stm_read_barrier(p1) == p2
+ assert lib.stm_read_barrier(p2) == p2
+ def f2(r):
+ r.wait(2)
+ p2 = lib.stm_read_barrier(p) # steals
+ assert classify(p2) == "public"
+ assert lib.rawgetlong(p2, 0) == 2782172
+ assert p2 == lib.stm_read_barrier(p) # short-circuit h_revision
+ if SHORTCUT:
+ assert p.h_revision == int(ffi.cast("revision_t", p2))
+ assert p2 == lib.stm_read_barrier(p)
+ assert p2 != plist[-1] # p2 is a public moved-out-of-nursery
+ assert plist[-1].h_tid & GCFLAG_PUBLIC
+ assert plist[-1].h_tid & GCFLAG_NURSERY_MOVED
+ assert plist[-1].h_revision == int(ffi.cast("revision_t", p2))
+ assert classify(p2) == "public"
+ r.set(3)
+ run_parallel(f1, f2)
+
def test_stealing_while_modifying(aborting=False):
p = palloc(HDR + WORD)
diff --git a/c4/test/test_nursery.py b/c4/test/test_nursery.py
--- a/c4/test/test_nursery.py
+++ b/c4/test/test_nursery.py
@@ -232,3 +232,32 @@
assert classify(p3) == "private"
assert p3 == p2 != p1
assert not lib.in_nursery(p2)
+
+def _public_and_protected_in_nursery():
+ p1 = palloc(HDR + WORD)
+ lib.rawsetlong(p1, 0, 1000000)
+ p2 = lib.stm_write_barrier(p1)
+ assert lib.in_nursery(p2)
+ lib.setlong(p1, 0, 2000000)
+ assert lib.rawgetlong(p2, 0) == 2000000
+ lib.stm_commit_transaction()
+ lib.stm_begin_inevitable_transaction()
+ assert classify(p1) == "public"
+ assert classify(p2) == "protected"
+ check_not_free(p2)
+ return p1, p2
+
+def test_public_not_in_nursery():
+ p1, p2 = _public_and_protected_in_nursery()
+ plist = []
+ def f1(_):
+ p3 = lib.stm_read_barrier(p1)
+ assert classify(p3) == "public"
+ assert not lib.in_nursery(p3)
+ assert p3 != p2 # not in-place, because p2 is in the nursery
+ plist.append(p3)
+ run_parallel(f1)
+ p3 = lib.stm_read_barrier(p1)
+ assert plist == [p3]
+ assert classify(p3) == "public"
+ assert not lib.in_nursery(p3)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit