Author: Remi Meier <[email protected]>
Branch: c8-small-uniform
Changeset: r1387:22150f9efdc0
Date: 2014-09-16 14:27 +0200
http://bitbucket.org/pypy/stmgc/changeset/22150f9efdc0/
Log: add synchronization to other segments on allocation of new (old)
objects
diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -5,6 +5,19 @@
#include <signal.h>
+#ifdef NDEBUG
+#define EVENTUALLY(condition) {/* nothing */}
+#else
+#define EVENTUALLY(condition) \
+ { \
+ if (!(condition)) { \
+ acquire_privatization_lock(STM_SEGMENT->segment_num);\
+ if (!(condition)) \
+ stm_fatalerror("fails: " #condition); \
+ release_privatization_lock(STM_SEGMENT->segment_num);\
+ } \
+ }
+#endif
/* ############# commit log ############# */
@@ -616,3 +629,102 @@
synchronize_all_threads(STOP_OTHERS_AND_BECOME_GLOBALLY_UNIQUE);
s_mutex_unlock();
}
+
+
+
+static inline void _synchronize_fragment(stm_char *frag, ssize_t frag_size)
+{
+ /* double-check that the result fits in one page */
+ assert(frag_size > 0);
+ assert(frag_size + ((uintptr_t)frag & 4095) <= 4096);
+
+ /* if the page of the fragment is fully shared, nothing to do */
+ assert(STM_PSEGMENT->privatization_lock);
+ if (is_shared_log_page((uintptr_t)frag / 4096))
+ return; /* nothing to do */
+
+ /* Enqueue this object (or fragemnt of object) */
+ if (STM_PSEGMENT->sq_len == SYNC_QUEUE_SIZE)
+ synchronize_objects_flush();
+ STM_PSEGMENT->sq_fragments[STM_PSEGMENT->sq_len] = frag;
+ STM_PSEGMENT->sq_fragsizes[STM_PSEGMENT->sq_len] = frag_size;
+ ++STM_PSEGMENT->sq_len;
+}
+
+static void synchronize_object_enqueue(object_t *obj)
+{
+ assert(!_is_young(obj));
+ assert(STM_PSEGMENT->privatization_lock);
+ assert(obj->stm_flags & GCFLAG_WRITE_BARRIER);
+ ssize_t obj_size = stmcb_size_rounded_up(
+ (struct object_s *)REAL_ADDRESS(STM_SEGMENT->segment_base, obj));
+ OPT_ASSERT(obj_size >= 16);
+
+ if (LIKELY(is_small_uniform(obj))) {
+ _synchronize_fragment((stm_char *)obj, obj_size);
+ return;
+ }
+
+ /* else, a more complicated case for large objects, to copy
+ around data only within the needed pages
+ */
+ uintptr_t start = (uintptr_t)obj;
+ uintptr_t end = start + obj_size;
+
+ do {
+ uintptr_t copy_up_to = (start + 4096) & ~4095; /* end of page */
+ if (copy_up_to >= end) {
+ copy_up_to = end; /* this is the last fragment */
+ }
+ uintptr_t copy_size = copy_up_to - start;
+
+ _synchronize_fragment((stm_char *)start, copy_size);
+
+ start = copy_up_to;
+ } while (start != end);
+}
+
+static void synchronize_objects_flush(void)
+{
+
+ /* Do a full memory barrier. We must make sure that other
+ CPUs see the changes we did to the shared page ("S", in
+ synchronize_object_enqueue()) before we check the other segments
+ with is_private_page() (below). Otherwise, we risk the
+ following: this CPU writes "S" but the writes are not visible yet;
+ then it checks is_private_page() and gets false, and does nothing
+ more; just afterwards another CPU sets its own private_page bit
+ and copies the page; but it risks doing so before seeing the "S"
+ writes.
+ */
+ /* XXX: not sure this applies anymore. */
+ long j = STM_PSEGMENT->sq_len;
+ if (j == 0)
+ return;
+ STM_PSEGMENT->sq_len = 0;
+
+ __sync_synchronize();
+
+ long i, myself = STM_SEGMENT->segment_num;
+ do {
+ --j;
+ stm_char *frag = STM_PSEGMENT->sq_fragments[j];
+ uintptr_t page = ((uintptr_t)frag) / 4096UL;
+ if (is_shared_log_page(page))
+ continue;
+
+ ssize_t frag_size = STM_PSEGMENT->sq_fragsizes[j];
+
+ char *src = REAL_ADDRESS(STM_SEGMENT->segment_base, frag);
+ for (i = 0; i < NB_SEGMENTS; i++) {
+ if (i == myself)
+ continue;
+
+ char *dst = REAL_ADDRESS(get_segment_base(i), frag);
+ if (is_private_log_page_in(i, page))
+ memcpy(dst, src, frag_size);
+ else
+ EVENTUALLY(memcmp(dst, src, frag_size) == 0); /* same page */
+ }
+ } while (j > 0);
+}
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,10 @@
};
+
+#define SYNC_QUEUE_SIZE 31
+
+
/************************************************************/
@@ -72,8 +76,15 @@
/* This is for smallmalloc.c */
struct small_malloc_data_s small_malloc_data;
+
+ /* The sync queue used to synchronize newly allocated objs to
+ other segments */
+ stm_char *sq_fragments[SYNC_QUEUE_SIZE];
+ int sq_fragsizes[SYNC_QUEUE_SIZE];
+ int sq_len;
};
+
enum /* safe_point */ {
SP_NO_TRANSACTION=0,
SP_RUNNING,
@@ -138,6 +149,8 @@
static stm_thread_local_t *abort_with_mutex_no_longjmp(void);
static void abort_data_structures_from_segment_num(int segment_num);
+static void synchronize_object_enqueue(object_t *obj);
+static void synchronize_objects_flush(void);
static inline void _duck(void) {
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -47,11 +47,14 @@
/************************************************************/
static object_t *find_existing_shadow(object_t *obj);
#define GCWORD_MOVED ((object_t *) -1)
+#define FLAG_SYNC_LARGE 0x01
+
static void minor_trace_if_young(object_t **pobj)
{
/* takes a normal pointer to a thread-local pointer to an object */
object_t *obj = *pobj;
+ uintptr_t nobj_sync_now;
object_t *nobj;
char *realobj;
size_t size;
@@ -97,7 +100,6 @@
else {
/* case "small enough" */
char *allocated = allocate_outside_nursery_small(size);
- dprintf(("outside small %p or %p, sz=%lu\n", allocated, allocated
- stm_object_pages, size));
nobj = (object_t *)(allocated - stm_object_pages);
}
@@ -106,6 +108,8 @@
char *realnobj = REAL_ADDRESS(STM_SEGMENT->segment_base, nobj);
memcpy(realnobj, realobj, size);
+ nobj_sync_now = ((uintptr_t)nobj) | FLAG_SYNC_LARGE;
+
pforwarded_array[0] = GCWORD_MOVED;
pforwarded_array[1] = nobj;
*pobj = nobj;
@@ -119,10 +123,12 @@
/* a young object outside the nursery */
nobj = obj;
tree_delete_item(STM_PSEGMENT->young_outside_nursery, (uintptr_t)nobj);
+
+ nobj_sync_now = ((uintptr_t)nobj) | FLAG_SYNC_LARGE;
}
/* Must trace the object later */
- LIST_APPEND(STM_PSEGMENT->objects_pointing_to_nursery, (uintptr_t)nobj);
+ LIST_APPEND(STM_PSEGMENT->objects_pointing_to_nursery, nobj_sync_now);
}
@@ -174,13 +180,24 @@
struct list_s *lst = STM_PSEGMENT->objects_pointing_to_nursery;
while (!list_is_empty(lst)) {
- object_t *obj = (object_t *)list_pop_item(lst);;
+ uintptr_t obj_sync_now = list_pop_item(lst);
+ object_t *obj = (object_t *)(obj_sync_now & ~FLAG_SYNC_LARGE);
_collect_now(obj);
+ if (obj_sync_now & FLAG_SYNC_LARGE) {
+ /* this is a newly allocated object. We must synchronize it
+ to other segments (after we added WRITE_BARRIER). */
+ acquire_privatization_lock(STM_SEGMENT->segment_num);
+ synchronize_object_enqueue(obj);
+ release_privatization_lock(STM_SEGMENT->segment_num);
+ }
+
/* the list could have moved while appending */
lst = STM_PSEGMENT->objects_pointing_to_nursery;
}
+
+ synchronize_objects_flush();
}
diff --git a/c8/test/test_nursery.py b/c8/test/test_nursery.py
--- a/c8/test/test_nursery.py
+++ b/c8/test/test_nursery.py
@@ -274,7 +274,6 @@
self.push_root(new2)
self.commit_transaction()
new2 = self.pop_root()
- print "new2", new2
# check that this new object was correctly sychronized
self.switch(1)
diff --git a/c8/test/test_random.py b/c8/test/test_random.py
--- a/c8/test/test_random.py
+++ b/c8/test/test_random.py
@@ -361,7 +361,7 @@
def op_allocate(ex, global_state, thread_state):
size = global_state.rnd.choice([
- "16",
+ "16", "48", "288",
str(4096+16),
str(80*1024+16),
#"SOME_MEDIUM_SIZE+16",
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit