Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r232:db385d3d043d Date: 2013-06-22 11:12 +0200 http://bitbucket.org/pypy/stmgc/changeset/db385d3d043d/
Log: hg merge implement-id Right now test_multi_thread doesn't pass, but I'll investigate diff --git a/c4/demo_random.c b/c4/demo_random.c --- a/c4/demo_random.c +++ b/c4/demo_random.c @@ -13,7 +13,7 @@ #define NUMTHREADS 4 -#define STEPS 100000 +#define STEPS 1000000 #define NUMROOTS 10 // per thread #define PREBUILT 3 // per thread #define MAXROOTS 1000 @@ -27,6 +27,7 @@ struct node { struct stm_object_s hdr; long value; + revision_t id; struct node *next; }; typedef struct node * nodeptr; @@ -82,6 +83,7 @@ gcptr x = calloc(1, size); x->h_tid = PREBUILT_FLAGS | tid; x->h_revision = PREBUILT_REVISION; + x->h_original = 0; return x; } @@ -183,12 +185,12 @@ return result1; } -static const revision_t C_PRIVATE_FROM_PROTECTED = 1; -static const revision_t C_PRIVATE = 2; -static const revision_t C_STUB = 3; -static const revision_t C_PUBLIC = 4; -static const revision_t C_BACKUP = 5; -static const revision_t C_PROTECTED = 6; +static const int C_PRIVATE_FROM_PROTECTED = 1; +static const int C_PRIVATE = 2; +static const int C_STUB = 3; +static const int C_PUBLIC = 4; +static const int C_BACKUP = 5; +static const int C_PROTECTED = 6; int classify(gcptr p) { int priv_from_prot = (p->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) != 0; @@ -260,7 +262,7 @@ num = get_rand(SHARED_ROOTS); _sr = shared_roots[num]; - k = get_rand(14); + k = get_rand(17); switch (k) { case 0: // remove a root @@ -316,8 +318,34 @@ w_sr = (nodeptr)write_barrier(_sr); w_sr->next = (nodeptr)shared_roots[get_rand(SHARED_ROOTS)]; break; - default: + case 14: + push_roots(); + stmgc_minor_collect(); + pop_roots(); + p = NULL; break; + case 15: /* test stm_id on non-shared roots */ + w_r = (nodeptr)read_barrier(_r); + if (w_r->id) { + assert(w_r->id == stm_id((gcptr)w_r)); + assert(w_r->id == stm_id((gcptr)_r)); + } + else { + w_r = (nodeptr)write_barrier(_r); + w_r->id = stm_id((gcptr)w_r); + assert(w_r->id == stm_id((gcptr)_r)); + } + case 16: /* test stm_id on shared roots */ + w_sr = (nodeptr)read_barrier(_sr); + if (w_sr->id) { + assert(w_sr->id == stm_id((gcptr)w_sr)); + assert(w_sr->id == stm_id((gcptr)_sr)); + } + else { + w_sr = (nodeptr)write_barrier(_sr); + w_sr->id = stm_id((gcptr)w_sr); + assert(w_sr->id == stm_id((gcptr)_sr)); + } } return p; } @@ -344,17 +372,21 @@ int interruptible_callback(gcptr arg1, int retry_counter) { td.num_roots = td.num_roots_outside_perform; - copy_roots(td.roots_outside_perform, td.roots, td.num_roots); + // done & overwritten by the following pop_roots(): + // copy_roots(td.roots_outside_perform, td.roots, td.num_roots); + // refresh td.roots: arg1 = stm_pop_root(); + assert(arg1 == NULL); pop_roots(); push_roots(); stm_push_root(arg1); int p = run_me(); - int restart = p == -1 ? get_rand(3) != 1 : 0; + if (p == -1) // maybe restart transaction + return get_rand(3) != 1; - return restart; + return 0; } int run_me() diff --git a/c4/et.c b/c4/et.c --- a/c4/et.c +++ b/c4/et.c @@ -447,7 +447,15 @@ B = stmgc_duplicate_old(P); B->h_tid |= GCFLAG_BACKUP_COPY; - + B->h_tid &= ~GCFLAG_HAS_ID; + if (!(P->h_original) && (P->h_tid & GCFLAG_OLD)) { + /* if P is old, it must be the original + if P is young, it will create a shadow original later + or it's getting decided when backup gets stolen. + */ + B->h_original = (revision_t)P; + } + P->h_tid |= GCFLAG_PRIVATE_FROM_PROTECTED; P->h_revision = (revision_t)B; @@ -474,6 +482,13 @@ /* note that stmgc_duplicate() usually returns a young object, but may return an old one if the nursery is full at this moment. */ gcptr L = stmgc_duplicate(R); + if (!(L->h_original)) { + /* if we don't have an original object yet, + it must be the old public R */ + assert(R->h_tid & GCFLAG_OLD); // if not, force stm_id?? + L->h_original = (revision_t)R; + } + assert(!(L->h_tid & GCFLAG_BACKUP_COPY)); assert(!(L->h_tid & GCFLAG_STUB)); assert(!(L->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); @@ -1010,7 +1025,19 @@ stub->h_tid = (L->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC | GCFLAG_STUB | GCFLAG_OLD; + assert(!(L->h_tid & GCFLAG_HAS_ID)); stub->h_revision = ((revision_t)L) | 2; + if (L->h_original) { + stub->h_original = L->h_original; + } + else if (L->h_tid & GCFLAG_OLD) { + stub->h_original = (revision_t)L; + } + else { + L->h_original = (revision_t)stub; + assert(0); + } + item->val = stub; } G2L_LOOP_END; @@ -1076,6 +1103,8 @@ if (B->h_tid & GCFLAG_PUBLIC) { + assert(!(P->h_tid & GCFLAG_HAS_ID)); + /* B was stolen */ while (1) { @@ -1086,7 +1115,15 @@ if (bool_cas(&B->h_revision, v, (revision_t)P)) break; } - } + } + else if (P->h_original == (revision_t)B) { + /* The backup is the "id object" */ + assert(!(P->h_tid & GCFLAG_HAS_ID)); + + B->h_tid &= ~GCFLAG_BACKUP_COPY; + B->h_tid |= GCFLAG_PUBLIC; + B->h_revision = (revision_t)P; + } else { stmgcpage_free(B); @@ -1118,6 +1155,7 @@ { assert(!(B->h_tid & GCFLAG_BACKUP_COPY)); P->h_tid |= GCFLAG_PUBLIC; + P->h_tid &= ~GCFLAG_HAS_ID; // just in case if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_NURSERY_MOVED; /* P becomes a public outdated object. It may create an exception documented in doc-objects.txt: a public but young @@ -1126,10 +1164,20 @@ stealing will follow its h_revision (to B). */ } + else if (P->h_original == (revision_t)B) { + /* The backup is the "id object". P becomes outdated. */ + assert(!(P->h_tid & GCFLAG_HAS_ID)); + P->h_tid |= GCFLAG_PUBLIC; + B->h_tid |= GCFLAG_PUBLIC; + B->h_tid &= ~GCFLAG_BACKUP_COPY; + if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_NURSERY_MOVED; + } else { /* copy the backup copy B back over the now-protected object P, and then free B, which will not be used any more. */ + assert(!(P->h_original) + || (B->h_original == (revision_t)P->h_original)); size_t size = stmcb_size(B); assert(B->h_tid & GCFLAG_BACKUP_COPY); memcpy(((char *)P) + offsetof(struct stm_object_s, h_revision), diff --git a/c4/et.h b/c4/et.h --- a/c4/et.h +++ b/c4/et.h @@ -57,6 +57,9 @@ * but converted from a protected. These are precisely the objects * that have a backup copy (in h_revision), which gives a copy of the * original protected object. + * + * GCFLAG_HAS_ID is set on young objects that have an old reserved + * memory to be copied to in minor collections (obj->h_original) */ static const revision_t GCFLAG_OLD = STM_FIRST_GCFLAG << 0; static const revision_t GCFLAG_VISITED = STM_FIRST_GCFLAG << 1; @@ -68,6 +71,7 @@ static const revision_t GCFLAG_BACKUP_COPY /*debug*/ = STM_FIRST_GCFLAG << 7; static const revision_t GCFLAG_STUB /*debug*/ = STM_FIRST_GCFLAG << 8; static const revision_t GCFLAG_PRIVATE_FROM_PROTECTED = STM_FIRST_GCFLAG << 9; +static const revision_t GCFLAG_HAS_ID = STM_FIRST_GCFLAG << 10; /* this value must be reflected in PREBUILT_FLAGS in stmgc.h */ #define GCFLAG_PREBUILT (GCFLAG_VISITED | \ diff --git a/c4/nursery.c b/c4/nursery.c --- a/c4/nursery.c +++ b/c4/nursery.c @@ -59,6 +59,7 @@ assert(tid == (tid & STM_USER_TID_MASK)); P->h_tid = tid; P->h_revision = stm_private_rev_num; + P->h_original = 0; return P; } @@ -71,6 +72,8 @@ memcpy(L, P, size); L->h_tid &= ~GCFLAG_OLD; + L->h_tid &= ~GCFLAG_HAS_ID; + return L; } @@ -80,11 +83,93 @@ gcptr L = (gcptr)stmgcpage_malloc(size); memcpy(L, P, size); L->h_tid |= GCFLAG_OLD; + return L; } /************************************************************/ + +revision_t stm_hash(gcptr p) +{ + return stm_id(p); +} + +revision_t stm_id(gcptr p) +{ + struct tx_descriptor *d = thread_descriptor; + revision_t result; + + + if (p->h_original) { /* fast path */ + fprintf(stderr, "stm_id(%p) has orig fst: %p\n", + p, (gcptr)p->h_original); + return p->h_original; + } + else if (!(p->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) + && (p->h_tid & GCFLAG_OLD)) { + /* we can be sure that p->h_original doesn't + get set during the if and the else-if */ + fprintf(stderr, "stm_id(%p) is old, orig=0 fst: %p\n", p, p); + return (revision_t)p; + } + + spinlock_acquire(d->public_descriptor->collection_lock, 'I'); + /* old objects must have an h_original xOR be + the original itself. + if some thread stole p when it was still young, + it must have set h_original. stealing an old obj + makes the old obj "original". + */ + if (p->h_original) { /* maybe now? */ + result = p->h_original; + fprintf(stderr, "stm_id(%p) has orig: %p\n", + p, (gcptr)p->h_original); + } + else if (p->h_tid & GCFLAG_OLD) { + /* it must be this exact object */ + result = (revision_t)p; + fprintf(stderr, "stm_id(%p) is old, orig=0: %p\n", p, p); + } + else { + /* must create shadow original object or use + backup, if exists */ + if (p->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) { + gcptr B = (gcptr)p->h_revision; + /* don't set, otherwise nursery will copy over backup */ + //p->h_tid |= GCFLAG_HAS_ID; // see AbortPrivateFromProtected + p->h_original = (revision_t)B; + // B->h_tid |= GCFLAG_PUBLIC; done by CommitPrivateFromProtected + + result = (revision_t)B; + fprintf(stderr, "stm_id(%p) young, pfp, use backup %p\n", + p, (gcptr)p->h_original); + } + else { + gcptr O = stmgc_duplicate_old(p); + p->h_original = (revision_t)O; + p->h_tid |= GCFLAG_HAS_ID; + O->h_tid |= GCFLAG_PUBLIC; + + result = (revision_t)O; + fprintf(stderr, "stm_id(%p) young, make shadow %p\n", p, O); + } + } + + spinlock_release(d->public_descriptor->collection_lock); + return result; +} + +revision_t stm_pointer_equal(gcptr p1, gcptr p2) +{ + /* types must be the same */ + if ((p1->h_tid & STM_USER_TID_MASK) != (p2->h_tid & STM_USER_TID_MASK)) + return 0; + return stm_id(p1) == stm_id(p2); +} + +/************************************************************/ + static inline gcptr create_old_object_copy(gcptr obj) { assert(!(obj->h_tid & GCFLAG_PUBLIC)); @@ -100,6 +185,18 @@ return fresh_old_copy; } +inline void copy_to_old_id_copy(gcptr obj, gcptr id) +{ + assert(!is_in_nursery(thread_descriptor, id)); + assert(id->h_tid & GCFLAG_OLD); + + size_t size = stmcb_size(obj); + memcpy(id, obj, size); + id->h_tid &= ~GCFLAG_HAS_ID; + id->h_tid |= GCFLAG_OLD; + fprintf(stderr, "copy_to_old_id_copy(%p -> %p)\n", obj, id); +} + static void visit_if_young(gcptr *root) { gcptr obj = *root; @@ -111,17 +208,29 @@ } else { /* it's a nursery object. Was it already moved? */ - if (UNLIKELY(obj->h_tid & GCFLAG_NURSERY_MOVED)) { /* yes. Such an object can be a public object in the nursery too (such objects are always NURSERY_MOVED). For all cases, - we can just fix the ref. */ + we can just fix the ref. + Can be stolen objects or those we already moved. + */ *root = (gcptr)obj->h_revision; return; } - /* make a copy of it outside */ - fresh_old_copy = create_old_object_copy(obj); + if (obj->h_tid & GCFLAG_HAS_ID) { + /* already has a place to go to */ + gcptr id_obj = (gcptr)obj->h_original; + + copy_to_old_id_copy(obj, id_obj); + fresh_old_copy = id_obj; + obj->h_tid &= ~GCFLAG_HAS_ID; + } + else { + /* make a copy of it outside */ + fresh_old_copy = create_old_object_copy(obj); + } + obj->h_tid |= GCFLAG_NURSERY_MOVED; obj->h_revision = (revision_t)fresh_old_copy; diff --git a/c4/steal.c b/c4/steal.c --- a/c4/steal.c +++ b/c4/steal.c @@ -11,6 +11,8 @@ struct stm_object_s stubs[STUB_NB_OBJS]; }; +inline void copy_to_old_id_copy(gcptr obj, gcptr id); + gcptr stm_stub_malloc(struct tx_public_descriptor *pd) { assert(pd->collection_lock != 0); @@ -80,6 +82,16 @@ | GCFLAG_STUB | GCFLAG_OLD; stub->h_revision = ((revision_t)obj) | 2; + if (obj->h_original) { + stub->h_original = obj->h_original; + } + else if (obj->h_tid & GCFLAG_OLD) { + stub->h_original = (revision_t)obj; + } + else { + obj->h_original = (revision_t)stub; + } + g2l_insert(&sd->all_stubs, obj, stub); if (!(obj->h_tid & GCFLAG_OLD)) @@ -107,6 +119,24 @@ */ if (L->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) { gcptr B = (gcptr)L->h_revision; /* the backup copy */ + + if (L->h_original) { + /* L has an original, may be GCFLAG_HAS_ID */ + B->h_original = L->h_original; + } + else if (L->h_tid & GCFLAG_OLD) { + /* If old, it must be the original */ + assert(!(L->h_tid & GCFLAG_HAS_ID)); + /* original must be L */ + B->h_original = (revision_t)L; + assert(0); + } + else { + /* we can make the backup the "original" + since L hasn't decided yet */ + L->h_original = (revision_t)B; + assert(0); + } /* B is now a backup copy, i.e. a protected object, and we own the foreign thread's collection_lock, so we can read/write the @@ -150,10 +180,28 @@ fprintf(stderr, "stolen: %p -> %p\n", P, L); - /* Copy the object out of the other thread's nursery, if needed */ - if (!(L->h_tid & GCFLAG_OLD)) { - gcptr O = stmgc_duplicate_old(L); - L->h_revision = (revision_t)O; + + if (!(L->h_tid & GCFLAG_OLD)) { + gcptr O; + + if (L->h_tid & GCFLAG_HAS_ID) { + /* use id-copy for us */ + O = (gcptr)L->h_original; + L->h_tid &= ~GCFLAG_HAS_ID; + L->h_revision = (revision_t)O; + copy_to_old_id_copy(L, (gcptr)L->h_original); + } else { + /* Copy the object out of the other thread's nursery, + if needed */ + O = stmgc_duplicate_old(L); + L->h_revision = (revision_t)O; + + /* young and without original? + we may lose the HAS_ID flag like above */ + if (!(L->h_original)) + L->h_original = (revision_t)O; + } + L->h_tid |= GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED; /* subtle: we need to remove L from the fxcache of the target thread, otherwise its read barrier might not trigger on it. @@ -165,6 +213,7 @@ L = O; fprintf(stderr, "\t---> %p\n", L); } + assert(L->h_tid & GCFLAG_OLD); } diff --git a/c4/steal.h b/c4/steal.h --- a/c4/steal.h +++ b/c4/steal.h @@ -2,7 +2,7 @@ #define _SRCSTM_STEAL_H -#define STUB_BLOCK_SIZE (16 * WORD) /* power of two */ +#define STUB_BLOCK_SIZE (32 * WORD) /* power of two */ #define STUB_THREAD(h) (*(struct tx_public_descriptor **) \ (((revision_t)(h)) & ~(STUB_BLOCK_SIZE-1))) diff --git a/c4/stmgc.h b/c4/stmgc.h --- a/c4/stmgc.h +++ b/c4/stmgc.h @@ -10,6 +10,7 @@ typedef struct stm_object_s { revision_t h_tid; revision_t h_revision; + revision_t h_original; } *gcptr; @@ -28,6 +29,14 @@ /* allocate an object out of the local nursery */ gcptr stm_allocate(size_t size, unsigned long tid); +/* returns a never changing hash for the object */ +revision_t stm_hash(gcptr); +/* returns an for the object which is unique during its lifetime */ +revision_t stm_id(gcptr); +/* returns nonzero if the two object-copy pointers belong to the +same original object */ +revision_t stm_pointer_equal(gcptr, gcptr); + /* to push/pop objects into the local shadowstack */ /* (could be turned into macros or something later) */ void stm_push_root(gcptr); diff --git a/c4/test/support.py b/c4/test/support.py --- a/c4/test/support.py +++ b/c4/test/support.py @@ -32,6 +32,7 @@ typedef struct stm_object_s { revision_t h_tid; revision_t h_revision; + revision_t h_original; } *gcptr; int gettid(gcptr); @@ -41,6 +42,9 @@ #define PREBUILT_REVISION ... gcptr stm_allocate(size_t size, unsigned int tid); + revision_t stm_hash(gcptr); + revision_t stm_id(gcptr); + revision_t stm_pointer_equal(gcptr, gcptr); void stm_push_root(gcptr); gcptr stm_pop_root(void); void stm_set_max_aborts(int max_aborts); @@ -107,6 +111,7 @@ #define GCFLAG_NURSERY_MOVED ... #define GCFLAG_STUB ... #define GCFLAG_PRIVATE_FROM_PROTECTED ... + #define GCFLAG_HAS_ID ... #define ABRT_MANUAL ... typedef struct { ...; } page_header_t; ''') @@ -606,4 +611,10 @@ assert (r % 4) == 0 return ffi.cast("gcptr", r) +def follow_original(p): + r = p.h_original + assert (r % 4) == 0 + return ffi.cast("gcptr", r) + + nrb_protected = ffi.cast("gcptr", -1) 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,6 +198,49 @@ assert p4 == p2 assert list_of_read_objects() == [p2] + +def test_id_young_to_old(): + # move out of nursery with shadow original + p = nalloc(HDR) + assert p.h_original == 0 + pid = lib.stm_id(p) + assert p.h_tid & GCFLAG_HAS_ID + porig = follow_original(p) + assert porig.h_tid & GCFLAG_OLD + lib.stm_push_root(p) + minor_collect() + p = lib.stm_pop_root() + assert not lib.in_nursery(p) + assert pid == lib.stm_id(p) + +def test_id_private_from_protected(): + # read and write from protected + p = oalloc(HDR) + pid = lib.stm_id(p) + porig = follow_original(p) + # impl detail { + # old objects have id==itself, if not set differently + assert porig == ffi.NULL + assert ffi.cast("gcptr", pid) == p + # } + + p1 = oalloc(HDR) + p1id = lib.stm_id(p1) + p1r = lib.stm_read_barrier(p1) + assert lib.stm_id(p1r) == p1id + p1w = lib.stm_write_barrier(p1) + assert lib.stm_id(p1w) == p1id + + p2 = oalloc(HDR) + p2w = lib.stm_write_barrier(p2) + p2id = lib.stm_id(p2) + assert p2id == lib.stm_id(p2w) + # impl detail { + assert p2w.h_original == 0 + assert follow_revision(p2w).h_original == lib.stm_id(p2w) + # } + + def test_stealing_old(): p = palloc(HDR + WORD) plist = [p] 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 @@ -12,7 +12,7 @@ def test_HDR(): import struct - assert HDR == struct.calcsize("PP") + assert HDR == struct.calcsize("PPP") def test_malloc_simple(): assert count_pages() == 0 _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit