Author: Remi Meier <[email protected]>
Branch: c8-private-pages
Changeset: r1531:4c49f4a4e817
Date: 2015-01-15 15:10 +0100
http://bitbucket.org/pypy/stmgc/changeset/4c49f4a4e817/
Log: WIP: starting with major collections, add a validation step to all
segs at the start of it
diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -254,9 +254,11 @@
static void reset_modified_from_backup_copies(int segment_num); /* forward */
-static void _stm_validate(void *free_if_abort)
+static bool _stm_validate()
{
- dprintf(("_stm_validate(%p)\n", free_if_abort));
+ /* returns true if we reached a valid state, or false if
+ we need to abort now */
+ dprintf(("_stm_validate()\n"));
/* go from last known entry in commit log to the
most current one and apply all changes done
by other transactions. Abort if we have read one of
@@ -268,7 +270,7 @@
if (STM_PSEGMENT->transaction_state == TS_INEVITABLE) {
assert(first_cl->next == INEV_RUNNING);
- return;
+ return true;
}
bool needs_abort = false;
@@ -284,8 +286,14 @@
if (first_cl->next == NULL)
break;
- if (first_cl->next == INEV_RUNNING)
- _stm_collectable_safe_point(); /* otherwise, we may deadlock */
+ if (first_cl->next == INEV_RUNNING) {
+#if STM_TESTS
+ stm_abort_transaction();
+#endif
+ /* need to reach safe point if an INEV transaction
+ is waiting for us, otherwise deadlock */
+ break;
+ }
/* Find the set of segments we need to copy from and lock them: */
uint64_t segments_to_lock = 1UL << my_segnum;
@@ -293,8 +301,6 @@
while ((next_cl = cl->next) != NULL) {
if (next_cl == INEV_RUNNING) {
#if STM_TESTS
- if (free_if_abort != (void *)-1)
- free(free_if_abort);
stm_abort_transaction();
#endif
/* only validate entries up to INEV */
@@ -381,13 +387,7 @@
release_privatization_lock(STM_SEGMENT->segment_num);
}
- if (needs_abort) {
- if (free_if_abort != (void *)-1)
- free(free_if_abort);
- /* pages may be inconsistent */
-
- stm_abort_transaction();
- }
+ return !needs_abort;
}
static struct stm_commit_log_entry_s *_create_commit_log_entry(void)
@@ -416,7 +416,10 @@
struct stm_commit_log_entry_s *old;
while (1) {
- _stm_validate(/* free_if_abort =*/ new);
+ if (!_stm_validate()) {
+ free(new);
+ stm_abort_transaction();
+ }
/* try to attach to commit log: */
old = STM_PSEGMENT->last_commit_log_entry;
@@ -468,7 +471,8 @@
/* ############# STM ############# */
void stm_validate()
{
- _stm_validate(NULL);
+ if (!_stm_validate())
+ stm_abort_transaction();
}
@@ -829,7 +833,7 @@
stm_rewind_jmp_restore_shadowstack(tl);
assert(tl->shadowstack == pseg->shadowstack_at_start_of_transaction);
#endif
-tl->last_abort__bytes_in_nursery = bytes_in_nursery;
+ tl->last_abort__bytes_in_nursery = bytes_in_nursery;
#pragma pop_macro("STM_SEGMENT")
#pragma pop_macro("STM_PSEGMENT")
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -38,6 +38,7 @@
GCFLAG_WRITE_BARRIER = _STM_GCFLAG_WRITE_BARRIER,
GCFLAG_HAS_SHADOW = 0x02,
GCFLAG_WB_EXECUTED = 0x04,
+ GCFLAG_VISITED = 0x05,
};
@@ -190,7 +191,7 @@
static void synchronize_objects_flush(void);
static void _signal_handler(int sig, siginfo_t *siginfo, void *context);
-static void _stm_validate(void *free_if_abort);
+static bool _stm_validate();
static inline void _duck(void) {
/* put a call to _duck() between two instructions that set 0 into
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -104,3 +104,258 @@
(uintptr_t)p / 4096UL));
return o;
}
+
+
+/************************************************************/
+
+
+static void major_collection_if_requested(void)
+{
+ assert(!_has_mutex());
+ if (!is_major_collection_requested())
+ return;
+
+ s_mutex_lock();
+
+ if (is_major_collection_requested()) { /* if still true */
+
+ synchronize_all_threads(STOP_OTHERS_UNTIL_MUTEX_UNLOCK);
+
+ if (is_major_collection_requested()) { /* if *still* true */
+ major_collection_now_at_safe_point();
+ }
+
+ }
+
+ s_mutex_unlock();
+}
+
+
+/************************************************************/
+
+/* objects to trace are traced in the sharing seg0 or in a
+ certain segment if there exist modifications there.
+ All other segments' versions should be identical to seg0's
+ version and thus don't need tracing. */
+static struct list_s *mark_objects_to_trace;
+
+/* we use the sharing seg0's pages for the GCFLAG_VISITED flag */
+
+static inline struct object_s *mark_loc(object_t *obj)
+{
+ /* uses the memory in seg0 for marking: */
+ struct object_s *result = (struct object_s*)REAL_ADDRESS(stm_object_pages,
obj);
+ return result;
+}
+
+static inline bool mark_visited_test(object_t *obj)
+{
+ struct object_s *realobj = mark_loc(obj);
+ return !!(realobj->stm_flags & GCFLAG_VISITED);
+}
+
+static inline bool mark_visited_test_and_set(object_t *obj)
+{
+ struct object_s *realobj = mark_loc(obj);
+ if (realobj->stm_flags & GCFLAG_VISITED) {
+ return true;
+ }
+ else {
+ realobj->stm_flags |= GCFLAG_VISITED;
+ return false;
+ }
+}
+
+static inline bool mark_visited_test_and_clear(object_t *obj)
+{
+ struct object_s *realobj = mark_loc(obj);
+ if (realobj->stm_flags & GCFLAG_VISITED) {
+ realobj->stm_flags &= ~GCFLAG_VISITED;
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+
+/************************************************************/
+
+/* static void mark_and_trace(object_t *obj, char *segment_base) */
+/* { */
+/* assert(list_is_empty(mark_objects_to_trace)); */
+
+/* while (1) { */
+/* /\* trace into the object (the version from 'segment_base') *\/ */
+/* struct object_s *realobj = */
+/* (struct object_s *)REAL_ADDRESS(segment_base, obj); */
+/* stmcb_trace(realobj, &mark_record_trace); */
+
+/* if (list_is_empty(mark_objects_to_trace)) */
+/* break; */
+
+/* obj = (object_t *)list_pop_item(mark_objects_to_trace); */
+/* } */
+/* } */
+
+/* static inline void mark_visit_object(object_t *obj, char *segment_base) */
+/* { */
+/* /\* if already visited, don't trace *\/ */
+/* if (obj == NULL || mark_visited_test_and_set(obj)) */
+/* return; */
+/* mark_and_trace(obj, segment_base); */
+/* } */
+
+/* static void *mark_visit_objects_from_ss(void *_, const void *slice, size_t
size) */
+/* { */
+/* const struct stm_shadowentry_s *p, *end; */
+/* p = (const struct stm_shadowentry_s *)slice; */
+/* end = (const struct stm_shadowentry_s *)(slice + size); */
+/* for (; p < end; p++) */
+/* if ((((uintptr_t)p->ss) & 3) == 0) */
+/* mark_visit_object(p->ss, stm_object_pages); */
+/* return NULL; */
+/* } */
+
+/* static void assert_obj_accessible_in(long segnum, object_t *obj) */
+/* { */
+/* #ifndef NDEBUG */
+/* uintptr_t page = (uintptr_t)item / 4096UL; */
+/* assert(get_page_status_in(segnum, page) == PAGE_ACCESSIBLE); */
+
+/* struct object_s *realobj = */
+/* (struct object_s *)REAL_ADDRESS(get_segment_base(segnum), obj); */
+
+/* size_t obj_size = stmcb_size_rounded_up(realobj); */
+/* uintptr_t count = obj_size / 4096UL + 1; */
+/* while (count--> 0) { */
+/* assert(get_page_status_in(segnum, page) == PAGE_ACCESSIBLE); */
+/* page++; */
+/* } */
+/* #endif */
+/* } */
+
+
+/* static void mark_visit_from_modified_objects(void) */
+/* { */
+/* /\* look for modified objects in segments and mark all of them */
+/* for further tracing (XXX: don't if we are going to share */
+/* some of the pages) *\/ */
+
+/* long i; */
+/* for (i = 1; i < NB_SEGMENTS; i++) { */
+/* char *base = get_segment_base(i); */
+
+/* LIST_FOREACH_R( */
+/* get_priv_segment(i)->modified_old_objects, */
+/* object_t * /\*item*\/, */
+/* ({ */
+/* /\* All modified objs have all pages accessible for now. */
+/* This is because we create a backup of the whole obj */
+/* and thus make all pages accessible. *\/ */
+/* assert_obj_accessible_in(i, item); */
+
+/* mark_visited_test_and_set(item); */
+/* mark_and_trace(item, base); /\* private
version *\/ */
+/* })); */
+/* } */
+/* } */
+
+/* static void mark_visit_from_roots(void) */
+/* { */
+/* if (testing_prebuilt_objs != NULL) { */
+/* LIST_FOREACH_R(testing_prebuilt_objs, object_t * /\*item*\/, */
+/* mark_visit_object(item, stm_object_pages)); */
+/* } */
+
+/* stm_thread_local_t *tl = stm_all_thread_locals; */
+/* do { */
+/* /\* If 'tl' is currently running, its 'associated_segment_num' */
+/* field is the segment number. If not, then the */
+/* field is still some correct segment number, and it doesn't */
+/* matter which one we pick. *\/ */
+/* char *segment_base = get_segment_base(tl->associated_segment_num);
*/
+
+/* struct stm_shadowentry_s *current = tl->shadowstack; */
+/* struct stm_shadowentry_s *base = tl->shadowstack_base; */
+/* while (current-- != base) { */
+/* if ((((uintptr_t)current->ss) & 3) == 0) */
+/* mark_visit_object(current->ss, segment_base); */
+/* } */
+/* mark_visit_object(tl->thread_local_obj, segment_base); */
+
+/* tl = tl->next; */
+/* } while (tl != stm_all_thread_locals); */
+
+/* long i; */
+/* for (i = 1; i <= NB_SEGMENTS; i++) { */
+/* if (get_priv_segment(i)->transaction_state != TS_NONE) { */
+/* mark_visit_object( */
+/* get_priv_segment(i)->threadlocal_at_start_of_transaction, */
+/* get_segment_base(i)); */
+/* stm_rewind_jmp_enum_shadowstack( */
+/* get_segment(i)->running_thread, */
+/* mark_visit_objects_from_ss); */
+/* } */
+/* } */
+/* } */
+
+static inline bool largemalloc_keep_object_at(char *data)
+{
+ /* /\* this is called by _stm_largemalloc_sweep() *\/ */
+ /* object_t *obj = (object_t *)(data - stm_object_pages); */
+ /* if (!mark_visited_test_and_clear(obj)) { */
+ /* /\* This is actually needed in order to avoid random write-read */
+ /* conflicts with objects read and freed long in the past. */
+ /* It is probably rare enough, but still, we want to avoid any */
+ /* false conflict. (test_random hits it sometimes) *\/ */
+ /* long i; */
+ /* for (i = 1; i <= NB_SEGMENTS; i++) { */
+ /* ((struct stm_read_marker_s *) */
+ /* (get_segment_base(i) + (((uintptr_t)obj) >> 4)))->rm = 0; */
+ /* } */
+ /* return false; */
+ /* } */
+ return true;
+}
+
+static void major_collection_now_at_safe_point(void)
+{
+ dprintf(("\n"));
+ dprintf((" .----- major collection -----------------------\n"));
+ assert(_has_mutex());
+
+ /* first, force a minor collection in each of the other segments */
+ major_do_validation_and_minor_collections();
+
+ dprintf((" | used before collection: %ld\n",
+ (long)pages_ctl.total_allocated));
+
+ /* only necessary because of assert that fails otherwise (XXX) */
+ acquire_all_privatization_locks();
+
+ DEBUG_EXPECT_SEGFAULT(false);
+
+ /* marking */
+ /* LIST_CREATE(mark_objects_to_trace); */
+ /* mark_visit_from_modified_objects(); */
+ /* mark_visit_from_roots(); */
+ /* LIST_FREE(mark_objects_to_trace); */
+
+ /* /\* cleanup *\/ */
+ /* clean_up_segment_lists(); */
+
+ /* /\* sweeping *\/ */
+ /* sweep_large_objects(); */
+ /* //sweep_uniform_pages(); */
+
+ dprintf((" | used after collection: %ld\n",
+ (long)pages_ctl.total_allocated));
+ dprintf((" `----------------------------------------------\n"));
+
+ reset_major_collection_requested();
+
+ DEBUG_EXPECT_SEGFAULT(true);
+
+ release_all_privatization_locks();
+}
diff --git a/c8/stm/gcpage.h b/c8/stm/gcpage.h
--- a/c8/stm/gcpage.h
+++ b/c8/stm/gcpage.h
@@ -16,3 +16,8 @@
static void teardown_gcpage(void);
static void setup_N_pages(char *pages_addr, long num);
static stm_char *allocate_outside_nursery_large(uint64_t size);
+
+
+static void major_collection_if_requested(void);
+static void major_collection_now_at_safe_point(void);
+static bool largemalloc_keep_object_at(char *data); /* for largemalloc.c */
diff --git a/c8/stm/largemalloc.c b/c8/stm/largemalloc.c
--- a/c8/stm/largemalloc.c
+++ b/c8/stm/largemalloc.c
@@ -588,8 +588,7 @@
if (_stm_largemalloc_keep != NULL)
return _stm_largemalloc_keep((char *)&chunk->d);
#endif
- return true;
- //XXX: return largemalloc_keep_object_at((char *)&chunk->d);
+ return largemalloc_keep_object_at((char *)&chunk->d);
}
void _stm_largemalloc_sweep(void)
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -18,10 +18,8 @@
assert(_STM_FAST_ALLOC <= NURSERY_SIZE);
_stm_nursery_start = NURSERY_START;
- long i = 0;
- get_segment(i)->nursery_current = (stm_char *)-1;
- get_segment(i)->nursery_end = -1;
- for (i = 1; i < NB_SEGMENTS; i++) {
+ long i;
+ for (i = 0; i < NB_SEGMENTS; i++) {
get_segment(i)->nursery_current = (stm_char *)NURSERY_START;
get_segment(i)->nursery_end = NURSERY_END;
}
@@ -292,10 +290,10 @@
void stm_collect(long level)
{
if (level > 0)
- abort();
+ force_major_collection_request();
minor_collection(/*commit=*/ false);
- /* XXX: major_collection_if_requested(); */
+ major_collection_if_requested();
}
@@ -382,6 +380,52 @@
}
+static void major_do_validation_and_minor_collections(void)
+{
+ int original_num = STM_SEGMENT->segment_num;
+ long i;
+
+ /* including the sharing seg0 */
+ for (i = 0; i < NB_SEGMENTS; i++) {
+ set_gs_register(get_segment_base(i));
+
+ if (!_stm_validate()) {
+ /* tell it to abort when continuing */
+ STM_PSEGMENT->pub.nursery_end = NSE_SIGABORT;
+ assert(must_abort());
+
+ dprintf(("abort data structures\n"));
+ abort_data_structures_from_segment_num(i);
+ continue;
+ }
+
+
+ if (MINOR_NOTHING_TO_DO(STM_PSEGMENT)) /*TS_NONE segments have
NOTHING_TO_DO*/
+ continue;
+
+ assert(STM_PSEGMENT->transaction_state != TS_NONE);
+ assert(STM_PSEGMENT->safe_point != SP_RUNNING);
+ assert(STM_PSEGMENT->safe_point != SP_NO_TRANSACTION);
+
+
+ /* Other segments that will abort immediately after resuming: we
+ have to ignore them, not try to collect them anyway!
+ Collecting might fail due to invalid state.
+ */
+ if (!must_abort()) {
+ _do_minor_collection(/*commit=*/ false);
+ assert(MINOR_NOTHING_TO_DO(STM_PSEGMENT));
+ }
+ else {
+ dprintf(("abort data structures\n"));
+ abort_data_structures_from_segment_num(i);
+ }
+ }
+
+ set_gs_register(get_segment_base(original_num));
+}
+
+
static object_t *allocate_shadow(object_t *obj)
{
char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
diff --git a/c8/stm/nursery.h b/c8/stm/nursery.h
--- a/c8/stm/nursery.h
+++ b/c8/stm/nursery.h
@@ -1,9 +1,11 @@
#define NSE_SIGPAUSE _STM_NSE_SIGNAL_MAX
+#define NSE_SIGABORT _STM_NSE_SIGNAL_ABORT
static void minor_collection(bool commit);
static void check_nursery_at_transaction_start(void);
static size_t throw_away_nursery(struct stm_priv_segment_info_s *pseg);
+static void major_do_validation_and_minor_collections(void);
static void assert_memset_zero(void *s, size_t n);
diff --git a/c8/stm/pages.c b/c8/stm/pages.c
--- a/c8/stm/pages.c
+++ b/c8/stm/pages.c
@@ -34,6 +34,29 @@
return ta;
}
+static bool is_major_collection_requested(void)
+{
+ return pages_ctl.major_collection_requested;
+}
+
+static void force_major_collection_request(void)
+{
+ pages_ctl.major_collection_requested = true;
+}
+
+static void reset_major_collection_requested(void)
+{
+ assert(_has_mutex());
+
+ uint64_t next_bound = (uint64_t)((double)pages_ctl.total_allocated *
+ GC_MAJOR_COLLECT);
+ if (next_bound < GC_MIN)
+ next_bound = GC_MIN;
+
+ pages_ctl.total_allocated_bound = next_bound;
+ pages_ctl.major_collection_requested = false;
+}
+
/************************************************************/
diff --git a/c8/stm/pages.h b/c8/stm/pages.h
--- a/c8/stm/pages.h
+++ b/c8/stm/pages.h
@@ -44,6 +44,9 @@
static void page_mark_inaccessible(long segnum, uintptr_t pagenum);
static uint64_t increment_total_allocated(ssize_t add_or_remove);
+static bool is_major_collection_requested(void);
+static void force_major_collection_request(void);
+static void reset_major_collection_requested(void);
static inline char *get_virtual_page(long segnum, uintptr_t pagenum)
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -59,7 +59,8 @@
#define _STM_GCFLAG_WRITE_BARRIER 0x01
#define _STM_FAST_ALLOC (66*1024)
-#define _STM_NSE_SIGNAL_MAX 1
+#define _STM_NSE_SIGNAL_ABORT 1
+#define _STM_NSE_SIGNAL_MAX 2
void _stm_write_slowpath(object_t *);
object_t *_stm_allocate_slowpath(ssize_t);
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -302,7 +302,7 @@
],
undef_macros=['NDEBUG'],
include_dirs=[parent_dir],
- extra_compile_args=['-g', '-O0', '-Wall', '-Werror', '-ferror-limit=1'],
+ extra_compile_args=['-g', '-O0', '-Wall', '-Werror', '-ferror-limit=5'],
extra_link_args=['-g', '-lrt'],
force_generic_engine=True)
diff --git a/c8/test/test_gcpage.py b/c8/test/test_gcpage.py
--- a/c8/test/test_gcpage.py
+++ b/c8/test/test_gcpage.py
@@ -60,6 +60,7 @@
assert ([stm_is_accessible_page(p) for p in pages]
== [True, True])
+
def test_partial_alloced_pages(self):
self.start_transaction()
new = stm_allocate(16)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit