Author: Remi Meier <remi.me...@gmail.com> Branch: copy-over-original2 Changeset: r427:b39faaf63e68 Date: 2013-07-19 16:15 +0200 http://bitbucket.org/pypy/stmgc/changeset/b39faaf63e68/
Log: merge weakrefs 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 extra.h dbgmem.h fprintcolor.h + stmsync.h extra.h weakref.h dbgmem.h fprintcolor.h C_FILES = et.c lists.c steal.c nursery.c gcpage.c \ - stmsync.c extra.c dbgmem.c fprintcolor.c + stmsync.c extra.c weakref.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/demo_random.c b/c4/demo_random.c --- a/c4/demo_random.c +++ b/c4/demo_random.c @@ -25,27 +25,46 @@ // SUPPORT #define GCTID_STRUCT_NODE 123 +#define GCTID_WEAKREF 122 + +struct node; +typedef struct node * nodeptr; +struct weak_node { + struct stm_object_s hdr; + nodeptr node; +}; +typedef struct weak_node * weaknodeptr; +#define WEAKNODE_SIZE sizeof(struct weak_node) struct node { struct stm_object_s hdr; long value; revision_t id; revision_t hash; - struct node *next; + nodeptr next; + weaknodeptr weakref; }; -typedef struct node * nodeptr; + + size_t stmcb_size(gcptr ob) { - assert(stm_get_tid(ob) == GCTID_STRUCT_NODE); - return sizeof(struct node); + if (stm_get_tid(ob) == GCTID_STRUCT_NODE) + return sizeof(struct node); + else if (stm_get_tid(ob) == GCTID_WEAKREF) + return WEAKNODE_SIZE; + assert(0); } + void stmcb_trace(gcptr ob, void visit(gcptr *)) { nodeptr n; + if (stm_get_tid(ob) == GCTID_WEAKREF) + return; assert(stm_get_tid(ob) == GCTID_STRUCT_NODE); n = (nodeptr)ob; visit((gcptr *)&n->next); + visit((gcptr *)&n->weakref); } @@ -53,11 +72,6 @@ time_t default_seed; gcptr shared_roots[SHARED_ROOTS]; -#define CACHE_MASK 65535 -#define CACHE_ENTRIES ((CACHE_MASK + 1) / sizeof(char *)) -#define CACHE_AT(cache, obj) (*(gcptr *)((char *)(cache) \ - + ((revision_t)(obj) & CACHE_MASK))) - struct thread_data { unsigned int thread_seed; gcptr roots[MAXROOTS]; @@ -67,7 +81,6 @@ int steps_left; int interruptible; int atomic; - revision_t writeable[CACHE_ENTRIES]; }; __thread struct thread_data td; @@ -105,6 +118,21 @@ return (int)(rand_r(&td.thread_seed) % (unsigned int)max); } +gcptr get_random_root() +{ + int num = get_rand(td.num_roots + 1); + if (num == 0) + return stm_thread_local_obj; + else + return td.roots[num - 1]; +} + +gcptr get_random_shared_root() +{ + int num = get_rand(SHARED_ROOTS); + return shared_roots[num]; +} + void copy_roots(gcptr *from, gcptr *to, int num) { int i; @@ -137,7 +165,7 @@ return x; } -void push_roots(int with_cache) +void push_roots() { int i; for (i = 0; i < td.num_roots; i++) { @@ -145,30 +173,11 @@ if (td.roots[i]) stm_push_root(td.roots[i]); } - - if (with_cache) { - stm_push_root(NULL); - for (i = 0; i < CACHE_ENTRIES; i++) { - if (td.writeable[i]) - stm_push_root((gcptr)td.writeable[i]); - } - } } -void pop_roots(int with_cache) +void pop_roots() { int i; - /* some objects may have changed positions */ - memset(td.writeable, 0, sizeof(td.writeable)); - - if (with_cache) { - gcptr obj = stm_pop_root(); - while (obj) { - CACHE_AT(td.writeable, obj) = obj; - obj = stm_pop_root(); - } - } - for (i = td.num_roots - 1; i >= 0; i--) { if (td.roots[i]) td.roots[i] = stm_pop_root(); @@ -186,12 +195,33 @@ nodeptr allocate_node() { nodeptr r; - push_roots(1); + push_roots(); r = (nodeptr)stm_allocate(sizeof(struct node), GCTID_STRUCT_NODE); - pop_roots(1); + pop_roots(); return r; } + +weaknodeptr allocate_weaknodeptr(nodeptr to) +{ + weaknodeptr w; + push_roots(); + w = (weaknodeptr)stm_weakref_allocate(WEAKNODE_SIZE, GCTID_WEAKREF, + (gcptr)to); + pop_roots(); + return w; +} + +void set_weakref(nodeptr n, nodeptr to) +{ + stm_push_root((gcptr)n); + weaknodeptr w = allocate_weaknodeptr(to); + n = (nodeptr)stm_pop_root(); + n = (nodeptr)stm_write_barrier((gcptr)n); + n->weakref = w; + dprintf(("set_weakref %p -> %p -> %p\n", n, w, to)); +} + int is_shared_prebuilt(gcptr p) { int i; @@ -252,7 +282,6 @@ if (p != NULL) { check(p); w = stm_write_barrier(p); - CACHE_AT(td.writeable, w) = w; check(w); assert(is_private(w)); } @@ -369,22 +398,22 @@ { int k = get_rand(100); if (k < 10) { - push_roots(1); + push_roots(); stm_push_root(p); stm_become_inevitable("fun"); p = stm_pop_root(); - pop_roots(1); + pop_roots(); } else if (k < 40) { - push_roots(1); + push_roots(); stmgc_minor_collect(); - pop_roots(1); + pop_roots(); p = NULL; } else if (k < 41 && DO_MAJOR_COLLECTS) { fprintf(stdout, "major collect\n"); - push_roots(1); + push_roots(); stmgcpage_possibly_major_collect(1); - pop_roots(1); + pop_roots(); p = NULL; } return p; @@ -423,10 +452,7 @@ break; case 7: // set 'p' as *next in one of the roots check(_r); - if (CACHE_AT(td.writeable, _r) == _r) - w_r = (nodeptr)_r; - else - w_r = (nodeptr)write_barrier(_r); + w_r = (nodeptr)write_barrier(_r); check((gcptr)w_r); check(p); w_r->next = (struct node*)p; @@ -447,6 +473,46 @@ return p; } +gcptr weakref_events(gcptr p, gcptr _r, gcptr _sr) +{ + nodeptr t; + weaknodeptr w, ww; + gcptr ptrs[] = {_r, _sr}; + + int i = get_rand(2); + int k = get_rand(3); + switch (k) { + case 0: // check weakref + t = (nodeptr)read_barrier(ptrs[i]); + w = t->weakref; + if(w) { + ww = (weaknodeptr)stm_read_barrier((gcptr)w); + assert(stm_get_tid((gcptr)ww) == GCTID_WEAKREF); + if (ww->node) { + check((gcptr)ww->node); + } + else { + t->weakref = NULL; + } + } + p = NULL; + break; + case 1: // set weakref to something + if (p) + set_weakref((nodeptr)_r, (nodeptr)p); + else + set_weakref((nodeptr)_r, (nodeptr)get_random_root()); + p = NULL; + break; + case 2: // set weakref on shared roots + set_weakref((nodeptr)_sr, (nodeptr)get_random_shared_root()); + p = NULL; + break; + } + return p; +} + + gcptr shared_roots_events(gcptr p, gcptr _r, gcptr _sr) { nodeptr w_sr; @@ -461,7 +527,7 @@ break; case 2: w_sr = (nodeptr)write_barrier(_sr); - w_sr->next = (nodeptr)shared_roots[get_rand(SHARED_ROOTS)]; + w_sr->next = (nodeptr)get_random_shared_root(); break; } return p; @@ -485,10 +551,7 @@ assert(w_t->id == stm_id((gcptr)_t)); } else { - if (CACHE_AT(td.writeable, _t) == _t) - w_t = (nodeptr)_t; - else - w_t = (nodeptr)write_barrier(_t); + w_t = (nodeptr)write_barrier(_t); w_t->id = stm_id((gcptr)w_t); assert(w_t->id == stm_id((gcptr)_t)); } @@ -504,10 +567,7 @@ assert(w_t->hash == stm_hash((gcptr)_t)); } else { - if (CACHE_AT(td.writeable, _t) == _t) - w_t = (nodeptr)_t; - else - w_t = (nodeptr)write_barrier(_t); + w_t = (nodeptr)write_barrier(_t); w_t->hash = stm_hash((gcptr)w_t); assert(w_t->hash == stm_hash((gcptr)_t)); } @@ -526,18 +586,12 @@ gcptr do_step(gcptr p) { gcptr _r, _sr; - int num, k; + int k; - num = get_rand(td.num_roots+1); - if (num == 0) - _r = stm_thread_local_obj; - else - _r = td.roots[num - 1]; - - num = get_rand(SHARED_ROOTS); - _sr = shared_roots[num]; + _r = get_random_root(); + _sr = get_random_shared_root(); - k = get_rand(9); + k = get_rand(11); check(p); assert(thread_descriptor->active); @@ -549,6 +603,8 @@ p = id_hash_events(p, _r, _sr); else if (k < 8) p = rare_events(p, _r, _sr); + else if (k < 10) + p = weakref_events(p, _r, _sr); else if (get_rand(20) == 1) { // transaction break fprintf(stdout, "|"); @@ -563,7 +619,7 @@ void transaction_break() { - push_roots(0); + push_roots(); td.interruptible = 1; copy_roots(td.roots, td.roots_outside_perform, td.num_roots); @@ -575,9 +631,7 @@ copy_roots(td.roots_outside_perform, td.roots, td.num_roots); td.interruptible = 0; - pop_roots(0); - - /* done by pop_roots() memset(&td.writeable, 0, sizeof(td.writeable)); */ + pop_roots(); } @@ -592,8 +646,8 @@ assert(end_marker == END_MARKER_ON || end_marker == END_MARKER_OFF); arg1 = stm_pop_root(); assert(arg1 == NULL); - pop_roots(0); - push_roots(0); + pop_roots(); + push_roots(); stm_push_root(arg1); stm_push_root(end_marker); @@ -609,9 +663,6 @@ { gcptr p = NULL; - // clear cache of writeables: - memset(&td.writeable, 0, sizeof(td.writeable)); - while (td.steps_left-->0 || td.atomic) { if (td.steps_left % 8 == 0) fprintf(stdout, "#"); diff --git a/c4/et.c b/c4/et.c --- a/c4/et.c +++ b/c4/et.c @@ -6,6 +6,29 @@ */ #include "stmimpl.h" +#ifdef _GC_DEBUG +char tmp_buf[128]; +char* stm_dbg_get_hdr_str(gcptr obj) +{ + char *cur; + char *flags[] = GC_FLAG_NAMES; + int i; + + i = 0; + cur = tmp_buf; + cur += sprintf(cur, "%p:", obj); + while (flags[i]) { + if (obj->h_tid & (STM_FIRST_GCFLAG << i)) { + cur += sprintf(cur, "%s|", flags[i]); + } + i++; + } + cur += sprintf(cur, "tid=%ld", stm_get_tid(obj)); + return tmp_buf; +} +#endif + + __thread struct tx_descriptor *thread_descriptor = NULL; @@ -545,6 +568,7 @@ gcptr stm_WriteBarrier(gcptr P) { + assert(!(P->h_tid & GCFLAG_IMMUTABLE)); if (is_private(P)) { /* If we have GCFLAG_WRITE_BARRIER in P, then list it into diff --git a/c4/et.h b/c4/et.h --- a/c4/et.h +++ b/c4/et.h @@ -72,6 +72,8 @@ 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; + /* this value must be reflected in PREBUILT_FLAGS in stmgc.h */ #define GCFLAG_PREBUILT (GCFLAG_VISITED | \ @@ -89,6 +91,8 @@ "BACKUP_COPY", \ "STUB", \ "PRIVATE_FROM_PROTECTED", \ + "HAS_ID", \ + "IMMUTABLE", \ NULL } #define IS_POINTER(v) (!((v) & 1)) /* even-valued number */ @@ -196,4 +200,7 @@ void DescriptorInit(void); void DescriptorDone(void); +#ifdef _GC_DEBUG +char* stm_dbg_get_hdr_str(gcptr obj); +#endif #endif /* _ET_H */ diff --git a/c4/extra.c b/c4/extra.c --- a/c4/extra.c +++ b/c4/extra.c @@ -3,7 +3,7 @@ void stm_copy_to_old_id_copy(gcptr obj, gcptr id) { - //assert(!is_in_nursery(thread_descriptor, id)); + //assert(!stmgc_is_in_nursery(thread_descriptor, id)); assert(id->h_tid & GCFLAG_OLD); size_t size = stmgc_size(obj); diff --git a/c4/gcpage.c b/c4/gcpage.c --- a/c4/gcpage.c +++ b/c4/gcpage.c @@ -222,12 +222,14 @@ if (!(id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL)) { id_copy->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see fix_outdated() */ - id_copy->h_tid |= GCFLAG_VISITED; - assert(!(id_copy->h_tid & GCFLAG_MOVED)); + if (!(id_copy->h_tid & GCFLAG_VISITED)) { + id_copy->h_tid |= GCFLAG_VISITED; + assert(!(id_copy->h_tid & GCFLAG_MOVED)); - /* XXX: may not always need tracing? */ - if (!(id_copy->h_tid & GCFLAG_STUB)) - gcptrlist_insert(&objects_to_trace, id_copy); + /* 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 @@ -237,6 +239,14 @@ } } +static void visit(gcptr *pobj); + +gcptr stmgcpage_visit(gcptr obj) +{ + visit(&obj); + return obj; +} + static gcptr copy_over_original(gcptr obj) { assert(!(obj->h_tid & GCFLAG_VISITED)); @@ -346,10 +356,10 @@ keep_original_alive(prev_obj); assert(*pobj == prev_obj); - gcptr obj1 = obj; - visit(&obj1); /* recursion, but should be only once */ + /* recursion, but should be only once */ + obj = stmgcpage_visit(obj); assert(prev_obj->h_tid & GCFLAG_STUB); - prev_obj->h_revision = ((revision_t)obj1) | 2; + prev_obj->h_revision = ((revision_t)obj) | 2; return; } } @@ -481,8 +491,6 @@ static void mark_all_stack_roots(void) { - int i; - gcptr *items; struct tx_descriptor *d; struct G2L new_public_to_private; memset(&new_public_to_private, 0, sizeof(struct G2L)); @@ -493,15 +501,6 @@ /* the roots pushed on the shadowstack */ mark_roots(d->shadowstack, *d->shadowstack_end_ref); - /* some roots (^^^) can also be in this list, and - we may have a stolen priv_from_prot in here that, - when visited, resolves to its backup (or further) */ - items = d->old_objects_to_trace.items; - for (i = d->old_objects_to_trace.size - 1; i >= 0; i--) { - visit(&items[i]); - gcptrlist_insert(&objects_to_trace, items[i]); - } - /* the thread-local object */ visit(d->thread_local_obj_ref); visit(&d->old_thread_local_obj); @@ -570,11 +569,11 @@ assert(gcptrlist_size(&d->public_with_young_copy) == 0); assert(gcptrlist_size(&d->public_descriptor->stolen_objects) == 0); assert(gcptrlist_size(&d->public_descriptor->stolen_young_stubs) == 0); + assert(gcptrlist_size(&d->old_objects_to_trace) == 0); /* NOT NECESSARILY EMPTY: - list_of_read_objects - private_from_protected - public_to_private - - old_objects_to_trace */ assert(gcptrlist_size(&d->list_of_read_objects) == d->num_read_objects_known_old); @@ -608,23 +607,20 @@ } } - - /* we visit old_objects_to_trace during marking and thus, they - should be up-to-date */ -#ifdef _GC_DEBUG - items = d->old_objects_to_trace.items; - for (i = d->old_objects_to_trace.size - 1; i >= 0; i--) { - gcptr obj = items[i]; - assert(!(obj->h_tid & GCFLAG_MOVED)); - assert(obj->h_tid & GCFLAG_VISITED); - } -#endif + assert(d->old_objects_to_trace.size == 0); /* If we're aborting this transaction anyway, we don't need to do * more here. */ - if (d->active < 0) - return; /* already "aborted" during forced minor collection */ + if (d->active < 0) { + /* already "aborted" during forced minor collection + clear list of read objects so that a possible minor collection + before the abort doesn't trip + fix_list_of_read_objects should not run */ + gcptrlist_clear(&d->list_of_read_objects); + d->num_read_objects_known_old = 0; + return; + } if (d->active == 2) { /* inevitable transaction: clear the list of read objects */ @@ -661,6 +657,9 @@ dprintf(("ABRT_COLLECT_MAJOR %p: " "%p was read but modified already\n", d, obj)); AbortTransactionAfterCollect(d, ABRT_COLLECT_MAJOR); + /* fix_list_of_read_objects should not run */ + gcptrlist_clear(&d->list_of_read_objects); + d->num_read_objects_known_old = 0; return; } @@ -919,9 +918,13 @@ assert(gcptrlist_size(&objects_to_trace) == 0); mark_prebuilt_roots(); mark_all_stack_roots(); - visit_all_objects(); + do { + visit_all_objects(); + stm_visit_old_weakrefs(); + } while (gcptrlist_size(&objects_to_trace) != 0); gcptrlist_delete(&objects_to_trace); clean_up_lists_of_read_objects_and_fix_outdated_flags(); + stm_clean_old_weakrefs(); mc_total_in_use = mc_total_reserved = 0; free_all_unused_local_pages(); diff --git a/c4/gcpage.h b/c4/gcpage.h --- a/c4/gcpage.h +++ b/c4/gcpage.h @@ -45,7 +45,8 @@ /* These fields are in tx_public_descriptor rather than tx_descriptor. The indirection allows us to keep around the lists of pages even - after the thread finishes, until the next major collection. + after the thread finishes. Such a "zombie" tx_public_descriptor + is reused by the next thread that starts. */ #define GCPAGE_FIELDS_DECL \ /* The array 'pages_for_size' contains GC_SMALL_REQUESTS \ @@ -65,7 +66,10 @@ /* A set of all non-small objects (outside the nursery). \ We could also have a single global set, but this avoids \ locking in stmgcpage_malloc/free. */ \ - struct G2L nonsmall_objects; + struct G2L nonsmall_objects; \ + \ + /* Weakref support */ \ + struct GcPtrList old_weakrefs; #define LOCAL_GCPAGES() (thread_descriptor->public_descriptor) @@ -80,6 +84,7 @@ void stmgcpage_add_prebuilt_root(gcptr obj); void stmgcpage_possibly_major_collect(int force); long stmgcpage_count(int quantity); +gcptr stmgcpage_visit(gcptr); extern struct GcPtrList stm_prebuilt_gcroots; diff --git a/c4/nursery.c b/c4/nursery.c --- a/c4/nursery.c +++ b/c4/nursery.c @@ -1,7 +1,6 @@ #include "stmimpl.h" - -static int is_in_nursery(struct tx_descriptor *d, gcptr obj) +int stmgc_is_in_nursery(struct tx_descriptor *d, gcptr obj) { return (d->nursery_base <= (char*)obj && ((char*)obj) < d->nursery_end); } @@ -54,6 +53,7 @@ gcptrlist_delete(&d->old_objects_to_trace); gcptrlist_delete(&d->public_with_young_copy); + gcptrlist_delete(&d->young_weakrefs); } void stmgc_minor_collect_soon(void) @@ -100,6 +100,13 @@ return P; } +gcptr stm_allocate_immutable(size_t size, unsigned long tid) +{ + gcptr P = stm_allocate(size, tid); + P->h_tid |= GCFLAG_IMMUTABLE; + return P; +} + gcptr stmgc_duplicate(gcptr P) { size_t size = stmgc_size(P); @@ -125,9 +132,6 @@ } /************************************************************/ -/* list for private/protected, old roots that need to be - kept in old_objects_to_trace */ -static __thread struct GcPtrList private_or_protected_roots = {0, 0, NULL}; static inline gcptr create_old_object_copy(gcptr obj) { @@ -150,7 +154,7 @@ gcptr fresh_old_copy; struct tx_descriptor *d = thread_descriptor; - if (!is_in_nursery(d, obj)) { + if (!stmgc_is_in_nursery(d, obj)) { /* not a nursery object */ } else { @@ -207,22 +211,6 @@ (revision_t)END_MARKER_ON)) { /* 'item' is a regular, non-null pointer */ visit_if_young(end); - item = *end; - /* if private or protected, this object needs to be - traced again in the next minor_collect if it is - currently in old_objects_to_trace. Because then - it may be seen as write-ready in the view of - someone: - pw = write_barrier(); push_root(pw); - minor_collect(); pw = pop_root(); // pw still write-ready - */ - if (item - && !(item->h_tid & GCFLAG_WRITE_BARRIER) /* not set in - obj_to_trace*/ - && (item->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED - || item->h_revision == stm_private_rev_num)) { - gcptrlist_insert(&private_or_protected_roots, item); - } } else if (item != NULL) { if (item == END_MARKER_OFF) @@ -377,29 +365,12 @@ stmgc_trace(obj, &visit_if_young); } - - while (gcptrlist_size(&private_or_protected_roots) > 0) { - gcptr obj = gcptrlist_pop(&private_or_protected_roots); - /* if it has the write_barrier flag, clear it so that - it doesn't get inserted twice by a later write-barrier */ - if (obj->h_tid & GCFLAG_WRITE_BARRIER) { - /* only insert those that were in old_obj_to_trace - and that we didn't insert already */ - obj->h_tid &= ~GCFLAG_WRITE_BARRIER; - gcptrlist_insert(&d->old_objects_to_trace, obj); - dprintf(("re-add %p to old_objects_to_trace\n", obj)); - } - } } static void fix_list_of_read_objects(struct tx_descriptor *d) { long i, limit = d->num_read_objects_known_old; gcptr *items = d->list_of_read_objects.items; - - if (d->active < 0) - return; // aborts anyway - assert(d->list_of_read_objects.size >= limit); if (d->active == 2) { @@ -410,7 +381,7 @@ for (i = d->list_of_read_objects.size - 1; i >= limit; --i) { gcptr obj = items[i]; - if (!is_in_nursery(d, obj)) { + if (!stmgc_is_in_nursery(d, obj)) { /* non-young or visited young objects are kept */ continue; } @@ -442,8 +413,9 @@ static void teardown_minor_collect(struct tx_descriptor *d) { - //assert(gcptrlist_size(&d->old_objects_to_trace) == 0); + assert(gcptrlist_size(&d->old_objects_to_trace) == 0); assert(gcptrlist_size(&d->public_with_young_copy) == 0); + assert(gcptrlist_size(&d->young_weakrefs) == 0); assert(gcptrlist_size(&d->public_descriptor->stolen_objects) == 0); spinlock_release(d->public_descriptor->collection_lock); @@ -479,6 +451,8 @@ surviving young-but-outside-the-nursery objects have been flagged with GCFLAG_OLD */ + stm_move_young_weakrefs(d); + teardown_minor_collect(d); assert(!stm_has_got_any_lock(d)); @@ -545,9 +519,9 @@ !g2l_any_entry(&d->young_objects_outside_nursery)*/ ) { /* there is no young object */ assert(gcptrlist_size(&d->public_with_young_copy) == 0); - assert(IMPLIES(d->active > 0, - gcptrlist_size(&d->list_of_read_objects) >= - d->num_read_objects_known_old)); + assert(gcptrlist_size(&d->young_weakrefs) == 0); + assert(gcptrlist_size(&d->list_of_read_objects) >= + d->num_read_objects_known_old); assert(gcptrlist_size(&d->private_from_protected) >= d->num_private_from_protected_known_old); d->num_read_objects_known_old = diff --git a/c4/nursery.h b/c4/nursery.h --- a/c4/nursery.h +++ b/c4/nursery.h @@ -50,7 +50,10 @@ still in the same transaction, to know that the initial \ part of the lists cannot contain young objects any more. */ \ long num_private_from_protected_known_old; \ - long num_read_objects_known_old; + long num_read_objects_known_old; \ + \ + /* Weakref support */ \ + struct GcPtrList young_weakrefs; struct tx_descriptor; /* from et.h */ @@ -64,5 +67,6 @@ size_t stmgc_size(gcptr); void stmgc_trace(gcptr, void visit(gcptr *)); void stmgc_minor_collect_soon(void); +int stmgc_is_in_nursery(struct tx_descriptor *d, gcptr obj); #endif diff --git a/c4/steal.c b/c4/steal.c --- a/c4/steal.c +++ b/c4/steal.c @@ -23,9 +23,56 @@ { gcptr stub, obj = *pobj; if (obj == NULL || (obj->h_tid & (GCFLAG_PUBLIC | GCFLAG_OLD)) == - (GCFLAG_PUBLIC | GCFLAG_OLD)) + (GCFLAG_PUBLIC | GCFLAG_OLD)) return; + if (obj->h_tid & GCFLAG_IMMUTABLE) { + 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_MOVED); + assert(IS_POINTER(obj->h_revision)); + stub = (gcptr)obj->h_revision; + assert(!IS_POINTER(stub->h_revision)); /* not outdated */ + goto done; + } + + /* old or young protected! mark as PUBLIC */ + if (!(obj->h_tid & GCFLAG_OLD)) { + /* young protected */ + gcptr O; + + if (obj->h_tid & GCFLAG_HAS_ID) { + /* use id-copy for us */ + O = (gcptr)obj->h_original; + obj->h_tid &= ~GCFLAG_HAS_ID; + stm_copy_to_old_id_copy(obj, O); + O->h_original = 0; + } else { + O = stmgc_duplicate_old(obj); + + /* young and without original? */ + if (!(obj->h_original)) + obj->h_original = (revision_t)O; + } + obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC); + obj->h_revision = (revision_t)O; + + O->h_tid |= GCFLAG_PUBLIC; + /* here it is fine if it stays in read caches because + the object is immutable anyway and there are no + write_barriers allowed. */ + dprintf(("steal prot immutable -> public: %p -> %p\n", obj, O)); + stub = O; + goto done; + } + /* old protected: */ + dprintf(("prot immutable -> public: %p\n", obj)); + obj->h_tid |= GCFLAG_PUBLIC; + + return; + } + /* we use 'all_stubs', a dictionary, in order to try to avoid duplicate stubs for the same object. XXX maybe it would be better to use a fast approximative cache that stays around for diff --git a/c4/stmgc.c b/c4/stmgc.c --- a/c4/stmgc.c +++ b/c4/stmgc.c @@ -10,5 +10,6 @@ #include "gcpage.c" #include "stmsync.c" #include "extra.c" +#include "weakref.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 @@ -29,6 +29,9 @@ /* allocate an object out of the local nursery */ gcptr stm_allocate(size_t size, unsigned long tid); +/* allocate an object that is be immutable. it cannot be changed with + a stm_write_barrier() or after the next commit */ +gcptr stm_allocate_immutable(size_t size, unsigned long tid); /* returns a never changing hash for the object */ revision_t stm_hash(gcptr); @@ -54,11 +57,19 @@ int stm_enter_callback_call(void); void stm_leave_callback_call(int); -/* read/write barriers (the most general versions only for now) */ -#if 0 // (optimized version below) -gcptr stm_read_barrier(gcptr); -gcptr stm_write_barrier(gcptr); -#endif +/* read/write barriers (the most general versions only for now). + + - the read barrier must be applied before reading from an object. + the result is valid as long as we're in the same transaction, + and stm_write_barrier() is not called on the same object. + + - the write barrier must be applied before writing to an object. + the result is valid for a shorter period of time: we have to + do stm_write_barrier() again if we ended the transaction, or + if we did a potential collection (e.g. stm_allocate()). +*/ +static inline gcptr stm_read_barrier(gcptr); +static inline gcptr stm_write_barrier(gcptr); /* start a new transaction, calls callback(), and when it returns finish that transaction. callback() is called with the 'arg' @@ -114,6 +125,14 @@ void stm_minor_collect(void); void stm_major_collect(void); +/* weakref support: allocate a weakref object, and set it to point + weakly to 'obj'. The weak pointer offset is hard-coded to be at + 'size - WORD'. Important: stmcb_trace() must NOT trace it. + Weakrefs are *immutable*! Don't attempt to use stm_write_barrier() + on them. */ +gcptr stm_weakref_allocate(size_t size, unsigned long tid, gcptr obj); + + /**************** END OF PUBLIC INTERFACE *****************/ /************************************************************/ diff --git a/c4/stmimpl.h b/c4/stmimpl.h --- a/c4/stmimpl.h +++ b/c4/stmimpl.h @@ -36,5 +36,6 @@ #include "steal.h" #include "stmsync.h" #include "extra.h" +#include "weakref.h" #endif 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 extra.h dbgmem.h fprintcolor.h " + "stmsync.h extra.h weakref.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 extra.c dbgmem.c fprintcolor.c".split()] + "stmsync.c extra.c weakref.c dbgmem.c fprintcolor.c".split()] _pycache_ = os.path.join(parent_dir, 'test', '__pycache__') if os.path.exists(_pycache_): @@ -46,7 +46,7 @@ #define PREBUILT_FLAGS ... #define PREBUILT_REVISION ... - gcptr stm_allocate(size_t size, unsigned int tid); + gcptr stm_allocate(size_t size, unsigned long tid); revision_t stm_hash(gcptr); revision_t stm_id(gcptr); _Bool stm_pointer_equal(gcptr, gcptr); @@ -69,6 +69,7 @@ void stm_abort_info_pop(long count); char *stm_inspect_abort_info(void); void stm_abort_and_retry(void); + gcptr stm_weakref_allocate(size_t size, unsigned long tid, gcptr obj); /* extra non-public code */ void printfcolor(char *msg); @@ -133,6 +134,7 @@ #define GCFLAG_STUB ... #define GCFLAG_PRIVATE_FROM_PROTECTED ... #define GCFLAG_HAS_ID ... + #define GCFLAG_IMMUTABLE ... #define ABRT_MANUAL ... typedef struct { ...; } page_header_t; ''') @@ -164,14 +166,18 @@ gcptr rawgetptr(gcptr obj, long index) { - assert(gettid(obj) > 42142 + index); + revision_t t = gettid(obj); + if (t == 42142) t++; + assert(t > 42142 + index); return ((gcptr *)(obj + 1))[index]; } void rawsetptr(gcptr obj, long index, gcptr newvalue) { fprintf(stderr, "%p->[%ld] = %p\n", obj, index, newvalue); - assert(gettid(obj) > 42142 + index); + revision_t t = gettid(obj); + if (t == 42142) t++; + assert(t > 42142 + index); ((gcptr *)(obj + 1))[index] = newvalue; } @@ -282,6 +288,8 @@ else { int nrefs = gettid(obj) - 42142; assert(nrefs < 100); + if (nrefs == 0) /* weakrefs */ + nrefs = 1; return sizeof(*obj) + nrefs * sizeof(gcptr); } } @@ -484,7 +492,7 @@ def oalloc_refs(nrefs): """Allocate an 'old' protected object, outside any nursery, with nrefs pointers""" - size = HDR + WORD * nrefs + size = HDR + WORD * (nrefs or 1) p = lib.stmgcpage_malloc(size) lib.memset(p, 0, size) p.h_tid = GCFLAG_OLD | GCFLAG_WRITE_BARRIER @@ -506,9 +514,9 @@ def nalloc_refs(nrefs): "Allocate a fresh object from the nursery, with nrefs pointers" - p = lib.stm_allocate(HDR + WORD * nrefs, 42142 + nrefs) + p = lib.stm_allocate(HDR + WORD * (nrefs or 1), 42142 + nrefs) assert p.h_revision == lib.get_private_rev_num() - for i in range(nrefs): + for i in range(nrefs or 1): assert rawgetptr(p, i) == ffi.NULL # must already be zero-filled return p @@ -524,9 +532,9 @@ def palloc_refs(nrefs, prehash=None): "Get a ``prebuilt'' object with nrefs pointers." if prehash is None: - p = lib.pseudoprebuilt(HDR + WORD * nrefs, 42142 + nrefs) + p = lib.pseudoprebuilt(HDR + WORD * (nrefs or 1), 42142 + nrefs) else: - p = lib.pseudoprebuilt_with_hash(HDR + WORD * nrefs, + p = lib.pseudoprebuilt_with_hash(HDR + WORD * (nrefs or 1), 42142 + nrefs, prehash) return p @@ -686,5 +694,8 @@ should_break_transaction = lib.stm_should_break_transaction - +WEAKREF_SIZE = HDR + WORD +WEAKREF_TID = 42142 + + 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 @@ -205,6 +205,8 @@ assert list_of_read_objects() == [p2] def test_write_barrier_after_minor_collect(): + # should fail + py.test.skip("should fail now") p = oalloc_refs(1) pw = lib.stm_write_barrier(p) @@ -220,8 +222,10 @@ assert pw.h_tid & GCFLAG_OLD rawsetptr(pw, 0, r) - # pw needs to be readded to old_objects_to_trace - # before the next minor gc in order for this test to pass + # pw not in old_objects_to_trace. A + # repeated write_barrier before + # rawsetptr() would fix that + lib.stm_push_root(r) minor_collect() minor_collect() @@ -232,6 +236,10 @@ pr = lib.stm_read_barrier(p) assert r != r2 + # these will fail because pw/pr was + # not traced in the last minor_collect, + # because they were not registered in + # old_objects_to_trace. assert getptr(pr, 0) != r assert getptr(pr, 0) == r2 @@ -251,6 +259,7 @@ assert getptr(pr, 0) != q2 def test_write_barrier_after_minor_collect_young_to_old(): + py.test.skip("should fail now") p = nalloc_refs(1) pw = lib.stm_write_barrier(p) diff --git a/c4/test/test_nursery.py b/c4/test/test_nursery.py --- a/c4/test/test_nursery.py +++ b/c4/test/test_nursery.py @@ -200,6 +200,84 @@ check_not_free(p2) assert classify(p2) == "private" +def test_old_private_from_protected_to_young_private_2(): + py.test.skip("not valid") + p0 = nalloc_refs(1) + lib.stm_commit_transaction() + lib.stm_begin_inevitable_transaction() + lib.setptr(p0, 0, ffi.NULL) + assert classify(p0) == "private_from_protected" + assert lib.in_nursery(p0) # a young private_from_protected + # + lib.stm_push_root(p0) + minor_collect() + p0 = lib.stm_pop_root() + assert classify(p0) == "private_from_protected" + assert not lib.in_nursery(p0) # becomes an old private_from_protected + # + # Because it's a private_from_protected, its h_revision is a pointer + # to the backup copy, and not stm_private_rev_num. It means that the + # write barrier will always enter its slow path, even though the + # GCFLAG_WRITE_BARRIER is not set. + assert p0.h_revision != lib.get_private_rev_num() + assert not (p0.h_tid & GCFLAG_WRITE_BARRIER) + # + p1 = nalloc(HDR) + lib.setptr(p0, 0, p1) # should trigger the write barrier again + assert classify(p0) == "private_from_protected" + lib.stm_push_root(p0) + minor_collect() + p0b = lib.stm_pop_root() + assert p0b == p0 + check_nursery_free(p1) + assert classify(p0) == "private_from_protected" + p2 = lib.getptr(p0, 0) + assert not lib.in_nursery(p2) + check_not_free(p2) + assert classify(p2) == "private" + +def test_old_private_from_protected_to_young_private_3(): + p0 = palloc_refs(1) + pw = lib.stm_write_barrier(p0) + lib.stm_commit_transaction() + lib.stm_begin_inevitable_transaction() + pr = lib.stm_read_barrier(p0) + assert classify(pr) == "protected" + assert lib.in_nursery(pr) # a young protected + # + minor_collect() + # each minor collect adds WRITE_BARRIER to protected/private + # objects it moves out of the nursery + pr = lib.stm_read_barrier(p0) + assert pr.h_tid & GCFLAG_WRITE_BARRIER + pw = lib.stm_write_barrier(pr) + # added to old_obj_to_trace + assert not (pw.h_tid & GCFLAG_WRITE_BARRIER) + + lib.setptr(pw, 0, ffi.NULL) + assert classify(pw) == "private_from_protected" + assert not lib.in_nursery(pw) + + assert pw.h_revision != lib.get_private_rev_num() + assert not (pw.h_tid & GCFLAG_WRITE_BARRIER) + # # + + lib.stm_push_root(pw) + minor_collect() + p1 = nalloc(HDR) + pw = lib.stm_pop_root() + assert pw.h_tid & GCFLAG_WRITE_BARRIER + lib.setptr(pw, 0, p1) # should trigger the write barrier again + assert classify(pr) == "private_from_protected" + minor_collect() + check_nursery_free(p1) + pr = lib.stm_read_barrier(p0) + assert classify(pr) == "private_from_protected" + p2 = lib.getptr(pr, 0) + assert not lib.in_nursery(p2) + check_not_free(p2) + assert classify(p2) == "private" + def test_new_version(): p1 = oalloc(HDR) assert lib.stm_write_barrier(p1) == p1 diff --git a/c4/test/test_weakref.py b/c4/test/test_weakref.py new file mode 100644 --- /dev/null +++ b/c4/test/test_weakref.py @@ -0,0 +1,120 @@ +import py +from support import * + + +class BaseTest(object): + def setup_method(self, meth): + lib.stm_clear_between_tests() + lib.stm_initialize_tests(0) + def teardown_method(self, meth): + lib.stm_finalize() + + +class TestMinorCollection(BaseTest): + + def test_weakref_invalidate(self): + p2 = nalloc(HDR) + p1 = lib.stm_weakref_allocate(WEAKREF_SIZE, WEAKREF_TID, p2) + assert p1.h_tid == WEAKREF_TID | GCFLAG_IMMUTABLE + assert p1.h_revision == lib.get_private_rev_num() + assert lib.rawgetptr(p1, 0) == p2 + lib.stm_push_root(p1) + minor_collect() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == ffi.NULL + + def test_weakref_itself_dies(self): + p2 = nalloc(HDR) + p1 = lib.stm_weakref_allocate(WEAKREF_SIZE, WEAKREF_TID, p2) + minor_collect() + + def test_weakref_keep(self): + p2 = nalloc(HDR) + p1 = lib.stm_weakref_allocate(WEAKREF_SIZE, WEAKREF_TID, p2) + assert p1.h_tid == WEAKREF_TID | GCFLAG_IMMUTABLE + assert p1.h_revision == lib.get_private_rev_num() + assert lib.rawgetptr(p1, 0) == p2 + lib.stm_push_root(p1) + lib.stm_push_root(p2) + minor_collect() + p2 = lib.stm_pop_root() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == p2 + + def test_weakref_old_keep(self): + p2 = oalloc(HDR) + p1 = lib.stm_weakref_allocate(WEAKREF_SIZE, WEAKREF_TID, p2) + assert p1.h_tid == WEAKREF_TID | GCFLAG_IMMUTABLE + assert p1.h_revision == lib.get_private_rev_num() + assert lib.rawgetptr(p1, 0) == p2 + lib.stm_push_root(p1) + lib.stm_push_root(p2) + minor_collect() + p2 = lib.stm_pop_root() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == p2 + + +class TestMajorCollection(BaseTest): + + def test_weakref_old(self): + p2 = nalloc(HDR) + p1 = lib.stm_weakref_allocate(WEAKREF_SIZE, WEAKREF_TID, p2) + # + lib.stm_push_root(p1) + lib.stm_push_root(p2) + major_collect() + p2 = lib.stm_pop_root() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == p2 + # + lib.stm_push_root(p1) + major_collect() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == ffi.NULL + + def test_weakref_to_prebuilt(self): + p2 = palloc(HDR) + p1 = lib.stm_weakref_allocate(WEAKREF_SIZE, WEAKREF_TID, p2) + # + lib.stm_push_root(p1) + major_collect() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == p2 + + def test_weakref_update_version(self): + p2 = oalloc(HDR + WORD); make_public(p2) + p1 = lib.stm_weakref_allocate(WEAKREF_SIZE, WEAKREF_TID, p2) + # + lib.stm_push_root(p1) + lib.stm_push_root(p2) + major_collect() + p2 = lib.stm_pop_root() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == p2 + # + lib.stm_commit_transaction() + lib.stm_begin_inevitable_transaction() + # + lib.setlong(p2, 0, 912809218) # write barrier + assert lib.rawgetlong(p2, 0) == 0 + lib.stm_push_root(p1) + lib.stm_push_root(p2) + major_collect() + p2 = lib.stm_pop_root() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == p2 + assert lib.rawgetlong(p2, 0) == 0 + # + lib.stm_commit_transaction() + lib.stm_begin_inevitable_transaction() + # + assert lib.rawgetlong(p2, 0) == 0 + lib.stm_push_root(p1) + lib.stm_push_root(p2) + major_collect() + p2b = lib.stm_pop_root() + p1 = lib.stm_pop_root() + assert lib.rawgetptr(p1, 0) == p2b + assert p2b != p2 + assert lib.getlong(p2b, 0) == 912809218 diff --git a/c4/weakref.c b/c4/weakref.c new file mode 100644 --- /dev/null +++ b/c4/weakref.c @@ -0,0 +1,210 @@ +#include "stmimpl.h" + +#define WEAKREF_PTR(wr, sz) (*(gcptr *)(((char *)(wr)) + (sz) - WORD)) + + +gcptr stm_weakref_allocate(size_t size, unsigned long tid, gcptr obj) +{ + stm_push_root(obj); + gcptr weakref = stm_allocate_immutable(size, tid); + obj = stm_pop_root(); + assert(!(weakref->h_tid & GCFLAG_OLD)); /* 'size' too big? */ + assert(stmgc_size(weakref) == size); + WEAKREF_PTR(weakref, size) = obj; + gcptrlist_insert(&thread_descriptor->young_weakrefs, weakref); + dprintf(("alloc weakref %p -> %p\n", weakref, obj)); + return weakref; +} + + +/***** Minor collection *****/ + +void stm_move_young_weakrefs(struct tx_descriptor *d) +{ + /* The code relies on the fact that no weakref can be an old object + weakly pointing to a young object. Indeed, weakrefs are immutable + so they cannot point to an object that was created after it. + */ + while (gcptrlist_size(&d->young_weakrefs) > 0) { + gcptr weakref = gcptrlist_pop(&d->young_weakrefs); + if (!(weakref->h_tid & GCFLAG_MOVED)) + continue; /* the weakref itself dies */ + + weakref = (gcptr)weakref->h_revision; + size_t size = stmgc_size(weakref); + gcptr pointing_to = WEAKREF_PTR(weakref, size); + assert(pointing_to != NULL); + + if (stmgc_is_in_nursery(d, pointing_to)) { + if (pointing_to->h_tid & GCFLAG_MOVED) { + dprintf(("weakref ptr moved %p->%p\n", + WEAKREF_PTR(weakref, size), + (gcptr)pointing_to->h_revision)); + WEAKREF_PTR(weakref, size) = (gcptr)pointing_to->h_revision; + } + else { + dprintf(("weakref lost ptr %p\n", WEAKREF_PTR(weakref, size))); + WEAKREF_PTR(weakref, size) = NULL; + continue; /* no need to remember this weakref any longer */ + } + } + else { + /* # see test_weakref_to_prebuilt: it's not useful to put + # weakrefs into 'old_objects_with_weakrefs' if they point + # to a prebuilt object (they are immortal). If moreover + # the 'pointing_to' prebuilt object still has the + # GCFLAG_NO_HEAP_PTRS flag, then it's even wrong, because + # 'pointing_to' will not get the GCFLAG_VISITED during + # the next major collection. Solve this by not registering + # the weakref into 'old_objects_with_weakrefs'. + */ + } + gcptrlist_insert(&d->public_descriptor->old_weakrefs, weakref); + } +} + + +/***** Major collection *****/ + +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. + */ + restart: + if (obj->h_tid & GCFLAG_VISITED) + return 1; + + if (obj->h_revision & 1) { + assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED)); + assert(!(obj->h_tid & GCFLAG_STUB)); + 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) + 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; +} + +static void visit_old_weakrefs(struct tx_public_descriptor *gcp) +{ + /* Note: it's possible that a weakref points to a public stub to a + protected object, and only the protected object was marked as + VISITED so far. In this case, this function needs to mark the + public stub as VISITED too. + */ + long i, size = gcp->old_weakrefs.size; + gcptr *items = gcp->old_weakrefs.items; + + for (i = 0; i < size; i++) { + gcptr weakref = items[i]; + + /* weakrefs are immutable: during a major collection, they + cannot be in the nursery, and so there should be only one + version of each weakref object. XXX relying on this is + a bit fragile, but simplifies things a lot... */ + assert(weakref->h_revision & 1); + + if (!(weakref->h_tid & GCFLAG_VISITED)) { + /* the weakref itself dies */ + } + else { + size_t size = stmgc_size(weakref); + gcptr pointing_to = WEAKREF_PTR(weakref, size); + assert(pointing_to != NULL); + if (is_partially_visited(pointing_to)) { + pointing_to = stmgcpage_visit(pointing_to); + dprintf(("mweakref ptr moved %p->%p\n", + WEAKREF_PTR(weakref, size), + pointing_to)); + + assert(pointing_to->h_tid & GCFLAG_VISITED); + WEAKREF_PTR(weakref, size) = pointing_to; + } + else { + /* the weakref appears to be pointing to a dying object, + but we don't know for sure now. Clearing it is left + to clean_old_weakrefs(). */ + } + } + } +} + +static void clean_old_weakrefs(struct tx_public_descriptor *gcp) +{ + long i, size = gcp->old_weakrefs.size; + gcptr *items = gcp->old_weakrefs.items; + + for (i = size - 1; i >= 0; i--) { + gcptr weakref = items[i]; + assert(weakref->h_revision & 1); + if (weakref->h_tid & GCFLAG_VISITED) { + size_t size = stmgc_size(weakref); + gcptr pointing_to = WEAKREF_PTR(weakref, size); + if (pointing_to->h_tid & GCFLAG_VISITED) { + continue; /* the target stays alive, the weakref remains */ + } + dprintf(("mweakref lost ptr %p\n", WEAKREF_PTR(weakref, size))); + WEAKREF_PTR(weakref, size) = NULL; /* the target dies */ + } + /* remove this weakref from the list */ + items[i] = items[--gcp->old_weakrefs.size]; + } + gcptrlist_compress(&gcp->old_weakrefs); +} + +static void for_each_public_descriptor( + void visit(struct tx_public_descriptor *)) { + struct tx_descriptor *d; + for (d = stm_tx_head; d; d = d->tx_next) + visit(d->public_descriptor); + + struct tx_public_descriptor *gcp; + revision_t index = -1; + while ((gcp = stm_get_free_public_descriptor(&index)) != NULL) + visit(gcp); +} + +void stm_visit_old_weakrefs(void) +{ + /* Figure out which weakrefs survive, which possibly + adds more objects to 'objects_to_trace'. + */ + for_each_public_descriptor(visit_old_weakrefs); +} + +void stm_clean_old_weakrefs(void) +{ + /* Clean up the non-surviving weakrefs + */ + for_each_public_descriptor(clean_old_weakrefs); +} diff --git a/c4/weakref.h b/c4/weakref.h new file mode 100644 --- /dev/null +++ b/c4/weakref.h @@ -0,0 +1,10 @@ +#ifndef _SRCSTM_WEAKREF_H +#define _SRCSTM_WEAKREF_H + + +void stm_move_young_weakrefs(struct tx_descriptor *); +void stm_visit_old_weakrefs(void); +void stm_clean_old_weakrefs(void); + + +#endif diff --git a/duhton/listobject.c b/duhton/listobject.c --- a/duhton/listobject.c +++ b/duhton/listobject.c @@ -75,7 +75,7 @@ void _list_append(DuListObject *ob, DuObject *x) { - _du_write1(ob); + _du_read1(ob); DuTupleObject *olditems = ob->ob_tuple; _du_read1(olditems); @@ -85,6 +85,8 @@ DuTupleObject *newitems = DuTuple_New(newcount); _du_restore3(ob, x, olditems); + _du_write1(ob); + for (i=0; i<newcount-1; i++) newitems->ob_items[i] = olditems->ob_items[i]; newitems->ob_items[newcount-1] = x; _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit