Author: Armin Rigo <ar...@tunes.org> Branch: c7-refactor Changeset: r821:976dbb25fe76 Date: 2014-02-24 12:09 +0100 http://bitbucket.org/pypy/stmgc/changeset/976dbb25fe76/
Log: progress diff --git a/c7/stm/core.c b/c7/stm/core.c --- a/c7/stm/core.c +++ b/c7/stm/core.c @@ -49,7 +49,6 @@ /* We need to privatize the pages containing the object, if they are still SHARED_PAGE. The common case is that there is only one page in total. */ - size_t obj_size = 0; uintptr_t first_page = ((uintptr_t)obj) / 4096UL; /* If the object is in the uniform pages of small objects @@ -60,12 +59,16 @@ pages_privatize(first_page, 1, true); } else { + char *realobj; + size_t obj_size; + uintptr_t end_page; + /* get the size of the object */ - obj_size = stmcb_size_rounded_up( - (struct object_s *)REAL_ADDRESS(STM_SEGMENT->segment_base, obj)); + realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj); + obj_size = stmcb_size_rounded_up((struct object_s *)realobj); /* that's the page *following* the last page with the object */ - uintptr_t end_page = (((uintptr_t)obj) + obj_size + 4095) / 4096UL; + end_page = (((uintptr_t)obj) + obj_size + 4095) / 4096UL; pages_privatize(first_page, end_page - first_page, true); } @@ -265,19 +268,16 @@ void stm_commit_transaction(void) { - minor_collection(); + assert(!_has_mutex()); + assert(STM_PSEGMENT->safe_point == SP_RUNNING); + + minor_collection(/*commit=*/ true); mutex_lock(); - - assert(STM_PSEGMENT->safe_point = SP_RUNNING); STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT; - restart: - abort_if_needed(); - /* wait until the other thread is at a safe-point */ - if (!try_wait_for_other_safe_points(SP_SAFE_POINT_CANNOT_COLLECT)) - goto restart; + wait_for_other_safe_points(SP_SAFE_POINT_CANNOT_COLLECT); /* the rest of this function runs either atomically without releasing the mutex, or it needs to restart. */ diff --git a/c7/stm/core.h b/c7/stm/core.h --- a/c7/stm/core.h +++ b/c7/stm/core.h @@ -96,10 +96,16 @@ "this segment has modified this old object". */ uint8_t write_lock_num; - /* The thread's safe-point state, one of the SP_xxx constants */ + /* The thread's safe-point state, one of the SP_xxx constants. The + thread is in a "safe point" if it is not concurrently doing any + change that might cause race conditions in other threads. A + thread may enter but not *leave* the safe point it is in without + getting hold of the mutex. Broadly speaking, any value other + than SP_RUNNING means a safe point of some kind. */ uint8_t safe_point; - /* The transaction status, one of the TS_xxx constants */ + /* The transaction status, one of the TS_xxx constants. This is + only accessed when we hold the mutex. */ uint8_t transaction_state; /* In case of abort, we restore the 'shadowstack' field. */ diff --git a/c7/stm/gcpage.c b/c7/stm/gcpage.c --- a/c7/stm/gcpage.c +++ b/c7/stm/gcpage.c @@ -12,7 +12,7 @@ largemalloc_init_arena(base, length); uninitialized_page_start = stm_object_pages + END_NURSERY_PAGE * 4096UL; - uninitialized_page_stop = stm_object_pages + (NB_PAGES - 1) * 4096UL; + uninitialized_page_stop = stm_object_pages + NB_PAGES * 4096UL; assert(GC_MEDIUM_REQUEST >= (1 << 8)); } @@ -70,31 +70,31 @@ } -#if 0 -static char *allocate_outside_nursery_large(uint64_t size) +static object_t *allocate_outside_nursery_large(uint64_t size) { - abort(); //XXX review - /* not thread-safe! Use only when holding the mutex */ - assert(_has_mutex()); + /* thread-safe: use the lock of pages.c to prevent any remapping + from occurring under our feet */ + mutex_pages_lock(); - /* Allocate the object with largemalloc.c from the lower addresses. - Assumes that 'size' is at least 256 bytes; it's needed for - the creation marker to uniquely identify this object */ - OPT_ASSERT(size >= (1 << 8)); - OPT_ASSERT((size & 7) == 0); - + /* Allocate the object with largemalloc.c from the lower addresses. */ char *addr = large_malloc(size); if (addr + size > uninitialized_page_start) { uintptr_t npages; npages = (addr + size - uninitialized_page_start) / 4096UL; npages += GCPAGE_NUM_PAGES; + if (uninitialized_page_stop - uninitialized_page_start < + npages * 4096UL) { + stm_fatalerror("out of memory!\n"); /* XXX */ + } setup_N_pages(uninitialized_page_start, npages); uninitialized_page_start += npages * 4096UL; } - return addr; + + mutex_pages_unlock(); + + return (object_t *)(addr - stm_object_pages); } -#endif object_t *_stm_allocate_old(ssize_t size_rounded_up) { diff --git a/c7/stm/gcpage.h b/c7/stm/gcpage.h --- a/c7/stm/gcpage.h +++ b/c7/stm/gcpage.h @@ -27,7 +27,7 @@ static void setup_gcpage(void); static void teardown_gcpage(void); -//static char *allocate_outside_nursery_large(uint64_t size); +static object_t *allocate_outside_nursery_large(uint64_t size); static char *_allocate_small_slowpath(uint64_t size); diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c --- a/c7/stm/nursery.c +++ b/c7/stm/nursery.c @@ -47,13 +47,6 @@ return _is_in_nursery(obj); } -#if 0 -static bool _is_young(object_t *obj) -{ - return _is_in_nursery(obj); /* for now */ -} -#endif - /************************************************************/ @@ -74,122 +67,46 @@ } } -#if 0 static void minor_trace_if_young(object_t **pobj) { - abort(); //... /* takes a normal pointer to a thread-local pointer to an object */ object_t *obj = *pobj; if (obj == NULL) return; - if (!_is_young(obj)) + if (!_is_in_nursery(obj)) return; /* If the object was already seen here, its first word was set to GCWORD_MOVED. In that case, the forwarding location, i.e. where the object moved to, is stored in the second word in 'obj'. */ - char *realobj = (char *)REAL_ADDRESS(stm_object_pages, obj); - object_t **pforwarded_array = (object_t **)realobj; + object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)obj; if (pforwarded_array[0] == GCWORD_MOVED) { *pobj = pforwarded_array[1]; /* already moved */ return; } - /* We need to make a copy of this object. There are three different - places where the copy can be located, based on four criteria. - - 1) object larger than GC_MEDIUM_REQUEST => largemalloc.c - 2) otherwise, object from current transaction => page S - 3) otherwise, object with the write lock => page W - 4) otherwise, object without the write lock => page S - - The pages S or W above are both pages of uniform sizes obtained - from the end of the address space. The difference is that page S - can be shared, but page W needs to be privatized. Moreover, - cases 2 and 4 differ in the creation_marker they need to put, - which has a granularity of 256 bytes. + /* We need to make a copy of this object. It goes either in + a largemalloc.c-managed area, or if it's small enough, in + one of the small uniform pages from gcpage.c. */ + char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj); size_t size = stmcb_size_rounded_up((struct object_s *)realobj); - uintptr_t lock_idx = (((uintptr_t)obj) >> 4) - WRITELOCK_START; - uint8_t write_lock = write_locks[lock_idx]; object_t *nobj; - long i; if (1 /*size >= GC_MEDIUM_REQUEST*/) { - /* case 1: object is larger than GC_MEDIUM_REQUEST. + /* case 1: object is not small enough. Ask gcpage.c for an allocation via largemalloc. */ - char *copyobj; - copyobj = allocate_outside_nursery_large(size < 256 ? 256 : size); // XXX temp + nobj = allocate_outside_nursery_large(size); - /* Copy the object to segment 0 (as a first step) */ - memcpy(copyobj, realobj, size); - ((struct object_s *)copyobj)->stm_flags |= GCFLAG_WRITE_BARRIER_CALLED; - - nobj = (object_t *)(copyobj - stm_object_pages); - - if (write_lock == 0) { - /* The object is not write-locked, so any copy should be - identical. Now some pages of the destination might be - private already (because of some older action); if so, we - need to replicate the corresponding parts. The hope is - that it's relatively uncommon. */ - uintptr_t p, pend = ((uintptr_t)(copyobj + size - 1)) & ~4095; - for (p = (uintptr_t)copyobj; p < pend; p = (p + 4096) & ~4095) { - minor_copy_in_page_to_other_segments(p, 4096 - (p & 4095)); - } - minor_copy_in_page_to_other_segments(p, ((size-1) & 4095) + 1); - } - else { - /* The object has the write lock. We need to privatize the - pages, and repeat the write lock in the new copy. */ - uintptr_t dataofs = (uintptr_t)nobj; - uintptr_t pagenum = dataofs / 4096UL; - uintptr_t lastpage= (dataofs + size - 1) / 4096UL; - pages_privatize(pagenum, lastpage - pagenum + 1, false); - - lock_idx = (dataofs >> 4) - WRITELOCK_START; - assert(write_locks[lock_idx] == 0); - write_locks[lock_idx] = write_lock; - - /* Then, for each segment > 0, we need to repeat the - memcpy() done above. XXX This could be optimized if - NB_SEGMENTS > 2 by reading all non-written copies from the - same segment, instead of reading really all segments. */ - for (i = 1; i < NB_SEGMENTS; i++) { - uintptr_t diff = get_segment_base(i) - stm_object_pages; - memcpy(copyobj + diff, realobj + diff, size); - ((struct object_s *)(copyobj + diff))->stm_flags |= - GCFLAG_WRITE_BARRIER_CALLED; - } - } - - /* If the source creation marker is CM_CURRENT_TRANSACTION_IN_NURSERY, - write CM_CURRENT_TRANSACTION_OUTSIDE_NURSERY in the destination */ - uintptr_t cmaddr = ((uintptr_t)obj) >> 8; - - for (i = 0; i < NB_SEGMENTS; i++) { - char *absaddr = get_segment_base(i) + cmaddr; - if (((struct stm_creation_marker_s *)absaddr)->cm != 0) { - uintptr_t ncmaddr = ((uintptr_t)nobj) >> 8; - absaddr = get_segment_base(i) + ncmaddr; - ((struct stm_creation_marker_s *)absaddr)->cm = - CM_CURRENT_TRANSACTION_OUTSIDE_NURSERY; - } - } + /* Copy the object */ + char *realnobj = REAL_ADDRESS(STM_SEGMENT->segment_base, nobj); + memcpy(realnobj, realobj, size); } else { - /* cases 2 to 4 */ + /* case "small enough" */ abort(); //... - allocate_outside_nursery_small(small_alloc_shared, size); - allocate_outside_nursery_small(small_alloc_privtz, size); - } - - /* Copy the read markers */ - for (i = 0; i < NB_SEGMENTS; i++) { - uint8_t rm = get_segment_base(i)[((uintptr_t)obj) >> 4]; - get_segment_base(i)[((uintptr_t)nobj) >> 4] = rm; } /* Done copying the object. */ @@ -199,173 +116,87 @@ *pobj = nobj; /* Must trace the object later */ - LIST_APPEND(old_objects_pointing_to_young, nobj); + LIST_APPEND(STM_PSEGMENT->old_objects_pointing_to_nursery, nobj); } static void collect_roots_in_nursery(void) { - stm_thread_local_t *tl = stm_all_thread_locals; - do { - object_t **current = tl->shadowstack; - object_t **base = tl->shadowstack_base; - while (current-- != base) { - minor_trace_if_young(current); - } - tl = tl->next; - } while (tl != stm_all_thread_locals); -} - -static void trace_and_drag_out_of_nursery(object_t *obj) -{ - long i; - for (i = 0; i < NB_SEGMENTS; i++) { - struct object_s *realobj = - (struct object_s *)REAL_ADDRESS(get_segment_base(i), obj); - - realobj->stm_flags |= GCFLAG_WRITE_BARRIER; - - stmcb_trace((struct object_s *)realobj, &minor_trace_if_young); - - if (i == 0 && is_in_shared_pages(obj)) { - /* the object needs fixing only in one copy, because all copies - are shared and identical. */ - break; - } + stm_thread_local_t *tl = STM_SEGMENT->running_thread; + object_t **current = tl->shadowstack; + object_t **base = tl->shadowstack_base; + while (current-- != base) { + minor_trace_if_young(current); } } -static void collect_oldrefs_to_nursery(struct list_s *lst) +static void collect_oldrefs_to_nursery(void) { + struct list_s *lst = STM_PSEGMENT->old_objects_pointing_to_nursery; + while (!list_is_empty(lst)) { object_t *obj = (object_t *)list_pop_item(lst); assert(!_is_in_nursery(obj)); - /* We must have GCFLAG_WRITE_BARRIER_CALLED so far. If we - don't, it's because the same object was stored in several - segment's old_objects_pointing_to_young. It's fine to - ignore duplicates. */ - abort();//... - //if ((obj->stm_flags & GCFLAG_WRITE_BARRIER_CALLED) == 0) - // continue; - - /* The flag GCFLAG_WRITE_BARRIER_CALLED is going to be removed: - no live object should have this flag set after a nursery - collection. It is done in either one or NB_SEGMENTS copies. */ + /* We must not have GCFLAG_WRITE_BARRIER so far. Add it now. */ + assert(!(obj->stm_flags & GCFLAG_WRITE_BARRIER)); + obj->stm_flags |= GCFLAG_WRITE_BARRIER; /* Trace the 'obj' to replace pointers to nursery with pointers outside the nursery, possibly forcing nursery objects out - and adding them to 'old_objects_pointing_to_young' as well. */ - trace_and_drag_out_of_nursery(obj); + and adding them to 'old_objects_pointing_to_nursery' as well. */ + char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj); + stmcb_trace((struct object_s *)realobj, &minor_trace_if_young); } } static void reset_nursery(void) { - abort();//... - /* reset the global amount-of-nursery-used-so-far */ - nursery_ctl.used = nursery_ctl.initial_value_of_used; + /* reset the nursery by zeroing it */ + size_t size; + char *realnursery; - long i; - for (i = 0; i < NB_SEGMENTS; i++) { - struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i); - /* no race condition here, because all other threads are paused - in safe points, so cannot be e.g. in _stm_allocate_slowpath() */ - uintptr_t old_end = other_pseg->real_nursery_section_end; - other_pseg->real_nursery_section_end = 0; - other_pseg->pub.v_nursery_section_end = 0; + realnursery = REAL_ADDRESS(STM_SEGMENT->segment_base, _stm_nursery_start); + size = STM_SEGMENT->nursery_current - (stm_char *)_stm_nursery_start; + memset(realnursery, 0, size); - /* we don't need to actually reset the read markers, unless - we run too many nursery collections in the same transaction: - in the normal case it is enough to increase - 'transaction_read_version' without changing - 'min_read_version_outside_nursery'. - */ - if (other_pseg->transaction_state == TS_NONE) { - /* no transaction running now, nothing to do */ - } - else if (other_pseg->pub.transaction_read_version < 0xff) { - other_pseg->pub.transaction_read_version++; - abort();//... - /*assert(0 < other_pseg->min_read_version_outside_nursery && - other_pseg->min_read_version_outside_nursery - < other_pseg->pub.transaction_read_version);*/ - } - else { - /* however, when the value 0xff is reached, we are stuck - and we need to clean all the nursery read markers. - We'll be un-stuck when this transaction finishes. */ - char *read_markers = REAL_ADDRESS(other_pseg->pub.segment_base, - NURSERY_START >> 4); - memset(read_markers, 0, NURSERY_SIZE >> 4); - } + STM_SEGMENT->nursery_current = (stm_char *)_stm_nursery_start; +} - /* reset the creation markers */ - if (old_end > NURSERY_START) { - char *creation_markers = REAL_ADDRESS(other_pseg->pub.segment_base, - NURSERY_START >> 8); - assert(old_end <= NURSERY_END); - memset(creation_markers, 0, (old_end - NURSERY_START) >> 8); - } - else { - assert(old_end == 0 || old_end == NURSERY_START); - } - } -} -#endif - -static void minor_collection(void) +static void minor_collection(bool commit) { assert(!_has_mutex()); abort_if_needed(); - dprintf(("minor_collection\n")); + /* We must move out of the nursery any object found within the + nursery. All objects touched are either from the current + transaction, or are from 'old_objects_pointing_to_young'. + In all cases, we should only read and change objects belonging + to the current segment. - abort();//... -#if 0 + XXX improve: it might be possible to run this function in + a safe-point but without the mutex, if we are careful + */ - /* List of what we need to do and invariants we need to preserve - ------------------------------------------------------------- + dprintf(("minor_collection commit=%d\n", (int)commit)); - We must move out of the nursery any object found within the - nursery. This requires either one or NB_SEGMENTS copies, - depending on the current write-state of the object. - - We need to move the mark stored in the write_locks, read_markers - and creation_markers arrays. The creation_markers need some care - because they work at a coarser granularity of 256 bytes, so - objects with an "on" mark should not be moved too close to - objects with an "off" mark and vice-versa. - - Then we must trace (= look inside) some objects outside the - nursery, and fix any pointer found that goes to a nursery object. - This tracing itself needs to be done either once or NB_SEGMENTS - times, depending on whether the object is fully in shared pages - or not. We assume that 'stmcb_size_rounded_up' produce the same - results on all copies (i.e. don't depend on modifiable - information). - */ + if (STM_PSEGMENT->old_objects_pointing_to_nursery == NULL) + STM_PSEGMENT->old_objects_pointing_to_nursery = list_create(); collect_roots_in_nursery(); - long i; - for (i = 0; i < NB_SEGMENTS; i++) { - struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i); - collect_oldrefs_to_nursery(other_pseg->old_objects_pointing_to_young); - } - - collect_oldrefs_to_nursery(old_objects_pointing_to_young); + collect_oldrefs_to_nursery(); reset_nursery(); - pages_make_shared_again(FIRST_NURSERY_PAGE, NB_NURSERY_PAGES); -#endif + assert(list_is_empty(STM_PSEGMENT->old_objects_pointing_to_nursery)); + if (!commit && STM_PSEGMENT->overflow_objects_pointing_to_nursery == NULL) + STM_PSEGMENT->overflow_objects_pointing_to_nursery = list_create(); } - void stm_collect(long level) { assert(level == 0); - minor_collection(); + minor_collection(/*commit=*/ false); } @@ -391,7 +222,7 @@ return (object_t *)p; } - minor_collection(); + minor_collection(/*commit=*/ false); goto restart; } diff --git a/c7/stm/nursery.h b/c7/stm/nursery.h --- a/c7/stm/nursery.h +++ b/c7/stm/nursery.h @@ -5,4 +5,5 @@ static uint32_t highest_overflow_number; +static void minor_collection(bool commit); static void check_nursery_at_transaction_start(void) __attribute__((unused)); diff --git a/c7/stm/pages.c b/c7/stm/pages.c --- a/c7/stm/pages.c +++ b/c7/stm/pages.c @@ -3,12 +3,35 @@ #endif +/************************************************************/ + +static union { + uint8_t mutex_pages; + char reserved[64]; +} pages_ctl __attribute__((aligned(64))); + +static void mutex_pages_lock(void) +{ + while (__sync_lock_test_and_set(&pages_ctl.mutex_pages, 1) != 0) { + spin_loop(); + } +} + +static void mutex_pages_unlock(void) +{ + __sync_lock_release(&pages_ctl.mutex_pages); +} + +/************************************************************/ + + static void pages_initialize_shared(uintptr_t pagenum, uintptr_t count) { /* call remap_file_pages() to make all pages in the range(pagenum, pagenum+count) refer to the same physical range of pages from segment 0. */ uintptr_t i; + mutex_pages_lock(); for (i = 1; i < NB_SEGMENTS; i++) { char *segment_base = get_segment_base(i); int res = remap_file_pages(segment_base + pagenum * 4096UL, @@ -21,6 +44,7 @@ } for (i = 0; i < count; i++) flag_page_private[pagenum + i] = SHARED_PAGE; + mutex_pages_unlock(); } #if 0 @@ -45,8 +69,7 @@ } #endif -static void privatize_range_and_unlock(uintptr_t pagenum, uintptr_t count, - bool full) +static void privatize_range(uintptr_t pagenum, uintptr_t count, bool full) { ssize_t pgoff1 = pagenum; ssize_t pgoff2 = pagenum + NB_PAGES; @@ -73,61 +96,41 @@ pagecopy(localpg + 4096 * (count-1), otherpg + 4096 * (count-1)); } write_fence(); - for (i = 0; i < count; i++) { - assert(flag_page_private[pagenum + i] == REMAPPING_PAGE); - flag_page_private[pagenum + i] = PRIVATE_PAGE; - } + memset(flag_page_private + pagenum, PRIVATE_PAGE, count); } static void _pages_privatize(uintptr_t pagenum, uintptr_t count, bool full) { - uintptr_t page_start_range = pagenum; - uintptr_t pagestop = pagenum + count; - while (flag_page_private[pagenum + count - 1] == PRIVATE_PAGE) { if (!--count) return; } + mutex_pages_lock(); + + uintptr_t page_start_range = pagenum; + uintptr_t pagestop = pagenum + count; + for (; pagenum < pagestop; pagenum++) { -#ifdef HAVE_FULL_EXCHANGE_INSN - /* use __sync_lock_test_and_set() as a cheaper alternative to - __sync_bool_compare_and_swap(). */ - int prev = __sync_lock_test_and_set(&flag_page_private[pagenum], - REMAPPING_PAGE); - assert(prev != FREE_PAGE); - if (prev == PRIVATE_PAGE) { - flag_page_private[pagenum] = PRIVATE_PAGE; - } - bool was_shared = (prev == SHARED_PAGE); -#else - bool was_shared = __sync_bool_compare_and_swap( - &flag_page_private[pagenum + cnt1], - SHARED_PAGE, REMAPPING_PAGE); -#endif - if (!was_shared) { + uint8_t prev = flag_page_private[pagenum]; + if (prev == SHARED_PAGE) { if (pagenum > page_start_range) { - privatize_range_and_unlock(page_start_range, - pagenum - page_start_range, full); + privatize_range(page_start_range, + pagenum - page_start_range, full); } page_start_range = pagenum + 1; - - while (1) { - uint8_t state; - state = ((uint8_t volatile *)flag_page_private)[pagenum]; - if (state != REMAPPING_PAGE) { - assert(state == PRIVATE_PAGE); - break; - } - spin_loop(); - } + } + else { + assert(prev == PRIVATE_PAGE); } } if (pagenum > page_start_range) { - privatize_range_and_unlock(page_start_range, - pagenum - page_start_range, full); + privatize_range(page_start_range, + pagenum - page_start_range, full); } + + mutex_pages_unlock(); } #if 0 diff --git a/c7/stm/pages.h b/c7/stm/pages.h --- a/c7/stm/pages.h +++ b/c7/stm/pages.h @@ -30,4 +30,7 @@ _pages_privatize(pagenum, count, full); } +static void mutex_pages_lock(void); +static void mutex_pages_unlock(void); + //static bool is_in_shared_pages(object_t *obj); diff --git a/c7/stm/sync.c b/c7/stm/sync.c --- a/c7/stm/sync.c +++ b/c7/stm/sync.c @@ -62,6 +62,9 @@ perror("pthread_mutex_lock"); abort(); } + + if (STM_PSEGMENT->transaction_state == TS_MUST_ABORT) + abort_with_mutex(); } static inline void mutex_unlock(void) @@ -77,7 +80,13 @@ static inline bool _has_mutex(void) { - return pthread_mutex_trylock(&sync_ctl.global_mutex) == EBUSY; + if (pthread_mutex_trylock(&sync_ctl.global_mutex) == EBUSY) { + return true; + } + else { + pthread_mutex_unlock(&sync_ctl.global_mutex); + return false; + } } static inline void cond_wait(void) @@ -197,38 +206,33 @@ #endif -static bool try_wait_for_other_safe_points(int requested_safe_point_kind) +static void wait_for_other_safe_points(int requested_safe_point_kind) { - /* Must be called with the mutex. If all other threads are in a - safe point of at least the requested kind, returns true. Otherwise, - asks them to enter a safe point, issues a cond_wait(), and returns - false; you can call repeatedly this function in this case. + /* Must be called with the mutex. When all other threads are in a + safe point of at least the requested kind, returns. Otherwise, + asks them to enter a safe point, issues a cond_wait(), and wait. - When this function returns true, the other threads are all - blocked at safe points as requested. They may be either in their - own cond_wait(), or running at SP_NO_TRANSACTION, in which case - they should not do anything related to stm until the next time - they call mutex_lock(). + When this function returns, the other threads are all blocked at + safe points as requested. They may be either in their own + cond_wait(), or running at SP_NO_TRANSACTION, in which case they + should not do anything related to stm until the next time they + call mutex_lock(). The next time we unlock the mutex (with mutex_unlock() or cond_wait()), they will proceed. This function requires that the calling thread is in a safe-point right now, so there is no deadlock if one thread calls - try_wait_for_other_safe_points() while another is currently blocked + wait_for_other_safe_points() while another is currently blocked in the cond_wait() in this same function. */ - abort();//... -#if 0 + + restart: assert(_has_mutex()); assert(STM_PSEGMENT->safe_point == SP_SAFE_POINT_CAN_COLLECT); long i; - bool must_wait = false; for (i = 0; i < NB_SEGMENTS; i++) { - if (i == STM_SEGMENT->segment_num) - continue; /* ignore myself */ - /* If the other thread is SP_NO_TRANSACTION, then it can be ignored here: as long as we have the mutex, it will remain SP_NO_TRANSACTION. If it is already at a suitable safe point, @@ -241,32 +245,18 @@ (requested_safe_point_kind == SP_SAFE_POINT_CAN_COLLECT && other_pseg->safe_point == SP_SAFE_POINT_CANNOT_COLLECT)) { - /* we need to wait for this thread. Use NSE_SIGNAL to - ask it to enter a safe-point soon. */ - other_pseg->pub.v_nursery_section_end = NSE_SIGNAL; - must_wait = true; + /* we need to wait for this thread. Use NSE_SIGNAL to ask + it (and possibly all other threads in the same case) to + enter a safe-point soon. */ + _stm_nursery_end = NSE_SIGNAL; + cond_wait(); + goto restart; } } - if (must_wait) { - cond_wait(); - return false; - } - /* done! All NSE_SIGNAL threads become NSE_SIGNAL_DONE now, which - mean they will actually run again the next time they grab the - mutex. */ - for (i = 0; i < NB_SEGMENTS; i++) { - if (i == STM_SEGMENT->segment_num) - continue; /* ignore myself */ - - struct stm_segment_info_s *other_seg = get_segment(i); - if (other_seg->v_nursery_section_end == NSE_SIGNAL) - other_seg->v_nursery_section_end = NSE_SIGNAL_DONE; - } + /* all threads are at a safe-point now. */ cond_broadcast(); /* to wake up the other threads, but later, when they get the mutex again */ - return true; -#endif } void _stm_collectable_safe_point(void) @@ -274,7 +264,7 @@ /* If nursery_section_end was set to NSE_SIGNAL by another thread, we end up here as soon as we try to call stm_allocate() or do a call to stm_safe_point(). - See try_wait_for_other_safe_points() for details. + See wait_for_other_safe_points() for details. */ mutex_lock(); assert(STM_PSEGMENT->safe_point == SP_RUNNING); diff --git a/c7/stm/sync.h b/c7/stm/sync.h --- a/c7/stm/sync.h +++ b/c7/stm/sync.h @@ -16,4 +16,4 @@ static void release_thread_segment(stm_thread_local_t *tl); /* see the source for an exact description */ -static bool try_wait_for_other_safe_points(int requested_safe_point_kind); +static void wait_for_other_safe_points(int requested_safe_point_kind); _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit