[pypy-commit] pypy stmgc-c8: import stmgc
Raemi
pypy.commits at gmail.com
Fri Feb 26 06:17:14 EST 2016
Author: Remi Meier <remi.meier at gmail.com>
Branch: stmgc-c8
Changeset: r82527:f1e7aa419f98
Date: 2016-02-26 12:14 +0100
http://bitbucket.org/pypy/pypy/changeset/f1e7aa419f98/
Log: import stmgc
diff --git a/rpython/translator/stm/src_stm/revision b/rpython/translator/stm/src_stm/revision
--- a/rpython/translator/stm/src_stm/revision
+++ b/rpython/translator/stm/src_stm/revision
@@ -1,1 +1,1 @@
-d31c9f671775
+8c9162341945
diff --git a/rpython/translator/stm/src_stm/stm/core.c b/rpython/translator/stm/src_stm/stm/core.c
--- a/rpython/translator/stm/src_stm/stm/core.c
+++ b/rpython/translator/stm/src_stm/stm/core.c
@@ -109,164 +109,6 @@
}
-/* ############# signal handler ############# */
-
-static void copy_bk_objs_in_page_from(int from_segnum, uintptr_t pagenum,
- bool only_if_not_modified)
-{
- /* looks at all bk copies of objects overlapping page 'pagenum' and
- copies the part in 'pagenum' back to the current segment */
- dprintf(("copy_bk_objs_in_page_from(%d, %ld, %d)\n",
- from_segnum, (long)pagenum, only_if_not_modified));
-
- assert(modification_lock_check_rdlock(from_segnum));
- struct list_s *list = get_priv_segment(from_segnum)->modified_old_objects;
- struct stm_undo_s *undo = (struct stm_undo_s *)list->items;
- struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
-
- import_objects(only_if_not_modified ? -2 : -1,
- pagenum, undo, end);
-}
-
-static void go_to_the_past(uintptr_t pagenum,
- struct stm_commit_log_entry_s *from,
- struct stm_commit_log_entry_s *to)
-{
- assert(modification_lock_check_wrlock(STM_SEGMENT->segment_num));
- assert(from->rev_num >= to->rev_num);
- /* walk BACKWARDS the commit log and update the page 'pagenum',
- initially at revision 'from', until we reach the revision 'to'. */
-
- /* XXXXXXX Recursive algo for now, fix this! */
- if (from != to) {
- struct stm_commit_log_entry_s *cl = to->next;
- go_to_the_past(pagenum, from, cl);
-
- struct stm_undo_s *undo = cl->written;
- struct stm_undo_s *end = cl->written + cl->written_count;
-
- import_objects(-1, pagenum, undo, end);
- }
-}
-
-
-
-static void handle_segfault_in_page(uintptr_t pagenum)
-{
- /* assumes page 'pagenum' is ACCESS_NONE, privatizes it,
- and validates to newest revision */
-
- dprintf(("handle_segfault_in_page(%lu), seg %d\n", pagenum, STM_SEGMENT->segment_num));
-
- /* XXX: bad, but no deadlocks: */
- acquire_all_privatization_locks();
-
- long i;
- int my_segnum = STM_SEGMENT->segment_num;
-
- assert(get_page_status_in(my_segnum, pagenum) == PAGE_NO_ACCESS);
-
- /* find who has the most recent revision of our page */
- int copy_from_segnum = -1;
- uint64_t most_recent_rev = 0;
- for (i = 1; i < NB_SEGMENTS; i++) {
- if (i == my_segnum)
- continue;
-
- struct stm_commit_log_entry_s *log_entry;
- log_entry = get_priv_segment(i)->last_commit_log_entry;
- if (get_page_status_in(i, pagenum) != PAGE_NO_ACCESS
- && (copy_from_segnum == -1 || log_entry->rev_num > most_recent_rev)) {
- copy_from_segnum = i;
- most_recent_rev = log_entry->rev_num;
- }
- }
- OPT_ASSERT(copy_from_segnum != my_segnum);
-
- /* make our page write-ready */
- page_mark_accessible(my_segnum, pagenum);
-
- /* account for this page now: XXX */
- /* increment_total_allocated(4096); */
-
- if (copy_from_segnum == -1) {
- /* this page is only accessible in the sharing segment seg0 so far (new
- allocation). We can thus simply mark it accessible here. */
- pagecopy(get_virtual_page(my_segnum, pagenum),
- get_virtual_page(0, pagenum));
- release_all_privatization_locks();
- return;
- }
-
- /* before copying anything, acquire modification locks from our and
- the other segment */
- uint64_t to_lock = (1UL << copy_from_segnum);
- acquire_modification_lock_set(to_lock, my_segnum);
- pagecopy(get_virtual_page(my_segnum, pagenum),
- get_virtual_page(copy_from_segnum, pagenum));
-
- /* if there were modifications in the page, revert them. */
- copy_bk_objs_in_page_from(copy_from_segnum, pagenum, false);
-
- /* we need to go from 'src_version' to 'target_version'. This
- might need a walk into the past. */
- struct stm_commit_log_entry_s *src_version, *target_version;
- src_version = get_priv_segment(copy_from_segnum)->last_commit_log_entry;
- target_version = STM_PSEGMENT->last_commit_log_entry;
-
-
- dprintf(("handle_segfault_in_page: rev %lu to rev %lu\n",
- src_version->rev_num, target_version->rev_num));
- /* adapt revision of page to our revision:
- if our rev is higher than the page we copy from, everything
- is fine as we never read/modified the page anyway
- */
- if (src_version->rev_num > target_version->rev_num)
- go_to_the_past(pagenum, src_version, target_version);
-
- release_modification_lock_set(to_lock, my_segnum);
- release_all_privatization_locks();
-}
-
-static void _signal_handler(int sig, siginfo_t *siginfo, void *context)
-{
- assert(_stm_segfault_expected > 0);
-
- int saved_errno = errno;
- char *addr = siginfo->si_addr;
- dprintf(("si_addr: %p\n", addr));
- if (addr == NULL || addr < stm_object_pages ||
- addr >= stm_object_pages+TOTAL_MEMORY) {
- /* actual segfault, unrelated to stmgc */
- fprintf(stderr, "Segmentation fault: accessing %p\n", addr);
- detect_shadowstack_overflow(addr);
- abort();
- }
-
- int segnum = get_segment_of_linear_address(addr);
- OPT_ASSERT(segnum != 0);
- if (segnum != STM_SEGMENT->segment_num) {
- fprintf(stderr, "Segmentation fault: accessing %p (seg %d) from"
- " seg %d\n", addr, segnum, STM_SEGMENT->segment_num);
- abort();
- }
- dprintf(("-> segment: %d\n", segnum));
-
- char *seg_base = STM_SEGMENT->segment_base;
- uintptr_t pagenum = ((char*)addr - seg_base) / 4096UL;
- if (pagenum < END_NURSERY_PAGE) {
- fprintf(stderr, "Segmentation fault: accessing %p (seg %d "
- "page %lu)\n", addr, segnum, pagenum);
- abort();
- }
-
- DEBUG_EXPECT_SEGFAULT(false);
- handle_segfault_in_page(pagenum);
- DEBUG_EXPECT_SEGFAULT(true);
-
- errno = saved_errno;
- /* now return and retry */
-}
/* ############# commit log ############# */
diff --git a/rpython/translator/stm/src_stm/stm/core.h b/rpython/translator/stm/src_stm/stm/core.h
--- a/rpython/translator/stm/src_stm/stm/core.h
+++ b/rpython/translator/stm/src_stm/stm/core.h
@@ -303,6 +303,14 @@
static bool _stm_validate(void);
static void _core_commit_transaction(bool external);
+static void import_objects(
+ int from_segnum, /* or -1: from undo->backup,
+ or -2: from undo->backup if not modified */
+ uintptr_t pagenum, /* or -1: "all accessible" */
+ struct stm_undo_s *undo,
+ struct stm_undo_s *end);
+
+
static inline bool was_read_remote(char *base, object_t *obj)
{
uint8_t other_transaction_read_version =
diff --git a/rpython/translator/stm/src_stm/stm/hashtable.c b/rpython/translator/stm/src_stm/stm/hashtable.c
--- a/rpython/translator/stm/src_stm/stm/hashtable.c
+++ b/rpython/translator/stm/src_stm/stm/hashtable.c
@@ -78,6 +78,7 @@
stm_hashtable_table_t *table;
stm_hashtable_table_t initial_table;
uint64_t additions;
+ uint64_t pickitem_index;
};
@@ -490,6 +491,90 @@
return nresult;
}
+stm_hashtable_entry_t *stm_hashtable_pickitem(object_t *hobj,
+ stm_hashtable_t *hashtable)
+{
+ /* We use hashtable->pickitem_index as a shared index counter (not
+ initialized, any initial garbage is fine). The goal is
+ two-folds:
+
+ - This is used to implement popitem(). Like CPython and PyPy's
+ non-STM dict implementations, the goal is that repeated calls
+ to pickitem() maintains a roughly O(1) time per call while
+ returning different items (in the case of popitem(), the
+ returned items are immediately deleted).
+
+ - Additionally, with STM, if several threads all call
+ pickitem(), this should give the best effort to distribute
+ different items to different threads and thus minimize
+ conflicts. (At least that's the theory; it should be tested
+ in practice.)
+ */
+ restart:;
+ uint64_t startindex = VOLATILE_HASHTABLE(hashtable)->pickitem_index;
+
+ /* Get the table. No synchronization is needed: we may miss some
+ entries that are being added, but they would contain NULL in
+ this segment anyway. */
+ stm_hashtable_table_t *table = VOLATILE_HASHTABLE(hashtable)->table;
+
+ /* Find the first entry with a non-NULL object, starting at
+ 'index'. */
+ uintptr_t mask = table->mask;
+ uintptr_t count;
+ stm_hashtable_entry_t *entry;
+
+ for (count = 0; count <= mask; ) {
+ entry = VOLATILE_TABLE(table)->items[(startindex + count) & mask];
+ count++;
+ if (entry != NULL && entry->object != NULL) {
+ /*
+ Found the next entry. Update pickitem_index now. If
+ it was already changed under our feet, we assume that
+ it is because another thread just did pickitem() too
+ and is likely to have got the very same entry. In that
+ case we start again from scratch to look for the
+ following entry.
+ */
+ if (!__sync_bool_compare_and_swap(&hashtable->pickitem_index,
+ startindex,
+ startindex + count))
+ goto restart;
+
+ /* Here we mark the entry as as read and return it.
+
+ Note a difference with notably stm_hashtable_list(): we
+ only call stm_read() after we checked that
+ entry->object is not NULL. If we find NULL, we don't
+ mark the entry as read from this thread at all in this
+ step---this is fine, as we can return a random
+ different entry here.
+ */
+ stm_read((object_t *)entry);
+ return entry;
+ }
+ }
+
+ /* Didn't find any entry. We have to be sure that the dictionary
+ is empty now, in the sense that returning NULL must guarantee
+ conflicts with a different thread adding items. This is done
+ by marking both the dict and all entries' read marker. */
+ stm_read(hobj);
+
+ /* Reload the table after setting the read marker */
+ uintptr_t i;
+ table = VOLATILE_HASHTABLE(hashtable)->table;
+ mask = table->mask;
+ for (i = 0; i <= mask; i++) {
+ entry = VOLATILE_TABLE(table)->items[i];
+ if (entry != NULL) {
+ stm_read((object_t *)entry);
+ assert(entry->object == NULL);
+ }
+ }
+ return NULL;
+}
+
static void _stm_compact_hashtable(struct object_s *hobj,
stm_hashtable_t *hashtable)
{
diff --git a/rpython/translator/stm/src_stm/stm/setup.c b/rpython/translator/stm/src_stm/stm/setup.c
--- a/rpython/translator/stm/src_stm/stm/setup.c
+++ b/rpython/translator/stm/src_stm/stm/setup.c
@@ -39,20 +39,6 @@
}
-static void setup_signal_handler(void)
-{
- struct sigaction act;
- memset(&act, 0, sizeof(act));
-
- act.sa_sigaction = &_signal_handler;
- /* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
- act.sa_flags = SA_SIGINFO | SA_NODEFER;
-
- if (sigaction(SIGSEGV, &act, NULL) < 0) {
- perror ("sigaction");
- abort();
- }
-}
void stm_setup(void)
{
@@ -301,13 +287,15 @@
if (addr == NULL)
return;
stm_thread_local_t *tl = stm_all_thread_locals;
- while (tl != NULL) {
+ if (tl == NULL)
+ return;
+ do {
char *trap = _shadowstack_trap_page(tl->shadowstack_base);
if (trap <= addr && addr <= trap + 4095) {
fprintf(stderr, "This is caused by a stack overflow.\n"
- "Sorry, proper RuntimeError support is not implemented yet.\n");
+ "Sorry, proper RuntimeError support is not implemented yet.\n");
return;
}
tl = tl->next;
- }
+ } while (tl != stm_all_thread_locals);
}
diff --git a/rpython/translator/stm/src_stm/stm/signal_handler.c b/rpython/translator/stm/src_stm/stm/signal_handler.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/stm/src_stm/stm/signal_handler.c
@@ -0,0 +1,178 @@
+/* Imported by rpython/translator/stm/import_stmgc.py */
+#ifndef _STM_CORE_H_
+# error "must be compiled via stmgc.c"
+#endif
+
+
+static void setup_signal_handler(void)
+{
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+
+ act.sa_sigaction = &_signal_handler;
+ /* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
+ act.sa_flags = SA_SIGINFO | SA_NODEFER;
+
+ if (sigaction(SIGSEGV, &act, NULL) < 0) {
+ perror ("sigaction");
+ abort();
+ }
+}
+
+
+static void copy_bk_objs_in_page_from(int from_segnum, uintptr_t pagenum,
+ bool only_if_not_modified)
+{
+ /* looks at all bk copies of objects overlapping page 'pagenum' and
+ copies the part in 'pagenum' back to the current segment */
+ dprintf(("copy_bk_objs_in_page_from(%d, %ld, %d)\n",
+ from_segnum, (long)pagenum, only_if_not_modified));
+
+ assert(modification_lock_check_rdlock(from_segnum));
+ struct list_s *list = get_priv_segment(from_segnum)->modified_old_objects;
+ struct stm_undo_s *undo = (struct stm_undo_s *)list->items;
+ struct stm_undo_s *end = (struct stm_undo_s *)(list->items + list->count);
+
+ import_objects(only_if_not_modified ? -2 : -1,
+ pagenum, undo, end);
+}
+
+static void go_to_the_past(uintptr_t pagenum,
+ struct stm_commit_log_entry_s *from,
+ struct stm_commit_log_entry_s *to)
+{
+ assert(modification_lock_check_wrlock(STM_SEGMENT->segment_num));
+ assert(from->rev_num >= to->rev_num);
+ /* walk BACKWARDS the commit log and update the page 'pagenum',
+ initially at revision 'from', until we reach the revision 'to'. */
+
+ /* XXXXXXX Recursive algo for now, fix this! */
+ if (from != to) {
+ struct stm_commit_log_entry_s *cl = to->next;
+ go_to_the_past(pagenum, from, cl);
+
+ struct stm_undo_s *undo = cl->written;
+ struct stm_undo_s *end = cl->written + cl->written_count;
+
+ import_objects(-1, pagenum, undo, end);
+ }
+}
+
+
+
+static void handle_segfault_in_page(uintptr_t pagenum)
+{
+ /* assumes page 'pagenum' is ACCESS_NONE, privatizes it,
+ and validates to newest revision */
+
+ dprintf(("handle_segfault_in_page(%lu), seg %d\n", pagenum, STM_SEGMENT->segment_num));
+
+ /* XXX: bad, but no deadlocks: */
+ acquire_all_privatization_locks();
+
+ long i;
+ int my_segnum = STM_SEGMENT->segment_num;
+
+ assert(get_page_status_in(my_segnum, pagenum) == PAGE_NO_ACCESS);
+
+ /* find who has the most recent revision of our page */
+ int copy_from_segnum = -1;
+ uint64_t most_recent_rev = 0;
+ for (i = 1; i < NB_SEGMENTS; i++) {
+ if (i == my_segnum)
+ continue;
+
+ struct stm_commit_log_entry_s *log_entry;
+ log_entry = get_priv_segment(i)->last_commit_log_entry;
+ if (get_page_status_in(i, pagenum) != PAGE_NO_ACCESS
+ && (copy_from_segnum == -1 || log_entry->rev_num > most_recent_rev)) {
+ copy_from_segnum = i;
+ most_recent_rev = log_entry->rev_num;
+ }
+ }
+ OPT_ASSERT(copy_from_segnum != my_segnum);
+
+ /* make our page write-ready */
+ page_mark_accessible(my_segnum, pagenum);
+
+ /* account for this page now: XXX */
+ /* increment_total_allocated(4096); */
+
+ if (copy_from_segnum == -1) {
+ /* this page is only accessible in the sharing segment seg0 so far (new
+ allocation). We can thus simply mark it accessible here. */
+ pagecopy(get_virtual_page(my_segnum, pagenum),
+ get_virtual_page(0, pagenum));
+ release_all_privatization_locks();
+ return;
+ }
+
+ /* before copying anything, acquire modification locks from our and
+ the other segment */
+ uint64_t to_lock = (1UL << copy_from_segnum);
+ acquire_modification_lock_set(to_lock, my_segnum);
+ pagecopy(get_virtual_page(my_segnum, pagenum),
+ get_virtual_page(copy_from_segnum, pagenum));
+
+ /* if there were modifications in the page, revert them. */
+ copy_bk_objs_in_page_from(copy_from_segnum, pagenum, false);
+
+ /* we need to go from 'src_version' to 'target_version'. This
+ might need a walk into the past. */
+ struct stm_commit_log_entry_s *src_version, *target_version;
+ src_version = get_priv_segment(copy_from_segnum)->last_commit_log_entry;
+ target_version = STM_PSEGMENT->last_commit_log_entry;
+
+
+ dprintf(("handle_segfault_in_page: rev %lu to rev %lu\n",
+ src_version->rev_num, target_version->rev_num));
+ /* adapt revision of page to our revision:
+ if our rev is higher than the page we copy from, everything
+ is fine as we never read/modified the page anyway
+ */
+ if (src_version->rev_num > target_version->rev_num)
+ go_to_the_past(pagenum, src_version, target_version);
+
+ release_modification_lock_set(to_lock, my_segnum);
+ release_all_privatization_locks();
+}
+
+static void _signal_handler(int sig, siginfo_t *siginfo, void *context)
+{
+ assert(_stm_segfault_expected > 0);
+
+ int saved_errno = errno;
+ char *addr = siginfo->si_addr;
+ dprintf(("si_addr: %p\n", addr));
+ if (addr == NULL || addr < stm_object_pages ||
+ addr >= stm_object_pages+TOTAL_MEMORY) {
+ /* actual segfault, unrelated to stmgc */
+ fprintf(stderr, "Segmentation fault: accessing %p\n", addr);
+ detect_shadowstack_overflow(addr);
+ abort();
+ }
+
+ int segnum = get_segment_of_linear_address(addr);
+ OPT_ASSERT(segnum != 0);
+ if (segnum != STM_SEGMENT->segment_num) {
+ fprintf(stderr, "Segmentation fault: accessing %p (seg %d) from"
+ " seg %d\n", addr, segnum, STM_SEGMENT->segment_num);
+ abort();
+ }
+ dprintf(("-> segment: %d\n", segnum));
+
+ char *seg_base = STM_SEGMENT->segment_base;
+ uintptr_t pagenum = ((char*)addr - seg_base) / 4096UL;
+ if (pagenum < END_NURSERY_PAGE) {
+ fprintf(stderr, "Segmentation fault: accessing %p (seg %d "
+ "page %lu)\n", addr, segnum, pagenum);
+ abort();
+ }
+
+ DEBUG_EXPECT_SEGFAULT(false);
+ handle_segfault_in_page(pagenum);
+ DEBUG_EXPECT_SEGFAULT(true);
+
+ errno = saved_errno;
+ /* now return and retry */
+}
diff --git a/rpython/translator/stm/src_stm/stm/signal_handler.h b/rpython/translator/stm/src_stm/stm/signal_handler.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/stm/src_stm/stm/signal_handler.h
@@ -0,0 +1,8 @@
+/* Imported by rpython/translator/stm/import_stmgc.py */
+static void copy_bk_objs_in_page_from(int from_segnum, uintptr_t pagenum,
+ bool only_if_not_modified);
+
+static void handle_segfault_in_page(uintptr_t pagenum);
+
+
+static void setup_signal_handler(void);
diff --git a/rpython/translator/stm/src_stm/stmgc.c b/rpython/translator/stm/src_stm/stmgc.c
--- a/rpython/translator/stm/src_stm/stmgc.c
+++ b/rpython/translator/stm/src_stm/stmgc.c
@@ -4,6 +4,7 @@
#include "stm/atomic.h"
#include "stm/list.h"
#include "stm/smallmalloc.h"
+#include "stm/signal_handler.h"
#include "stm/core.h"
#include "stm/pagecopy.h"
#include "stm/pages.h"
@@ -36,6 +37,7 @@
#include "stm/forksupport.c"
#include "stm/setup.c"
#include "stm/hash_id.c"
+#include "stm/signal_handler.c"
#include "stm/core.c"
#include "stm/extra.c"
#include "stm/fprintcolor.c"
diff --git a/rpython/translator/stm/src_stm/stmgc.h b/rpython/translator/stm/src_stm/stmgc.h
--- a/rpython/translator/stm/src_stm/stmgc.h
+++ b/rpython/translator/stm/src_stm/stmgc.h
@@ -749,6 +749,7 @@
void stm_hashtable_write_entry(object_t *hobj, stm_hashtable_entry_t *entry,
object_t *nvalue);
long stm_hashtable_length_upper_bound(stm_hashtable_t *);
+stm_hashtable_entry_t *stm_hashtable_pickitem(object_t *, stm_hashtable_t *);
/* WARNING: stm_hashtable_list does not do a stm_write() on the 'results'
argument. 'results' may point inside an object. So if 'results' may be
More information about the pypy-commit
mailing list