Author: Remi Meier Branch: Changeset: r996:ae62acdb5d7c Date: 2014-03-13 10:31 +0100 http://bitbucket.org/pypy/stmgc/changeset/ae62acdb5d7c/
Log: merge weakref support diff --git a/c7/stm/core.c b/c7/stm/core.c --- a/c7/stm/core.c +++ b/c7/stm/core.c @@ -180,6 +180,7 @@ } assert(list_is_empty(STM_PSEGMENT->modified_old_objects)); + assert(list_is_empty(STM_PSEGMENT->young_weakrefs)); assert(tree_is_cleared(STM_PSEGMENT->young_outside_nursery)); assert(tree_is_cleared(STM_PSEGMENT->nursery_objects_shadows)); assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_abort)); @@ -483,6 +484,7 @@ /* reset these lists to NULL too on abort */ LIST_FREE(pseg->objects_pointing_to_nursery); LIST_FREE(pseg->large_overflow_objects); + list_clear(pseg->young_weakrefs); } static void abort_with_mutex(void) diff --git a/c7/stm/core.h b/c7/stm/core.h --- a/c7/stm/core.h +++ b/c7/stm/core.h @@ -61,7 +61,7 @@ current transaction that have been flushed out of the nursery, which occurs if the same transaction allocates too many objects. */ - GCFLAG_OVERFLOW_NUMBER_bit0 = 0x08 /* must be last */ + GCFLAG_OVERFLOW_NUMBER_bit0 = 0x8 /* must be last */ }; @@ -105,6 +105,15 @@ next minor collection. */ struct tree_s *nursery_objects_shadows; + /* List of all young weakrefs to check in minor collections. These + are the only weakrefs that may point to young objects and never + contain NULL. */ + struct list_s *young_weakrefs; + + /* List of all old weakrefs to check in major collections. These + weakrefs never point to young objects and never contain NULL. */ + struct list_s *old_weakrefs; + /* Tree of 'key->callback' associations from stm_call_on_abort() */ struct tree_s *callbacks_on_abort; diff --git a/c7/stm/gcpage.c b/c7/stm/gcpage.c --- a/c7/stm/gcpage.c +++ b/c7/stm/gcpage.c @@ -450,7 +450,11 @@ /* 'objects_pointing_to_nursery' should be empty, but isn't necessarily because it also lists objects that have been written to but don't actually point to the nursery. Clear - it up and set GCFLAG_WRITE_BARRIER again on the objects. */ + it up and set GCFLAG_WRITE_BARRIER again on the objects. + This is the case for transactions where + MINOR_NOTHING_TO_DO() == false + but they still did write-barriers on objects + */ lst = pseg->objects_pointing_to_nursery; if (lst != NULL) { LIST_FOREACH_R(lst, uintptr_t /*item*/, @@ -537,6 +541,9 @@ mark_visit_from_roots(); LIST_FREE(mark_objects_to_trace); + /* weakrefs: */ + stm_visit_old_weakrefs(); + /* cleanup */ clean_up_segment_lists(); diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c --- a/c7/stm/nursery.c +++ b/c7/stm/nursery.c @@ -299,6 +299,9 @@ collect_oldrefs_to_nursery(); + /* now all surviving nursery objects have been moved out */ + stm_move_young_weakrefs(); + throw_away_nursery(get_priv_segment(STM_SEGMENT->segment_num)); assert(MINOR_NOTHING_TO_DO(STM_PSEGMENT)); diff --git a/c7/stm/setup.c b/c7/stm/setup.c --- a/c7/stm/setup.c +++ b/c7/stm/setup.c @@ -57,6 +57,8 @@ pr->objects_pointing_to_nursery = NULL; pr->large_overflow_objects = NULL; pr->modified_old_objects = list_create(); + pr->young_weakrefs = list_create(); + pr->old_weakrefs = list_create(); pr->young_outside_nursery = tree_create(); pr->nursery_objects_shadows = tree_create(); pr->callbacks_on_abort = tree_create(); @@ -95,6 +97,8 @@ assert(pr->objects_pointing_to_nursery == NULL); assert(pr->large_overflow_objects == NULL); list_free(pr->modified_old_objects); + list_free(pr->young_weakrefs); + list_free(pr->old_weakrefs); tree_free(pr->young_outside_nursery); tree_free(pr->nursery_objects_shadows); tree_free(pr->callbacks_on_abort); diff --git a/c7/stm/weakref.c b/c7/stm/weakref.c new file mode 100644 --- /dev/null +++ b/c7/stm/weakref.c @@ -0,0 +1,138 @@ +#ifndef _STM_CORE_H_ +# error "must be compiled via stmgc.c" +#endif + +#define WEAKREF_PTR(wr, sz) ((object_t * TLPREFIX *)(((stm_char *)(wr)) + (sz) - sizeof(void*))) + +object_t *stm_allocate_weakref(ssize_t size_rounded_up) +{ + OPT_ASSERT(size_rounded_up > sizeof(struct object_s)); + OPT_ASSERT(size_rounded_up == 16); /* no reason for it to be anything else */ + + object_t *obj = stm_allocate(size_rounded_up); + assert(_is_in_nursery(obj)); /* because it's so small */ + + LIST_APPEND(STM_PSEGMENT->young_weakrefs, obj); + return obj; +} + + +static void _set_weakref_in_all_segments(object_t *weakref, object_t *value) +{ + ssize_t size = 16; + + stm_char *point_to_loc = (stm_char*)WEAKREF_PTR(weakref, size); + if (flag_page_private[(uintptr_t)point_to_loc / 4096UL] == PRIVATE_PAGE) { + long i; + for (i = 0; i < NB_SEGMENTS; i++) { + char *base = get_segment_base(i); /* two different segments */ + + object_t ** ref_loc = (object_t **)REAL_ADDRESS(base, point_to_loc); + *ref_loc = value; + } + } + else { + *WEAKREF_PTR(weakref, size) = value; + } +} + +/***** Minor collection *****/ + +static void stm_move_young_weakrefs() +{ + /* 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. + */ + LIST_FOREACH_R( + STM_PSEGMENT->young_weakrefs, + object_t * /*item*/, + ({ + /* weakrefs are so small, they always are in the nursery. Never + a young outside nursery object. */ + assert(_is_in_nursery(item)); + object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)item; + + /* the following checks are done like in nursery.c: */ + if (!(item->stm_flags & GCFLAG_HAS_SHADOW) + || (pforwarded_array[0] != GCWORD_MOVED)) { + /* weakref dies */ + continue; + } + + item = pforwarded_array[1]; /* moved location */ + + assert(!_is_young(item)); + + ssize_t size = 16; + object_t *pointing_to = *WEAKREF_PTR(item, size); + assert(pointing_to != NULL); + + if (_is_in_nursery(pointing_to)) { + object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)pointing_to; + /* the following checks are done like in nursery.c: */ + if (!(pointing_to->stm_flags & GCFLAG_HAS_SHADOW) + || (pforwarded_array[0] != GCWORD_MOVED)) { + /* pointing_to dies */ + _set_weakref_in_all_segments(item, NULL); + continue; /* no need to remember in old_weakrefs */ + } + else { + /* moved location */ + _set_weakref_in_all_segments(item, pforwarded_array[1]); + } + } + else { + /* young outside nursery object or old object */ + if (tree_contains(STM_PSEGMENT->young_outside_nursery, + (uintptr_t)pointing_to)) { + /* still in the tree -> wasn't seen by the minor collection, + so it doesn't survive */ + _set_weakref_in_all_segments(item, NULL); + continue; /* no need to remember in old_weakrefs */ + } + /* pointing_to was already old */ + } + LIST_APPEND(STM_PSEGMENT->old_weakrefs, item); + })); + list_clear(STM_PSEGMENT->young_weakrefs); +} + + +/***** Major collection *****/ + + +static void stm_visit_old_weakrefs(void) +{ + long i; + for (i = 0; i < NB_SEGMENTS; i++) { + struct stm_priv_segment_info_s *pseg = get_priv_segment(i); + struct list_s *lst; + + lst = pseg->old_weakrefs; + uintptr_t n = list_count(lst); + while (n > 0) { + object_t *weakref = (object_t *)list_item(lst, --n); + if (!mark_visited_test(weakref)) { + /* weakref dies */ + list_set_item(lst, n, list_pop_item(lst)); + continue; + } + + ssize_t size = 16; + object_t *pointing_to = *WEAKREF_PTR(weakref, size); + assert(pointing_to != NULL); + if (!mark_visited_test(pointing_to)) { + //assert(flag_page_private[(uintptr_t)weakref / 4096UL] != PRIVATE_PAGE); + _set_weakref_in_all_segments(weakref, NULL); + + /* we don't need it in this list anymore */ + list_set_item(lst, n, list_pop_item(lst)); + continue; + } + else { + /* it survives! */ + } + } + } +} diff --git a/c7/stm/weakref.h b/c7/stm/weakref.h new file mode 100644 --- /dev/null +++ b/c7/stm/weakref.h @@ -0,0 +1,9 @@ +#ifndef _SRCSTM_WEAKREF_H +#define _SRCSTM_WEAKREF_H + +object_t *stm_allocate_weakref(ssize_t size_rounded_up); +static void stm_move_young_weakrefs(void); +static void stm_visit_old_weakrefs(void); + + +#endif diff --git a/c7/stmgc.c b/c7/stmgc.c --- a/c7/stmgc.c +++ b/c7/stmgc.c @@ -12,6 +12,7 @@ #include "stm/contention.h" #include "stm/extra.h" #include "stm/fprintcolor.h" +#include "stm/weakref.h" #include "stm/misc.c" #include "stm/list.c" @@ -28,3 +29,4 @@ #include "stm/contention.c" #include "stm/extra.c" #include "stm/fprintcolor.c" +#include "stm/weakref.c" diff --git a/c7/stmgc.h b/c7/stmgc.h --- a/c7/stmgc.h +++ b/c7/stmgc.h @@ -194,6 +194,17 @@ return (object_t *)p; } +/* Allocate a weakref object. Weakref objects have a + reference to an object at the byte-offset + stmcb_size_rounded_up(obj) - sizeof(void*) + You must assign the reference before the next collection may happen. + After that, you must not mutate the reference anymore. However, + it can become NULL after any GC if the reference dies during that + collection. + NOTE: For performance, we assume stmcb_size_rounded_up(weakref)==16 +*/ +object_t *stm_allocate_weakref(ssize_t size_rounded_up); + /* stm_setup() needs to be called once at the beginning of the program. stm_teardown() can be called at the end, but that's not necessary diff --git a/c7/test/common.py b/c7/test/common.py --- a/c7/test/common.py +++ b/c7/test/common.py @@ -14,7 +14,7 @@ os.path.join(parent_dir, "stmgc.c")] + [ os.path.join(parent_dir, 'stm', _n) for _n in os.listdir(os.path.join(parent_dir, 'stm')) - if _n.endswith('.h') or _n.endswith('.c')] + if (_n.endswith('.h') or _n.endswith('.c')) and not _n.startswith('.')] _pycache_ = os.path.join(parent_dir, 'test', '__pycache__') if os.path.exists(_pycache_): diff --git a/c7/test/support.py b/c7/test/support.py --- a/c7/test/support.py +++ b/c7/test/support.py @@ -25,6 +25,7 @@ void stm_read(object_t *obj); /*void stm_write(object_t *obj); use _checked_stm_write() instead */ object_t *stm_allocate(ssize_t size_rounded_up); +object_t *stm_allocate_weakref(ssize_t size_rounded_up); object_t *_stm_allocate_old(ssize_t size_rounded_up); void stm_setup(void); @@ -54,6 +55,10 @@ void _set_ptr(object_t *obj, int n, object_t *v); object_t * _get_ptr(object_t *obj, int n); +void _set_weakref(object_t *obj, object_t *v); +object_t* _get_weakref(object_t *obj); + + void _stm_start_safe_point(void); bool _check_stop_safe_point(void); @@ -163,6 +168,21 @@ } +#define WEAKREF_PTR(wr, sz) ((object_t * TLPREFIX *)(((stm_char *)(wr)) + (sz) - sizeof(void*))) +void _set_weakref(object_t *obj, object_t *v) +{ + char *realobj = _stm_real_address(obj); + ssize_t size = stmcb_size_rounded_up((struct object_s *)realobj); + *WEAKREF_PTR(obj, size) = v; +} + +object_t * _get_weakref(object_t *obj) +{ + char *realobj = _stm_real_address(obj); + ssize_t size = stmcb_size_rounded_up((struct object_s *)realobj); + return *WEAKREF_PTR(obj, size); +} + void _set_ptr(object_t *obj, int n, object_t *v) { long nrefs = (long)((myobj_t*)obj)->type_id - 421420; @@ -266,6 +286,18 @@ lib._set_type_id(o, tid) return o +def stm_allocate_weakref(point_to_obj, size=None): + assert HDR+WORD == 16 + o = lib.stm_allocate_weakref(HDR + WORD) + + tid = 421420 + lib._set_type_id(o, tid) + lib._set_weakref(o, point_to_obj) + return o + +def stm_get_weakref(o): + return lib._get_weakref(o) + def stm_allocate_refs(n): o = lib.stm_allocate(HDR + n * WORD) tid = 421420 + n diff --git a/c7/test/test_weakref.py b/c7/test/test_weakref.py new file mode 100644 --- /dev/null +++ b/c7/test/test_weakref.py @@ -0,0 +1,255 @@ +import py +from support import * + + + + +class TestMinorCollection(BaseTest): + def test_simple(self): + lib._stm_set_nursery_free_count(2048) + self.start_transaction() + + self.push_root_no_gc() + lp2 = stm_allocate(48) + lp1 = stm_allocate_weakref(lp2) # no collection here + self.pop_root() + + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + stm_minor_collect() + lp1 = self.pop_root() + # lp2 died + assert stm_get_weakref(lp1) == ffi.NULL + + self.push_root(lp1) + stm_minor_collect() + lp1 = self.pop_root() + # lp2 died + assert stm_get_weakref(lp1) == ffi.NULL + + def test_still_simple(self): + lib._stm_set_nursery_free_count(2048) + self.start_transaction() + + self.push_root_no_gc() + lp2 = stm_allocate(48) + lp1 = stm_allocate_weakref(lp2) # no collection here + self.pop_root() + + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + self.push_root(lp2) + stm_minor_collect() + lp2 = self.pop_root() + lp1 = self.pop_root() + # lp2 survived + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + self.push_root(lp2) + stm_minor_collect() + lp2 = self.pop_root() + lp1 = self.pop_root() + # lp2 survived + assert stm_get_weakref(lp1) == lp2 + + def test_weakref_itself_dies(self): + self.start_transaction() + + self.push_root_no_gc() + lp2 = stm_allocate(48) + stm_allocate_weakref(lp2) # no collection here + self.pop_root() + stm_minor_collect() + assert lib._stm_total_allocated() == 0 + + + def test_weakref_old_keep(self): + lp0 = stm_allocate_old(48) + + self.start_transaction() + self.push_root_no_gc() + lp1 = stm_allocate_weakref(lp0) # no collection here + self.pop_root() + + self.push_root(lp1) + stm_minor_collect() + lp1 = self.pop_root() + + assert stm_get_weakref(lp1) == lp0 + + + def test_abort_cleanup(self): + self.start_transaction() + + self.push_root_no_gc() + lp1 = stm_allocate_weakref(ffi.NULL) # no collection here + self.pop_root() + + self.abort_transaction() + self.start_transaction() + + def test_big_alloc_sizes(self): + sizes = [lib._STM_FAST_ALLOC + 16, 48,] + + for osize in sizes: + self.start_transaction() + self.push_root_no_gc() + lp2 = stm_allocate(osize) + lp1 = stm_allocate_weakref(lp2) # no collection here + self.pop_root() + + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + self.push_root(lp2) + stm_minor_collect() + lp2 = self.pop_root() + lp1 = self.pop_root() + # lp2 survived + assert stm_get_weakref(lp1) == lp2 + self.abort_transaction() + + + def test_multiple_threads(self): + self.start_transaction() + lp0 = stm_allocate(1024) + self.push_root(lp0) + self.commit_transaction() + + self.start_transaction() + lp0 = self.pop_root() + self.push_root(lp0) + stm_write(lp0) # privatize page + + self.push_root_no_gc() + lp2 = stm_allocate(48) + lp1 = stm_allocate_weakref(lp2) # no collection here + self.pop_root() + + self.push_root(lp0) + self.push_root(lp1) + self.commit_transaction() + # lp2 dies + lp1 = self.pop_root() + self.push_root(lp1) + + assert stm_get_weakref(lp1) == ffi.NULL + + self.switch(1) + + self.start_transaction() + assert stm_get_weakref(lp1) == ffi.NULL + + + + +class TestMajorCollection(BaseTest): + def test_simple(self): + self.start_transaction() + + self.push_root_no_gc() + lp2 = stm_allocate(48) + lp1 = stm_allocate_weakref(lp2) # no collection here + self.pop_root() + + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + self.push_root(lp2) + stm_minor_collect() + lp2 = self.pop_root() + lp1 = self.pop_root() + # lp2 survived + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + stm_minor_collect() + lp1 = self.pop_root() + # lp2 survived because no major collection + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + stm_major_collect() + lp1 = self.pop_root() + # lp2 died + assert stm_get_weakref(lp1) == ffi.NULL + + def test_weakref_old_keep(self): + lp0 = stm_allocate_old(48) + + self.start_transaction() + self.push_root_no_gc() + lp1 = stm_allocate_weakref(lp0) # no collection here + self.pop_root() + + self.push_root(lp1) + stm_major_collect() + lp1 = self.pop_root() + + assert stm_get_weakref(lp1) == lp0 + + def test_survive(self): + self.start_transaction() + + self.push_root_no_gc() + lp2 = stm_allocate(48) + lp1 = stm_allocate_weakref(lp2) # no collection here + self.pop_root() + + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + self.push_root(lp2) + stm_major_collect() + lp2 = self.pop_root() + lp1 = self.pop_root() + # lp2 survived + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + stm_minor_collect() + lp1 = self.pop_root() + # lp2 survived because no major collection + assert stm_get_weakref(lp1) == lp2 + + self.push_root(lp1) + stm_major_collect() + lp1 = self.pop_root() + # lp2 died + assert stm_get_weakref(lp1) == ffi.NULL + + def test_multiple_threads(self): + self.start_transaction() + lp0 = stm_allocate(48) + lp1 = stm_allocate_weakref(lp0) # no collection here + self.push_root(lp1) + self.push_root(lp0) + self.commit_transaction() + + self.start_transaction() + lp0 = self.pop_root() + lp1 = self.pop_root() + self.push_root(lp1) + + stm_write(lp0) # privatize page with weakref in it too + + assert stm_get_page_flag(stm_get_obj_pages(lp1)[0]) == PRIVATE_PAGE + assert stm_get_weakref(lp1) == lp0 + + self.commit_transaction() + self.start_transaction() + + # lp0 dies + stm_major_collect() + + assert stm_get_weakref(lp1) == ffi.NULL + print stm_get_real_address(lp1) + + self.switch(1) + + self.start_transaction() + assert stm_get_weakref(lp1) == ffi.NULL + print stm_get_real_address(lp1) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit