Author: Remi Meier <[email protected]>
Branch: c7
Changeset: r612:53b7a774625c
Date: 2014-01-15 11:56 +0100
http://bitbucket.org/pypy/stmgc/changeset/53b7a774625c/
Log: try introducing safe-points
diff --git a/c7/core.c b/c7/core.c
--- a/c7/core.c
+++ b/c7/core.c
@@ -8,6 +8,7 @@
#include <sys/syscall.h>
#include <asm/prctl.h>
#include <sys/prctl.h>
+#include <pthread.h>
#include "core.h"
#include "list.h"
@@ -49,6 +50,7 @@
struct _thread_local1_s _tl1;
int thread_num;
bool running_transaction;
+ bool need_abort;
char *thread_base;
struct stm_list_s *modified_objects;
struct stm_list_s *new_object_ranges;
@@ -69,6 +71,7 @@
/************************************************************/
uintptr_t _stm_reserve_page(void);
+void stm_abort_transaction(void);
static void spin_loop(void)
{
@@ -115,6 +118,59 @@
#endif
}
+/************************************************************/
+
+/* a multi-reader, single-writer lock: transactions normally take a reader
+ lock, so don't conflict with each other; when we need to do a global GC,
+ we take a writer lock to "stop the world". Note the initializer here,
+ which should give the correct priority for stm_possible_safe_point(). */
+static pthread_rwlock_t rwlock_shared =
+ PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP;
+
+struct tx_descriptor *in_single_thread = NULL;
+
+void stm_start_sharedlock(void)
+{
+ int err = pthread_rwlock_rdlock(&rwlock_shared);
+ if (err != 0)
+ abort();
+}
+
+void stm_stop_sharedlock(void)
+{
+ int err = pthread_rwlock_unlock(&rwlock_shared);
+ if (err != 0)
+ abort();
+}
+
+static void start_exclusivelock(void)
+{
+ int err = pthread_rwlock_wrlock(&rwlock_shared);
+ if (err != 0)
+ abort();
+}
+
+static void stop_exclusivelock(void)
+{
+ int err = pthread_rwlock_unlock(&rwlock_shared);
+ if (err != 0)
+ abort();
+}
+
+void _stm_start_safe_point(void)
+{
+ stm_stop_sharedlock();
+}
+
+void _stm_stop_safe_point(void)
+{
+ stm_start_sharedlock();
+ if (_STM_TL2->need_abort)
+ stm_abort_transaction();
+}
+
+
+
bool _stm_was_read(object_t *obj)
{
read_marker_t *marker = (read_marker_t *)(((uintptr_t)obj) >> 4);
@@ -227,9 +283,8 @@
return (object_t*)res;
}
-void stm_abort_transaction(void);
-enum detect_conflicts_e { CANNOT_CONFLICT, CAN_CONFLICT };
+enum detect_conflicts_e { CANNOT_CONFLICT, CAN_CONFLICT, CHECK_CONFLICT };
static void update_to_current_version(enum detect_conflicts_e check_conflict)
{
@@ -265,8 +320,12 @@
write_fence();
pending_updates = NULL;
- if (conflict_found_or_dont_check && check_conflict == CAN_CONFLICT) {
- stm_abort_transaction();
+ if (conflict_found_or_dont_check) {
+ if (check_conflict == CAN_CONFLICT) {
+ stm_abort_transaction();
+ } else { /* CHECK_CONFLICT */
+ _STM_TL2->need_abort = 1;
+ }
}
}
@@ -532,6 +591,8 @@
{
assert(!_STM_TL2->running_transaction);
+ stm_start_sharedlock();
+
uint8_t old_rv = _STM_TL1->transaction_read_version;
_STM_TL1->transaction_read_version = old_rv + 1;
if (UNLIKELY(old_rv == 0xff))
@@ -557,6 +618,7 @@
_STM_TL1->jmpbufptr = jmpbufptr;
_STM_TL2->running_transaction = 1;
+ _STM_TL2->need_abort = 0;
}
#if 0
@@ -580,100 +642,87 @@
void stm_stop_transaction(void)
{
assert(_STM_TL2->running_transaction);
-#if 0
+ stm_stop_sharedlock();
+ start_exclusivelock();
- write_fence(); /* see later in this function for why */
+ _STM_TL1->jmpbufptr = NULL; /* cannot abort any more */
+
+ /* copy modified object versions to other threads */
+ pending_updates = _STM_TL2->modified_objects;
+ int my_thread_num = _STM_TL2->thread_num;
+ int other_thread_num = 1 - my_thread_num;
+ _stm_restore_local_state(other_thread_num);
+ update_to_current_version(CHECK_CONFLICT); /* sets need_abort */
+ _stm_restore_local_state(my_thread_num);
+
+
+ /* /\* walk the new_object_ranges and manually copy the new objects */
+ /* to the other thread's pages in the (hopefully rare) case that */
+ /* the page they belong to is already unshared *\/ */
+ /* long i; */
+ /* struct stm_list_s *lst = _STM_TL2->new_object_ranges; */
+ /* for (i = stm_list_count(lst); i > 0; ) { */
+ /* i -= 2; */
+ /* uintptr_t pagenum = (uintptr_t)stm_list_item(lst, i); */
+ /* /\* NB. the read next line should work even against a parallel */
+ /* thread, thanks to the lock acquisition we do earlier (see the */
+ /* beginning of this function). Indeed, if this read returns */
+ /* SHARED_PAGE, then we know that the real value in memory was */
+ /* actually SHARED_PAGE at least at the time of the */
+ /* acquire_lock(). It may have been modified afterwards by a */
+ /* compare_and_swap() in the other thread, but then we know for */
+ /* sure that the other thread is seeing the last, up-to-date */
+ /* version of our data --- this is the reason of the */
+ /* write_fence() just before the acquire_lock(). */
+ /* *\/ */
+ /* if (flag_page_private[pagenum] != SHARED_PAGE) { */
+ /* object_t *range = stm_list_item(lst, i + 1); */
+ /* uint16_t start, stop; */
+ /* FROM_RANGE(start, stop, range); */
+ /* update_new_objects_in_other_threads(pagenum, start, stop); */
+ /* } */
+ /* } */
+
+ /* /\* do the same for the partially-allocated pages *\/ */
+ /* long j; */
+ /* for (j = 2; j < LARGE_OBJECT_WORDS; j++) { */
+ /* alloc_for_size_t *alloc = &_STM_TL2->alloc[j]; */
+ /* uint16_t start = alloc->start; */
+ /* uint16_t cur = (uintptr_t)alloc->next; */
- if (leader_thread_num != _STM_TL2->thread_num) {
- /* non-leader thread */
- if (global_history != NULL) {
- update_to_current_version(CAN_CONFLICT);
- assert(global_history == NULL);
- }
+ /* if (start == cur) { */
+ /* /\* nothing to do: this page (or fraction thereof) was left */
+ /* empty by the previous transaction, and starts empty as */
+ /* well in the new transaction. 'flag_partial_page' is */
+ /* unchanged. *\/ */
+ /* } */
+ /* else { */
+ /* uintptr_t pagenum = ((uintptr_t)(alloc->next - 1)) / 4096UL; */
+ /* /\* for the new transaction, it will start here: *\/ */
+ /* alloc->start = cur; */
- /* steal leadership now */
- leader_thread_num = _STM_TL2->thread_num;
- }
+ /* if (alloc->flag_partial_page) { */
+ /* if (flag_page_private[pagenum] != SHARED_PAGE) { */
+ /* update_new_objects_in_other_threads(pagenum, start,
cur); */
+ /* } */
+ /* } */
+ /* else { */
+ /* /\* we can skip checking flag_page_private[] in non-debug */
+ /* builds, because the whole page can only contain */
+ /* objects made by the just-finished transaction. *\/ */
+ /* assert(flag_page_private[pagenum] == SHARED_PAGE); */
- /* now we are the leader thread. the leader can always commit */
- _STM_TL1->jmpbufptr = NULL; /* cannot abort any more */
- undo_log_current = undo_log_pages; /* throw away the content */
+ /* /\* the next transaction will start with this page */
+ /* containing objects that are now committed, so */
+ /* we need to set this flag now *\/ */
+ /* alloc->flag_partial_page = true; */
+ /* } */
+ /* } */
+ /* } */
- /* add these objects to the global_history */
- _STM_TL2->modified_objects->nextlist = global_history;
- global_history = _STM_TL2->modified_objects;
- _STM_TL2->modified_objects = stm_list_create();
-
- uint16_t wv = _STM_TL1->transaction_write_version;
- if (gh_write_version_first < wv) gh_write_version_first = wv;
-
- /* walk the new_object_ranges and manually copy the new objects
- to the other thread's pages in the (hopefully rare) case that
- the page they belong to is already unshared */
- long i;
- struct stm_list_s *lst = _STM_TL2->new_object_ranges;
- for (i = stm_list_count(lst); i > 0; ) {
- i -= 2;
- uintptr_t pagenum = (uintptr_t)stm_list_item(lst, i);
-
- /* NB. the read next line should work even against a parallel
- thread, thanks to the lock acquisition we do earlier (see the
- beginning of this function). Indeed, if this read returns
- SHARED_PAGE, then we know that the real value in memory was
- actually SHARED_PAGE at least at the time of the
- acquire_lock(). It may have been modified afterwards by a
- compare_and_swap() in the other thread, but then we know for
- sure that the other thread is seeing the last, up-to-date
- version of our data --- this is the reason of the
- write_fence() just before the acquire_lock().
- */
- if (flag_page_private[pagenum] != SHARED_PAGE) {
- object_t *range = stm_list_item(lst, i + 1);
- uint16_t start, stop;
- FROM_RANGE(start, stop, range);
- update_new_objects_in_other_threads(pagenum, start, stop);
- }
- }
-
- /* do the same for the partially-allocated pages */
- long j;
- for (j = 2; j < LARGE_OBJECT_WORDS; j++) {
- alloc_for_size_t *alloc = &_STM_TL2->alloc[j];
- uint16_t start = alloc->start;
- uint16_t cur = (uintptr_t)alloc->next;
-
- if (start == cur) {
- /* nothing to do: this page (or fraction thereof) was left
- empty by the previous transaction, and starts empty as
- well in the new transaction. 'flag_partial_page' is
- unchanged. */
- }
- else {
- uintptr_t pagenum = ((uintptr_t)(alloc->next - 1)) / 4096UL;
- /* for the new transaction, it will start here: */
- alloc->start = cur;
-
- if (alloc->flag_partial_page) {
- if (flag_page_private[pagenum] != SHARED_PAGE) {
- update_new_objects_in_other_threads(pagenum, start, cur);
- }
- }
- else {
- /* we can skip checking flag_page_private[] in non-debug
- builds, because the whole page can only contain
- objects made by the just-finished transaction. */
- assert(flag_page_private[pagenum] == SHARED_PAGE);
-
- /* the next transaction will start with this page
- containing objects that are now committed, so
- we need to set this flag now */
- alloc->flag_partial_page = true;
- }
- }
- }
-#endif
_STM_TL2->running_transaction = 0;
+ stop_exclusivelock();
}
void stm_abort_transaction(void)
@@ -686,10 +735,11 @@
uint16_t num_allocated = ((uintptr_t)alloc->next) - alloc->start;
alloc->next -= num_allocated;
}
- stm_list_clear(_STM_TL2->new_object_ranges);
+ /* stm_list_clear(_STM_TL2->new_object_ranges); */
stm_list_clear(_STM_TL2->modified_objects);
assert(_STM_TL1->jmpbufptr != NULL);
assert(_STM_TL1->jmpbufptr != (jmpbufptr_t *)-1); /* for tests only */
_STM_TL2->running_transaction = 0;
+ stm_stop_sharedlock();
__builtin_longjmp(*_STM_TL1->jmpbufptr, 1);
}
diff --git a/c7/core.h b/c7/core.h
--- a/c7/core.h
+++ b/c7/core.h
@@ -98,4 +98,10 @@
bool _stm_is_in_nursery(char *ptr);
object_t *_stm_allocate_old(size_t size);
+
+void _stm_start_safe_point(void);
+void _stm_stop_safe_point(void);
#endif
+
+
+
diff --git a/c7/test/support.py b/c7/test/support.py
--- a/c7/test/support.py
+++ b/c7/test/support.py
@@ -50,6 +50,10 @@
bool _stm_is_in_nursery(char *ptr);
object_t *_stm_allocate_old(size_t size);
+void _stm_start_safe_point(void);
+void _stm_stop_safe_point(void);
+bool _stm_check_stop_safe_point(void);
+
void *memset(void *s, int c, size_t n);
""")
@@ -65,7 +69,7 @@
bool _stm_stop_transaction(void) {
jmpbufptr_t here;
- if (__builtin_setjmp(here) == 0) {
+ if (__builtin_setjmp(here) == 0) { // returned directly
assert(_STM_TL1->jmpbufptr == (jmpbufptr_t*)-1);
_STM_TL1->jmpbufptr = &here;
stm_stop_transaction();
@@ -76,6 +80,20 @@
return 1;
}
+bool _stm_check_stop_safe_point(void) {
+ jmpbufptr_t here;
+ if (__builtin_setjmp(here) == 0) { // returned directly
+ assert(_STM_TL1->jmpbufptr == (jmpbufptr_t*)-1);
+ _STM_TL1->jmpbufptr = &here;
+ _stm_stop_safe_point();
+ _STM_TL1->jmpbufptr = (jmpbufptr_t*)-1;
+ return 0;
+ }
+ _STM_TL1->jmpbufptr = (jmpbufptr_t*)-1;
+ return 1;
+}
+
+
''', sources=source_files,
define_macros=[('STM_TESTS', '1')],
undef_macros=['NDEBUG'],
@@ -123,6 +141,16 @@
else:
assert res == 0
+def stm_start_safe_point():
+ lib._stm_start_safe_point()
+
+def stm_stop_safe_point(expected_conflict=False):
+ res = lib._stm_check_stop_safe_point()
+ if expected_conflict:
+ assert res == 1
+ else:
+ assert res == 0
+
class BaseTest(object):
diff --git a/c7/test/test_basic.py b/c7/test/test_basic.py
--- a/c7/test/test_basic.py
+++ b/c7/test/test_basic.py
@@ -25,10 +25,12 @@
def test_transaction_start_stop(self):
stm_start_transaction()
+ stm_start_safe_point()
self.switch(1)
stm_start_transaction()
stm_stop_transaction()
self.switch(0)
+ stm_stop_safe_point()
stm_stop_transaction()
def test_simple_read(self):
@@ -54,52 +56,85 @@
lp1, p1 = stm_allocate_old(16)
stm_start_transaction()
stm_write(lp1)
+ assert stm_was_written(lp1)
p1[15] = 'a'
self.switch(1)
stm_start_transaction()
stm_read(lp1)
+ assert stm_was_read(lp1)
tp1 = stm_get_real_address(lp1)
assert tp1[15] == '\0'
+ def test_read_write_1(self):
+ lp1, p1 = stm_allocate_old(16)
+ p1[8] = 'a'
+ stm_start_transaction()
+ stm_stop_transaction()
+ #
+ self.switch(1)
+ stm_start_transaction()
+ stm_write(lp1)
+ p1 = stm_get_real_address(lp1)
+ assert p1[8] == 'a'
+ p1[8] = 'b'
+ stm_start_safe_point()
+ #
+ self.switch(0)
+ stm_start_transaction()
+ stm_read(lp1)
+ p1 = stm_get_real_address(lp1)
+ assert p1[8] == 'a'
+ stm_start_safe_point()
+ #
+ self.switch(1)
+ stm_stop_safe_point()
+ stm_stop_transaction(False)
+ #
+ self.switch(0)
+ stm_stop_safe_point(True) # detects rw conflict
-
- def test_read_write_1(self):
+
+ def test_read_write_2(self):
stm_start_transaction()
- p1 = stm_allocate(16)
+ lp1, p1 = stm_allocate(16)
p1[8] = 'a'
stm_stop_transaction(False)
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
- stm_write(p1)
+ stm_write(lp1)
+ p1 = stm_get_real_address(lp1)
assert p1[8] == 'a'
p1[8] = 'b'
#
- self.switch("main")
+ self.switch(0)
stm_start_transaction()
- stm_read(p1)
+ stm_read(lp1)
+ p1 = stm_get_real_address(lp1)
assert p1[8] == 'a'
#
- self.switch("sub1")
+ self.switch(1)
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
+ p1 = stm_get_real_address(lp1)
assert p1[8] == 'a'
+
def test_start_transaction_updates(self):
stm_start_transaction()
p1 = stm_allocate(16)
p1[8] = 'a'
stm_stop_transaction(False)
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
assert p1[8] == 'a'
p1[8] = 'b'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
assert p1[8] == 'a'
stm_start_transaction()
assert p1[8] == 'b'
@@ -107,11 +142,11 @@
def test_resolve_no_conflict_empty(self):
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
stm_stop_transaction(False)
def test_resolve_no_conflict_write_only_in_already_committed(self):
@@ -121,13 +156,13 @@
stm_stop_transaction(False)
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
p1[8] = 'b'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
assert p1[8] == 'a'
stm_stop_transaction(False)
assert p1[8] == 'b'
@@ -139,13 +174,13 @@
stm_stop_transaction(False)
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
p1[8] = 'b'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
stm_read(p1)
assert p1[8] == 'a'
stm_stop_transaction(expected_conflict=True)
@@ -160,13 +195,13 @@
stm_stop_transaction(False)
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
p1[8] = 'b'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
assert p1[8] == 'a'
stm_write(p1)
p1[8] = 'c'
@@ -184,13 +219,13 @@
stm_stop_transaction(False)
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
p1[8] = 'b'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
stm_write(p2)
p2[8] = 'C'
stm_stop_transaction(False)
@@ -206,14 +241,14 @@
stm_stop_transaction(False)
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
assert p1[8] == 'A'
p1[8] = 'B'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
stm_read(p2)
assert p2[8] == 'a'
p3 = stm_allocate(16) # goes into the same page, which is
@@ -233,14 +268,14 @@
stm_stop_transaction(False)
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
assert p1[8] == 'A'
p1[8] = 'B'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
stm_write(p2)
assert p2[8] == 'a'
p2[8] = 'b'
@@ -261,14 +296,14 @@
stm_stop_transaction(False)
stm_start_transaction()
#
- self.switch("sub1")
+ self.switch(1)
stm_start_transaction()
stm_write(p1)
assert p1[8] == 'A'
p1[8] = 'B'
stm_stop_transaction(False)
#
- self.switch("main")
+ self.switch(0)
p3 = stm_allocate(16) # goes into the same page, which I will
p3[8] = ':' # modify just below
stm_write(p2)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit