Author: Remi Meier <remi.me...@gmail.com> Branch: stmgc-c4 Changeset: r65771:ed40f1172b80 Date: 2013-07-29 08:41 +0200 http://bitbucket.org/pypy/pypy/changeset/ed40f1172b80/
Log: Merge diff --git a/rpython/memory/gc/stmgc.py b/rpython/memory/gc/stmgc.py --- a/rpython/memory/gc/stmgc.py +++ b/rpython/memory/gc/stmgc.py @@ -44,15 +44,16 @@ GCFLAG_PREBUILT_ORIGINAL = first_gcflag << 3 GCFLAG_PUBLIC_TO_PRIVATE = first_gcflag << 4 GCFLAG_WRITE_BARRIER = first_gcflag << 5 # stmgc.h - GCFLAG_NURSERY_MOVED = first_gcflag << 6 + GCFLAG_MOVED = first_gcflag << 6 GCFLAG_BACKUP_COPY = first_gcflag << 7 # debug GCFLAG_STUB = first_gcflag << 8 # debug GCFLAG_PRIVATE_FROM_PROTECTED = first_gcflag << 9 GCFLAG_HAS_ID = first_gcflag << 10 GCFLAG_IMMUTABLE = first_gcflag << 11 GCFLAG_SMALLSTUB = first_gcflag << 12 + GCFLAG_MARKED = first_gcflag << 13 - PREBUILT_FLAGS = first_gcflag * (1 + 2 + 4 + 8) + PREBUILT_FLAGS = first_gcflag * ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<13)) PREBUILT_REVISION = r_uint(1) FX_MASK = 65535 @@ -109,7 +110,7 @@ # XXX finalizers are ignored for now #ll_assert(not needs_finalizer, 'XXX needs_finalizer') #ll_assert(not is_finalizer_light, 'XXX is_finalizer_light') - #ll_assert(not contains_weakptr, 'XXX contains_weakptr') + ll_assert(not contains_weakptr, 'contains_weakptr: use malloc_weakref') # XXX call optimized versions, e.g. if size < GC_NURSERY_SECTION return llop.stm_allocate(llmemory.GCREF, size, typeid16) @@ -131,12 +132,14 @@ seen by the GC, then it can get collected.""" tid = self.get_hdr_tid(obj)[0] if bool(tid & self.GCFLAG_OLD): - return False + return False # XXX wrong so far. We should add a flag to the + # object that means "don't ever kill this copy" return True @classmethod def JIT_max_size_of_young_obj(cls): + # XXX there is actually a maximum, check return None @classmethod diff --git a/rpython/translator/stm/src_stm/dbgmem.c b/rpython/translator/stm/src_stm/dbgmem.c --- a/rpython/translator/stm/src_stm/dbgmem.c +++ b/rpython/translator/stm/src_stm/dbgmem.c @@ -9,7 +9,7 @@ #ifdef _GC_DEBUG /************************************************************/ -#define MMAP_TOTAL 671088640 /* 640MB */ +#define MMAP_TOTAL 1280*1024*1024 /* 1280MB */ static pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER; static char *zone_start, *zone_current = NULL, *zone_end = NULL; @@ -71,6 +71,10 @@ void stm_free(void *p, size_t sz) { + if (p == NULL) { + assert(sz == 0); + return; + } assert(((intptr_t)((char *)p + sz) & (PAGE_SIZE-1)) == 0); size_t nb_pages = (sz + PAGE_SIZE - 1) / PAGE_SIZE + 1; @@ -84,6 +88,14 @@ _stm_dbgmem(p, sz, PROT_NONE); } +void *stm_realloc(void *p, size_t newsz, size_t oldsz) +{ + void *r = stm_malloc(newsz); + memcpy(r, p, oldsz < newsz ? oldsz : newsz); + stm_free(p, oldsz); + return r; +} + int _stm_can_access_memory(char *p) { long base = ((char *)p - zone_start) / PAGE_SIZE; diff --git a/rpython/translator/stm/src_stm/dbgmem.h b/rpython/translator/stm/src_stm/dbgmem.h --- a/rpython/translator/stm/src_stm/dbgmem.h +++ b/rpython/translator/stm/src_stm/dbgmem.h @@ -7,6 +7,7 @@ void *stm_malloc(size_t); void stm_free(void *, size_t); +void *stm_realloc(void *, size_t, size_t); int _stm_can_access_memory(char *); void assert_cleared(char *, size_t); @@ -14,6 +15,7 @@ #define stm_malloc(sz) malloc(sz) #define stm_free(p,sz) free(p) +#define stm_realloc(p,newsz,oldsz) realloc(p,newsz) #define assert_cleared(p,sz) do { } while(0) #endif diff --git a/rpython/translator/stm/src_stm/et.c b/rpython/translator/stm/src_stm/et.c --- a/rpython/translator/stm/src_stm/et.c +++ b/rpython/translator/stm/src_stm/et.c @@ -146,7 +146,7 @@ gcptr P_prev = P; P = (gcptr)v; assert((P->h_tid & GCFLAG_PUBLIC) || - (P_prev->h_tid & GCFLAG_NURSERY_MOVED)); + (P_prev->h_tid & GCFLAG_MOVED)); v = ACCESS_ONCE(P->h_revision); @@ -238,7 +238,7 @@ add_in_recent_reads_cache: /* The risks are that the following assert fails, because the flag was added just now by a parallel thread during stealing... */ - /*assert(!(P->h_tid & GCFLAG_NURSERY_MOVED));*/ + /*assert(!(P->h_tid & GCFLAG_MOVED));*/ fxcache_add(&d->recent_reads_cache, P); return P; @@ -281,7 +281,7 @@ */ if (P->h_tid & GCFLAG_PUBLIC) { - if (P->h_tid & GCFLAG_NURSERY_MOVED) + if (P->h_tid & GCFLAG_MOVED) { P = (gcptr)P->h_revision; assert(P->h_tid & GCFLAG_PUBLIC); @@ -413,7 +413,7 @@ while (v = P->h_revision, IS_POINTER(v)) { - if (P->h_tid & GCFLAG_NURSERY_MOVED) + if (P->h_tid & GCFLAG_MOVED) dprintf(("nursery_moved ")); if (v & 2) @@ -510,7 +510,7 @@ static gcptr LocalizePublic(struct tx_descriptor *d, gcptr R) { assert(R->h_tid & GCFLAG_PUBLIC); - assert(!(R->h_tid & GCFLAG_NURSERY_MOVED)); + assert(!(R->h_tid & GCFLAG_MOVED)); #ifdef _GC_DEBUG wlog_t *entry; @@ -570,6 +570,13 @@ gcptr stm_WriteBarrier(gcptr P) { assert(!(P->h_tid & GCFLAG_IMMUTABLE)); + assert((P->h_tid & GCFLAG_STUB) || + stmgc_size(P) > sizeof(struct stm_stub_s) - WORD); + /* If stmgc_size(P) gives a number <= sizeof(stub)-WORD, then there is a + risk of overrunning the object later in gcpage.c when copying a stub + over it. However such objects are so small that they contain no field + at all, and so no write barrier should occur on them. */ + if (is_private(P)) { /* If we have GCFLAG_WRITE_BARRIER in P, then list it into @@ -606,7 +613,7 @@ Add R into the list 'public_with_young_copy', unless W is actually an old object, in which case we need to record W. */ - if (R->h_tid & GCFLAG_NURSERY_MOVED) + if (R->h_tid & GCFLAG_MOVED) { /* Bah, the object turned into this kind of stub, possibly while we were waiting for the collection_lock, because it @@ -696,8 +703,8 @@ continue; } } - else if ((R->h_tid & (GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED)) - == (GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED)) + else if ((R->h_tid & (GCFLAG_PUBLIC | GCFLAG_MOVED)) + == (GCFLAG_PUBLIC | GCFLAG_MOVED)) { /* such an object is identical to the one it points to (stolen protected young object with h_revision pointing @@ -970,6 +977,7 @@ revision_t my_lock = d->my_lock; wlog_t *item; + dprintf(("acquire_locks\n")); assert(!stm_has_got_any_lock(d)); assert(d->public_descriptor->stolen_objects.size == 0); @@ -982,6 +990,7 @@ revision_t v; retry: assert(R->h_tid & GCFLAG_PUBLIC); + assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE); v = ACCESS_ONCE(R->h_revision); if (IS_POINTER(v)) /* "has a more recent revision" */ { @@ -1014,7 +1023,7 @@ static void CancelLocks(struct tx_descriptor *d) { wlog_t *item; - + dprintf(("cancel_locks\n")); if (!g2l_any_entry(&d->public_to_private)) return; @@ -1107,7 +1116,7 @@ assert(!(L->h_tid & GCFLAG_VISITED)); assert(!(L->h_tid & GCFLAG_PUBLIC_TO_PRIVATE)); assert(!(L->h_tid & GCFLAG_PREBUILT_ORIGINAL)); - assert(!(L->h_tid & GCFLAG_NURSERY_MOVED)); + assert(!(L->h_tid & GCFLAG_MOVED)); assert(L->h_revision != localrev); /* modified by AcquireLocks() */ #ifdef DUMP_EXTRA @@ -1119,7 +1128,9 @@ gcptr stub = stm_stub_malloc(d->public_descriptor, 0); stub->h_tid = (L->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC | GCFLAG_STUB + | GCFLAG_SMALLSTUB | GCFLAG_OLD; + dprintf(("et.c: stm_stub_malloc -> %p\n", stub)); stub->h_revision = ((revision_t)L) | 2; assert(!(L->h_tid & GCFLAG_HAS_ID)); @@ -1154,7 +1165,7 @@ assert(R->h_tid & GCFLAG_PUBLIC); assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE); - assert(!(R->h_tid & GCFLAG_NURSERY_MOVED)); + assert(!(R->h_tid & GCFLAG_MOVED)); assert(R->h_revision != localrev); #ifdef DUMP_EXTRA @@ -1249,7 +1260,7 @@ assert(!(B->h_tid & GCFLAG_BACKUP_COPY)); P->h_tid |= GCFLAG_PUBLIC; assert(!(P->h_tid & GCFLAG_HAS_ID)); - if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_NURSERY_MOVED; + if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_MOVED; /* P becomes a public outdated object. It may create an exception documented in doc-objects.txt: a public but young object. It's still fine because it should only be seen by @@ -1282,7 +1293,7 @@ revision_t cur_time; struct tx_descriptor *d = thread_descriptor; assert(d->active >= 1); - + dprintf(("CommitTransaction(%p)\n", d)); spinlock_acquire(d->public_descriptor->collection_lock, 'C'); /*committing*/ if (d->public_descriptor->stolen_objects.size != 0) stm_normalize_stolen_objects(d); @@ -1366,6 +1377,7 @@ d->active = 2; d->reads_size_limit_nonatomic = 0; update_reads_size_limit(d); + dprintf(("make_inevitable(%p)\n", d)); } static revision_t acquire_inev_mutex_and_mark_global_cur_time( diff --git a/rpython/translator/stm/src_stm/et.h b/rpython/translator/stm/src_stm/et.h --- a/rpython/translator/stm/src_stm/et.h +++ b/rpython/translator/stm/src_stm/et.h @@ -26,7 +26,11 @@ * * GCFLAG_OLD is set on old objects. * - * GCFLAG_VISITED is used temporarily during major collections. + * GCFLAG_VISITED and GCFLAG_MARKED are used temporarily during major + * collections. The objects are MARKED|VISITED as soon as they have been + * added to 'objects_to_trace', and so will be or have been traced. The + * objects are only MARKED if their memory must be kept alive, but (so far) + * we found that tracing them is not useful. * * GCFLAG_PUBLIC is set on public objects. * @@ -47,7 +51,7 @@ * the list 'old_objects_to_trace'; it is set again at the next minor * collection. * - * GCFLAG_NURSERY_MOVED is used temporarily during minor collections. + * GCFLAG_MOVED is used temporarily during minor/major collections. * * GCFLAG_STUB is set for debugging on stub objects made by stealing or * by major collections. 'p_stub->h_revision' might be a value @@ -68,16 +72,20 @@ static const revision_t GCFLAG_PREBUILT_ORIGINAL = STM_FIRST_GCFLAG << 3; static const revision_t GCFLAG_PUBLIC_TO_PRIVATE = STM_FIRST_GCFLAG << 4; // in stmgc.h: GCFLAG_WRITE_BARRIER = STM_FIRST_GCFLAG << 5; -static const revision_t GCFLAG_NURSERY_MOVED = STM_FIRST_GCFLAG << 6; +static const revision_t GCFLAG_MOVED = STM_FIRST_GCFLAG << 6; 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; static const revision_t GCFLAG_IMMUTABLE = STM_FIRST_GCFLAG << 11; +static const revision_t GCFLAG_SMALLSTUB /*debug*/ = STM_FIRST_GCFLAG << 12; +static const revision_t GCFLAG_MARKED = STM_FIRST_GCFLAG << 13; +/* warning, the last flag available is "<< 15" on 32-bit */ /* this value must be reflected in PREBUILT_FLAGS in stmgc.h */ #define GCFLAG_PREBUILT (GCFLAG_VISITED | \ + GCFLAG_MARKED | \ GCFLAG_PREBUILT_ORIGINAL | \ GCFLAG_OLD | \ GCFLAG_PUBLIC) @@ -88,12 +96,14 @@ "PREBUILT_ORIGINAL", \ "PUBLIC_TO_PRIVATE", \ "WRITE_BARRIER", \ - "NURSERY_MOVED", \ + "MOVED", \ "BACKUP_COPY", \ "STUB", \ "PRIVATE_FROM_PROTECTED", \ - "HAS_ID", \ - "IMMUTABLE", \ + "HAS_ID", \ + "IMMUTABLE", \ + "SMALLSTUB", \ + "MARKED", \ NULL } #define IS_POINTER(v) (!((v) & 1)) /* even-valued number */ diff --git a/rpython/translator/stm/src_stm/extra.c b/rpython/translator/stm/src_stm/extra.c --- a/rpython/translator/stm/src_stm/extra.c +++ b/rpython/translator/stm/src_stm/extra.c @@ -132,17 +132,16 @@ _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); + if (p1 != NULL && p2 != NULL) { + /* resolve h_original, but only if !PREBUILT_ORIGINAL */ + if (p1->h_original && !(p1->h_tid & GCFLAG_PREBUILT_ORIGINAL)) { + p1 = (gcptr)p1->h_original; + } + if (p2->h_original && !(p2->h_tid & GCFLAG_PREBUILT_ORIGINAL)) { + p2 = (gcptr)p2->h_original; + } + } + return (p1 == p2); } /************************************************************/ diff --git a/rpython/translator/stm/src_stm/gcpage.c b/rpython/translator/stm/src_stm/gcpage.c --- a/rpython/translator/stm/src_stm/gcpage.c +++ b/rpython/translator/stm/src_stm/gcpage.c @@ -213,157 +213,229 @@ static struct GcPtrList objects_to_trace; -static void keep_original_alive(gcptr obj) +static gcptr copy_over_original(gcptr obj, gcptr id_copy) { - /* keep alive the original of a visited object */ - gcptr id_copy = (gcptr)obj->h_original; - /* prebuilt original objects may have a predifined - hash in h_original */ - if (id_copy && !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) { - if (!(id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL)) { - id_copy->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; - /* see fix_outdated() */ - if (!(id_copy->h_tid & GCFLAG_VISITED)) { - id_copy->h_tid |= GCFLAG_VISITED; + assert(obj != id_copy); + assert(id_copy == (gcptr)obj->h_original); + assert(!(id_copy->h_revision & 1)); /* not head-revision itself */ - /* XXX: may not always need tracing? */ - if (!(id_copy->h_tid & GCFLAG_STUB)) - gcptrlist_insert(&objects_to_trace, id_copy); - } - } - else { - /* prebuilt originals won't get collected anyway - and if they are not reachable in any other way, - we only ever need their location, not their content */ + /* check a few flags */ + assert(obj->h_tid & GCFLAG_PUBLIC); + assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)); + assert(!(obj->h_tid & GCFLAG_BACKUP_COPY)); + + assert(id_copy->h_tid & GCFLAG_PUBLIC); + assert(!(id_copy->h_tid & GCFLAG_BACKUP_COPY)); + + /* id_copy may be a stub, but in this case, as the original, it + should have been allocated with a big enough chunk of memory. + Also, obj itself might be a stub. */ + assert(!(id_copy->h_tid & GCFLAG_SMALLSTUB)); + if (!(id_copy->h_tid & GCFLAG_STUB) && !(obj->h_tid & GCFLAG_STUB)) { + assert(stmgc_size(id_copy) == stmgc_size(obj)); + } + + /* add the MOVED flag to 'obj' */ + obj->h_tid |= GCFLAG_MOVED; + + /* copy the object's content */ + size_t objsize; + if (obj->h_tid & GCFLAG_STUB) + objsize = sizeof(struct stm_stub_s); + else { + objsize = stmgc_size(obj); + assert(objsize > sizeof(struct stm_stub_s) - WORD); + } + dprintf(("copy %p over %p (%zd bytes)\n", obj, id_copy, objsize)); + memcpy(id_copy + 1, obj + 1, objsize - sizeof(struct stm_object_s)); + + /* copy the object's h_revision number */ + id_copy->h_revision = obj->h_revision; + + /* copy the STUB flag */ + id_copy->h_tid &= ~GCFLAG_STUB; + id_copy->h_tid |= (obj->h_tid & GCFLAG_STUB); + + return id_copy; +} + +static void visit_nonpublic(gcptr obj, struct tx_public_descriptor *gcp) +{ + /* Visit a protected or private object. 'gcp' must be either NULL or + point to the thread that has got the object. This 'gcp' is only an + optimization: it lets us trace (most) private/protected objects + and replace pointers to public objects in them with pointers to + private/protected objects if they are the most recent ones, + provided they belong to the same thread. + */ + assert(!(obj->h_tid & GCFLAG_PUBLIC)); + assert(!(obj->h_tid & GCFLAG_STUB)); + assert(!(obj->h_tid & GCFLAG_HAS_ID)); + assert(!(obj->h_tid & GCFLAG_SMALLSTUB)); + assert(!(obj->h_tid & GCFLAG_MOVED)); + + if (obj->h_tid & GCFLAG_VISITED) + return; /* already visited */ + + obj->h_tid |= GCFLAG_VISITED | GCFLAG_MARKED; + gcptrlist_insert2(&objects_to_trace, obj, (gcptr)gcp); + + obj = (gcptr)obj->h_original; + if (obj != NULL) + obj->h_tid |= GCFLAG_MARKED; +} + +static gcptr visit_public(gcptr obj, struct tx_public_descriptor *gcp) +{ + /* The goal is to walk to the most recent copy, then copy its + content back into the h_original, and finally returns this + h_original. Or, if gcp != NULL and the most recent copy is + protected by precisely 'gcp', then we return it instead. + */ + assert(obj->h_tid & GCFLAG_PUBLIC); + assert(!(obj->h_tid & GCFLAG_BACKUP_COPY)); + assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); + + gcptr original; + if (obj->h_original != 0 && + !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) { + original = (gcptr)obj->h_original; + /* the h_original may be protected, or private_from_protected, + in some cases. Then we can't use it. We'll use the most + recent h_revision which is public. */ + if (!(original->h_tid & GCFLAG_PUBLIC)) { + original->h_tid |= GCFLAG_MARKED; + original = NULL; } } + else + original = obj; + + /* the original object must not be a small stub. */ + assert(original == NULL || !(original->h_tid & GCFLAG_SMALLSTUB)); + + /* if 'original' was already visited, we are done */ + if (original != NULL && original->h_tid & GCFLAG_VISITED) + return original; + + /* walk to the head of the chained list */ + while (IS_POINTER(obj->h_revision)) { + if (!(obj->h_revision & 2)) { + obj = (gcptr)obj->h_revision; + assert(obj->h_tid & GCFLAG_PUBLIC); + continue; + } + + /* it's a stub: check the current stealing status */ + assert(obj->h_tid & GCFLAG_STUB); + gcptr obj2 = (gcptr)(obj->h_revision - 2); + + if (obj2->h_tid & GCFLAG_PUBLIC) { + /* the stub target itself was stolen, so is public now. + Continue looping from there. */ + obj = obj2; + continue; + } + + if (obj2->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) { + /* the stub target is a private_from_protected. */ + gcptr obj3 = (gcptr)obj2->h_revision; + if (obj3->h_tid & GCFLAG_PUBLIC) { + assert(!(obj3->h_tid & GCFLAG_BACKUP_COPY)); + /* the backup copy was stolen and is now a regular + public object. */ + obj = obj3; + continue; + } + else { + /* the backup copy was not stolen. Ignore this pair + obj2/obj3, and the head of the public chain is obj. + The pair obj2/obj3 was or will be handled by + mark_all_stack_roots(). */ + assert(obj3->h_tid & GCFLAG_BACKUP_COPY); + + assert(STUB_THREAD(obj) != NULL); + if (STUB_THREAD(obj) == gcp) + return obj2; + break; + } + } + else { + /* the stub target is just a protected object. + The head of the public chain is obj. We have to + explicitly keep obj2 alive. */ + assert(!IS_POINTER(obj2->h_revision)); + visit_nonpublic(obj2, STUB_THREAD(obj)); + + assert(STUB_THREAD(obj) != NULL); + if (STUB_THREAD(obj) == gcp) + return obj2; + break; + } + } + + /* at this point, 'obj' contains the most recent revision which is + public. */ + if (original == NULL) { + original = obj; + if (original->h_tid & GCFLAG_VISITED) + return original; + } + else if (obj != original) { + /* copy obj over original */ + copy_over_original(obj, original); + } + + /* return this original */ + original->h_tid |= GCFLAG_VISITED | GCFLAG_MARKED; + if (!(original->h_tid & GCFLAG_STUB)) + gcptrlist_insert2(&objects_to_trace, original, NULL); + return original; } -static void visit(gcptr *pobj); +static struct tx_public_descriptor *visit_protected_gcp; -gcptr stmgcpage_visit(gcptr obj) +static void visit_take_protected(gcptr *pobj) { - visit(&obj); - return obj; -} - -static void visit(gcptr *pobj) -{ + /* Visits '*pobj', marking it as surviving and possibly adding it to + objects_to_trace. Fixes *pobj to point to the exact copy that + survived. This function will replace *pobj with a protected + copy if it belongs to the thread 'visit_protected_gcp', so the + latter must be initialized before any call! + */ gcptr obj = *pobj; if (obj == NULL) return; - restart: - if (obj->h_revision & 1) { - assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); - assert(!(obj->h_tid & GCFLAG_STUB)); - if (!(obj->h_tid & GCFLAG_VISITED)) { - obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see fix_outdated() */ - obj->h_tid |= GCFLAG_VISITED; - gcptrlist_insert(&objects_to_trace, obj); - - keep_original_alive(obj); - } - } - else if (obj->h_tid & GCFLAG_PUBLIC) { - /* h_revision is a ptr: we have a more recent version */ - gcptr prev_obj = obj; - - 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; - keep_original_alive(prev_obj); - - assert(*pobj == prev_obj); - /* recursion, but should be only once */ - obj = stmgcpage_visit(obj); - assert(prev_obj->h_tid & GCFLAG_STUB); - prev_obj->h_revision = ((revision_t)obj) | 2; - return; - } - } - - if (!(obj->h_revision & 3)) { - /* obj is neither a stub nor a most recent revision: - completely ignore obj->h_revision */ - - obj = (gcptr)obj->h_revision; - assert(obj->h_tid & GCFLAG_PUBLIC); - prev_obj->h_revision = (revision_t)obj; - } - *pobj = obj; - goto restart; - } - else if (obj->h_tid & GCFLAG_VISITED) { - dprintf(("[already visited: %p]\n", obj)); - assert(obj == *pobj); - assert((obj->h_revision & 3) || /* either odd, or stub */ - (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); - return; /* already seen */ + if (!(obj->h_tid & GCFLAG_PUBLIC)) { + /* 'obj' is a private or protected copy. */ + visit_nonpublic(obj, visit_protected_gcp); } else { - assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED); - gcptr B = (gcptr)obj->h_revision; - assert(B->h_tid & (GCFLAG_PUBLIC | GCFLAG_BACKUP_COPY)); - - if (obj->h_original && (gcptr)obj->h_original != B) { - /* if B is original, it will be visited anyway */ - assert(obj->h_original == B->h_original); - assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)); - keep_original_alive(obj); - } - - obj->h_tid |= GCFLAG_VISITED; - B->h_tid |= GCFLAG_VISITED; - assert(!(obj->h_tid & GCFLAG_STUB)); - assert(!(B->h_tid & GCFLAG_STUB)); - gcptrlist_insert2(&objects_to_trace, obj, B); - - if (IS_POINTER(B->h_revision)) { - assert(B->h_tid & GCFLAG_PUBLIC); - assert(!(B->h_tid & GCFLAG_BACKUP_COPY)); - assert(!(B->h_revision & 2)); - - pobj = (gcptr *)&B->h_revision; - obj = *pobj; - goto restart; - } + *pobj = visit_public(obj, visit_protected_gcp); } } - -static void visit_keep(gcptr obj) +gcptr stmgcpage_visit(gcptr obj) { - if (!(obj->h_tid & GCFLAG_VISITED)) { - obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see fix_outdated() */ - obj->h_tid |= GCFLAG_VISITED; - gcptrlist_insert(&objects_to_trace, obj); - - if (IS_POINTER(obj->h_revision)) { - assert(!(obj->h_revision & 2)); - visit((gcptr *)&obj->h_revision); - } - keep_original_alive(obj); + if (!(obj->h_tid & GCFLAG_PUBLIC)) { + visit_nonpublic(obj, NULL); } + else { + obj = visit_public(obj, NULL); + } + return obj; } static void visit_all_objects(void) { while (gcptrlist_size(&objects_to_trace) > 0) { + visit_protected_gcp = + (struct tx_public_descriptor *)gcptrlist_pop(&objects_to_trace); gcptr obj = gcptrlist_pop(&objects_to_trace); - stmgc_trace(obj, &visit); + stmgc_trace(obj, &visit_take_protected); } + visit_protected_gcp = NULL; } static void mark_prebuilt_roots(void) @@ -371,18 +443,20 @@ /* Note about prebuilt roots: 'stm_prebuilt_gcroots' is a list that contains all the ones that have been modified. Because they are themselves not in any page managed by this file, their - GCFLAG_VISITED will not be removed at the end of the current - collection. This is fine because the base object cannot contain - references to the heap. So we decided to systematically set - GCFLAG_VISITED on prebuilt objects. */ + GCFLAG_VISITED is not removed at the end of the current + collection. That's why we remove it here. GCFLAG_MARKED is not + relevant for prebuilt objects, but we avoid objects with MARKED + but not VISITED, which trigger some asserts. */ gcptr *pobj = stm_prebuilt_gcroots.items; gcptr *pend = stm_prebuilt_gcroots.items + stm_prebuilt_gcroots.size; - gcptr obj; + gcptr obj, obj2; for (; pobj != pend; pobj++) { obj = *pobj; + obj->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED); assert(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL); - assert(IS_POINTER(obj->h_revision)); - visit((gcptr *)&obj->h_revision); + + obj2 = visit_public(obj, NULL); + assert(obj2 == obj); /* it is its own original */ } } @@ -396,7 +470,7 @@ if (((revision_t)item) & ~((revision_t)END_MARKER_OFF | (revision_t)END_MARKER_ON)) { /* 'item' is a regular, non-null pointer */ - visit(root); + visit_take_protected(root); dprintf(("visit stack root: %p -> %p\n", item, *root)); } else if (item == END_MARKER_OFF) { @@ -409,15 +483,19 @@ static void mark_all_stack_roots(void) { struct tx_descriptor *d; + struct GcPtrList new_public_to_private; + memset(&new_public_to_private, 0, sizeof(new_public_to_private)); + for (d = stm_tx_head; d; d = d->tx_next) { assert(!stm_has_got_any_lock(d)); + visit_protected_gcp = d->public_descriptor; /* the roots pushed on the shadowstack */ mark_roots(d->shadowstack, *d->shadowstack_end_ref); /* the thread-local object */ - visit(d->thread_local_obj_ref); - visit(&d->old_thread_local_obj); + visit_take_protected(d->thread_local_obj_ref); + visit_take_protected(&d->old_thread_local_obj); /* the current transaction's private copies of public objects */ wlog_t *item; @@ -427,37 +505,50 @@ gcptr R = item->addr; gcptr L = item->val; - /* Objects that were not visited yet must have the PUB_TO_PRIV - flag. Except if that transaction will abort anyway, then it - may be removed from a previous major collection that didn't - fix the PUB_TO_PRIV because the transaction was going to - abort anyway: - 1. minor_collect before major collect (R->L, R is outdated, abort) - 2. major collect removes flag - 3. major collect again, same thread, no time to abort - 4. flag still removed - */ - assert(IMPLIES(!(R->h_tid & GCFLAG_VISITED) && d->active > 0, - R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE)); - visit_keep(R); + /* we visit the public object R. Must keep a public object + here, so we pass NULL as second argument. */ + gcptr new_R = visit_public(R, NULL); + assert(new_R->h_tid & GCFLAG_PUBLIC); + + if (new_R != R) { + /* we have to update the key in public_to_private, which + can only be done by deleting the existing key and + (after the loop) re-inserting the new key. */ + G2L_LOOP_DELETE(item); + gcptrlist_insert2(&new_public_to_private, new_R, L); + } + + /* we visit the private copy L --- which at this point + should be private, possibly private_from_protected, + so visit() should return the same private copy */ if (L != NULL) { - /* minor collection found R->L in public_to_young - and R was modified. It then sets item->val to NULL and wants - to abort later. */ - revision_t v = L->h_revision; - visit_keep(L); - /* a bit of custom logic here: if L->h_revision used to - point exactly to R, as set by stealing, then we must - keep this property, even though visit_keep(L) might - decide it would be better to make it point to a more - recent copy. */ - if (v == (revision_t)R) { - assert(L->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED); - L->h_revision = v; /* restore */ - } + visit_nonpublic(L, visit_protected_gcp); } + } G2L_LOOP_END; + /* reinsert to real pub_to_priv */ + long i, size = new_public_to_private.size; + gcptr *items = new_public_to_private.items; + for (i = 0; i < size; i += 2) { + g2l_insert(&d->public_to_private, items[i], items[i + 1]); + } + gcptrlist_clear(&new_public_to_private); + + /* the current transaction's private copies of protected objects */ + items = d->private_from_protected.items; + for (i = d->private_from_protected.size - 1; i >= 0; i--) { + gcptr obj = items[i]; + assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED); + visit_nonpublic(obj, visit_protected_gcp); + + gcptr backup_obj = (gcptr)obj->h_revision; + if (!(backup_obj->h_tid & GCFLAG_PUBLIC)) + visit_nonpublic(backup_obj, visit_protected_gcp); + else + obj->h_revision = (revision_t)visit_public(backup_obj, NULL); + } + /* make sure that the other lists are empty */ assert(gcptrlist_size(&d->public_with_young_copy) == 0); assert(gcptrlist_size(&d->public_descriptor->stolen_objects) == 0); @@ -473,27 +564,16 @@ assert(gcptrlist_size(&d->private_from_protected) == d->num_private_from_protected_known_old); } + + visit_protected_gcp = NULL; + gcptrlist_delete(&new_public_to_private); } static void cleanup_for_thread(struct tx_descriptor *d) { long i; gcptr *items; - - /* It can occur that 'private_from_protected' contains an object that - * has not been visited at all (maybe only in inevitable - * transactions). - */ - items = d->private_from_protected.items; - for (i = d->private_from_protected.size - 1; i >= 0; i--) { - gcptr obj = items[i]; - assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED); - - if (!(obj->h_tid & GCFLAG_VISITED)) { - /* forget 'obj' */ - items[i] = items[--d->private_from_protected.size]; - } - } + assert(d->old_objects_to_trace.size == 0); /* If we're aborting this transaction anyway, we don't need to do * more here. @@ -516,21 +596,29 @@ items = d->list_of_read_objects.items; for (i = d->list_of_read_objects.size - 1; i >= 0; --i) { gcptr obj = items[i]; - assert(!(obj->h_tid & GCFLAG_STUB)); - /* Warning: in case the object listed is outdated and has been - replaced with a more recent revision, then it might be the - case that obj->h_revision doesn't have GCFLAG_VISITED, but - just removing it is very wrong --- we want 'd' to abort. - */ - if (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) { + if (obj->h_tid & GCFLAG_MOVED) { + assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); + assert(IS_POINTER(obj->h_original)); + obj = (gcptr)obj->h_original; + items[i] = obj; + } + else if (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) { + /* Warning: in case the object listed is outdated and has been + replaced with a more recent revision, then it might be the + case that obj->h_revision doesn't have GCFLAG_VISITED, but + just removing it is very wrong --- we want 'd' to abort. + */ /* follow obj to its backup */ assert(IS_POINTER(obj->h_revision)); obj = (gcptr)obj->h_revision; + + /* the backup-ptr should already be updated: */ + assert(!(obj->h_tid & GCFLAG_MOVED)); } revision_t v = obj->h_revision; - if (IS_POINTER(v)) { + if ((obj->h_tid & GCFLAG_STUB) || IS_POINTER(v)) { /* has a more recent revision. Oups. */ dprintf(("ABRT_COLLECT_MAJOR %p: " "%p was read but modified already\n", d, obj)); @@ -572,7 +660,7 @@ G2L_LOOP_FORWARD(d->public_to_private, item) { assert(item->addr->h_tid & GCFLAG_VISITED); assert(item->val->h_tid & GCFLAG_VISITED); - + assert(!(item->addr->h_tid & GCFLAG_MOVED)); assert(item->addr->h_tid & GCFLAG_PUBLIC); /* assert(is_private(item->val)); but in the other thread, which becomes: */ @@ -611,7 +699,9 @@ and the flag is removed; other locations are marked as free. */ p = (gcptr)(lpage + 1); for (j = 0; j < objs_per_page; j++) { - if (p->h_tid & GCFLAG_VISITED) + assert(IMPLIES(p->h_tid & GCFLAG_VISITED, + p->h_tid & GCFLAG_MARKED)); + if (p->h_tid & GCFLAG_MARKED) break; /* first object that stays alive */ p = (gcptr)(((char *)p) + obj_size); } @@ -621,8 +711,10 @@ surviving_pages = lpage; p = (gcptr)(lpage + 1); for (j = 0; j < objs_per_page; j++) { - if (p->h_tid & GCFLAG_VISITED) { - p->h_tid &= ~GCFLAG_VISITED; + assert(IMPLIES(p->h_tid & GCFLAG_VISITED, + p->h_tid & GCFLAG_MARKED)); + if (p->h_tid & GCFLAG_MARKED) { + p->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED); mc_total_in_use += obj_size; } else { @@ -648,6 +740,7 @@ p = (gcptr)(lpage + 1); for (j = 0; j < objs_per_page; j++) { assert(!(p->h_tid & GCFLAG_VISITED)); + assert(!(p->h_tid & GCFLAG_MARKED)); if (p->h_tid != DEBUG_WORD(0xDD)) { dprintf(("| freeing %p (with page %p)\n", p, lpage)); } @@ -677,8 +770,10 @@ G2L_LOOP_FORWARD(gcp->nonsmall_objects, item) { gcptr p = item->addr; - if (p->h_tid & GCFLAG_VISITED) { - p->h_tid &= ~GCFLAG_VISITED; + assert(IMPLIES(p->h_tid & GCFLAG_VISITED, + p->h_tid & GCFLAG_MARKED)); + if (p->h_tid & GCFLAG_MARKED) { + p->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED); } else { G2L_LOOP_DELETE(item); diff --git a/rpython/translator/stm/src_stm/lists.c b/rpython/translator/stm/src_stm/lists.c --- a/rpython/translator/stm/src_stm/lists.c +++ b/rpython/translator/stm/src_stm/lists.c @@ -19,7 +19,7 @@ void g2l_delete(struct G2L *g2l) { - free(g2l->raw_start); + stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start); memset(g2l, 0, sizeof(struct G2L)); } @@ -57,7 +57,7 @@ long alloc = g2l->raw_end - g2l->raw_start; long newalloc = (alloc + extra + (alloc >> 2) + 31) & ~15; //fprintf(stderr, "growth: %ld\n", newalloc); - char *newitems = malloc(newalloc); + char *newitems = stm_malloc(newalloc); newg2l.raw_start = newitems; newg2l.raw_current = newitems; newg2l.raw_end = newitems + newalloc; @@ -66,7 +66,7 @@ { g2l_insert(&newg2l, item->addr, item->val); } G2L_LOOP_END; - free(g2l->raw_start); + stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start); *g2l = newg2l; } @@ -152,7 +152,7 @@ //fprintf(stderr, "list %p deleted (%ld KB)\n", //gcptrlist, gcptrlist->alloc * sizeof(gcptr) / 1024); gcptrlist->size = 0; - free(gcptrlist->items); + stm_free(gcptrlist->items, gcptrlist->alloc * sizeof(gcptr)); gcptrlist->items = NULL; gcptrlist->alloc = 0; } @@ -163,7 +163,8 @@ return; size_t nsize = gcptrlist->size * sizeof(gcptr); - gcptr *newitems = realloc(gcptrlist->items, nsize); + gcptr *newitems = stm_realloc(gcptrlist->items, nsize, + gcptrlist->alloc * sizeof(gcptr)); if (newitems != NULL || nsize == 0) { gcptrlist->items = newitems; @@ -178,11 +179,11 @@ //fprintf(stderr, "list %p growth to %ld items (%ld KB)\n", // gcptrlist, newalloc, newalloc * sizeof(gcptr) / 1024); - gcptr *newitems = malloc(newalloc * sizeof(gcptr)); + gcptr *newitems = stm_malloc(newalloc * sizeof(gcptr)); long i; for (i=0; i<gcptrlist->size; i++) newitems[i] = gcptrlist->items[i]; - free(gcptrlist->items); + stm_free(gcptrlist->items, gcptrlist->alloc * sizeof(gcptr)); gcptrlist->items = newitems; gcptrlist->alloc = newalloc; } diff --git a/rpython/translator/stm/src_stm/lists.h b/rpython/translator/stm/src_stm/lists.h --- a/rpython/translator/stm/src_stm/lists.h +++ b/rpython/translator/stm/src_stm/lists.h @@ -2,6 +2,8 @@ #ifndef _SRCSTM_LISTS_H #define _SRCSTM_LISTS_H +#include "dbgmem.h" + /************************************************************/ /* The g2l_xx functions ("global_to_local") are implemented as a tree, @@ -37,7 +39,7 @@ void g2l_clear(struct G2L *g2l); void g2l_delete(struct G2L *g2l); static inline void g2l_delete_not_used_any_more(struct G2L *g2l) { - free(g2l->raw_start); + stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start); } static inline int g2l_any_entry(struct G2L *g2l) { diff --git a/rpython/translator/stm/src_stm/nursery.c b/rpython/translator/stm/src_stm/nursery.c --- a/rpython/translator/stm/src_stm/nursery.c +++ b/rpython/translator/stm/src_stm/nursery.c @@ -137,7 +137,7 @@ static inline gcptr create_old_object_copy(gcptr obj) { assert(!(obj->h_tid & GCFLAG_PUBLIC)); - assert(!(obj->h_tid & GCFLAG_NURSERY_MOVED)); + assert(!(obj->h_tid & GCFLAG_MOVED)); assert(!(obj->h_tid & GCFLAG_VISITED)); assert(!(obj->h_tid & GCFLAG_WRITE_BARRIER)); assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)); @@ -160,9 +160,9 @@ } else { /* it's a nursery object. Was it already moved? */ - if (UNLIKELY(obj->h_tid & GCFLAG_NURSERY_MOVED)) { + if (UNLIKELY(obj->h_tid & GCFLAG_MOVED)) { /* yes. Such an object can be a public object in the nursery - too (such objects are always NURSERY_MOVED). For all cases, + too (such objects are always MOVED). For all cases, we can just fix the ref. Can be stolen objects or those we already moved. */ @@ -183,7 +183,7 @@ fresh_old_copy = create_old_object_copy(obj); } - obj->h_tid |= GCFLAG_NURSERY_MOVED; + obj->h_tid |= GCFLAG_MOVED; obj->h_revision = (revision_t)fresh_old_copy; /* fix the original reference */ @@ -233,8 +233,23 @@ assert(items[i]->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED); assert(IS_POINTER(items[i]->h_revision)); + /* if items[i] is young, move it, update the pointer, and + schedule the object for later consideration by + visit_all_outside_objects() (which will for example ensure + that the WRITE_BARRIER flag is added to it). + */ visit_if_young(&items[i]); + /* the backup copy is always allocated outside the nursery, + but we have to trace it as well, as it may contain its own + young pointers. + + but only once: if the transaction was running for long enough + to have num_private_from_protected_known_old > 0, then the + backup copies of known-old objects have already been traced + in a previous minor collection, and as they are read-only, + they cannot contain young pointers any more. + */ stmgc_trace((gcptr)items[i]->h_revision, &visit_if_young); } @@ -386,13 +401,13 @@ /* non-young or visited young objects are kept */ continue; } - else if (obj->h_tid & GCFLAG_NURSERY_MOVED) { + else if (obj->h_tid & GCFLAG_MOVED) { /* visited nursery objects are kept and updated */ items[i] = (gcptr)obj->h_revision; assert(!(items[i]->h_tid & GCFLAG_STUB)); continue; } - /* Sanity check: a nursery object without the NURSERY_MOVED flag + /* Sanity check: a nursery object without the MOVED flag is necessarily a private-without-backup object, or a protected object; it cannot be a public object. */ assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); @@ -432,7 +447,7 @@ setup_minor_collect(d); /* first do this, which asserts that some objects are private --- - which fails if they have already been GCFLAG_NURSERY_MOVED */ + which fails if they have already been GCFLAG_MOVED */ mark_public_to_young(d); mark_young_roots(d); diff --git a/rpython/translator/stm/src_stm/revision b/rpython/translator/stm/src_stm/revision --- a/rpython/translator/stm/src_stm/revision +++ b/rpython/translator/stm/src_stm/revision @@ -1,1 +1,1 @@ -4cad3aa5a20b +c528da482152 diff --git a/rpython/translator/stm/src_stm/steal.c b/rpython/translator/stm/src_stm/steal.c --- a/rpython/translator/stm/src_stm/steal.c +++ b/rpython/translator/stm/src_stm/steal.c @@ -31,7 +31,7 @@ assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); if (obj->h_tid & GCFLAG_PUBLIC) { /* young public, replace with stolen old copy */ - assert(obj->h_tid & GCFLAG_NURSERY_MOVED); + assert(obj->h_tid & GCFLAG_MOVED); assert(IS_POINTER(obj->h_revision)); stub = (gcptr)obj->h_revision; assert(!IS_POINTER(stub->h_revision)); /* not outdated */ @@ -56,7 +56,7 @@ if (!(obj->h_original)) obj->h_original = (revision_t)O; } - obj->h_tid |= (GCFLAG_NURSERY_MOVED | GCFLAG_PUBLIC); + obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC); obj->h_revision = (revision_t)O; O->h_tid |= GCFLAG_PUBLIC; @@ -105,6 +105,8 @@ stub->h_tid = (obj->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC | GCFLAG_STUB | GCFLAG_OLD; + if (size == 0) + stub->h_tid |= GCFLAG_SMALLSTUB; stub->h_revision = ((revision_t)obj) | 2; if (obj->h_original) { stub->h_original = obj->h_original; @@ -206,7 +208,7 @@ /* note that we should follow h_revision at least one more step: it is necessary if L is public but young (and then - has GCFLAG_NURSERY_MOVED), but it is fine to do it more + has GCFLAG_MOVED), but it is fine to do it more generally. */ v = ACCESS_ONCE(L->h_revision); if (IS_POINTER(v)) { @@ -239,7 +241,7 @@ } L->h_revision = (revision_t)O; - L->h_tid |= GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED; + L->h_tid |= GCFLAG_PUBLIC | GCFLAG_MOVED; /* subtle: we need to remove L from the fxcache of the target thread, otherwise its read barrier might not trigger on it. It is mostly fine because it is anyway identical to O. But diff --git a/rpython/translator/stm/src_stm/stmgc.h b/rpython/translator/stm/src_stm/stmgc.h --- a/rpython/translator/stm/src_stm/stmgc.h +++ b/rpython/translator/stm/src_stm/stmgc.h @@ -24,7 +24,8 @@ #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 + 4 + 8)) +#define PREBUILT_FLAGS (STM_FIRST_GCFLAG * ((1<<0) | (1<<1) | \ + (1<<2) | (1<<3) | (1<<13))) #define PREBUILT_REVISION 1 diff --git a/rpython/translator/stm/src_stm/stmsync.c b/rpython/translator/stm/src_stm/stmsync.c --- a/rpython/translator/stm/src_stm/stmsync.c +++ b/rpython/translator/stm/src_stm/stmsync.c @@ -53,7 +53,7 @@ static void init_shadowstack(void) { struct tx_descriptor *d = thread_descriptor; - d->shadowstack = malloc(sizeof(gcptr) * LENGTH_SHADOW_STACK); + d->shadowstack = stm_malloc(sizeof(gcptr) * LENGTH_SHADOW_STACK); if (!d->shadowstack) { stm_fatalerror("out of memory: shadowstack\n"); } @@ -69,7 +69,7 @@ assert(x == END_MARKER_ON); assert(stm_shadowstack == d->shadowstack); stm_shadowstack = NULL; - free(d->shadowstack); + stm_free(d->shadowstack, sizeof(gcptr) * LENGTH_SHADOW_STACK); } void stm_set_max_aborts(int max_aborts) diff --git a/rpython/translator/stm/src_stm/weakref.c b/rpython/translator/stm/src_stm/weakref.c --- a/rpython/translator/stm/src_stm/weakref.c +++ b/rpython/translator/stm/src_stm/weakref.c @@ -28,7 +28,7 @@ */ while (gcptrlist_size(&d->young_weakrefs) > 0) { gcptr weakref = gcptrlist_pop(&d->young_weakrefs); - if (!(weakref->h_tid & GCFLAG_NURSERY_MOVED)) + if (!(weakref->h_tid & GCFLAG_MOVED)) continue; /* the weakref itself dies */ weakref = (gcptr)weakref->h_revision; @@ -37,7 +37,7 @@ assert(pointing_to != NULL); if (stmgc_is_in_nursery(d, pointing_to)) { - if (pointing_to->h_tid & GCFLAG_NURSERY_MOVED) { + if (pointing_to->h_tid & GCFLAG_MOVED) { dprintf(("weakref ptr moved %p->%p\n", WEAKREF_PTR(weakref, size), (gcptr)pointing_to->h_revision)); @@ -69,49 +69,25 @@ static _Bool is_partially_visited(gcptr obj) { - /* Based on gcpage.c:visit(). Check the code here if we simplify - visit(). Returns True or False depending on whether we find any - version of 'obj' to be VISITED or not. + /* Based on gcpage.c:visit_public(). Check the code here if we change + visit_public(). Returns True or False depending on whether we find any + version of 'obj' to be MARKED or not. */ - restart: - if (obj->h_tid & GCFLAG_VISITED) + assert(IMPLIES(obj->h_tid & GCFLAG_VISITED, + obj->h_tid & GCFLAG_MARKED)); + if (obj->h_tid & GCFLAG_MARKED) return 1; - if (obj->h_revision & 1) { - assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); - assert(!(obj->h_tid & GCFLAG_STUB)); + if (!(obj->h_tid & GCFLAG_PUBLIC)) return 0; - } - else if (obj->h_tid & GCFLAG_PUBLIC) { - /* h_revision is a ptr: we have a more recent version */ - if (!(obj->h_revision & 2)) { - /* go visit the more recent version */ - obj = (gcptr)obj->h_revision; - } - else { - /* it's a stub */ - assert(obj->h_tid & GCFLAG_STUB); - obj = (gcptr)(obj->h_revision - 2); - } - goto restart; - } - else { - assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED); - gcptr B = (gcptr)obj->h_revision; - assert(B->h_tid & (GCFLAG_PUBLIC | GCFLAG_BACKUP_COPY)); - if (B->h_tid & GCFLAG_VISITED) + + if (obj->h_original != 0 && + !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) { + gcptr original = (gcptr)obj->h_original; + assert(IMPLIES(original->h_tid & GCFLAG_VISITED, + original->h_tid & GCFLAG_MARKED)); + if (original->h_tid & GCFLAG_MARKED) return 1; - assert(!(obj->h_tid & GCFLAG_STUB)); - assert(!(B->h_tid & GCFLAG_STUB)); - - if (IS_POINTER(B->h_revision)) { - assert(B->h_tid & GCFLAG_PUBLIC); - assert(!(B->h_tid & GCFLAG_BACKUP_COPY)); - assert(!(B->h_revision & 2)); - - obj = (gcptr)B->h_revision; - goto restart; - } } return 0; } diff --git a/rpython/translator/stm/test/test_ztranslated.py b/rpython/translator/stm/test/test_ztranslated.py --- a/rpython/translator/stm/test/test_ztranslated.py +++ b/rpython/translator/stm/test/test_ztranslated.py @@ -1,5 +1,5 @@ import py -from rpython.rlib import rstm, rgc +from rpython.rlib import rstm, rgc, objectmodel from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rclass from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rtyper.annlowlevel import cast_instance_to_base_ptr @@ -276,3 +276,30 @@ t, cbuilder = self.compile(main) data = cbuilder.cmdexec('a b') assert 'li102ee10:hi there 3e\n0\n' in data + + def test_weakref(self): + import weakref + class Foo(object): + pass + + def f(argv): + foo = Foo() + foo.n = argv + w = weakref.ref(foo) + assert w() is foo + objectmodel.keepalive_until_here(foo) + return w + f._dont_inline_ = True + + def main(argv): + w = f(argv) + assert w() is not None + assert len(w().n) == len(argv) + rgc.collect() + assert w() is None + print 'test ok' + return 0 + + t, cbuilder = self.compile(main) + data = cbuilder.cmdexec('a b') + assert 'test ok\n' in data _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit