Author: Armin Rigo <[email protected]>
Branch: c7-refactor
Changeset: r755:2248bbaba3b2
Date: 2014-02-16 19:17 +0100
http://bitbucket.org/pypy/stmgc/changeset/2248bbaba3b2/
Log: in-progress
diff --git a/c7/stm/contention.c b/c7/stm/contention.c
--- a/c7/stm/contention.c
+++ b/c7/stm/contention.c
@@ -5,9 +5,9 @@
static void contention_management(uint8_t other_segment_num, bool wait)
{
- /* A simple contention manager. Called when we do stm_write()
- on an object, but some other thread already holds the write
- lock on the same object. */
+ /* A simple contention manager. Called when some other thread
+ holds the write lock on an object. The current thread tries
+ to do either a write or a read on it. */
assert_has_mutex();
assert(other_segment_num != STM_SEGMENT->segment_num);
diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -127,19 +127,7 @@
# error "The logic in the functions below only works with two segments"
#endif
-static void wait_for_other_safe_points(void)
-{
- long remote_num = 1 - STM_SEGMENT->segment_num;
- while (get_priv_segment(remote_num)->safe_point == SP_RUNNING) {
-
- /* we have the mutex here */
- get_segment(remote_num)->nursery_section_end = NSE_SIGNAL;
-
- cond_wait();
- }
-}
-
-static void detect_write_read_conflicts(void)
+static bool detect_write_read_conflicts(void)
{
long remote_num = 1 - STM_SEGMENT->segment_num;
char *remote_base = get_segment_base(remote_num);
@@ -148,7 +136,7 @@
switch (get_priv_segment(remote_num)->transaction_state) {
case TS_NONE:
case TS_MUST_ABORT:
- return; /* no need to do any check */
+ return false; /* no need to do any check */
}
LIST_FOREACH_R(
@@ -161,9 +149,11 @@
/* If we reach this point, it means we aborted the other
thread. We're done here. */
- return;
+ return true;
}
}));
+
+ return false;
}
static void push_modified_to_other_segments(void)
@@ -207,8 +197,11 @@
void stm_commit_transaction(void)
{
mutex_lock();
+
assert(STM_PSEGMENT->safe_point = SP_RUNNING);
+ STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT;
+ restart:
switch (STM_PSEGMENT->transaction_state) {
case TS_REGULAR:
@@ -223,10 +216,14 @@
}
/* wait until the other thread is at a safe-point */
- wait_for_other_safe_points();
+ 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. */
/* detect conflicts */
- detect_write_read_conflicts();
+ if (UNLIKELY(detect_write_read_conflicts()))
+ goto restart;
/* cannot abort any more from here */
assert(STM_PSEGMENT->transaction_state != TS_MUST_ABORT);
@@ -237,7 +234,7 @@
/* done */
stm_thread_local_t *tl = STM_SEGMENT->running_thread;
- release_thread_segment(tl); /* includes the cond_broadcast(); */
+ release_thread_segment(tl);
STM_PSEGMENT->safe_point = SP_NO_TRANSACTION;
STM_PSEGMENT->transaction_state = TS_NONE;
reset_all_creation_markers();
@@ -312,11 +309,12 @@
stm_jmpbuf_t *jmpbuf_ptr = STM_SEGMENT->jmpbuf_ptr;
stm_thread_local_t *tl = STM_SEGMENT->running_thread;
- release_thread_segment(tl); /* includes the cond_broadcast(); */
+ release_thread_segment(tl);
STM_PSEGMENT->safe_point = SP_NO_TRANSACTION;
STM_PSEGMENT->transaction_state = TS_NONE;
reset_all_creation_markers();
+ cond_broadcast();
mutex_unlock();
assert(jmpbuf_ptr != NULL);
diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c
--- a/c7/stm/nursery.c
+++ b/c7/stm/nursery.c
@@ -33,9 +33,6 @@
char reserved[64];
} nursery_ctl __attribute__((aligned(64)));
-static uint64_t requested_minor_collections = 0;
-static uint64_t completed_minor_collections = 0;
-
/************************************************************/
@@ -58,64 +55,82 @@
/************************************************************/
-static void minor_collection(void)
+
+static void minor_trace_roots(void)
{
+ stm_thread_local_t *tl = stm_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_thread_locals);
+}
+
+static void do_minor_collection(void)
+{
+ minor_trace_roots();
+
+ /* visit shadowstack & add to old_obj_to_trace */
+ object_t **current = _STM_TL->shadow_stack;
+ object_t **base = _STM_TL->shadow_stack_base;
+ while (current-- != base) {
+ trace_if_young(current);
+ }
+
+
+
+
fprintf(stderr, "minor_collection\n");
abort(); //...;
+
+ /* reset all segments' nursery_section_end, as well as nursery_ctl.used */
+ long i;
+ for (i = 0; i < NB_SEGMENTS; i++) {
+ get_segment(i)->nursery_section_end = 0;
+ get_priv_segment(i)->real_nursery_section_end = 0;
+ }
+ nursery_ctl.used = 0;
+
+ /* done */
assert(requested_minor_collections == completed_minor_collections + 1);
completed_minor_collections += 1;
- nursery_ctl.used = 0;
}
-static void sync_point_for_collection(void)
+static void restore_nursery_section_end(uintptr_t prev_value)
{
+ __sync_bool_compare_and_swap(&STM_SEGMENT->v_nursery_section_end,
+ prev_value,
+ STM_PSEGMENT->real_nursery_section_end);
+}
+
+static void stm_minor_collection(uint64_t request_size)
+{
+ /* Run a minor collection --- but only if we can't get 'request_size'
+ bytes out of the nursery; if we can, no-op. */
mutex_lock();
+ assert(STM_PSEGMENT->safe_point == SP_RUNNING);
STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT;
restart:
- if (requested_minor_collections == completed_minor_collections) {
- if (nursery_ctl.used < NURSERY_SIZE)
- goto exit;
+ /* We just waited here, either from mutex_lock() or from cond_wait(),
+ so we should check again if another thread did the minor
+ collection itself */
+ if (nursery_ctl.used + bytes <= NURSERY_SIZE)
+ goto exit;
- requested_minor_collections++;
- }
+ if (!try_wait_for_other_safe_points(SP_SAFE_POINT_CAN_COLLECT))
+ goto restart;
- /* are all threads in a safe-point? */
- long i;
- bool must_wait = false;
- for (i = 0; i < NB_SEGMENTS; i++) {
- struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i);
-
- if (other_pseg->safe_point != SP_NO_TRANSACTION &&
- other_pseg->safe_point != SP_SAFE_POINT_CAN_COLLECT) {
- /* segment i is not at a safe point, or at one where
- collection is not possible (SP_SAFE_POINT_CANNOT_COLLECT) */
-
- /* we have the mutex here */
- other_pseg->pub.nursery_section_end = NSE_SIGNAL;
- must_wait = true;
- }
- }
- if (must_wait) {
- /* wait until all threads are indeed in a safe-point that allows
- collection */
- cond_wait();
- goto restart;
- }
-
- /* now we can run minor collection */
- minor_collection();
+ /* now we can run our minor collection */
+ do_minor_collection();
exit:
- /* we have the mutex here, and at this point there is no
- pending requested minor collection, so we simply reset
- our value of nursery_section_end and return. */
- STM_SEGMENT->nursery_section_end =
- STM_PSEGMENT->real_nursery_section_end;
-
STM_PSEGMENT->safe_point = SP_RUNNING;
mutex_unlock();
@@ -137,7 +152,9 @@
if (LIKELY(p + bytes <= NURSERY_SIZE)) {
return (stm_char *)(NURSERY_START + p);
}
- sync_point_for_collection();
+
+ /* nursery full! */
+ stm_minor_collection(bytes);
}
}
@@ -147,47 +164,21 @@
/* may collect! */
STM_SEGMENT->nursery_current -= size_rounded_up; /* restore correct val */
- restart:
- if (UNLIKELY(STM_SEGMENT->nursery_section_end == NSE_SIGNAL)) {
-
- /* 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(). */
- sync_point_for_collection();
-
- /* Once the sync point is done, retry. */
- goto restart;
- }
+ if (collectable_safe_point())
+ return stm_allocate(size_rounded_up);
if (size_rounded_up < MEDIUM_OBJECT) {
- /* This is a small object. We first try to check if the current
- section really doesn't fit the object; maybe all we were called
- for was the sync point above */
- stm_char *p1 = STM_SEGMENT->nursery_current;
- stm_char *end1 = p1 + size_rounded_up;
- if ((uintptr_t)end1 <= STM_PSEGMENT->real_nursery_section_end) {
- /* fits */
- STM_SEGMENT->nursery_current = end1;
- return p1;
- }
-
- /* Otherwise, the current section is really full.
+ /* This is a small object. The current section is really full.
Allocate the next section and initialize it with zeroes. */
stm_char *p = allocate_from_nursery(NURSERY_SECTION_SIZE);
STM_SEGMENT->nursery_current = p + size_rounded_up;
- /* Set nursery_section_end, but carefully: another thread may
+ /* Set v_nursery_section_end, but carefully: another thread may
have forced it to be equal to NSE_SIGNAL. */
uintptr_t end = (uintptr_t)p + NURSERY_SECTION_SIZE;
-
- if (UNLIKELY(!__sync_bool_compare_and_swap(
- &STM_SEGMENT->nursery_section_end,
- STM_PSEGMENT->real_nursery_section_end,
- end))) {
- assert(STM_SEGMENT->nursery_section_end == NSE_SIGNAL);
- goto restart;
- }
-
+ uintptr_t prev_end = STM_PSEGMENT->real_nursery_section_end;
STM_PSEGMENT->real_nursery_section_end = end;
+ restore_nursery_section_end(prev_end);
memset(REAL_ADDRESS(STM_SEGMENT->segment_base, p), 0,
NURSERY_SECTION_SIZE);
diff --git a/c7/stm/nursery.h b/c7/stm/nursery.h
--- a/c7/stm/nursery.h
+++ b/c7/stm/nursery.h
@@ -1,4 +1,7 @@
-#define NSE_SIGNAL 1
+/* special values of 'v_nursery_section_end' */
+#define NSE_SIGNAL 1
+#define NSE_SIGNAL_DONE 2
static void align_nursery_at_transaction_start(void);
+static void restore_nursery_section_end(uintptr_t prev_value);
diff --git a/c7/stm/sync.c b/c7/stm/sync.c
--- a/c7/stm/sync.c
+++ b/c7/stm/sync.c
@@ -152,8 +152,6 @@
assert(sync_ctl.in_use[tl->associated_segment_num] == 1);
sync_ctl.in_use[tl->associated_segment_num] = 0;
-
- cond_broadcast();
}
static bool _running_transaction(void)
@@ -189,7 +187,103 @@
assert(STM_PSEGMENT->safe_point == SP_SAFE_POINT_CAN_COLLECT);
STM_PSEGMENT->safe_point = SP_RUNNING;
+ restore_nursery_section_end(NSE_SIGNAL_DONE);
if (STM_PSEGMENT->transaction_state == TS_MUST_ABORT)
stm_abort_transaction();
}
#endif
+
+
+static bool try_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.
+
+ When this function returns true, the other threads are all
+ blocked at safe points as requested, until the next time we
+ unlock the mutex (with mutex_unlock() or cond_wait()).
+
+ This function requires that the calling thread is in a safe-point
+ right now, so there is no deadlock if one thread calls
+ wait_for_other_safe_points() while another is currently blocked
+ in the cond_wait() in this same function.
+ */
+ 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 */
+
+ struct stm_priv_segment_info_s *other_pseg = get_priv_segment(i);
+ if (other_pseg->safe_point == SP_RUNNING ||
+ (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;
+ }
+ }
+ 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_priv_segment_info_s *other_pseg = get_priv_segment(i);
+ if (other_pseg->v_nursery_section_end == NSE_SIGNAL)
+ other_pseg->v_nursery_section_end = NSE_SIGNAL_DONE;
+ }
+ cond_broadcast(); /* to wake up the other threads, but later,
+ when they get the mutex again */
+ return true;
+}
+
+static void wait_for_other_safe_points(int requested_safe_point_kind)
+{
+ while (!try_wait_for_other_safe_points(requested_safe_point_kind))
+ /* repeat */;
+}
+
+static bool collectable_safe_point(void)
+{
+ bool any_operation = false;
+ restart:;
+ switch (STM_SEGMENT->v_nursery_section_end) {
+
+ case NSE_SIGNAL:
+ /* 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().
+ See try_wait_for_other_safe_points() for details. */
+ mutex_lock();
+ STM_PSEGMENT->safe_point = SP_SAFE_POINT_CAN_COLLECT;
+ cond_broadcast();
+ cond_wait();
+ STM_PSEGMENT->safe_point = SP_RUNNING;
+ mutex_unlock();
+
+ /* Once the sync point is done, retry. */
+ any_operation = true;
+ goto restart;
+
+ case NSE_SIGNAL_DONE:
+ restore_nursery_section_end(NSE_SIGNAL_DONE);
+ any_operation = true;
+ break;
+
+ default:;
+ }
+ return any_operation;
+}
diff --git a/c7/stm/sync.h b/c7/stm/sync.h
--- a/c7/stm/sync.h
+++ b/c7/stm/sync.h
@@ -13,3 +13,8 @@
(must have the mutex acquired!) */
static void acquire_thread_segment(stm_thread_local_t *tl);
static void release_thread_segment(stm_thread_local_t *tl);
+
+/* see the source for an exact description */
+static void wait_for_other_safe_points(int requested_safe_point_kind);
+static bool try_wait_for_other_safe_points(int requested_safe_point_kind);
+static bool collectable_safe_point(void);
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -62,8 +62,7 @@
int segment_num;
char *segment_base;
stm_char *nursery_current;
- uintptr_t nursery_section_end; /* forced to 1 by
- sync_all_threads_for_collection() */
+ volatile uintptr_t v_nursery_section_end; /* see nursery.h */
struct stm_thread_local_s *running_thread;
stm_jmpbuf_t *jmpbuf_ptr;
};
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit