Author: Maciej Fijalkowski <fij...@gmail.com> Branch: Changeset: r367:158be99cc7db Date: 2013-07-08 10:07 +0200 http://bitbucket.org/pypy/stmgc/changeset/158be99cc7db/
Log: merge diff --git a/c4/Makefile b/c4/Makefile --- a/c4/Makefile +++ b/c4/Makefile @@ -16,10 +16,10 @@ H_FILES = atomic_ops.h stmgc.h stmimpl.h \ et.h lists.h steal.h nursery.h gcpage.h \ - stmsync.h dbgmem.h fprintcolor.h + stmsync.h extra.h dbgmem.h fprintcolor.h C_FILES = et.c lists.c steal.c nursery.c gcpage.c \ - stmsync.c dbgmem.c fprintcolor.c + stmsync.c extra.c dbgmem.c fprintcolor.c DEBUG = -g -DGC_NURSERY=0x10000 -D_GC_DEBUG=1 -DDUMP_EXTRA=1 -D_GC_DEBUGPRINTS=1 diff --git a/c4/et.c b/c4/et.c --- a/c4/et.c +++ b/c4/et.c @@ -248,6 +248,36 @@ } } +gcptr stm_RepeatReadBarrier(gcptr P) +{ + /* Version of stm_DirectReadBarrier() that doesn't abort and assumes + * that 'P' was already an up-to-date result of a previous + * stm_DirectReadBarrier(). We only have to check if we did in the + * meantime a stm_write_barrier(). + */ + if (P->h_tid & GCFLAG_PUBLIC) + { + if (P->h_tid & GCFLAG_NURSERY_MOVED) + { + P = (gcptr)P->h_revision; + assert(P->h_tid & GCFLAG_PUBLIC); + } + if (P->h_tid & GCFLAG_PUBLIC_TO_PRIVATE) + { + struct tx_descriptor *d = thread_descriptor; + wlog_t *item; + G2L_FIND(d->public_to_private, P, item, goto no_private_obj); + + P = item->val; + assert(!(P->h_tid & GCFLAG_PUBLIC)); + no_private_obj: + ; + } + } + assert(!(P->h_tid & GCFLAG_STUB)); + return P; +} + static gcptr _match_public_to_private(gcptr P, gcptr pubobj, gcptr privobj, int from_stolen) { @@ -422,29 +452,6 @@ goto restart_all; } -#if 0 -void *stm_DirectReadBarrierFromR(void *G1, void *R_Container1, size_t offset) -{ - return _direct_read_barrier((gcptr)G1, (gcptr)R_Container1, offset); -} -#endif - -gcptr stm_RepeatReadBarrier(gcptr O) -{ - abort();//XXX -#if 0 - // LatestGlobalRevision(O) would either return O or abort - // the whole transaction, so omitting it is not wrong - struct tx_descriptor *d = thread_descriptor; - gcptr L; - wlog_t *entry; - G2L_FIND(d->global_to_local, O, entry, return O); - L = entry->val; - assert(L->h_revision == stm_local_revision); - return L; -#endif -} - static gcptr LocalizeProtected(struct tx_descriptor *d, gcptr P) { gcptr B; @@ -749,10 +756,10 @@ smp_spinloop(); } -#if 0 -size_t _stm_decode_abort_info(struct tx_descriptor *d, long long elapsed_time, - int abort_reason, char *output); -#endif +void stm_abort_and_retry(void) +{ + AbortTransaction(ABRT_MANUAL); +} void AbortPrivateFromProtected(struct tx_descriptor *d); @@ -795,41 +802,24 @@ elapsed_time = 1; } -#if 0 - size_t size; if (elapsed_time >= d->longest_abort_info_time) { /* decode the 'abortinfo' and produce a human-readable summary in the string 'longest_abort_info' */ - size = _stm_decode_abort_info(d, elapsed_time, num, NULL); + size_t size = stm_decode_abort_info(d, elapsed_time, num, NULL); free(d->longest_abort_info); d->longest_abort_info = malloc(size); if (d->longest_abort_info == NULL) d->longest_abort_info_time = 0; /* out of memory! */ else { - if (_stm_decode_abort_info(d, elapsed_time, + if (stm_decode_abort_info(d, elapsed_time, num, d->longest_abort_info) != size) stm_fatalerror("during stm abort: object mutated unexpectedly\n"); d->longest_abort_info_time = elapsed_time; } } -#endif - -#if 0 - /* run the undo log in reverse order, cancelling the values set by - stm_ThreadLocalRef_LLSet(). */ - if (d->undolog.size > 0) { - gcptr *item = d->undolog.items; - long i; - for (i=d->undolog.size; i>=0; i-=2) { - void **addr = (void **)(item[i-2]); - void *oldvalue = (void *)(item[i-1]); - *addr = oldvalue; - } - } -#endif /* upon abort, set the reads size limit to 94% of how much was read so far. This should ensure that, assuming the retry does the same @@ -936,10 +926,7 @@ d->count_reads = 1; fxcache_clear(&d->recent_reads_cache); -#if 0 - gcptrlist_clear(&d->undolog); gcptrlist_clear(&d->abortinfo); -#endif } void BeginTransaction(jmp_buf* buf) @@ -1496,17 +1483,6 @@ /************************************************************/ -#if 0 -void stm_ThreadLocalRef_LLSet(void **addr, void *newvalue) -{ - struct tx_descriptor *d = thread_descriptor; - gcptrlist_insert2(&d->undolog, (gcptr)addr, (gcptr)*addr); - *addr = newvalue; -} -#endif - -/************************************************************/ - struct tx_descriptor *stm_tx_head = NULL; struct tx_public_descriptor *stm_descriptor_array[MAX_THREADS] = {0}; static revision_t descriptor_array_free_list = 0; @@ -1635,11 +1611,8 @@ assert(d->private_from_protected.size == 0); gcptrlist_delete(&d->private_from_protected); gcptrlist_delete(&d->list_of_read_objects); -#if 0 gcptrlist_delete(&d->abortinfo); free(d->longest_abort_info); - gcptrlist_delete(&d->undolog); -#endif int num_aborts = 0, num_spinloops = 0; char line[256], *p = line; diff --git a/c4/et.h b/c4/et.h --- a/c4/et.h +++ b/c4/et.h @@ -152,9 +152,9 @@ unsigned int num_aborts[ABORT_REASONS]; unsigned int num_spinloops[SPINLOOP_REASONS]; struct GcPtrList list_of_read_objects; - //struct GcPtrList abortinfo; struct GcPtrList private_from_protected; struct G2L public_to_private; + struct GcPtrList abortinfo; char *longest_abort_info; long long longest_abort_info_time; revision_t *private_revision_ref; diff --git a/c4/extra.c b/c4/extra.c new file mode 100644 --- /dev/null +++ b/c4/extra.c @@ -0,0 +1,263 @@ +#include "stmimpl.h" + + +void stm_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 = stmgc_size(obj); + memcpy(id, obj, size); + id->h_tid &= ~GCFLAG_HAS_ID; + id->h_tid |= GCFLAG_OLD; + dprintf(("copy_to_old_id_copy(%p -> %p)\n", obj, id)); +} + +/************************************************************/ +/* Each object has a h_original pointer to an old copy of + the same object (e.g. an old revision), the "original". + The memory location of this old object is used as the ID + for this object. If h_original is NULL *and* it is an + old object copy, it itself is the original. This invariant + must be upheld by all code dealing with h_original. + The original copy must never be moved again. Also, it may + be just a stub-object. + + If we want the ID of an object which is still young, + we must preallocate an old shadow-original that is used + as the target of the young object in a minor collection. + In this case, we set the HAS_ID flag on the young obj + to notify minor_collect. + This flag can be lost if the young obj is stolen. Then + the stealing thread uses the shadow-original itself and + minor_collect must not overwrite it again. + Also, if there is already a backup-copy around, we use + this instead of allocating another old object to use as + the shadow-original. + */ + +static revision_t mangle_hash(revision_t n) +{ + /* To hash pointers in dictionaries. Assumes that i shows some + alignment (to 4, 8, maybe 16 bytes), so we use the following + formula to avoid the trailing bits being always 0. + This formula is reversible: two different values of 'i' will + always give two different results. + */ + return n ^ (((urevision_t)n) >> 4); +} + + +revision_t stm_hash(gcptr p) +{ + /* Prebuilt objects may have a specific hash stored in an extra + field. For now, we will simply always follow h_original and + see, if it is a prebuilt object (XXX: maybe propagate a flag + to all copies of a prebuilt to avoid this cache miss). + */ + if (p->h_original) { + if (p->h_tid & GCFLAG_PREBUILT_ORIGINAL) { + return p->h_original; + } + gcptr orig = (gcptr)p->h_original; + if ((orig->h_tid & GCFLAG_PREBUILT_ORIGINAL) && orig->h_original) { + return orig->h_original; + } + } + return mangle_hash(stm_id(p)); +} + + +revision_t stm_id(gcptr p) +{ + struct tx_descriptor *d = thread_descriptor; + revision_t result; + + if (p->h_original) { /* fast path */ + if (p->h_tid & GCFLAG_PREBUILT_ORIGINAL) { + /* h_original may contain a specific hash value, + but in case of the prebuilt original version, + its memory location is the id */ + return (revision_t)p; + } + + dprintf(("stm_id(%p) has orig fst: %p\n", + p, (gcptr)p->h_original)); + return p->h_original; + } + else if (p->h_tid & GCFLAG_OLD) { + /* old objects must have an h_original xOR be + the original itself. */ + dprintf(("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; + dprintf(("stm_id(%p) has orig: %p\n", + p, (gcptr)p->h_original)); + } + else { + /* must create shadow original object XXX: or use + backup, if exists */ + + /* XXX use stmgcpage_malloc() directly, we don't need to copy + * the contents yet */ + gcptr O = stmgc_duplicate_old(p); + p->h_original = (revision_t)O; + p->h_tid |= GCFLAG_HAS_ID; + + if (p->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) { + gcptr B = (gcptr)p->h_revision; + B->h_original = (revision_t)O; + } + + result = (revision_t)O; + dprintf(("stm_id(%p) young, make shadow %p\n", p, O)); + } + + spinlock_release(d->public_descriptor->collection_lock); + return result; +} + +_Bool stm_pointer_equal(gcptr p1, gcptr p2) +{ + /* fast path for two equal pointers */ + if (p1 == p2) + return 1; + /* if p1 or p2 is NULL (but not both, because they are different + pointers), then return 0 */ + if (p1 == NULL || p2 == NULL) + return 0; + /* 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); +} + +/************************************************************/ + +void stm_abort_info_push(gcptr obj, long fieldoffsets[]) +{ + struct tx_descriptor *d = thread_descriptor; + obj = stm_read_barrier(obj); + gcptrlist_insert2(&d->abortinfo, obj, (gcptr)fieldoffsets); +} + +void stm_abort_info_pop(long count) +{ + struct tx_descriptor *d = thread_descriptor; + long newsize = d->abortinfo.size - 2 * count; + gcptrlist_reduce_size(&d->abortinfo, newsize < 0 ? 0 : newsize); +} + +size_t stm_decode_abort_info(struct tx_descriptor *d, long long elapsed_time, + int abort_reason, char *output) +{ + /* re-encodes the abort info as a single string. + For convenience (no escaping needed, no limit on integer + sizes, etc.) we follow the bittorrent format. */ + size_t totalsize = 0; + long i; + char buffer[32]; + size_t res_size; +#define WRITE(c) { totalsize++; if (output) *output++=(c); } +#define WRITE_BUF(p, sz) { totalsize += (sz); \ + if (output) { \ + memcpy(output, (p), (sz)); output += (sz); \ + } \ + } + WRITE('l'); + WRITE('l'); + res_size = sprintf(buffer, "i%llde", (long long)elapsed_time); + WRITE_BUF(buffer, res_size); + res_size = sprintf(buffer, "i%de", (int)abort_reason); + WRITE_BUF(buffer, res_size); + res_size = sprintf(buffer, "i%lde", (long)d->public_descriptor_index); + WRITE_BUF(buffer, res_size); + res_size = sprintf(buffer, "i%lde", (long)d->atomic); + WRITE_BUF(buffer, res_size); + res_size = sprintf(buffer, "i%de", (int)d->active); + WRITE_BUF(buffer, res_size); + res_size = sprintf(buffer, "i%lue", (unsigned long)d->count_reads); + WRITE_BUF(buffer, res_size); + res_size = sprintf(buffer, "i%lue", + (unsigned long)d->reads_size_limit_nonatomic); + WRITE_BUF(buffer, res_size); + WRITE('e'); + for (i=0; i<d->abortinfo.size; i+=2) { + char *object = (char *)stm_RepeatReadBarrier(d->abortinfo.items[i+0]); + long *fieldoffsets = (long*)d->abortinfo.items[i+1]; + long kind, offset; + size_t rps_size; + char *rps; + + while (1) { + kind = *fieldoffsets++; + if (kind <= 0) { + if (kind == -2) { + WRITE('l'); /* '[', start of sublist */ + continue; + } + if (kind == -1) { + WRITE('e'); /* ']', end of sublist */ + continue; + } + break; /* 0, terminator */ + } + offset = *fieldoffsets++; + switch(kind) { + case 1: /* signed */ + res_size = sprintf(buffer, "i%lde", + *(long*)(object + offset)); + WRITE_BUF(buffer, res_size); + break; + case 2: /* unsigned */ + res_size = sprintf(buffer, "i%lue", + *(unsigned long*)(object + offset)); + WRITE_BUF(buffer, res_size); + break; + case 3: /* a string of bytes from the target object */ + rps = *(char **)(object + offset); + offset = *fieldoffsets++; + if (rps) { + /* xxx a bit ad-hoc: it's a string whose length is a + * long at 'offset', following immediately the offset */ + rps_size = *(long *)(rps + offset); + offset += sizeof(long); + assert(rps_size >= 0); + res_size = sprintf(buffer, "%zu:", rps_size); + WRITE_BUF(buffer, res_size); + WRITE_BUF(rps + offset, rps_size); + } + else { + WRITE_BUF("0:", 2); + } + break; + default: + stm_fatalerror("corrupted abort log\n"); + } + } + } + WRITE('e'); + WRITE('\0'); /* final null character */ +#undef WRITE +#undef WRITE_BUF + return totalsize; +} + +char *stm_inspect_abort_info(void) +{ + struct tx_descriptor *d = thread_descriptor; + if (d->longest_abort_info_time <= 0) + return NULL; + d->longest_abort_info_time = 0; + return d->longest_abort_info; +} diff --git a/c4/extra.h b/c4/extra.h new file mode 100644 --- /dev/null +++ b/c4/extra.h @@ -0,0 +1,9 @@ +#ifndef _SRCSTM_EXTRA_H +#define _SRCSTM_EXTRA_H + + +void stm_copy_to_old_id_copy(gcptr obj, gcptr id); +size_t stm_decode_abort_info(struct tx_descriptor *d, long long elapsed_time, + int abort_reason, char *output); + +#endif diff --git a/c4/gcpage.c b/c4/gcpage.c --- a/c4/gcpage.c +++ b/c4/gcpage.c @@ -225,7 +225,8 @@ id_copy->h_tid |= GCFLAG_VISITED; /* XXX: may not always need tracing? */ - gcptrlist_insert(&objects_to_trace, id_copy); + //if (!(id_copy->h_tid & GCFLAG_STUB)) + // gcptrlist_insert(&objects_to_trace, id_copy); } else { /* prebuilt originals won't get collected anyway diff --git a/c4/nursery.c b/c4/nursery.c --- a/c4/nursery.c +++ b/c4/nursery.c @@ -125,131 +125,6 @@ } /************************************************************/ -/* Each object has a h_original pointer to an old copy of - the same object (e.g. an old revision), the "original". - The memory location of this old object is used as the ID - for this object. If h_original is NULL *and* it is an - old object copy, it itself is the original. This invariant - must be upheld by all code dealing with h_original. - The original copy must never be moved again. Also, it may - be just a stub-object. - - If we want the ID of an object which is still young, - we must preallocate an old shadow-original that is used - as the target of the young object in a minor collection. - In this case, we set the HAS_ID flag on the young obj - to notify minor_collect. - This flag can be lost if the young obj is stolen. Then - the stealing thread uses the shadow-original itself and - minor_collect must not overwrite it again. - Also, if there is already a backup-copy around, we use - this instead of allocating another old object to use as - the shadow-original. - */ - -static revision_t mangle_hash(revision_t n) -{ - /* To hash pointers in dictionaries. Assumes that i shows some - alignment (to 4, 8, maybe 16 bytes), so we use the following - formula to avoid the trailing bits being always 0. - This formula is reversible: two different values of 'i' will - always give two different results. - */ - return n ^ (((urevision_t)n) >> 4); -} - - -revision_t stm_hash(gcptr p) -{ - /* Prebuilt objects may have a specific hash stored in an extra - field. For now, we will simply always follow h_original and - see, if it is a prebuilt object (XXX: maybe propagate a flag - to all copies of a prebuilt to avoid this cache miss). - */ - if (p->h_original) { - if (p->h_tid & GCFLAG_PREBUILT_ORIGINAL) { - return p->h_original; - } - gcptr orig = (gcptr)p->h_original; - if ((orig->h_tid & GCFLAG_PREBUILT_ORIGINAL) && orig->h_original) { - return orig->h_original; - } - } - return mangle_hash(stm_id(p)); -} - - -revision_t stm_id(gcptr p) -{ - struct tx_descriptor *d = thread_descriptor; - revision_t result; - - if (p->h_original) { /* fast path */ - if (p->h_tid & GCFLAG_PREBUILT_ORIGINAL) { - /* h_original may contain a specific hash value, - but in case of the prebuilt original version, - its memory location is the id */ - return (revision_t)p; - } - - dprintf(("stm_id(%p) has orig fst: %p\n", - p, (gcptr)p->h_original)); - return p->h_original; - } - else if (p->h_tid & GCFLAG_OLD) { - /* old objects must have an h_original xOR be - the original itself. */ - dprintf(("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; - dprintf(("stm_id(%p) has orig: %p\n", - p, (gcptr)p->h_original)); - } - else { - /* must create shadow original object XXX: or use - backup, if exists */ - - /* XXX use stmgcpage_malloc() directly, we don't need to copy - * the contents yet */ - gcptr O = stmgc_duplicate_old(p); - p->h_original = (revision_t)O; - p->h_tid |= GCFLAG_HAS_ID; - - if (p->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) { - gcptr B = (gcptr)p->h_revision; - B->h_original = (revision_t)O; - } - - result = (revision_t)O; - dprintf(("stm_id(%p) young, make shadow %p\n", p, O)); - } - - spinlock_release(d->public_descriptor->collection_lock); - return result; -} - -_Bool stm_pointer_equal(gcptr p1, gcptr p2) -{ - /* fast path for two equal pointers */ - if (p1 == p2) - return 1; - /* 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) { @@ -266,18 +141,6 @@ return fresh_old_copy; } -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 = stmgc_size(obj); - memcpy(id, obj, size); - id->h_tid &= ~GCFLAG_HAS_ID; - id->h_tid |= GCFLAG_OLD; - dprintf(("copy_to_old_id_copy(%p -> %p)\n", obj, id)); -} - static void visit_if_young(gcptr *root) { gcptr obj = *root; @@ -303,7 +166,7 @@ /* already has a place to go to */ gcptr id_obj = (gcptr)obj->h_original; - copy_to_old_id_copy(obj, id_obj); + stm_copy_to_old_id_copy(obj, id_obj); fresh_old_copy = id_obj; obj->h_tid &= ~GCFLAG_HAS_ID; } @@ -485,7 +348,7 @@ we may occasionally see a PUBLIC object --- one that was a private/protected object when it was added to old_objects_to_trace, and has been stolen. So we have to - check and not do any change the obj->h_tid in that case. + check and not do any change to the obj->h_tid in that case. Otherwise this conflicts with the rule that we may only modify obj->h_tid of a public object in order to add PUBLIC_TO_PRIVATE. diff --git a/c4/steal.c b/c4/steal.c --- a/c4/steal.c +++ b/c4/steal.c @@ -1,8 +1,6 @@ #include "stmimpl.h" -void copy_to_old_id_copy(gcptr obj, gcptr id); - gcptr stm_stub_malloc(struct tx_public_descriptor *pd) { assert(pd->collection_lock != 0); @@ -167,7 +165,7 @@ /* use id-copy for us */ O = (gcptr)L->h_original; L->h_tid &= ~GCFLAG_HAS_ID; - copy_to_old_id_copy(L, O); + stm_copy_to_old_id_copy(L, O); O->h_original = 0; } else { /* Copy the object out of the other thread's nursery, diff --git a/c4/stmgc.c b/c4/stmgc.c --- a/c4/stmgc.c +++ b/c4/stmgc.c @@ -9,5 +9,6 @@ #include "nursery.c" #include "gcpage.c" #include "stmsync.c" +#include "extra.c" #include "dbgmem.c" #include "fprintcolor.c" diff --git a/c4/stmgc.h b/c4/stmgc.h --- a/c4/stmgc.h +++ b/c4/stmgc.h @@ -101,6 +101,22 @@ It is set to NULL by stm_initialize(). */ extern __thread gcptr stm_thread_local_obj; +/* For tracking where aborts occurs, you can push/pop information + into this stack. When an abort occurs this information is encoded + and flattened into a buffer which can later be retrieved with + stm_inspect_abort_info(). (XXX details not documented yet) */ +void stm_abort_info_push(gcptr obj, long fieldoffsets[]); +void stm_abort_info_pop(long count); +char *stm_inspect_abort_info(void); + +/* mostly for debugging support */ +void stm_abort_and_retry(void); +void stm_minor_collect(void); +void stm_major_collect(void); + + +/**************** END OF PUBLIC INTERFACE *****************/ +/************************************************************/ /* macro-like functionality */ diff --git a/c4/stmimpl.h b/c4/stmimpl.h --- a/c4/stmimpl.h +++ b/c4/stmimpl.h @@ -12,7 +12,7 @@ # endif #endif -#ifdef _GC_DEBUG +#if defined(_GC_DEBUG) && !defined(DUMP_EXTRA) # if _GC_DEBUG >= 2 # define DUMP_EXTRA # endif @@ -35,5 +35,6 @@ #include "et.h" #include "steal.h" #include "stmsync.h" +#include "extra.h" #endif diff --git a/c4/stmsync.c b/c4/stmsync.c --- a/c4/stmsync.c +++ b/c4/stmsync.c @@ -328,6 +328,18 @@ AbortNowIfDelayed(); /* if another thread ran a major GC */ } +void stm_minor_collect(void) +{ + stmgc_minor_collect(); + stmgcpage_possibly_major_collect(0); +} + +void stm_major_collect(void) +{ + stmgc_minor_collect(); + stmgcpage_possibly_major_collect(1); +} + /************************************************************/ /***** Prebuilt roots, added in the list as the transaction that changed diff --git a/c4/test/support.py b/c4/test/support.py --- a/c4/test/support.py +++ b/c4/test/support.py @@ -11,11 +11,11 @@ header_files = [os.path.join(parent_dir, _n) for _n in "et.h lists.h steal.h nursery.h gcpage.h " - "stmsync.h dbgmem.h fprintcolor.h " + "stmsync.h extra.h dbgmem.h fprintcolor.h " "stmgc.h stmimpl.h atomic_ops.h".split()] source_files = [os.path.join(parent_dir, _n) for _n in "et.c lists.c steal.c nursery.c gcpage.c " - "stmsync.c dbgmem.c fprintcolor.c".split()] + "stmsync.c extra.c dbgmem.c fprintcolor.c".split()] _pycache_ = os.path.join(parent_dir, 'test', '__pycache__') if os.path.exists(_pycache_): @@ -65,6 +65,10 @@ long stm_atomic(long delta); int stm_enter_callback_call(void); void stm_leave_callback_call(int); + void stm_abort_info_push(gcptr obj, long fieldoffsets[]); + void stm_abort_info_pop(long count); + char *stm_inspect_abort_info(void); + void stm_abort_and_retry(void); /* extra non-public code */ void printfcolor(char *msg); @@ -619,7 +623,7 @@ assert fine == [True] def abort_and_retry(): - lib.AbortTransaction(lib.ABRT_MANUAL) + lib.stm_abort_and_retry() def classify(p): private_from_protected = (p.h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) != 0 diff --git a/c4/test/test_extra.py b/c4/test/test_extra.py new file mode 100644 --- /dev/null +++ b/c4/test/test_extra.py @@ -0,0 +1,116 @@ +import py, sys, struct +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_abort_info_stack(): + p = nalloc(HDR) + q = nalloc(HDR) + lib.stm_abort_info_push(p, ffi.cast("long *", 123)) + lib.stm_abort_info_push(q, ffi.cast("long *", 125)) + lib.stm_abort_info_pop(2) + # no real test here + +def test_inspect_abort_info_signed(): + fo1 = ffi.new("long[]", [-2, 1, HDR, -1, 0]) + # + @perform_transaction + def run(retry_counter): + if retry_counter == 0: + p = nalloc(HDR + WORD) + lib.setlong(p, 0, -421289712) + lib.stm_abort_info_push(p, fo1) + abort_and_retry() + else: + c = lib.stm_inspect_abort_info() + assert c + assert ffi.string(c).endswith("eli-421289712eee") + +def test_inspect_abort_info_nested_unsigned(): + fo1 = ffi.new("long[]", [-2, 2, HDR, 0]) + fo2 = ffi.new("long[]", [2, HDR + WORD, -1, 0]) + # + @perform_transaction + def run(retry_counter): + if retry_counter == 0: + p = nalloc(HDR + WORD) + q = nalloc(HDR + 2 * WORD) + lib.setlong(p, 0, sys.maxint) + lib.setlong(q, 1, -1) + lib.stm_abort_info_push(p, fo1) + lib.stm_abort_info_push(q, fo2) + abort_and_retry() + else: + c = lib.stm_inspect_abort_info() + assert c + assert ffi.string(c).endswith("eli%dei%deee" % ( + sys.maxint, sys.maxint * 2 + 1)) + +def test_inspect_abort_info_string(): + fo1 = ffi.new("long[]", [3, HDR + WORD, HDR, 0]) + # + @perform_transaction + def run(retry_counter): + if retry_counter == 0: + p = nalloc_refs(2) + q = nalloc(HDR + 2 * WORD) + lib.setptr(p, 1, q) + lib.setlong(q, 0, 3) + word = "ABC" + "\xFF" * (WORD - 3) + lib.setlong(q, 1, struct.unpack("l", word)[0]) + lib.stm_abort_info_push(p, fo1) + abort_and_retry() + else: + c = lib.stm_inspect_abort_info() + assert c + assert ffi.string(c).endswith("e3:ABCe") + +def test_inspect_null(): + fo1 = ffi.new("long[]", [3, HDR, HDR + 1, 0]) + # + @perform_transaction + def run(retry_counter): + if retry_counter == 0: + p = nalloc_refs(1) + lib.setptr(p, 0, ffi.NULL) # default + lib.stm_abort_info_push(p, fo1) + abort_and_retry() + else: + c = lib.stm_inspect_abort_info() + assert c + assert ffi.string(c).endswith("e0:e") + +def test_latest_version(): + fo1 = ffi.new("long[]", [1, HDR, 0]) + p = palloc(HDR + WORD) + lib.rawsetlong(p, 0, -9827892) + # + @perform_transaction + def run(retry_counter): + if retry_counter == 0: + lib.stm_abort_info_push(p, fo1) + lib.setlong(p, 0, 424242) + abort_and_retry() + else: + c = lib.stm_inspect_abort_info() + assert c + assert ffi.string(c).endswith("ei424242ee") + +def test_pointer_equal(): + p = palloc(HDR) + assert lib.stm_pointer_equal(p, p) + assert not lib.stm_pointer_equal(p, ffi.NULL) + assert not lib.stm_pointer_equal(ffi.NULL, p) + assert lib.stm_pointer_equal(ffi.NULL, ffi.NULL) + q = lib.stm_write_barrier(p) + assert q != p + assert lib.stm_pointer_equal(p, q) + assert lib.stm_pointer_equal(q, q) + assert lib.stm_pointer_equal(q, p) diff --git a/duhton/Makefile b/duhton/Makefile --- a/duhton/Makefile +++ b/duhton/Makefile @@ -5,7 +5,7 @@ gcc -pthread -g -O2 -o duhton *.c ../c4/stmgc.c -Wall -lrt duhton_debug: *.c *.h ../c4/*.c ../c4/*.h - gcc -pthread -g -DDu_DEBUG -D_GC_DEBUG=2 -DGC_NURSERY=2048 -o duhton_debug *.c ../c4/stmgc.c -Wall -lrt + gcc -pthread -g -DDu_DEBUG -D_GC_DEBUGPRINTS=1 -DGC_NURSERY=2048 -o duhton_debug *.c ../c4/stmgc.c -Wall -lrt clean: rm -f duhton duhton_debug _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit