Author: Armin Rigo <[email protected]>
Branch:
Changeset: r958:cb55e6140145
Date: 2014-03-05 10:10 +0100
http://bitbucket.org/pypy/stmgc/changeset/cb55e6140145/
Log: Finish copying the shadow logic from pypy.
diff --git a/c7/stm/core.c b/c7/stm/core.c
--- a/c7/stm/core.c
+++ b/c7/stm/core.c
@@ -36,7 +36,7 @@
by finding that we already own the write-lock. */
uintptr_t lock_idx = (((uintptr_t)obj) >> 4) - WRITELOCK_START;
uint8_t lock_num = STM_PSEGMENT->write_lock_num;
- assert((intptr_t)lock_idx >= 0);
+ assert(lock_idx < sizeof(write_locks));
retry:
if (write_locks[lock_idx] == 0) {
if (UNLIKELY(!__sync_bool_compare_and_swap(&write_locks[lock_idx],
@@ -179,6 +179,7 @@
assert(list_is_empty(STM_PSEGMENT->modified_old_objects));
assert(tree_is_cleared(STM_PSEGMENT->young_outside_nursery));
+ assert(tree_is_cleared(STM_PSEGMENT->nursery_objects_shadows));
assert(STM_PSEGMENT->objects_pointing_to_nursery == NULL);
assert(STM_PSEGMENT->large_overflow_objects == NULL);
@@ -300,7 +301,7 @@
/* clear the write-lock (note that this runs with all other
threads paused, so no need to be careful about ordering) */
uintptr_t lock_idx = (((uintptr_t)item) >> 4) - WRITELOCK_START;
- assert((intptr_t)lock_idx >= 0);
+ assert(lock_idx < sizeof(write_locks));
assert(write_locks[lock_idx] == STM_PSEGMENT->write_lock_num);
write_locks[lock_idx] = 0;
@@ -371,7 +372,8 @@
/* update 'overflow_number' if needed */
if (STM_PSEGMENT->overflow_number_has_been_used) {
highest_overflow_number += GCFLAG_OVERFLOW_NUMBER_bit0;
- assert(highest_overflow_number != 0); /* XXX else, overflow! */
+ assert(highest_overflow_number != /* XXX else, overflow! */
+ (uint32_t)-GCFLAG_OVERFLOW_NUMBER_bit0);
STM_PSEGMENT->overflow_number = highest_overflow_number;
STM_PSEGMENT->overflow_number_has_been_used = false;
}
@@ -440,7 +442,7 @@
/* clear the write-lock */
uintptr_t lock_idx = (((uintptr_t)item) >> 4) - WRITELOCK_START;
- assert((intptr_t)lock_idx >= 0);
+ assert(lock_idx < sizeof(write_locks));
assert(write_locks[lock_idx] == pseg->write_lock_num);
write_locks[lock_idx] = 0;
}));
diff --git a/c7/stm/core.h b/c7/stm/core.h
--- a/c7/stm/core.h
+++ b/c7/stm/core.h
@@ -95,11 +95,16 @@
current transaction spanned a minor collection. */
struct list_s *large_overflow_objects;
- /* List of all young objects outside the nursery ("young" in the
+ /* Set of all young objects outside the nursery ("young" in the
sense that they should be in the nursery, but were too big for
that). */
struct tree_s *young_outside_nursery;
+ /* Support for id and identityhash: this is a dict mapping nursery
+ objects with GCFLAG_HAS_SHADOW to their future location at the
+ next minor collection. */
+ struct tree_s *nursery_objects_shadows;
+
/* Start time: to know approximately for how long a transaction has
been running, in contention management */
uint64_t start_time;
diff --git a/c7/stm/gcpage.c b/c7/stm/gcpage.c
--- a/c7/stm/gcpage.c
+++ b/c7/stm/gcpage.c
@@ -151,7 +151,6 @@
static inline uintptr_t mark_loc(object_t *obj)
{
uintptr_t lock_idx = (((uintptr_t)obj) >> 4) - WRITELOCK_START;
- assert(lock_idx >= 0);
assert(lock_idx < sizeof(write_locks));
return lock_idx;
}
diff --git a/c7/stm/hash_id.c b/c7/stm/hash_id.c
--- a/c7/stm/hash_id.c
+++ b/c7/stm/hash_id.c
@@ -17,7 +17,7 @@
if (obj != NULL) {
if (_is_in_nursery(obj)) {
- abort();//obj = find_shadow(obj);
+ obj = find_shadow(obj);
}
else if (is_hash) {
if (obj->stm_flags & GCFLAG_HAS_SHADOW) {
diff --git a/c7/stm/nursery.c b/c7/stm/nursery.c
--- a/c7/stm/nursery.c
+++ b/c7/stm/nursery.c
@@ -53,10 +53,12 @@
return _is_in_nursery(obj);
}
+static object_t *find_existing_shadow(object_t *obj);
+
/************************************************************/
-#define GCWORD_MOVED ((object_t *) -42)
+#define GCWORD_MOVED ((object_t *) -1)
#define FLAG_SYNC_LARGE 0x01
@@ -76,18 +78,33 @@
to GCWORD_MOVED. In that case, the forwarding location, i.e.
where the object moved to, is stored in the second word in 'obj'. */
object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)obj;
+ char *realobj;
+ size_t size;
- if (pforwarded_array[0] == GCWORD_MOVED) {
- *pobj = pforwarded_array[1]; /* already moved */
- return;
+ if (obj->stm_flags & GCFLAG_HAS_SHADOW) {
+ /* ^^ the single check above detects both already-moved objects
+ and objects with HAS_SHADOW. This is because GCWORD_MOVED
+ overrides completely the stm_flags field with 1's bits. */
+
+ if (LIKELY(pforwarded_array[0] == GCWORD_MOVED)) {
+ *pobj = pforwarded_array[1]; /* already moved */
+ return;
+ }
+ else {
+ /* really has a shadow */
+ nobj = find_existing_shadow(obj);
+ obj->stm_flags &= ~GCFLAG_HAS_SHADOW;
+ realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
+ size = stmcb_size_rounded_up((struct object_s *)realobj);
+ goto copy_large_object;
+ }
}
-
/* We need to make a copy of this object. It goes either in
a largemalloc.c-managed area, or if it's small enough, in
one of the small uniform pages from gcpage.c.
*/
- char *realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
- size_t size = stmcb_size_rounded_up((struct object_s *)realobj);
+ realobj = REAL_ADDRESS(STM_SEGMENT->segment_base, obj);
+ size = stmcb_size_rounded_up((struct object_s *)realobj);
if (1 /*size >= GC_N_SMALL_REQUESTS*8*/) {
@@ -97,6 +114,7 @@
nobj = (object_t *)(allocated - stm_object_pages);
/* Copy the object */
+ copy_large_object:;
char *realnobj = REAL_ADDRESS(STM_SEGMENT->segment_base, nobj);
memcpy(realnobj, realobj, size);
@@ -229,6 +247,8 @@
tree_clear(pseg->young_outside_nursery);
}
+
+ tree_clear(pseg->nursery_objects_shadows);
}
#define MINOR_NOTHING_TO_DO(pseg) \
@@ -340,7 +360,7 @@
char *result = allocate_outside_nursery_large(size_rounded_up);
object_t *o = (object_t *)(result - stm_object_pages);
- tree_insert(STM_PSEGMENT->young_outside_nursery, (intptr_t)o, 0);
+ tree_insert(STM_PSEGMENT->young_outside_nursery, (uintptr_t)o, 0);
memset(REAL_ADDRESS(STM_SEGMENT->segment_base, o), 0, size_rounded_up);
return o;
@@ -413,3 +433,60 @@
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);
+ size_t size = stmcb_size_rounded_up((struct object_s *)realobj);
+
+ /* always gets outside as a large object for now */
+ char *allocated = allocate_outside_nursery_large(size);
+ object_t *nobj = (object_t *)(allocated - stm_object_pages);
+
+ /* Initialize the shadow enough to be considered a valid gc object.
+ If the original object stays alive at the next minor collection,
+ it will anyway be copied over the shadow and overwrite the
+ following fields. But if the object dies, then the shadow will
+ stay around and only be freed at the next major collection, at
+ which point we want it to look valid (but ready to be freed).
+
+ Here, in the general case, it requires copying the whole object.
+ It could be more optimized in special cases like in PyPy, by
+ copying only the typeid and (for var-sized objects) the length
+ field. It's probably overkill to add a special stmcb_xxx
+ interface just for that.
+ */
+ char *realnobj = REAL_ADDRESS(STM_SEGMENT->segment_base, nobj);
+ memcpy(realnobj, realobj, size);
+
+ obj->stm_flags |= GCFLAG_HAS_SHADOW;
+ tree_insert(STM_PSEGMENT->nursery_objects_shadows,
+ (uintptr_t)obj, (uintptr_t)nobj);
+ return nobj;
+}
+
+static object_t *find_existing_shadow(object_t *obj)
+{
+ wlog_t *item;
+
+ TREE_FIND(*STM_PSEGMENT->nursery_objects_shadows,
+ (uintptr_t)obj, item, goto not_found);
+
+ /* The answer is the address of the shadow. */
+ return (object_t *)item->val;
+
+ not_found:
+ stm_fatalerror("GCFLAG_HAS_SHADOW but no shadow found");
+}
+
+static object_t *find_shadow(object_t *obj)
+{
+ /* The object 'obj' is still in the nursery. Find or allocate a
+ "shadow" object, which is where the object will be moved by the
+ next minor collection
+ */
+ if (obj->stm_flags & GCFLAG_HAS_SHADOW)
+ return find_existing_shadow(obj);
+ else
+ return allocate_shadow(obj);
+}
diff --git a/c7/stm/nursery.h b/c7/stm/nursery.h
--- a/c7/stm/nursery.h
+++ b/c7/stm/nursery.h
@@ -19,3 +19,5 @@
}
static void assert_memset_zero(void *s, size_t n);
+
+static object_t *find_shadow(object_t *obj);
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -58,6 +58,7 @@
pr->large_overflow_objects = NULL;
pr->modified_old_objects = list_create();
pr->young_outside_nursery = tree_create();
+ pr->nursery_objects_shadows = tree_create();
pr->overflow_number = GCFLAG_OVERFLOW_NUMBER_bit0 * (i + 1);
highest_overflow_number = pr->overflow_number;
}
@@ -94,6 +95,7 @@
assert(pr->large_overflow_objects == NULL);
list_free(pr->modified_old_objects);
tree_free(pr->young_outside_nursery);
+ tree_free(pr->nursery_objects_shadows);
}
munmap(stm_object_pages, TOTAL_MEMORY);
diff --git a/c7/test/test_hash_id.py b/c7/test/test_hash_id.py
--- a/c7/test/test_hash_id.py
+++ b/c7/test/test_hash_id.py
@@ -37,3 +37,38 @@
h2 = lib.stm_id(lp2)
assert h1 == int(ffi.cast("long", lp1))
assert h2 == int(ffi.cast("long", lp2))
+
+ def test_hash_nursery(self):
+ self.start_transaction()
+ lp1 = stm_allocate(16)
+ lp2 = stm_allocate(16)
+ lp3 = stm_allocate(16)
+ lp4 = stm_allocate(16)
+ h1 = lib.stm_identityhash(lp1)
+ h2 = lib.stm_identityhash(lp2)
+ h3 = lib.stm_identityhash(lp3)
+ h4 = lib.stm_identityhash(lp4)
+ assert len(set([h1, h2, h3, h4])) == 4 # guaranteed by the algo
+
+ def test_hash_lower_bits(self):
+ self.start_transaction()
+ lp1 = stm_allocate(32)
+ lp2 = stm_allocate(32)
+ lp3 = stm_allocate(32)
+ lp4 = stm_allocate(32)
+ h1 = lib.stm_identityhash(lp1)
+ h2 = lib.stm_identityhash(lp2)
+ h3 = lib.stm_identityhash(lp3)
+ h4 = lib.stm_identityhash(lp4)
+ assert len(set([h1 & 15, h2 & 15, h3 & 15, h4 & 15])) == 4
+
+ def test_hash_around_minor_collect(self):
+ self.start_transaction()
+ lp = stm_allocate(16)
+ h1 = lib.stm_identityhash(lp)
+ self.push_root(lp)
+ stm_minor_collect()
+ lp = self.pop_root()
+ h2 = lib.stm_identityhash(lp)
+ assert h2 == h1
+ assert h2 != lib.stm_id(lp)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit