[pypy-commit] stmgc default: hg merge marker
arigo
noreply at buildbot.pypy.org
Sun May 4 21:01:53 CEST 2014
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r1200:84f5fbe03d5d
Date: 2014-05-04 21:01 +0200
http://bitbucket.org/pypy/stmgc/changeset/84f5fbe03d5d/
Log: hg merge marker
Adds simple markers, which record the location in the user program,
extracted for every entry in modified_old_object. This allows us to
record and retrieve the marker corresponding to the longest abort or
pause.
diff --git a/c7/demo/demo2.c b/c7/demo/demo2.c
--- a/c7/demo/demo2.c
+++ b/c7/demo/demo2.c
@@ -46,6 +46,15 @@
void stmcb_commit_soon() {}
+static void expand_marker(char *base, uintptr_t odd_number,
+ object_t *following_object,
+ char *outputbuf, size_t outputbufsize)
+{
+ assert(following_object == NULL);
+ snprintf(outputbuf, outputbufsize, "<%p %lu>", base, odd_number);
+}
+
+
nodeptr_t global_chained_list;
@@ -89,6 +98,18 @@
STM_START_TRANSACTION(&stm_thread_local, here);
+ if (stm_thread_local.longest_marker_state != 0) {
+ fprintf(stderr, "[%p] marker %d for %.6f seconds:\n",
+ &stm_thread_local,
+ stm_thread_local.longest_marker_state,
+ stm_thread_local.longest_marker_time);
+ fprintf(stderr, "\tself:\t\"%s\"\n\tother:\t\"%s\"\n",
+ stm_thread_local.longest_marker_self,
+ stm_thread_local.longest_marker_other);
+ stm_thread_local.longest_marker_state = 0;
+ stm_thread_local.longest_marker_time = 0.0;
+ }
+
nodeptr_t prev = initial;
stm_read((objptr_t)prev);
@@ -199,8 +220,16 @@
STM_PUSH_ROOT(stm_thread_local, global_chained_list); /* remains forever in the shadow stack */
+ int loops = 0;
+
while (check_sorted() == -1) {
+
+ STM_PUSH_MARKER(stm_thread_local, 2 * loops + 1, NULL);
+
bubble_run();
+
+ STM_POP_MARKER(stm_thread_local);
+ loops++;
}
STM_POP_ROOT(stm_thread_local, global_chained_list);
@@ -247,6 +276,7 @@
stm_setup();
stm_register_thread_local(&stm_thread_local);
+ stmcb_expand_marker = expand_marker;
setup_list();
diff --git a/c7/doc/marker.txt b/c7/doc/marker.txt
new file mode 100644
--- /dev/null
+++ b/c7/doc/marker.txt
@@ -0,0 +1,42 @@
+
+Reports
+=======
+
+- self-abort:
+ WRITE_WRITE_CONTENTION, INEVITABLE_CONTENTION:
+ marker in both threads, time lost by this thread
+ WRITE_READ_CONTENTION:
+ marker pointing back to the write, time lost by this thread
+
+- aborted by a different thread:
+ WRITE_WRITE_CONTENTION:
+ marker in both threads, time lost by this thread
+ WRITE_READ_CONTENTION:
+ remote marker pointing back to the write, time lost by this thread
+ (no local marker available to know where we've read the object from)
+ INEVITABLE_CONTENTION:
+ n/a
+
+- self-pausing:
+ same as self-abort, but reporting the time lost by pausing
+
+- waiting for a free segment:
+ - if we're waiting because of inevitability, report with a
+ marker and the time lost
+ - if we're just waiting because of no free segment, don't report it,
+ or maybe with only the total time lost and no marker
+
+- more internal reasons for cond_wait(), like synchronizing the threads,
+ should all be resolved quickly and are unlikely worth a report
+
+
+Internal Measurements
+=====================
+
+- use clock_gettime(CLOCK_MONOTONIC), it seems to be the fastest way
+ (less than 5 times slower than a RDTSC instruction, which is itself
+ not safe in the presence of threads migrating among CPUs)
+
+- record only the highest-time entry. The user of the library is
+ responsible for getting and clearing it often enough if it wants
+ more details.
diff --git a/c7/stm/contention.c b/c7/stm/contention.c
--- a/c7/stm/contention.c
+++ b/c7/stm/contention.c
@@ -99,7 +99,8 @@
static void contention_management(uint8_t other_segment_num,
- enum contention_kind_e kind)
+ enum contention_kind_e kind,
+ object_t *obj)
{
assert(_has_mutex());
assert(other_segment_num != STM_SEGMENT->segment_num);
@@ -161,6 +162,7 @@
itself already paused here.
*/
contmgr.other_pseg->signal_when_done = true;
+ marker_contention(kind, false, other_segment_num, obj);
change_timing_state(wait_category);
@@ -177,7 +179,13 @@
if (must_abort())
abort_with_mutex();
- change_timing_state(STM_TIME_RUN_CURRENT);
+ struct stm_priv_segment_info_s *pseg =
+ get_priv_segment(STM_SEGMENT->segment_num);
+ double elapsed =
+ change_timing_state_tl(pseg->pub.running_thread,
+ STM_TIME_RUN_CURRENT);
+ marker_copy(pseg->pub.running_thread, pseg,
+ wait_category, elapsed);
}
else if (!contmgr.abort_other) {
@@ -186,6 +194,7 @@
dprintf(("abort in contention\n"));
STM_SEGMENT->nursery_end = abort_category;
+ marker_contention(kind, false, other_segment_num, obj);
abort_with_mutex();
}
@@ -193,6 +202,7 @@
/* We have to signal the other thread to abort, and wait until
it does. */
contmgr.other_pseg->pub.nursery_end = abort_category;
+ marker_contention(kind, true, other_segment_num, obj);
int sp = contmgr.other_pseg->safe_point;
switch (sp) {
@@ -270,7 +280,8 @@
}
}
-static void write_write_contention_management(uintptr_t lock_idx)
+static void write_write_contention_management(uintptr_t lock_idx,
+ object_t *obj)
{
s_mutex_lock();
@@ -281,7 +292,7 @@
assert(get_priv_segment(other_segment_num)->write_lock_num ==
prev_owner);
- contention_management(other_segment_num, WRITE_WRITE_CONTENTION);
+ contention_management(other_segment_num, WRITE_WRITE_CONTENTION, obj);
/* now we return into _stm_write_slowpath() and will try again
to acquire the write lock on our object. */
@@ -290,12 +301,13 @@
s_mutex_unlock();
}
-static void write_read_contention_management(uint8_t other_segment_num)
+static void write_read_contention_management(uint8_t other_segment_num,
+ object_t *obj)
{
- contention_management(other_segment_num, WRITE_READ_CONTENTION);
+ contention_management(other_segment_num, WRITE_READ_CONTENTION, obj);
}
static void inevitable_contention_management(uint8_t other_segment_num)
{
- contention_management(other_segment_num, INEVITABLE_CONTENTION);
+ contention_management(other_segment_num, INEVITABLE_CONTENTION, NULL);
}
diff --git a/c7/stm/contention.h b/c7/stm/contention.h
--- a/c7/stm/contention.h
+++ b/c7/stm/contention.h
@@ -1,6 +1,8 @@
-static void write_write_contention_management(uintptr_t lock_idx);
-static void write_read_contention_management(uint8_t other_segment_num);
+static void write_write_contention_management(uintptr_t lock_idx,
+ object_t *obj);
+static void write_read_contention_management(uint8_t other_segment_num,
+ object_t *obj);
static void inevitable_contention_management(uint8_t other_segment_num);
static inline bool is_abort(uintptr_t nursery_end) {
diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -73,9 +73,15 @@
assert(lock_idx < sizeof(write_locks));
retry:
if (write_locks[lock_idx] == 0) {
+ /* A lock to prevent reading garbage from
+ lookup_other_thread_recorded_marker() */
+ acquire_marker_lock(STM_SEGMENT->segment_base);
+
if (UNLIKELY(!__sync_bool_compare_and_swap(&write_locks[lock_idx],
- 0, lock_num)))
+ 0, lock_num))) {
+ release_marker_lock(STM_SEGMENT->segment_base);
goto retry;
+ }
dprintf_test(("write_slowpath %p -> mod_old\n", obj));
@@ -83,6 +89,15 @@
Add it to the list 'modified_old_objects'. */
LIST_APPEND(STM_PSEGMENT->modified_old_objects, obj);
+ /* Add the current marker, recording where we wrote to this object */
+ uintptr_t marker[2];
+ marker_fetch(STM_SEGMENT->running_thread, marker);
+ STM_PSEGMENT->modified_old_objects_markers =
+ list_append2(STM_PSEGMENT->modified_old_objects_markers,
+ marker[0], marker[1]);
+
+ release_marker_lock(STM_SEGMENT->segment_base);
+
/* 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. */
@@ -124,7 +139,7 @@
else {
/* call the contention manager, and then retry (unless we were
aborted). */
- write_write_contention_management(lock_idx);
+ write_write_contention_management(lock_idx, obj);
goto retry;
}
@@ -194,6 +209,11 @@
STM_PSEGMENT->start_time = tl->_timing_cur_start;
STM_PSEGMENT->signalled_to_commit_soon = false;
STM_PSEGMENT->safe_point = SP_RUNNING;
+#ifndef NDEBUG
+ STM_PSEGMENT->marker_inev[1] = 99999999999999999L;
+#endif
+ if (jmpbuf == NULL)
+ marker_fetch_inev();
STM_PSEGMENT->transaction_state = (jmpbuf != NULL ? TS_REGULAR
: TS_INEVITABLE);
STM_SEGMENT->jmpbuf_ptr = jmpbuf;
@@ -221,12 +241,17 @@
}
assert(list_is_empty(STM_PSEGMENT->modified_old_objects));
+ assert(list_is_empty(STM_PSEGMENT->modified_old_objects_markers));
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));
assert(STM_PSEGMENT->objects_pointing_to_nursery == NULL);
assert(STM_PSEGMENT->large_overflow_objects == NULL);
+#ifndef NDEBUG
+ /* this should not be used when objects_pointing_to_nursery == NULL */
+ STM_PSEGMENT->modified_old_objects_markers_num_old = 99999999999999999L;
+#endif
check_nursery_at_transaction_start();
}
@@ -261,7 +286,7 @@
({
if (was_read_remote(remote_base, item, remote_version)) {
/* A write-read conflict! */
- write_read_contention_management(i);
+ write_read_contention_management(i, item);
/* If we reach this point, we didn't abort, but maybe we
had to wait for the other thread to commit. If we
@@ -446,6 +471,7 @@
release_privatization_lock();
list_clear(STM_PSEGMENT->modified_old_objects);
+ list_clear(STM_PSEGMENT->modified_old_objects_markers);
}
static void _finish_transaction(int attribute_to)
@@ -584,6 +610,7 @@
}));
list_clear(pseg->modified_old_objects);
+ list_clear(pseg->modified_old_objects_markers);
}
static void abort_data_structures_from_segment_num(int segment_num)
@@ -608,6 +635,10 @@
(int)pseg->transaction_state);
}
+ /* if we don't have marker information already, look up and preserve
+ the marker information from the shadowstack as a string */
+ marker_default_for_abort(pseg);
+
/* throw away the content of the nursery */
long bytes_in_nursery = throw_away_nursery(pseg);
@@ -618,6 +649,7 @@
value before the transaction start */
stm_thread_local_t *tl = pseg->pub.running_thread;
assert(tl->shadowstack >= pseg->shadowstack_at_start_of_transaction);
+ pseg->shadowstack_at_abort = tl->shadowstack;
tl->shadowstack = pseg->shadowstack_at_start_of_transaction;
tl->thread_local_obj = pseg->threadlocal_at_start_of_transaction;
tl->last_abort__bytes_in_nursery = bytes_in_nursery;
@@ -689,6 +721,7 @@
if (STM_PSEGMENT->transaction_state == TS_REGULAR) {
dprintf(("become_inevitable: %s\n", msg));
+ marker_fetch_inev();
wait_for_end_of_inevitable_transaction(NULL);
STM_PSEGMENT->transaction_state = TS_INEVITABLE;
STM_SEGMENT->jmpbuf_ptr = NULL;
diff --git a/c7/stm/core.h b/c7/stm/core.h
--- a/c7/stm/core.h
+++ b/c7/stm/core.h
@@ -78,9 +78,17 @@
/* List of old objects (older than the current transaction) that the
current transaction attempts to modify. This is used to track
the STM status: they are old objects that where written to and
- that need to be copied to other segments upon commit. */
+ that need to be copied to other segments upon commit. Note that
+ every object takes three list items: the object, and two words for
+ the location marker. */
struct list_s *modified_old_objects;
+ /* For each entry in 'modified_old_objects', we have two entries
+ in the following list, which give the marker at the time we added
+ the entry to modified_old_objects. */
+ struct list_s *modified_old_objects_markers;
+ uintptr_t modified_old_objects_markers_num_old;
+
/* List of out-of-nursery objects that may contain pointers to
nursery objects. This is used to track the GC status: they are
all objects outside the nursery on which an stm_write() occurred
@@ -157,10 +165,18 @@
many reads / rare writes.) */
uint8_t privatization_lock;
+ /* This lock is acquired when we mutate 'modified_old_objects' but
+ we don't have the global mutex. It is also acquired during minor
+ collection. It protects against a different thread that tries to
+ get this segment's marker corresponding to some object, or to
+ expand the marker into a full description. */
+ uint8_t marker_lock;
+
/* In case of abort, we restore the 'shadowstack' field and the
'thread_local_obj' field. */
struct stm_shadowentry_s *shadowstack_at_start_of_transaction;
object_t *threadlocal_at_start_of_transaction;
+ struct stm_shadowentry_s *shadowstack_at_abort;
/* Already signalled to commit soon: */
bool signalled_to_commit_soon;
@@ -169,6 +185,11 @@
#ifndef NDEBUG
pthread_t running_pthread;
#endif
+
+ /* Temporarily stores the marker information */
+ char marker_self[_STM_MARKER_LEN];
+ char marker_other[_STM_MARKER_LEN];
+ uintptr_t marker_inev[2]; /* marker where this thread became inevitable */
};
enum /* safe_point */ {
@@ -252,3 +273,17 @@
&STM_PSEGMENT->privatization_lock);
spinlock_release(*lock);
}
+
+static inline void acquire_marker_lock(char *segment_base)
+{
+ uint8_t *lock = (uint8_t *)REAL_ADDRESS(segment_base,
+ &STM_PSEGMENT->marker_lock);
+ spinlock_acquire(*lock);
+}
+
+static inline void release_marker_lock(char *segment_base)
+{
+ uint8_t *lock = (uint8_t *)REAL_ADDRESS(segment_base,
+ &STM_PSEGMENT->marker_lock);
+ spinlock_release(*lock);
+}
diff --git a/c7/stm/gcpage.c b/c7/stm/gcpage.c
--- a/c7/stm/gcpage.c
+++ b/c7/stm/gcpage.c
@@ -382,7 +382,7 @@
struct stm_shadowentry_s *current = tl->shadowstack;
struct stm_shadowentry_s *base = tl->shadowstack_base;
while (current-- != base) {
- if (((uintptr_t)current->ss) > STM_STACK_MARKER_OLD)
+ if ((((uintptr_t)current->ss) & 3) == 0)
mark_visit_object(current->ss, segment_base);
}
mark_visit_object(tl->thread_local_obj, segment_base);
@@ -421,6 +421,23 @@
}
}
+static void mark_visit_from_markers(void)
+{
+ long j;
+ for (j = 1; j <= NB_SEGMENTS; j++) {
+ char *base = get_segment_base(j);
+ struct list_s *lst = get_priv_segment(j)->modified_old_objects_markers;
+ uintptr_t i;
+ for (i = list_count(lst); i > 0; i -= 2) {
+ mark_visit_object((object_t *)list_item(lst, i - 1), base);
+ }
+ if (get_priv_segment(j)->transaction_state == TS_INEVITABLE) {
+ uintptr_t marker_inev_obj = get_priv_segment(j)->marker_inev[1];
+ mark_visit_object((object_t *)marker_inev_obj, base);
+ }
+ }
+}
+
static void clean_up_segment_lists(void)
{
long i;
@@ -523,6 +540,7 @@
/* marking */
LIST_CREATE(mark_objects_to_trace);
mark_visit_from_modified_objects();
+ mark_visit_from_markers();
mark_visit_from_roots();
LIST_FREE(mark_objects_to_trace);
diff --git a/c7/stm/list.h b/c7/stm/list.h
--- a/c7/stm/list.h
+++ b/c7/stm/list.h
@@ -33,6 +33,18 @@
#define LIST_APPEND(lst, e) ((lst) = list_append((lst), (uintptr_t)(e)))
+static inline struct list_s *list_append2(struct list_s *lst,
+ uintptr_t item0, uintptr_t item1)
+{
+ uintptr_t index = lst->count;
+ lst->count += 2;
+ if (UNLIKELY(index >= lst->last_allocated))
+ lst = _list_grow(lst, index + 1);
+ lst->items[index + 0] = item0;
+ lst->items[index + 1] = item1;
+ return lst;
+}
+
static inline void list_clear(struct list_s *lst)
{
@@ -66,6 +78,11 @@
lst->items[index] = newitem;
}
+static inline uintptr_t *list_ptr_to_item(struct list_s *lst, uintptr_t index)
+{
+ return &lst->items[index];
+}
+
#define LIST_FOREACH_R(lst, TYPE, CODE) \
do { \
struct list_s *_lst = (lst); \
diff --git a/c7/stm/marker.c b/c7/stm/marker.c
new file mode 100644
--- /dev/null
+++ b/c7/stm/marker.c
@@ -0,0 +1,198 @@
+#ifndef _STM_CORE_H_
+# error "must be compiled via stmgc.c"
+#endif
+
+
+void (*stmcb_expand_marker)(char *segment_base, uintptr_t odd_number,
+ object_t *following_object,
+ char *outputbuf, size_t outputbufsize);
+
+void (*stmcb_debug_print)(const char *cause, double time,
+ const char *marker);
+
+
+static void marker_fetch(stm_thread_local_t *tl, uintptr_t marker[2])
+{
+ /* fetch the current marker from the tl's shadow stack,
+ and return it in 'marker[2]'. */
+ struct stm_shadowentry_s *current = tl->shadowstack - 1;
+ struct stm_shadowentry_s *base = tl->shadowstack_base;
+
+ /* The shadowstack_base contains STM_STACK_MARKER_OLD, which is
+ a convenient stopper for the loop below but which shouldn't
+ be returned. */
+ assert(base->ss == (object_t *)STM_STACK_MARKER_OLD);
+
+ while (!(((uintptr_t)current->ss) & 1)) {
+ current--;
+ assert(current >= base);
+ }
+ if (current != base) {
+ /* found the odd marker */
+ marker[0] = (uintptr_t)current[0].ss;
+ marker[1] = (uintptr_t)current[1].ss;
+ }
+ else {
+ /* no marker found */
+ marker[0] = 0;
+ marker[1] = 0;
+ }
+}
+
+static void marker_expand(uintptr_t marker[2], char *segment_base,
+ char *outmarker)
+{
+ /* Expand the marker given by 'marker[2]' into a full string. This
+ works assuming that the marker was produced inside the segment
+ given by 'segment_base'. If that's from a different thread, you
+ must first acquire the corresponding 'marker_lock'. */
+ assert(_has_mutex());
+ outmarker[0] = 0;
+ if (marker[0] == 0)
+ return; /* no marker entry found */
+ if (stmcb_expand_marker != NULL) {
+ stmcb_expand_marker(segment_base, marker[0], (object_t *)marker[1],
+ outmarker, _STM_MARKER_LEN);
+ }
+}
+
+static void marker_default_for_abort(struct stm_priv_segment_info_s *pseg)
+{
+ if (pseg->marker_self[0] != 0)
+ return; /* already collected an entry */
+
+ uintptr_t marker[2];
+ marker_fetch(pseg->pub.running_thread, marker);
+ marker_expand(marker, pseg->pub.segment_base, pseg->marker_self);
+ pseg->marker_other[0] = 0;
+}
+
+char *_stm_expand_marker(void)
+{
+ /* for tests only! */
+ static char _result[_STM_MARKER_LEN];
+ uintptr_t marker[2];
+ _result[0] = 0;
+ s_mutex_lock();
+ marker_fetch(STM_SEGMENT->running_thread, marker);
+ marker_expand(marker, STM_SEGMENT->segment_base, _result);
+ s_mutex_unlock();
+ return _result;
+}
+
+static void marker_copy(stm_thread_local_t *tl,
+ struct stm_priv_segment_info_s *pseg,
+ enum stm_time_e attribute_to, double time)
+{
+ /* Copies the marker information from pseg to tl. This is called
+ indirectly from abort_with_mutex(), but only if the lost time is
+ greater than that of the previous recorded marker. By contrast,
+ pseg->marker_self has been filled already in all cases. The
+ reason for the two steps is that we must fill pseg->marker_self
+ earlier than now (some objects may be GCed), but we only know
+ here the total time it gets attributed.
+ */
+ if (stmcb_debug_print) {
+ stmcb_debug_print(timer_names[attribute_to], time, pseg->marker_self);
+ }
+ if (time * 0.99 > tl->longest_marker_time) {
+ tl->longest_marker_state = attribute_to;
+ tl->longest_marker_time = time;
+ memcpy(tl->longest_marker_self, pseg->marker_self, _STM_MARKER_LEN);
+ memcpy(tl->longest_marker_other, pseg->marker_other, _STM_MARKER_LEN);
+ }
+ pseg->marker_self[0] = 0;
+ pseg->marker_other[0] = 0;
+}
+
+static void marker_fetch_obj_write(uint8_t in_segment_num, object_t *obj,
+ uintptr_t marker[2])
+{
+ assert(_has_mutex());
+
+ /* here, we acquired the other thread's marker_lock, which means that:
+
+ (1) it has finished filling 'modified_old_objects' after it sets
+ up the write_locks[] value that we're conflicting with
+
+ (2) it is not mutating 'modified_old_objects' right now (we have
+ the global mutex_lock at this point too).
+ */
+ long i;
+ struct stm_priv_segment_info_s *pseg = get_priv_segment(in_segment_num);
+ struct list_s *mlst = pseg->modified_old_objects;
+ struct list_s *mlstm = pseg->modified_old_objects_markers;
+ for (i = list_count(mlst); --i >= 0; ) {
+ if (list_item(mlst, i) == (uintptr_t)obj) {
+ assert(list_count(mlstm) == 2 * list_count(mlst));
+ marker[0] = list_item(mlstm, i * 2 + 0);
+ marker[1] = list_item(mlstm, i * 2 + 1);
+ return;
+ }
+ }
+ marker[0] = 0;
+ marker[1] = 0;
+}
+
+static void marker_contention(int kind, bool abort_other,
+ uint8_t other_segment_num, object_t *obj)
+{
+ uintptr_t self_marker[2];
+ uintptr_t other_marker[2];
+ struct stm_priv_segment_info_s *my_pseg, *other_pseg;
+
+ my_pseg = get_priv_segment(STM_SEGMENT->segment_num);
+ other_pseg = get_priv_segment(other_segment_num);
+
+ char *my_segment_base = STM_SEGMENT->segment_base;
+ char *other_segment_base = get_segment_base(other_segment_num);
+
+ acquire_marker_lock(other_segment_base);
+
+ /* Collect the location for myself. It's usually the current
+ location, except in a write-read abort, in which case it's the
+ older location of the write. */
+ if (kind == WRITE_READ_CONTENTION)
+ marker_fetch_obj_write(my_pseg->pub.segment_num, obj, self_marker);
+ else
+ marker_fetch(my_pseg->pub.running_thread, self_marker);
+
+ /* Expand this location into either my_pseg->marker_self or
+ other_pseg->marker_other, depending on who aborts. */
+ marker_expand(self_marker, my_segment_base,
+ abort_other ? other_pseg->marker_other
+ : my_pseg->marker_self);
+
+ /* For some categories, we can also collect the relevant information
+ for the other segment. */
+ char *outmarker = abort_other ? other_pseg->marker_self
+ : my_pseg->marker_other;
+ switch (kind) {
+ case WRITE_WRITE_CONTENTION:
+ marker_fetch_obj_write(other_segment_num, obj, other_marker);
+ marker_expand(other_marker, other_segment_base, outmarker);
+ break;
+ case INEVITABLE_CONTENTION:
+ assert(abort_other == false);
+ other_marker[0] = other_pseg->marker_inev[0];
+ other_marker[1] = other_pseg->marker_inev[1];
+ marker_expand(other_marker, other_segment_base, outmarker);
+ break;
+ case WRITE_READ_CONTENTION:
+ strcpy(outmarker, "<read at unknown location>");
+ break;
+ default:
+ outmarker[0] = 0;
+ break;
+ }
+
+ release_marker_lock(other_segment_base);
+}
+
+static void marker_fetch_inev(void)
+{
+ uintptr_t marker[2];
+ marker_fetch(STM_SEGMENT->running_thread, marker);
+ STM_PSEGMENT->marker_inev[0] = marker[0];
+ STM_PSEGMENT->marker_inev[1] = marker[1];
+}
diff --git a/c7/stm/marker.h b/c7/stm/marker.h
new file mode 100644
--- /dev/null
+++ b/c7/stm/marker.h
@@ -0,0 +1,12 @@
+
+static void marker_fetch(stm_thread_local_t *tl, uintptr_t marker[2]);
+static void marker_fetch_inev(void);
+static void marker_expand(uintptr_t marker[2], char *segment_base,
+ char *outmarker);
+static void marker_default_for_abort(struct stm_priv_segment_info_s *pseg);
+static void marker_copy(stm_thread_local_t *tl,
+ struct stm_priv_segment_info_s *pseg,
+ enum stm_time_e attribute_to, double time);
+
+static void marker_contention(int kind, bool abort_other,
+ uint8_t other_segment_num, object_t *obj);
diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c
--- a/c7/stm/nursery.c
+++ b/c7/stm/nursery.c
@@ -160,28 +160,26 @@
--current;
OPT_ASSERT(current >= base);
- switch ((uintptr_t)current->ss) {
+ uintptr_t x = (uintptr_t)current->ss;
- case 0: /* NULL */
- continue;
-
- case STM_STACK_MARKER_NEW:
+ if ((x & 3) == 0) {
+ /* the stack entry is a regular pointer (possibly NULL) */
+ minor_trace_if_young(¤t->ss);
+ }
+ else if (x == STM_STACK_MARKER_NEW) {
/* the marker was not already seen: mark it as seen,
but continue looking more deeply in the shadowstack */
current->ss = (object_t *)STM_STACK_MARKER_OLD;
- continue;
-
- case STM_STACK_MARKER_OLD:
+ }
+ else if (x == STM_STACK_MARKER_OLD) {
/* the marker was already seen: we can stop the
root stack tracing at this point */
- goto interrupt;
-
- default:
- /* the stack entry is a regular pointer */
- minor_trace_if_young(¤t->ss);
+ break;
+ }
+ else {
+ /* it is an odd-valued marker, ignore */
}
}
- interrupt:
minor_trace_if_young(&tl->thread_local_obj);
}
@@ -236,6 +234,24 @@
_collect_now(item));
}
+static void collect_roots_from_markers(uintptr_t num_old)
+{
+ /* visit the marker objects */
+ struct list_s *mlst = STM_PSEGMENT->modified_old_objects_markers;
+ STM_PSEGMENT->modified_old_objects_markers_num_old = list_count(mlst);
+ uintptr_t i, total = list_count(mlst);
+ assert((total & 1) == 0);
+ for (i = num_old + 1; i < total; i += 2) {
+ minor_trace_if_young((object_t **)list_ptr_to_item(mlst, i));
+ }
+ if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
+ uintptr_t *pmarker_inev_obj = (uintptr_t *)
+ REAL_ADDRESS(STM_SEGMENT->segment_base,
+ &STM_PSEGMENT->marker_inev[1]);
+ minor_trace_if_young((object_t **)pmarker_inev_obj);
+ }
+}
+
static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg)
{
/* reset the nursery by zeroing it */
@@ -285,6 +301,8 @@
dprintf(("minor_collection commit=%d\n", (int)commit));
+ acquire_marker_lock(STM_SEGMENT->segment_base);
+
STM_PSEGMENT->minor_collect_will_commit_now = commit;
if (!commit) {
/* We should commit soon, probably. This is kind of a
@@ -306,6 +324,7 @@
/* All the objects we move out of the nursery become "overflow"
objects. We use the list 'objects_pointing_to_nursery'
to hold the ones we didn't trace so far. */
+ uintptr_t num_old;
if (STM_PSEGMENT->objects_pointing_to_nursery == NULL) {
STM_PSEGMENT->objects_pointing_to_nursery = list_create();
@@ -315,7 +334,12 @@
into objects_pointing_to_nursery, but instead we use the
following shortcut */
collect_modified_old_objects();
+ num_old = 0;
}
+ else
+ num_old = STM_PSEGMENT->modified_old_objects_markers_num_old;
+
+ collect_roots_from_markers(num_old);
collect_roots_in_nursery();
@@ -328,6 +352,8 @@
assert(MINOR_NOTHING_TO_DO(STM_PSEGMENT));
assert(list_is_empty(STM_PSEGMENT->objects_pointing_to_nursery));
+
+ release_marker_lock(STM_SEGMENT->segment_base);
}
static void minor_collection(bool commit)
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -78,6 +78,7 @@
pr->objects_pointing_to_nursery = NULL;
pr->large_overflow_objects = NULL;
pr->modified_old_objects = list_create();
+ pr->modified_old_objects_markers = list_create();
pr->young_weakrefs = list_create();
pr->old_weakrefs = list_create();
pr->young_outside_nursery = tree_create();
@@ -115,6 +116,7 @@
assert(pr->objects_pointing_to_nursery == NULL);
assert(pr->large_overflow_objects == NULL);
list_free(pr->modified_old_objects);
+ list_free(pr->modified_old_objects_markers);
list_free(pr->young_weakrefs);
list_free(pr->old_weakrefs);
tree_free(pr->young_outside_nursery);
diff --git a/c7/stm/timing.c b/c7/stm/timing.c
--- a/c7/stm/timing.c
+++ b/c7/stm/timing.c
@@ -25,18 +25,26 @@
return oldstate;
}
-static void change_timing_state_tl(stm_thread_local_t *tl,
- enum stm_time_e newstate)
+static double change_timing_state_tl(stm_thread_local_t *tl,
+ enum stm_time_e newstate)
{
TIMING_CHANGE(tl, newstate);
+ return elasped;
}
static void timing_end_transaction(enum stm_time_e attribute_to)
{
stm_thread_local_t *tl = STM_SEGMENT->running_thread;
TIMING_CHANGE(tl, STM_TIME_OUTSIDE_TRANSACTION);
- add_timing(tl, attribute_to, tl->timing[STM_TIME_RUN_CURRENT]);
+ double time_this_transaction = tl->timing[STM_TIME_RUN_CURRENT];
+ add_timing(tl, attribute_to, time_this_transaction);
tl->timing[STM_TIME_RUN_CURRENT] = 0.0f;
+
+ if (attribute_to != STM_TIME_RUN_COMMITTED) {
+ struct stm_priv_segment_info_s *pseg =
+ get_priv_segment(STM_SEGMENT->segment_num);
+ marker_copy(tl, pseg, attribute_to, time_this_transaction);
+ }
}
static const char *timer_names[] = {
@@ -74,6 +82,10 @@
fprintf(stderr, " %-24s %9u %8.3f s\n",
timer_names[i], tl->events[i], (double)tl->timing[i]);
}
+ fprintf(stderr, " %-24s %6s %11.6f s\n",
+ "longest recorded marker", "", tl->longest_marker_time);
+ fprintf(stderr, " \"%.*s\"\n",
+ (int)_STM_MARKER_LEN, tl->longest_marker_self);
s_mutex_unlock();
}
}
diff --git a/c7/stm/timing.h b/c7/stm/timing.h
--- a/c7/stm/timing.h
+++ b/c7/stm/timing.h
@@ -8,7 +8,7 @@
}
static enum stm_time_e change_timing_state(enum stm_time_e newstate);
-static void change_timing_state_tl(stm_thread_local_t *tl,
- enum stm_time_e newstate);
+static double change_timing_state_tl(stm_thread_local_t *tl,
+ enum stm_time_e newstate);
static void timing_end_transaction(enum stm_time_e attribute_to);
diff --git a/c7/stmgc.c b/c7/stmgc.c
--- a/c7/stmgc.c
+++ b/c7/stmgc.c
@@ -14,6 +14,7 @@
#include "stm/fprintcolor.h"
#include "stm/weakref.h"
#include "stm/timing.h"
+#include "stm/marker.h"
#include "stm/misc.c"
#include "stm/list.c"
@@ -33,3 +34,4 @@
#include "stm/fprintcolor.c"
#include "stm/weakref.c"
#include "stm/timing.c"
+#include "stm/marker.c"
diff --git a/c7/stmgc.h b/c7/stmgc.h
--- a/c7/stmgc.h
+++ b/c7/stmgc.h
@@ -74,6 +74,8 @@
_STM_TIME_N
};
+#define _STM_MARKER_LEN 80
+
typedef struct stm_thread_local_s {
/* every thread should handle the shadow stack itself */
struct stm_shadowentry_s *shadowstack, *shadowstack_base;
@@ -91,6 +93,11 @@
float timing[_STM_TIME_N];
double _timing_cur_start;
enum stm_time_e _timing_cur_state;
+ /* the marker with the longest associated time so far */
+ enum stm_time_e longest_marker_state;
+ double longest_marker_time;
+ char longest_marker_self[_STM_MARKER_LEN];
+ char longest_marker_other[_STM_MARKER_LEN];
/* the next fields are handled internally by the library */
int associated_segment_num;
struct stm_thread_local_s *prev, *next;
@@ -269,8 +276,8 @@
#define STM_PUSH_ROOT(tl, p) ((tl).shadowstack++->ss = (object_t *)(p))
#define STM_POP_ROOT(tl, p) ((p) = (typeof(p))((--(tl).shadowstack)->ss))
#define STM_POP_ROOT_RET(tl) ((--(tl).shadowstack)->ss)
-#define STM_STACK_MARKER_NEW 1
-#define STM_STACK_MARKER_OLD 2
+#define STM_STACK_MARKER_NEW (-41)
+#define STM_STACK_MARKER_OLD (-43)
/* Every thread needs to have a corresponding stm_thread_local_t
@@ -373,6 +380,43 @@
void stm_flush_timing(stm_thread_local_t *tl, int verbose);
+/* The markers pushed in the shadowstack are an odd number followed by a
+ regular pointer. When needed, this library invokes this callback to
+ turn this pair into a human-readable explanation. */
+extern void (*stmcb_expand_marker)(char *segment_base, uintptr_t odd_number,
+ object_t *following_object,
+ char *outputbuf, size_t outputbufsize);
+extern void (*stmcb_debug_print)(const char *cause, double time,
+ const char *marker);
+
+/* Conventience macros to push the markers into the shadowstack */
+#define STM_PUSH_MARKER(tl, odd_num, p) do { \
+ uintptr_t _odd_num = (odd_num); \
+ assert(_odd_num & 1); \
+ STM_PUSH_ROOT(tl, _odd_num); \
+ STM_PUSH_ROOT(tl, p); \
+} while (0)
+
+#define STM_POP_MARKER(tl) ({ \
+ object_t *_popped = STM_POP_ROOT_RET(tl); \
+ STM_POP_ROOT_RET(tl); \
+ _popped; \
+})
+
+#define STM_UPDATE_MARKER_NUM(tl, odd_num) do { \
+ uintptr_t _odd_num = (odd_num); \
+ assert(_odd_num & 1); \
+ struct stm_shadowentry_s *_ss = (tl).shadowstack - 2; \
+ while (!(((uintptr_t)(_ss->ss)) & 1)) { \
+ _ss--; \
+ assert(_ss >= (tl).shadowstack_base); \
+ } \
+ _ss->ss = (object_t *)_odd_num; \
+} while (0)
+
+char *_stm_expand_marker(void);
+
+
/* ==================== END ==================== */
#endif
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -28,6 +28,10 @@
int associated_segment_num;
uint32_t events[];
float timing[];
+ int longest_marker_state;
+ double longest_marker_time;
+ char longest_marker_self[];
+ char longest_marker_other[];
...;
} stm_thread_local_t;
@@ -118,6 +122,17 @@
#define STM_TIME_SYNC_PAUSE ...
void stm_flush_timing(stm_thread_local_t *, int);
+
+void (*stmcb_expand_marker)(char *segment_base, uintptr_t odd_number,
+ object_t *following_object,
+ char *outputbuf, size_t outputbufsize);
+void (*stmcb_debug_print)(const char *cause, double time,
+ const char *marker);
+
+void stm_push_marker(stm_thread_local_t *, uintptr_t, object_t *);
+void stm_update_marker_num(stm_thread_local_t *, uintptr_t);
+void stm_pop_marker(stm_thread_local_t *);
+char *_stm_expand_marker(void);
""")
@@ -272,10 +287,24 @@
}
}
+void stm_push_marker(stm_thread_local_t *tl, uintptr_t onum, object_t *ob)
+{
+ STM_PUSH_MARKER(*tl, onum, ob);
+}
+
+void stm_update_marker_num(stm_thread_local_t *tl, uintptr_t onum)
+{
+ STM_UPDATE_MARKER_NUM(*tl, onum);
+}
+
+void stm_pop_marker(stm_thread_local_t *tl)
+{
+ STM_POP_MARKER(*tl);
+}
+
void stmcb_commit_soon()
{
}
-
''', sources=source_files,
define_macros=[('STM_TESTS', '1'),
('STM_LARGEMALLOC_TEST', '1'),
@@ -439,6 +468,8 @@
self.current_thread = 0
def teardown_method(self, meth):
+ lib.stmcb_expand_marker = ffi.NULL
+ lib.stmcb_debug_print = ffi.NULL
tl = self.tls[self.current_thread]
if lib._stm_in_transaction(tl) and lib.stm_is_inevitable():
self.commit_transaction() # must succeed!
diff --git a/c7/test/test_marker.py b/c7/test/test_marker.py
new file mode 100644
--- /dev/null
+++ b/c7/test/test_marker.py
@@ -0,0 +1,340 @@
+from support import *
+import py, time
+
+class TestMarker(BaseTest):
+
+ def test_marker_odd_simple(self):
+ self.start_transaction()
+ self.push_root(ffi.cast("object_t *", 29))
+ stm_minor_collect()
+ stm_major_collect()
+ # assert did not crash
+ x = self.pop_root()
+ assert int(ffi.cast("uintptr_t", x)) == 29
+
+ def test_abort_marker_no_shadowstack(self):
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_state == lib.STM_TIME_OUTSIDE_TRANSACTION
+ assert tl.longest_marker_time == 0.0
+ #
+ self.start_transaction()
+ start = time.time()
+ while abs(time.time() - start) <= 0.1:
+ pass
+ self.abort_transaction()
+ #
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_OTHER
+ assert 0.099 <= tl.longest_marker_time <= 0.9
+ assert tl.longest_marker_self[0] == '\x00'
+ assert tl.longest_marker_other[0] == '\x00'
+
+ def test_abort_marker_shadowstack(self):
+ self.start_transaction()
+ p = stm_allocate(16)
+ self.push_root(ffi.cast("object_t *", 29))
+ self.push_root(p)
+ start = time.time()
+ while abs(time.time() - start) <= 0.1:
+ pass
+ self.abort_transaction()
+ #
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_OTHER
+ assert 0.099 <= tl.longest_marker_time <= 0.9
+ assert tl.longest_marker_self[0] == '\x00'
+ assert tl.longest_marker_other[0] == '\x00'
+
+ def test_abort_marker_no_shadowstack_cb(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ seen.append(1)
+ lib.stmcb_expand_marker = expand_marker
+ seen = []
+ #
+ self.start_transaction()
+ self.abort_transaction()
+ #
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_self[0] == '\x00'
+ assert not seen
+
+ def test_abort_marker_shadowstack_cb(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ s = '%d %r\x00' % (number, ptr)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ lib.stmcb_expand_marker = expand_marker
+ #
+ self.start_transaction()
+ p = stm_allocate(16)
+ self.push_root(ffi.cast("object_t *", 29))
+ self.push_root(p)
+ start = time.time()
+ while abs(time.time() - start) <= 0.1:
+ pass
+ self.abort_transaction()
+ #
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_OTHER
+ assert 0.099 <= tl.longest_marker_time <= 0.9
+ assert ffi.string(tl.longest_marker_self) == '29 %r' % (p,)
+ assert ffi.string(tl.longest_marker_other) == ''
+
+ def test_macros(self):
+ self.start_transaction()
+ p = stm_allocate(16)
+ tl = self.get_stm_thread_local()
+ lib.stm_push_marker(tl, 29, p)
+ p1 = self.pop_root()
+ assert p1 == p
+ p1 = self.pop_root()
+ assert p1 == ffi.cast("object_t *", 29)
+ py.test.raises(EmptyStack, self.pop_root)
+ #
+ lib.stm_push_marker(tl, 29, p)
+ lib.stm_update_marker_num(tl, 27)
+ p1 = self.pop_root()
+ assert p1 == p
+ p1 = self.pop_root()
+ assert p1 == ffi.cast("object_t *", 27)
+ py.test.raises(EmptyStack, self.pop_root)
+ #
+ lib.stm_push_marker(tl, 29, p)
+ self.push_root(p)
+ lib.stm_update_marker_num(tl, 27)
+ p1 = self.pop_root()
+ assert p1 == p
+ p1 = self.pop_root()
+ assert p1 == p
+ p1 = self.pop_root()
+ assert p1 == ffi.cast("object_t *", 27)
+ py.test.raises(EmptyStack, self.pop_root)
+ #
+ lib.stm_push_marker(tl, 29, p)
+ lib.stm_pop_marker(tl)
+ py.test.raises(EmptyStack, self.pop_root)
+
+ def test_stm_expand_marker(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ s = '%d %r\x00' % (number, ptr)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ lib.stmcb_expand_marker = expand_marker
+ self.start_transaction()
+ p = stm_allocate(16)
+ self.push_root(ffi.cast("object_t *", 29))
+ self.push_root(p)
+ self.push_root(stm_allocate(32))
+ self.push_root(stm_allocate(16))
+ raw = lib._stm_expand_marker()
+ assert ffi.string(raw) == '29 %r' % (p,)
+
+ def test_stmcb_debug_print(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ s = '<<<%d>>>\x00' % (number,)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ @ffi.callback("void(char *, double, char *)")
+ def debug_print(cause, time, marker):
+ if 0.0 < time < 1.0:
+ time = "time_ok"
+ seen.append((ffi.string(cause), time, ffi.string(marker)))
+ seen = []
+ lib.stmcb_expand_marker = expand_marker
+ lib.stmcb_debug_print = debug_print
+ #
+ self.start_transaction()
+ p = stm_allocate(16)
+ self.push_root(ffi.cast("object_t *", 29))
+ self.push_root(p)
+ self.abort_transaction()
+ #
+ assert seen == [("run aborted other", "time_ok", "<<<29>>>")]
+
+ def test_multiple_markers(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ seen.append(number)
+ s = '%d %r\x00' % (number, ptr == ffi.NULL)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ seen = []
+ lib.stmcb_expand_marker = expand_marker
+ #
+ self.start_transaction()
+ p = stm_allocate(16)
+ self.push_root(ffi.cast("object_t *", 27))
+ self.push_root(p)
+ self.push_root(ffi.cast("object_t *", 29))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ raw = lib._stm_expand_marker()
+ assert ffi.string(raw) == '29 True'
+ assert seen == [29]
+
+ def test_double_abort_markers_cb_write_write(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ s = '%d\x00' % (number,)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ lib.stmcb_expand_marker = expand_marker
+ p = stm_allocate_old(16)
+ #
+ self.start_transaction()
+ self.push_root(ffi.cast("object_t *", 19))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ stm_set_char(p, 'A')
+ self.pop_root()
+ self.pop_root()
+ self.push_root(ffi.cast("object_t *", 17))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ stm_minor_collect()
+ #
+ self.switch(1)
+ self.start_transaction()
+ self.push_root(ffi.cast("object_t *", 21))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ py.test.raises(Conflict, stm_set_char, p, 'B')
+ #
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_WRITE_WRITE
+ assert ffi.string(tl.longest_marker_self) == '21'
+ assert ffi.string(tl.longest_marker_other) == '19'
+
+ def test_double_abort_markers_cb_inevitable(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ c = (base + int(ffi.cast("uintptr_t", ptr)))[8]
+ s = '%d %r\x00' % (number, c)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ lib.stmcb_expand_marker = expand_marker
+ #
+ self.start_transaction()
+ p = stm_allocate(16)
+ stm_set_char(p, 'A')
+ self.push_root(ffi.cast("object_t *", 19))
+ self.push_root(ffi.cast("object_t *", p))
+ self.become_inevitable()
+ self.pop_root()
+ self.pop_root()
+ self.push_root(ffi.cast("object_t *", 17))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ stm_minor_collect()
+ #
+ self.switch(1)
+ self.start_transaction()
+ p = stm_allocate(16)
+ stm_set_char(p, 'B')
+ self.push_root(ffi.cast("object_t *", 21))
+ self.push_root(ffi.cast("object_t *", p))
+ py.test.raises(Conflict, self.become_inevitable)
+ #
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_INEVITABLE
+ assert ffi.string(tl.longest_marker_self) == "21 'B'"
+ assert ffi.string(tl.longest_marker_other) == "19 'A'"
+
+ def test_read_write_contention(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ s = '%d\x00' % (number,)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ lib.stmcb_expand_marker = expand_marker
+ p = stm_allocate_old(16)
+ #
+ self.start_transaction()
+ assert stm_get_char(p) == '\x00'
+ #
+ self.switch(1)
+ self.start_transaction()
+ self.push_root(ffi.cast("object_t *", 19))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ stm_set_char(p, 'A')
+ self.pop_root()
+ self.pop_root()
+ self.push_root(ffi.cast("object_t *", 17))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ py.test.raises(Conflict, self.commit_transaction)
+ #
+ tl = self.get_stm_thread_local()
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_WRITE_READ
+ assert ffi.string(tl.longest_marker_self) == '19'
+ assert ffi.string(tl.longest_marker_other) == (
+ '<read at unknown location>')
+
+ def test_double_remote_markers_cb_write_write(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ s = '%d\x00' % (number,)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ lib.stmcb_expand_marker = expand_marker
+ p = stm_allocate_old(16)
+ #
+ self.start_transaction()
+ self.push_root(ffi.cast("object_t *", 19))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ stm_set_char(p, 'A')
+ self.pop_root()
+ self.pop_root()
+ self.push_root(ffi.cast("object_t *", 17))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ tl0 = self.get_stm_thread_local()
+ #
+ self.switch(1)
+ self.start_transaction()
+ self.become_inevitable()
+ self.push_root(ffi.cast("object_t *", 21))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ stm_set_char(p, 'B') # aborts in #0
+ self.pop_root()
+ self.pop_root()
+ self.push_root(ffi.cast("object_t *", 23))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ #
+ py.test.raises(Conflict, self.switch, 0)
+ #
+ tl = self.get_stm_thread_local()
+ assert tl is tl0
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_WRITE_WRITE
+ assert ffi.string(tl.longest_marker_self) == '19'
+ assert ffi.string(tl.longest_marker_other) == '21'
+
+ def test_double_remote_markers_cb_write_read(self):
+ @ffi.callback("void(char *, uintptr_t, object_t *, char *, size_t)")
+ def expand_marker(base, number, ptr, outbuf, outbufsize):
+ s = '%d\x00' % (number,)
+ assert len(s) <= outbufsize
+ outbuf[0:len(s)] = s
+ lib.stmcb_expand_marker = expand_marker
+ p = stm_allocate_old(16)
+ #
+ self.start_transaction()
+ assert stm_get_char(p) == '\x00' # read
+ tl0 = self.get_stm_thread_local()
+ #
+ self.switch(1)
+ self.start_transaction()
+ self.become_inevitable()
+ self.push_root(ffi.cast("object_t *", 21))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ stm_set_char(p, 'B') # write, will abort #0
+ self.pop_root()
+ self.pop_root()
+ self.push_root(ffi.cast("object_t *", 23))
+ self.push_root(ffi.cast("object_t *", ffi.NULL))
+ self.commit_transaction()
+ #
+ py.test.raises(Conflict, self.switch, 0)
+ #
+ tl = self.get_stm_thread_local()
+ assert tl is tl0
+ assert tl.longest_marker_state == lib.STM_TIME_RUN_ABORTED_WRITE_READ
+ assert ffi.string(tl.longest_marker_self)=='<read at unknown location>'
+ assert ffi.string(tl.longest_marker_other) == '21'
diff --git a/c7/test/test_nursery.py b/c7/test/test_nursery.py
--- a/c7/test/test_nursery.py
+++ b/c7/test/test_nursery.py
@@ -1,7 +1,7 @@
from support import *
import py
-class TestBasic(BaseTest):
+class TestNursery(BaseTest):
def test_nursery_full(self):
lib._stm_set_nursery_free_count(2048)
More information about the pypy-commit
mailing list