Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r64:87d10f56bb42 Date: 2013-06-05 12:53 +0200 http://bitbucket.org/pypy/stmgc/changeset/87d10f56bb42/
Log: in-progress diff --git a/.hgignore b/.hgignore new file mode 100644 --- /dev/null +++ b/.hgignore @@ -0,0 +1,3 @@ +syntax: glob +*.pyc +*~ diff --git a/c3/et.c b/c3/et.c --- a/c3/et.c +++ b/c3/et.c @@ -18,21 +18,18 @@ die if we start more than 0x7fff threads. */ static revision_t next_locked_value = (LOCKED + 1) | 1; -/* a negative odd number that uniquely identifies the currently running - transaction (but the number in aborted transactions is reused). - Because we don't know yet the value of 'global_cur_time' that we'll - be assigned when we commit, we use the (negative of) the value of - 'global_cur_time' when we committed the previous transaction. */ -__thread revision_t stm_local_revision; +/* a negative odd number that identifies the currently running + transaction within the thread. */ +__thread revision_t stm_private_rev_num; revision_t stm_global_cur_time(void) /* for tests */ { return global_cur_time; } -revision_t stm_local_rev(void) /* for tests */ +revision_t get_private_rev_num(void) /* for tests */ { - return stm_local_revision; + return stm_private_rev_num; } struct tx_descriptor *stm_thread_descriptor(void) /* for tests */ { @@ -70,6 +67,8 @@ static gcptr HeadOfRevisionChainList(struct tx_descriptor *d, gcptr G) { + abort(); +#if 0 gcptr R = G; revision_t v; @@ -135,10 +134,13 @@ goto retry; // restart searching from R } return R; +#endif } static inline gcptr AddInReadSet(struct tx_descriptor *d, gcptr R) { + abort(); +#if 0 fprintf(stderr, "AddInReadSet(%p)\n", R); d->count_reads++; if (!fxcache_add(&d->recent_reads_cache, R)) { @@ -153,10 +155,13 @@ // return Localize(d, R); // } return R; +#endif } gcptr stm_DirectReadBarrier(gcptr G) { + abort(); +#if 0 gcptr R; struct tx_descriptor *d = thread_descriptor; assert(d->active >= 1); @@ -174,19 +179,13 @@ G2L_FIND(d->public_to_private, R, entry, goto not_found); L = entry->val; assert(L->h_revision == stm_local_revision); -#if 0 - if (R_Container && !(R_Container->h_tid & GCFLAG_GLOBAL)) - { /* R_Container is a local object */ - gcptr *ref = (gcptr *)(((char *)R_Container) + offset); - *ref = L; /* fix in-place */ - } -#endif return L; not_found:; } R = AddInReadSet(d, R); return R; +#endif } static gcptr _latest_gcptr(gcptr R) @@ -312,6 +311,8 @@ static gcptr LocalizePublic(struct tx_descriptor *d, gcptr R) { + abort(); +#if 0 if (R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE) { wlog_t *entry; @@ -332,6 +333,7 @@ AddInReadSet(d, R); /*mark*/ return L; +#endif } gcptr stm_WriteBarrier(gcptr P) @@ -376,6 +378,8 @@ static _Bool ValidateDuringTransaction(struct tx_descriptor *d, _Bool during_commit) { + abort(); +#if 0 long i, size = d->list_of_read_objects.size; gcptr *items = d->list_of_read_objects.items; @@ -416,6 +420,7 @@ } } return 1; +#endif } static void ValidateNow(struct tx_descriptor *d) @@ -638,8 +643,8 @@ goto retry; gcptr L = item->val; - assert(L->h_revision == stm_local_revision); - assert(v != stm_local_revision); + assert(L->h_revision == stm_private_rev_num); + assert(v != stm_private_rev_num); L->h_revision = v; /* store temporarily this value here */ } G2L_LOOP_END; @@ -647,6 +652,8 @@ static void CancelLocks(struct tx_descriptor *d) { + abort(); +#if 0 revision_t my_lock = d->my_lock; wlog_t *item; @@ -672,6 +679,7 @@ ACCESS_ONCE(R->h_revision) = v; } G2L_LOOP_END; +#endif } static pthread_mutex_t mutex_prebuilt_gcroots = PTHREAD_MUTEX_INITIALIZER; @@ -820,15 +828,15 @@ "*************************************\n", (long)cur_time); - revision_t localrev = stm_local_revision; + revision_t localrev = stm_private_rev_num; UpdateProtectedChainHeads(d, cur_time, localrev); smp_wmb(); revision_t newrev = -(cur_time + 1); assert(newrev & 1); - ACCESS_ONCE(stm_local_revision) = newrev; + ACCESS_ONCE(stm_private_rev_num) = newrev; fprintf(stderr, "%p: stm_local_revision = %ld\n", d, (long)newrev); - assert(d->local_revision_ref = &stm_local_revision); + assert(d->private_revision_ref = &stm_private_rev_num); UpdateChainHeads(d, cur_time, localrev); @@ -1027,8 +1035,8 @@ } assert(d->my_lock & 1); assert(d->my_lock > LOCKED); - stm_local_revision = -d->my_lock; /* a unique negative odd value */ - d->local_revision_ref = &stm_local_revision; + stm_private_rev_num = -1; + d->private_revision_ref = &stm_private_rev_num; d->max_aborts = -1; thread_descriptor = d; diff --git a/c3/et.h b/c3/et.h --- a/c3/et.h +++ b/c3/et.h @@ -1,7 +1,7 @@ /*** Extendable Timestamps * * Documentation: - * https://bitbucket.org/pypy/extradoc/raw/extradoc/talk/stm2012/stmimpl.rst + * doc-*.txt * * This is very indirectly based on rstm_r5/stm/et.hpp. * See http://www.cs.rochester.edu/research/synchronization/rstm/api.shtml @@ -20,35 +20,28 @@ * collection: "young" objects are the ones in the nursery (plus a few big * ones outside) and will be collected by the following minor collection. * - * Additionally, objects are either "public", "protected" or "private". The - * private objects have h_revision == stm_local_revision and are invisible - * to other threads. They become non-private when the transaction commits. - * - * non-private | private - * +------------------------------------------------------------ - * | - * old | public objects | old private objects - * ---------| - * | - * young | [ protected objects | private objects (--> grows) ] - * (nursery)| + * Additionally, objects are either "public", "protected" or "private". * * GCFLAG_OLD is set on old objects. * * GCFLAG_VISITED is used temporarily during major collections. * + * GCFLAG_PUBLIC is set on public objects. + * + * GCFLAG_BACKUP_COPY means the object is a (protected) backup copy. + * * GCFLAG_PUBLIC_TO_PRIVATE is added to a *public* object that has got a * *private* copy. It is sticky, reset only at the next major collection. * * GCFLAG_PREBUILT_ORIGINAL is only set on the original version of * prebuilt objects. * - * GCFLAG_WRITE_BARRIER is set on *old* *private* objects to track old-to- - * young pointers. It may be left set on *public* objects but is ignored - * there, because the write barrier will trigger anyway on any non-private - * object. On an old private object, it is removed once a write occurs - * and the object is recorded in 'private_old_pointing_to_young'; it is - * set again at the next minor collection. + * GCFLAG_WRITE_BARRIER is set on *old* objects to track old-to- young + * pointers. It may be left set on *public* objects but is ignored + * there, because public objects are read-only. The flag is removed + * once a write occurs and the object is recorded in the list + * 'old_pointing_to_young'; it is set again at the next minor + * collection. * * GCFLAG_NURSERY_MOVED is used temporarily during minor collections. * @@ -56,26 +49,31 @@ * have been stolen. * * GCFLAG_STUB is used for debugging: it's set on stub objects made by - * create_yo_stubs() + * stealing or by major collections. */ #define GCFLAG_OLD (STM_FIRST_GCFLAG << 0) #define GCFLAG_VISITED (STM_FIRST_GCFLAG << 1) -#define GCFLAG_PUBLIC_TO_PRIVATE (STM_FIRST_GCFLAG << 2) +#define GCFLAG_PUBLIC (STM_FIRST_GCFLAG << 2) #define GCFLAG_PREBUILT_ORIGINAL (STM_FIRST_GCFLAG << 3) -#define GCFLAG_WRITE_BARRIER (STM_FIRST_GCFLAG << 4) -#define GCFLAG_NURSERY_MOVED (STM_FIRST_GCFLAG << 5) -#define GCFLAG_STOLEN (STM_FIRST_GCFLAG << 6) -#define GCFLAG_STUB (STM_FIRST_GCFLAG << 7) /* debugging */ +#define GCFLAG_BACKUP_COPY (STM_FIRST_GCFLAG << 4) +#define GCFLAG_PUBLIC_TO_PRIVATE (STM_FIRST_GCFLAG << 5) +#define GCFLAG_WRITE_BARRIER (STM_FIRST_GCFLAG << 6) +#define GCFLAG_NURSERY_MOVED (STM_FIRST_GCFLAG << 7) +#define GCFLAG_STOLEN (STM_FIRST_GCFLAG << 8) +#define GCFLAG_STUB (STM_FIRST_GCFLAG << 9) /* debugging */ /* this value must be reflected in PREBUILT_FLAGS in stmgc.h */ #define GCFLAG_PREBUILT (GCFLAG_VISITED | \ GCFLAG_PREBUILT_ORIGINAL | \ - GCFLAG_OLD) + GCFLAG_OLD | \ + GCFLAG_PUBLIC) #define GC_FLAG_NAMES { "OLD", \ "VISITED", \ + "PUBLIC", \ + "PREBUILT_ORIGINAL", \ + "BACKUP_COPY", \ "PUBLIC_TO_PRIVATE", \ - "PREBUILT_ORIGINAL", \ "WRITE_BARRIER", \ "NURSERY_MOVED", \ "STOLEN", \ @@ -121,12 +119,12 @@ char *longest_abort_info; long long longest_abort_info_time; struct FXCache recent_reads_cache; - revision_t *local_revision_ref; + revision_t *private_revision_ref; struct tx_descriptor *tx_next, *tx_prev; /* a doubly linked list */ }; extern __thread struct tx_descriptor *thread_descriptor; -extern __thread revision_t stm_local_revision; +extern __thread revision_t stm_private_rev_num; /************************************************************/ diff --git a/c3/gcpage.c b/c3/gcpage.c --- a/c3/gcpage.c +++ b/c3/gcpage.c @@ -423,7 +423,7 @@ assert(stmgc_classify(item->addr) == K_PUBLIC); /*..rt(stmgc_classify(item->val) == K_PRIVATE); but in the other thread, which becomes: */ - assert(item->val->h_revision == *d->local_revision_ref); + assert(item->val->h_revision == *d->private_revision_ref); item->addr->h_tid |= GCFLAG_PUBLIC_TO_PRIVATE; @@ -559,8 +559,8 @@ { struct tx_descriptor *d; struct tx_descriptor *saved = thread_descriptor; - revision_t saved_local_rev = stm_local_revision; - assert(saved_local_rev == *saved->local_revision_ref); + revision_t saved_private_rev = stm_private_rev_num; + assert(saved_private_rev == *saved->private_revision_ref); for (d = tx_head; d; d = d->tx_next) { /* Force a minor collection to run in the thread 'd'. @@ -572,12 +572,12 @@ /* Hack: temporarily pretend that we "are" the other thread... */ thread_descriptor = d; - stm_local_revision = *d->local_revision_ref; + 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_local_revision = saved_local_rev; + stm_private_rev_num = saved_private_rev; } } } diff --git a/c3/lists.c b/c3/lists.c --- a/c3/lists.c +++ b/c3/lists.c @@ -223,15 +223,10 @@ /************************************************************/ -void fxcache_clear(struct FXCache *fxcache) +void _fxcache_reset(struct FXCache *fxcache) { - fxcache->shift += 4; - /* FX_ENTRIES+1 entries are needed */ - if (fxcache->shift + FX_ENTRIES + 1 > FX_TOTAL) { + fxcache->shift = 0; memset(fxcache->cache, 0, sizeof(fxcache->cache)); - fxcache->shift = 0; - } - fxcache->cache_start = (char *)(fxcache->cache + fxcache->shift); } /************************************************************/ diff --git a/c3/lists.h b/c3/lists.h --- a/c3/lists.h +++ b/c3/lists.h @@ -168,16 +168,12 @@ /* The fxcache_xx functions implement a fixed-size set of gcptr's. Moreover the gcptr's in the set are mapped to small integers. In case - of collisions, old items are discarded. The cache uses 3-way caching, - stored in 3 consecutive entries, but the 3 entries are in "cache lines" - that are only aligned to a multiple of 2. This means that among the 3 - items, the item 0 overlaps with the item 2 of the previous cache line, - and the item 2 overlaps with the item 0 of the following cache line. - The item 1 can only be seen by the current cache line. + of collisions, old items are discarded. The cache doesn't use + multi-way caching for now. - The cache itself uses a total of FX_ENTRIES+1 entries in the 'cache' - array below, starting at 'cache_start'. The reason it is bigger than - necessary is that fxcache_clear() simply shifts 'cache_start', making + The cache itself uses a total of FX_ENTRIES entries in the 'cache' + array below, starting at 'shift'. The reason it is bigger than + necessary is that fxcache_clear() simply increments 'shift', making any previous entries invalid by not being in the correct position any more. */ @@ -187,41 +183,18 @@ struct FXCache { char *cache_start; - revision_t nextadd; revision_t shift; revision_t cache[FX_TOTAL]; }; -void fxcache_clear(struct FXCache *fxcache); +void _fxcache_reset(struct FXCache *fxcache); -static inline int fxcache_add(struct FXCache *fxcache, gcptr item) { - /* If 'item' is not in the cache, add it and returns 0. - If it is already, return 1. - */ - revision_t uitem = (revision_t)item; - /* 'entry' points to 'cache_start[mask of uitem, even-valued]' */ - revision_t *entry = (revision_t *) - (fxcache->cache_start + (uitem & ((FX_ENTRIES-2) * sizeof(revision_t)))); - revision_t current; - - current = entry[1]; /* first look here, the cache-private entry */ - if (current == uitem) - return 1; - - if (entry[0] == uitem) { - entry[0] = current; /* move from this collidable entry to */ - entry[1] = uitem; /* the cache-private entry */ - return 1; - } - if (entry[2] == uitem) { - entry[2] = current; /* move from this collidable entry to */ - entry[1] = uitem; /* the cache-private entry */ - return 1; - } - - entry[fxcache->nextadd] = uitem; - fxcache->nextadd ^= 2; - return 0; +static inline void fxcache_clear(struct FXCache *fxcache) +{ + fxcache->shift++; + if (fxcache->shift > FX_TOTAL - FX_ENTRIES) + _fxcache_reset(fxcache); + fxcache->cache_start = (char *)(fxcache->cache + fxcache->shift); } /************************************************************/ diff --git a/c3/nursery.c b/c3/nursery.c --- a/c3/nursery.c +++ b/c3/nursery.c @@ -29,7 +29,7 @@ enum protection_class_t stmgc_classify(gcptr obj) { /* note that this function never returns K_OLD_PRIVATE. */ - if (obj->h_revision == stm_local_revision) + if (obj->h_revision == stm_private_rev_num) return K_PRIVATE; if (is_young(obj)) return K_PROTECTED; @@ -42,7 +42,7 @@ /* for assertions only; moreover this function returns K_PRIVATE only for young private objects, and K_OLD_PRIVATE for old ones. */ struct tx_descriptor *d = thread_descriptor; - int private = (obj->h_revision == stm_local_revision); + int private = (obj->h_revision == stm_private_rev_num); enum protection_class_t e; if (is_in_nursery(d, obj)) { @@ -128,7 +128,7 @@ { gcptr p = stmgcpage_malloc(size); memset(p, 0, size); - p->h_revision = stm_local_revision; + p->h_revision = stm_private_rev_num; p->h_tid = GCFLAG_OLD; return p; } @@ -146,7 +146,7 @@ } stm_dbgmem_used_again(cur, size, 1); gcptr p = (gcptr)cur; - p->h_revision = stm_local_revision; + p->h_revision = stm_private_rev_num; return p; } @@ -185,7 +185,7 @@ GCFLAG_PREBUILT_ORIGINAL | GCFLAG_WRITE_BARRIER | GCFLAG_OLD); - localobj->h_revision = stm_local_revision; + localobj->h_revision = stm_private_rev_num; return localobj; } @@ -413,7 +413,7 @@ /* nb. don't use stmgc_classify() here, because some objects trigger an assert at this point: young non-nursery objects which just grew the flag GCFLAG_OLD */ - assert(obj->h_revision != stm_local_revision); /* not a private object */ + assert(obj->h_revision != stm_private_rev_num); /* not a private object */ PATCH_ROOT_WITH(obj); goto retry; } @@ -490,7 +490,7 @@ /* then we record the dependency in the dictionary 'public_to_private' */ - assert(L->h_revision == stm_local_revision); + assert(L->h_revision == stm_private_rev_num); g2l_insert(&d->public_to_private, R, L); /*mark*/ } @@ -956,6 +956,8 @@ static gcptr extract_from_foreign_nursery(struct tx_descriptor *source_d, gcptr R) { + abort(); +#if 0 /* "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. */ @@ -1011,6 +1013,7 @@ source_d->stolen_objects.size - 1); return N; +#endif } void stmgc_public_to_foreign_protected(gcptr P) @@ -1112,7 +1115,7 @@ /* we re-insert L as a private copy of the public object N */ N->h_tid |= GCFLAG_PUBLIC_TO_PRIVATE; - assert(L->h_revision == stm_local_revision); + assert(L->h_revision == stm_private_rev_num); g2l_insert(&d->public_to_private, N, L); gcptrlist_insert(&d->public_to_young, N); } diff --git a/c3/stmgc.h b/c3/stmgc.h --- a/c3/stmgc.h +++ b/c3/stmgc.h @@ -21,7 +21,7 @@ #define STM_SIZE_OF_USER_TID (sizeof(revision_t) / 2) /* in bytes */ #define STM_FIRST_GCFLAG (1L << (8 * STM_SIZE_OF_USER_TID)) #define STM_USER_TID_MASK (STM_FIRST_GCFLAG - 1) -#define PREBUILT_FLAGS (STM_FIRST_GCFLAG * (1 + 2 + 8)) +#define PREBUILT_FLAGS (STM_FIRST_GCFLAG * (1 + 2 + 4 + 8)) #define PREBUILT_REVISION 1 diff --git a/c3/stmsync.c b/c3/stmsync.c --- a/c3/stmsync.c +++ b/c3/stmsync.c @@ -78,8 +78,11 @@ gcptr stm_read_barrier(gcptr obj) { /* XXX inline in the caller */ + abort(); +#if 0 if (UNLIKELY(obj->h_revision != stm_local_revision)) obj = stm_DirectReadBarrier(obj); +#endif return obj; } @@ -87,7 +90,7 @@ { /* XXX inline in the caller */ if (UNLIKELY(((obj->h_tid & GCFLAG_WRITE_BARRIER) != 0) | - (obj->h_revision != stm_local_revision))) + (obj->h_revision != stm_private_rev_num))) obj = stm_WriteBarrier(obj); return obj; } diff --git a/c3/test/support.py b/c3/test/support.py --- a/c3/test/support.py +++ b/c3/test/support.py @@ -80,7 +80,7 @@ void rawsetlong(gcptr, long, long); gcptr pseudoprebuilt(size_t size, int tid); - revision_t get_local_revision(void); + revision_t get_private_rev_num(void); revision_t get_start_time(void); gcptr *addr_of_thread_local(void); @@ -112,6 +112,7 @@ extern revision_t stm_global_cur_time(void); extern void stmgcpage_add_prebuilt_root(gcptr); extern void stm_clear_between_tests(void); + extern revision_t get_private_rev_num(void); int gettid(gcptr obj) { @@ -194,11 +195,6 @@ return x; } - revision_t get_local_revision(void) - { - return stm_local_revision; - } - revision_t get_start_time(void) { return thread_descriptor->start_time; @@ -421,7 +417,8 @@ def nalloc(size): "Allocate a fresh object from the nursery" p = lib.stm_allocate(size, 42 + size) - assert p.h_revision == lib.get_local_revision() + assert p.h_tid == 42 + size # no GC flags + assert p.h_revision == lib.get_private_rev_num() return p def nalloc_refs(nrefs): @@ -442,6 +439,7 @@ p = lib.pseudoprebuilt(HDR + WORD * nrefs, 421 + nrefs) return p +gettid = lib.gettid setptr = lib.setptr getptr = lib.getptr rawsetptr = lib.rawsetptr diff --git a/c3/test/test_et.py b/c3/test/test_et.py new file mode 100644 --- /dev/null +++ b/c3/test/test_et.py @@ -0,0 +1,64 @@ +import py +from support import * + + +def setup_function(f): + lib.stm_clear_between_tests() + lib.stm_initialize_tests(getattr(f, 'max_aborts', 0)) + +def teardown_function(_): + lib.stm_finalize() + + +def test_freshly_created(): + p = nalloc(HDR) + r = lib.get_private_rev_num() + assert r < 0 and r % 2 == 1 + assert p.h_revision == r + assert p.h_tid == lib.gettid(p) | 0 # no GC flags + +def test_write_barrier_private(): + p = nalloc(HDR) + assert lib.stm_write_barrier(p) == p + assert p.h_revision == lib.get_private_rev_num() + assert p.h_tid == lib.gettid(p) | 0 # no GC flags + +def test_protected_no_backup(): + p = nalloc(HDR) + r = lib.get_private_rev_num() + assert p.h_revision == r + lib.stm_commit_transaction() + lib.stm_begin_inevitable_transaction() + r2 = lib.get_private_rev_num() + assert r2 < 0 and r2 % 2 == 1 + assert r != r2 + assert p.h_revision == r + assert p.h_tid == lib.gettid(p) | 0 # no GC flags + +def test_private_with_backup(): + p = nalloc(HDR) + lib.stm_commit_transaction() + lib.stm_begin_inevitable_transaction() + r2 = lib.get_private_rev_num() + assert p.h_revision != r2 + p2 = lib.stm_write_barrier(p) + assert p2 == p # does not move + assert p.h_revision == r2 + +def test_get_backup_copy(): + p = nalloc(HDR + WORD) + lib.setlong(p, 0, 78927812) + lib.stm_commit_transaction() + lib.stm_begin_inevitable_transaction() + org_r = p.h_revision + lib.setlong(p, 0, 927122) + assert p.h_revision == lib.get_private_rev_num() + pback = lib.stm_get_backup_copy(p) + assert pback and pback != p + assert pback.h_revision == org_r + assert pback.h_tid == p.h_tid | GCFLAG_BACKUP_COPY + assert lib.rawgetlong(pback, 0) == 78927812 + assert lib.rawgetlong(p, 0) == 927122 + +def test_protected_with_backup(): + xxx _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit