Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit