Author: Remi Meier <[email protected]>
Branch: stmgc-c4
Changeset: r65771:ed40f1172b80
Date: 2013-07-29 08:41 +0200
http://bitbucket.org/pypy/pypy/changeset/ed40f1172b80/
Log: Merge
diff --git a/rpython/memory/gc/stmgc.py b/rpython/memory/gc/stmgc.py
--- a/rpython/memory/gc/stmgc.py
+++ b/rpython/memory/gc/stmgc.py
@@ -44,15 +44,16 @@
GCFLAG_PREBUILT_ORIGINAL = first_gcflag << 3
GCFLAG_PUBLIC_TO_PRIVATE = first_gcflag << 4
GCFLAG_WRITE_BARRIER = first_gcflag << 5 # stmgc.h
- GCFLAG_NURSERY_MOVED = first_gcflag << 6
+ GCFLAG_MOVED = first_gcflag << 6
GCFLAG_BACKUP_COPY = first_gcflag << 7 # debug
GCFLAG_STUB = first_gcflag << 8 # debug
GCFLAG_PRIVATE_FROM_PROTECTED = first_gcflag << 9
GCFLAG_HAS_ID = first_gcflag << 10
GCFLAG_IMMUTABLE = first_gcflag << 11
GCFLAG_SMALLSTUB = first_gcflag << 12
+ GCFLAG_MARKED = first_gcflag << 13
- PREBUILT_FLAGS = first_gcflag * (1 + 2 + 4 + 8)
+ PREBUILT_FLAGS = first_gcflag * ((1<<0) | (1<<1) | (1<<2) | (1<<3) |
(1<<13))
PREBUILT_REVISION = r_uint(1)
FX_MASK = 65535
@@ -109,7 +110,7 @@
# XXX finalizers are ignored for now
#ll_assert(not needs_finalizer, 'XXX needs_finalizer')
#ll_assert(not is_finalizer_light, 'XXX is_finalizer_light')
- #ll_assert(not contains_weakptr, 'XXX contains_weakptr')
+ ll_assert(not contains_weakptr, 'contains_weakptr: use malloc_weakref')
# XXX call optimized versions, e.g. if size < GC_NURSERY_SECTION
return llop.stm_allocate(llmemory.GCREF, size, typeid16)
@@ -131,12 +132,14 @@
seen by the GC, then it can get collected."""
tid = self.get_hdr_tid(obj)[0]
if bool(tid & self.GCFLAG_OLD):
- return False
+ return False # XXX wrong so far. We should add a flag to the
+ # object that means "don't ever kill this copy"
return True
@classmethod
def JIT_max_size_of_young_obj(cls):
+ # XXX there is actually a maximum, check
return None
@classmethod
diff --git a/rpython/translator/stm/src_stm/dbgmem.c
b/rpython/translator/stm/src_stm/dbgmem.c
--- a/rpython/translator/stm/src_stm/dbgmem.c
+++ b/rpython/translator/stm/src_stm/dbgmem.c
@@ -9,7 +9,7 @@
#ifdef _GC_DEBUG
/************************************************************/
-#define MMAP_TOTAL 671088640 /* 640MB */
+#define MMAP_TOTAL 1280*1024*1024 /* 1280MB */
static pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER;
static char *zone_start, *zone_current = NULL, *zone_end = NULL;
@@ -71,6 +71,10 @@
void stm_free(void *p, size_t sz)
{
+ if (p == NULL) {
+ assert(sz == 0);
+ return;
+ }
assert(((intptr_t)((char *)p + sz) & (PAGE_SIZE-1)) == 0);
size_t nb_pages = (sz + PAGE_SIZE - 1) / PAGE_SIZE + 1;
@@ -84,6 +88,14 @@
_stm_dbgmem(p, sz, PROT_NONE);
}
+void *stm_realloc(void *p, size_t newsz, size_t oldsz)
+{
+ void *r = stm_malloc(newsz);
+ memcpy(r, p, oldsz < newsz ? oldsz : newsz);
+ stm_free(p, oldsz);
+ return r;
+}
+
int _stm_can_access_memory(char *p)
{
long base = ((char *)p - zone_start) / PAGE_SIZE;
diff --git a/rpython/translator/stm/src_stm/dbgmem.h
b/rpython/translator/stm/src_stm/dbgmem.h
--- a/rpython/translator/stm/src_stm/dbgmem.h
+++ b/rpython/translator/stm/src_stm/dbgmem.h
@@ -7,6 +7,7 @@
void *stm_malloc(size_t);
void stm_free(void *, size_t);
+void *stm_realloc(void *, size_t, size_t);
int _stm_can_access_memory(char *);
void assert_cleared(char *, size_t);
@@ -14,6 +15,7 @@
#define stm_malloc(sz) malloc(sz)
#define stm_free(p,sz) free(p)
+#define stm_realloc(p,newsz,oldsz) realloc(p,newsz)
#define assert_cleared(p,sz) do { } while(0)
#endif
diff --git a/rpython/translator/stm/src_stm/et.c
b/rpython/translator/stm/src_stm/et.c
--- a/rpython/translator/stm/src_stm/et.c
+++ b/rpython/translator/stm/src_stm/et.c
@@ -146,7 +146,7 @@
gcptr P_prev = P;
P = (gcptr)v;
assert((P->h_tid & GCFLAG_PUBLIC) ||
- (P_prev->h_tid & GCFLAG_NURSERY_MOVED));
+ (P_prev->h_tid & GCFLAG_MOVED));
v = ACCESS_ONCE(P->h_revision);
@@ -238,7 +238,7 @@
add_in_recent_reads_cache:
/* The risks are that the following assert fails, because the flag was
added just now by a parallel thread during stealing... */
- /*assert(!(P->h_tid & GCFLAG_NURSERY_MOVED));*/
+ /*assert(!(P->h_tid & GCFLAG_MOVED));*/
fxcache_add(&d->recent_reads_cache, P);
return P;
@@ -281,7 +281,7 @@
*/
if (P->h_tid & GCFLAG_PUBLIC)
{
- if (P->h_tid & GCFLAG_NURSERY_MOVED)
+ if (P->h_tid & GCFLAG_MOVED)
{
P = (gcptr)P->h_revision;
assert(P->h_tid & GCFLAG_PUBLIC);
@@ -413,7 +413,7 @@
while (v = P->h_revision, IS_POINTER(v))
{
- if (P->h_tid & GCFLAG_NURSERY_MOVED)
+ if (P->h_tid & GCFLAG_MOVED)
dprintf(("nursery_moved "));
if (v & 2)
@@ -510,7 +510,7 @@
static gcptr LocalizePublic(struct tx_descriptor *d, gcptr R)
{
assert(R->h_tid & GCFLAG_PUBLIC);
- assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
+ assert(!(R->h_tid & GCFLAG_MOVED));
#ifdef _GC_DEBUG
wlog_t *entry;
@@ -570,6 +570,13 @@
gcptr stm_WriteBarrier(gcptr P)
{
assert(!(P->h_tid & GCFLAG_IMMUTABLE));
+ assert((P->h_tid & GCFLAG_STUB) ||
+ stmgc_size(P) > sizeof(struct stm_stub_s) - WORD);
+ /* If stmgc_size(P) gives a number <= sizeof(stub)-WORD, then there is a
+ risk of overrunning the object later in gcpage.c when copying a stub
+ over it. However such objects are so small that they contain no field
+ at all, and so no write barrier should occur on them. */
+
if (is_private(P))
{
/* If we have GCFLAG_WRITE_BARRIER in P, then list it into
@@ -606,7 +613,7 @@
Add R into the list 'public_with_young_copy', unless W is
actually an old object, in which case we need to record W.
*/
- if (R->h_tid & GCFLAG_NURSERY_MOVED)
+ if (R->h_tid & GCFLAG_MOVED)
{
/* Bah, the object turned into this kind of stub, possibly
while we were waiting for the collection_lock, because it
@@ -696,8 +703,8 @@
continue;
}
}
- else if ((R->h_tid & (GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED))
- == (GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED))
+ else if ((R->h_tid & (GCFLAG_PUBLIC | GCFLAG_MOVED))
+ == (GCFLAG_PUBLIC | GCFLAG_MOVED))
{
/* such an object is identical to the one it points to
(stolen protected young object with h_revision pointing
@@ -970,6 +977,7 @@
revision_t my_lock = d->my_lock;
wlog_t *item;
+ dprintf(("acquire_locks\n"));
assert(!stm_has_got_any_lock(d));
assert(d->public_descriptor->stolen_objects.size == 0);
@@ -982,6 +990,7 @@
revision_t v;
retry:
assert(R->h_tid & GCFLAG_PUBLIC);
+ assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
v = ACCESS_ONCE(R->h_revision);
if (IS_POINTER(v)) /* "has a more recent revision" */
{
@@ -1014,7 +1023,7 @@
static void CancelLocks(struct tx_descriptor *d)
{
wlog_t *item;
-
+ dprintf(("cancel_locks\n"));
if (!g2l_any_entry(&d->public_to_private))
return;
@@ -1107,7 +1116,7 @@
assert(!(L->h_tid & GCFLAG_VISITED));
assert(!(L->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
assert(!(L->h_tid & GCFLAG_PREBUILT_ORIGINAL));
- assert(!(L->h_tid & GCFLAG_NURSERY_MOVED));
+ assert(!(L->h_tid & GCFLAG_MOVED));
assert(L->h_revision != localrev); /* modified by AcquireLocks() */
#ifdef DUMP_EXTRA
@@ -1119,7 +1128,9 @@
gcptr stub = stm_stub_malloc(d->public_descriptor, 0);
stub->h_tid = (L->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC
| GCFLAG_STUB
+ | GCFLAG_SMALLSTUB
| GCFLAG_OLD;
+ dprintf(("et.c: stm_stub_malloc -> %p\n", stub));
stub->h_revision = ((revision_t)L) | 2;
assert(!(L->h_tid & GCFLAG_HAS_ID));
@@ -1154,7 +1165,7 @@
assert(R->h_tid & GCFLAG_PUBLIC);
assert(R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE);
- assert(!(R->h_tid & GCFLAG_NURSERY_MOVED));
+ assert(!(R->h_tid & GCFLAG_MOVED));
assert(R->h_revision != localrev);
#ifdef DUMP_EXTRA
@@ -1249,7 +1260,7 @@
assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
P->h_tid |= GCFLAG_PUBLIC;
assert(!(P->h_tid & GCFLAG_HAS_ID));
- if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_NURSERY_MOVED;
+ if (!(P->h_tid & GCFLAG_OLD)) P->h_tid |= GCFLAG_MOVED;
/* P becomes a public outdated object. It may create an
exception documented in doc-objects.txt: a public but young
object. It's still fine because it should only be seen by
@@ -1282,7 +1293,7 @@
revision_t cur_time;
struct tx_descriptor *d = thread_descriptor;
assert(d->active >= 1);
-
+ dprintf(("CommitTransaction(%p)\n", d));
spinlock_acquire(d->public_descriptor->collection_lock, 'C'); /*committing*/
if (d->public_descriptor->stolen_objects.size != 0)
stm_normalize_stolen_objects(d);
@@ -1366,6 +1377,7 @@
d->active = 2;
d->reads_size_limit_nonatomic = 0;
update_reads_size_limit(d);
+ dprintf(("make_inevitable(%p)\n", d));
}
static revision_t acquire_inev_mutex_and_mark_global_cur_time(
diff --git a/rpython/translator/stm/src_stm/et.h
b/rpython/translator/stm/src_stm/et.h
--- a/rpython/translator/stm/src_stm/et.h
+++ b/rpython/translator/stm/src_stm/et.h
@@ -26,7 +26,11 @@
*
* GCFLAG_OLD is set on old objects.
*
- * GCFLAG_VISITED is used temporarily during major collections.
+ * GCFLAG_VISITED and GCFLAG_MARKED are used temporarily during major
+ * collections. The objects are MARKED|VISITED as soon as they have been
+ * added to 'objects_to_trace', and so will be or have been traced. The
+ * objects are only MARKED if their memory must be kept alive, but (so far)
+ * we found that tracing them is not useful.
*
* GCFLAG_PUBLIC is set on public objects.
*
@@ -47,7 +51,7 @@
* the list 'old_objects_to_trace'; it is set again at the next minor
* collection.
*
- * GCFLAG_NURSERY_MOVED is used temporarily during minor collections.
+ * GCFLAG_MOVED is used temporarily during minor/major collections.
*
* GCFLAG_STUB is set for debugging on stub objects made by stealing or
* by major collections. 'p_stub->h_revision' might be a value
@@ -68,16 +72,20 @@
static const revision_t GCFLAG_PREBUILT_ORIGINAL = STM_FIRST_GCFLAG << 3;
static const revision_t GCFLAG_PUBLIC_TO_PRIVATE = STM_FIRST_GCFLAG << 4;
// in stmgc.h: GCFLAG_WRITE_BARRIER = STM_FIRST_GCFLAG << 5;
-static const revision_t GCFLAG_NURSERY_MOVED = STM_FIRST_GCFLAG << 6;
+static const revision_t GCFLAG_MOVED = STM_FIRST_GCFLAG << 6;
static const revision_t GCFLAG_BACKUP_COPY /*debug*/ = STM_FIRST_GCFLAG << 7;
static const revision_t GCFLAG_STUB /*debug*/ = STM_FIRST_GCFLAG << 8;
static const revision_t GCFLAG_PRIVATE_FROM_PROTECTED = STM_FIRST_GCFLAG << 9;
static const revision_t GCFLAG_HAS_ID = STM_FIRST_GCFLAG << 10;
static const revision_t GCFLAG_IMMUTABLE = STM_FIRST_GCFLAG << 11;
+static const revision_t GCFLAG_SMALLSTUB /*debug*/ = STM_FIRST_GCFLAG << 12;
+static const revision_t GCFLAG_MARKED = STM_FIRST_GCFLAG << 13;
+/* warning, the last flag available is "<< 15" on 32-bit */
/* this value must be reflected in PREBUILT_FLAGS in stmgc.h */
#define GCFLAG_PREBUILT (GCFLAG_VISITED | \
+ GCFLAG_MARKED | \
GCFLAG_PREBUILT_ORIGINAL | \
GCFLAG_OLD | \
GCFLAG_PUBLIC)
@@ -88,12 +96,14 @@
"PREBUILT_ORIGINAL", \
"PUBLIC_TO_PRIVATE", \
"WRITE_BARRIER", \
- "NURSERY_MOVED", \
+ "MOVED", \
"BACKUP_COPY", \
"STUB", \
"PRIVATE_FROM_PROTECTED", \
- "HAS_ID", \
- "IMMUTABLE", \
+ "HAS_ID", \
+ "IMMUTABLE", \
+ "SMALLSTUB", \
+ "MARKED", \
NULL }
#define IS_POINTER(v) (!((v) & 1)) /* even-valued number */
diff --git a/rpython/translator/stm/src_stm/extra.c
b/rpython/translator/stm/src_stm/extra.c
--- a/rpython/translator/stm/src_stm/extra.c
+++ b/rpython/translator/stm/src_stm/extra.c
@@ -132,17 +132,16 @@
_Bool stm_pointer_equal(gcptr p1, gcptr p2)
{
- /* fast path for two equal pointers */
- if (p1 == p2)
- return 1;
- /* if p1 or p2 is NULL (but not both, because they are different
- pointers), then return 0 */
- if (p1 == NULL || p2 == NULL)
- return 0;
- /* types must be the same */
- if ((p1->h_tid & STM_USER_TID_MASK) != (p2->h_tid & STM_USER_TID_MASK))
- return 0;
- return stm_id(p1) == stm_id(p2);
+ if (p1 != NULL && p2 != NULL) {
+ /* resolve h_original, but only if !PREBUILT_ORIGINAL */
+ if (p1->h_original && !(p1->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+ p1 = (gcptr)p1->h_original;
+ }
+ if (p2->h_original && !(p2->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+ p2 = (gcptr)p2->h_original;
+ }
+ }
+ return (p1 == p2);
}
/************************************************************/
diff --git a/rpython/translator/stm/src_stm/gcpage.c
b/rpython/translator/stm/src_stm/gcpage.c
--- a/rpython/translator/stm/src_stm/gcpage.c
+++ b/rpython/translator/stm/src_stm/gcpage.c
@@ -213,157 +213,229 @@
static struct GcPtrList objects_to_trace;
-static void keep_original_alive(gcptr obj)
+static gcptr copy_over_original(gcptr obj, gcptr id_copy)
{
- /* keep alive the original of a visited object */
- gcptr id_copy = (gcptr)obj->h_original;
- /* prebuilt original objects may have a predifined
- hash in h_original */
- if (id_copy && !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
- if (!(id_copy->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
- id_copy->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE;
- /* see fix_outdated() */
- if (!(id_copy->h_tid & GCFLAG_VISITED)) {
- id_copy->h_tid |= GCFLAG_VISITED;
+ assert(obj != id_copy);
+ assert(id_copy == (gcptr)obj->h_original);
+ assert(!(id_copy->h_revision & 1)); /* not head-revision itself */
- /* XXX: may not always need tracing? */
- if (!(id_copy->h_tid & GCFLAG_STUB))
- gcptrlist_insert(&objects_to_trace, id_copy);
- }
- }
- else {
- /* prebuilt originals won't get collected anyway
- and if they are not reachable in any other way,
- we only ever need their location, not their content */
+ /* check a few flags */
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+ assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
+ assert(!(obj->h_tid & GCFLAG_BACKUP_COPY));
+
+ assert(id_copy->h_tid & GCFLAG_PUBLIC);
+ assert(!(id_copy->h_tid & GCFLAG_BACKUP_COPY));
+
+ /* id_copy may be a stub, but in this case, as the original, it
+ should have been allocated with a big enough chunk of memory.
+ Also, obj itself might be a stub. */
+ assert(!(id_copy->h_tid & GCFLAG_SMALLSTUB));
+ if (!(id_copy->h_tid & GCFLAG_STUB) && !(obj->h_tid & GCFLAG_STUB)) {
+ assert(stmgc_size(id_copy) == stmgc_size(obj));
+ }
+
+ /* add the MOVED flag to 'obj' */
+ obj->h_tid |= GCFLAG_MOVED;
+
+ /* copy the object's content */
+ size_t objsize;
+ if (obj->h_tid & GCFLAG_STUB)
+ objsize = sizeof(struct stm_stub_s);
+ else {
+ objsize = stmgc_size(obj);
+ assert(objsize > sizeof(struct stm_stub_s) - WORD);
+ }
+ dprintf(("copy %p over %p (%zd bytes)\n", obj, id_copy, objsize));
+ memcpy(id_copy + 1, obj + 1, objsize - sizeof(struct stm_object_s));
+
+ /* copy the object's h_revision number */
+ id_copy->h_revision = obj->h_revision;
+
+ /* copy the STUB flag */
+ id_copy->h_tid &= ~GCFLAG_STUB;
+ id_copy->h_tid |= (obj->h_tid & GCFLAG_STUB);
+
+ return id_copy;
+}
+
+static void visit_nonpublic(gcptr obj, struct tx_public_descriptor *gcp)
+{
+ /* Visit a protected or private object. 'gcp' must be either NULL or
+ point to the thread that has got the object. This 'gcp' is only an
+ optimization: it lets us trace (most) private/protected objects
+ and replace pointers to public objects in them with pointers to
+ private/protected objects if they are the most recent ones,
+ provided they belong to the same thread.
+ */
+ assert(!(obj->h_tid & GCFLAG_PUBLIC));
+ assert(!(obj->h_tid & GCFLAG_STUB));
+ assert(!(obj->h_tid & GCFLAG_HAS_ID));
+ assert(!(obj->h_tid & GCFLAG_SMALLSTUB));
+ assert(!(obj->h_tid & GCFLAG_MOVED));
+
+ if (obj->h_tid & GCFLAG_VISITED)
+ return; /* already visited */
+
+ obj->h_tid |= GCFLAG_VISITED | GCFLAG_MARKED;
+ gcptrlist_insert2(&objects_to_trace, obj, (gcptr)gcp);
+
+ obj = (gcptr)obj->h_original;
+ if (obj != NULL)
+ obj->h_tid |= GCFLAG_MARKED;
+}
+
+static gcptr visit_public(gcptr obj, struct tx_public_descriptor *gcp)
+{
+ /* The goal is to walk to the most recent copy, then copy its
+ content back into the h_original, and finally returns this
+ h_original. Or, if gcp != NULL and the most recent copy is
+ protected by precisely 'gcp', then we return it instead.
+ */
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+ assert(!(obj->h_tid & GCFLAG_BACKUP_COPY));
+ assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+
+ gcptr original;
+ if (obj->h_original != 0 &&
+ !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+ original = (gcptr)obj->h_original;
+ /* the h_original may be protected, or private_from_protected,
+ in some cases. Then we can't use it. We'll use the most
+ recent h_revision which is public. */
+ if (!(original->h_tid & GCFLAG_PUBLIC)) {
+ original->h_tid |= GCFLAG_MARKED;
+ original = NULL;
}
}
+ else
+ original = obj;
+
+ /* the original object must not be a small stub. */
+ assert(original == NULL || !(original->h_tid & GCFLAG_SMALLSTUB));
+
+ /* if 'original' was already visited, we are done */
+ if (original != NULL && original->h_tid & GCFLAG_VISITED)
+ return original;
+
+ /* walk to the head of the chained list */
+ while (IS_POINTER(obj->h_revision)) {
+ if (!(obj->h_revision & 2)) {
+ obj = (gcptr)obj->h_revision;
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+ continue;
+ }
+
+ /* it's a stub: check the current stealing status */
+ assert(obj->h_tid & GCFLAG_STUB);
+ gcptr obj2 = (gcptr)(obj->h_revision - 2);
+
+ if (obj2->h_tid & GCFLAG_PUBLIC) {
+ /* the stub target itself was stolen, so is public now.
+ Continue looping from there. */
+ obj = obj2;
+ continue;
+ }
+
+ if (obj2->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) {
+ /* the stub target is a private_from_protected. */
+ gcptr obj3 = (gcptr)obj2->h_revision;
+ if (obj3->h_tid & GCFLAG_PUBLIC) {
+ assert(!(obj3->h_tid & GCFLAG_BACKUP_COPY));
+ /* the backup copy was stolen and is now a regular
+ public object. */
+ obj = obj3;
+ continue;
+ }
+ else {
+ /* the backup copy was not stolen. Ignore this pair
+ obj2/obj3, and the head of the public chain is obj.
+ The pair obj2/obj3 was or will be handled by
+ mark_all_stack_roots(). */
+ assert(obj3->h_tid & GCFLAG_BACKUP_COPY);
+
+ assert(STUB_THREAD(obj) != NULL);
+ if (STUB_THREAD(obj) == gcp)
+ return obj2;
+ break;
+ }
+ }
+ else {
+ /* the stub target is just a protected object.
+ The head of the public chain is obj. We have to
+ explicitly keep obj2 alive. */
+ assert(!IS_POINTER(obj2->h_revision));
+ visit_nonpublic(obj2, STUB_THREAD(obj));
+
+ assert(STUB_THREAD(obj) != NULL);
+ if (STUB_THREAD(obj) == gcp)
+ return obj2;
+ break;
+ }
+ }
+
+ /* at this point, 'obj' contains the most recent revision which is
+ public. */
+ if (original == NULL) {
+ original = obj;
+ if (original->h_tid & GCFLAG_VISITED)
+ return original;
+ }
+ else if (obj != original) {
+ /* copy obj over original */
+ copy_over_original(obj, original);
+ }
+
+ /* return this original */
+ original->h_tid |= GCFLAG_VISITED | GCFLAG_MARKED;
+ if (!(original->h_tid & GCFLAG_STUB))
+ gcptrlist_insert2(&objects_to_trace, original, NULL);
+ return original;
}
-static void visit(gcptr *pobj);
+static struct tx_public_descriptor *visit_protected_gcp;
-gcptr stmgcpage_visit(gcptr obj)
+static void visit_take_protected(gcptr *pobj)
{
- visit(&obj);
- return obj;
-}
-
-static void visit(gcptr *pobj)
-{
+ /* Visits '*pobj', marking it as surviving and possibly adding it to
+ objects_to_trace. Fixes *pobj to point to the exact copy that
+ survived. This function will replace *pobj with a protected
+ copy if it belongs to the thread 'visit_protected_gcp', so the
+ latter must be initialized before any call!
+ */
gcptr obj = *pobj;
if (obj == NULL)
return;
- restart:
- if (obj->h_revision & 1) {
- assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
- assert(!(obj->h_tid & GCFLAG_STUB));
- if (!(obj->h_tid & GCFLAG_VISITED)) {
- obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see fix_outdated() */
- obj->h_tid |= GCFLAG_VISITED;
- gcptrlist_insert(&objects_to_trace, obj);
-
- keep_original_alive(obj);
- }
- }
- else if (obj->h_tid & GCFLAG_PUBLIC) {
- /* h_revision is a ptr: we have a more recent version */
- gcptr prev_obj = obj;
-
- if (!(obj->h_revision & 2)) {
- /* go visit the more recent version */
- obj = (gcptr)obj->h_revision;
- }
- else {
- /* it's a stub: keep it if it points to a protected version,
- because we need to keep the effect of stealing if it is
- later accessed by the wrong thread. If it points to a
- public object (possibly outdated), we can ignore the stub.
- */
- assert(obj->h_tid & GCFLAG_STUB);
- obj = (gcptr)(obj->h_revision - 2);
- if (!(obj->h_tid & GCFLAG_PUBLIC)) {
- prev_obj->h_tid |= GCFLAG_VISITED;
- keep_original_alive(prev_obj);
-
- assert(*pobj == prev_obj);
- /* recursion, but should be only once */
- obj = stmgcpage_visit(obj);
- assert(prev_obj->h_tid & GCFLAG_STUB);
- prev_obj->h_revision = ((revision_t)obj) | 2;
- return;
- }
- }
-
- if (!(obj->h_revision & 3)) {
- /* obj is neither a stub nor a most recent revision:
- completely ignore obj->h_revision */
-
- obj = (gcptr)obj->h_revision;
- assert(obj->h_tid & GCFLAG_PUBLIC);
- prev_obj->h_revision = (revision_t)obj;
- }
- *pobj = obj;
- goto restart;
- }
- else if (obj->h_tid & GCFLAG_VISITED) {
- dprintf(("[already visited: %p]\n", obj));
- assert(obj == *pobj);
- assert((obj->h_revision & 3) || /* either odd, or stub */
- (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
- return; /* already seen */
+ if (!(obj->h_tid & GCFLAG_PUBLIC)) {
+ /* 'obj' is a private or protected copy. */
+ visit_nonpublic(obj, visit_protected_gcp);
}
else {
- assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
- gcptr B = (gcptr)obj->h_revision;
- assert(B->h_tid & (GCFLAG_PUBLIC | GCFLAG_BACKUP_COPY));
-
- if (obj->h_original && (gcptr)obj->h_original != B) {
- /* if B is original, it will be visited anyway */
- assert(obj->h_original == B->h_original);
- assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
- keep_original_alive(obj);
- }
-
- obj->h_tid |= GCFLAG_VISITED;
- B->h_tid |= GCFLAG_VISITED;
- assert(!(obj->h_tid & GCFLAG_STUB));
- assert(!(B->h_tid & GCFLAG_STUB));
- gcptrlist_insert2(&objects_to_trace, obj, B);
-
- if (IS_POINTER(B->h_revision)) {
- assert(B->h_tid & GCFLAG_PUBLIC);
- assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
- assert(!(B->h_revision & 2));
-
- pobj = (gcptr *)&B->h_revision;
- obj = *pobj;
- goto restart;
- }
+ *pobj = visit_public(obj, visit_protected_gcp);
}
}
-
-static void visit_keep(gcptr obj)
+gcptr stmgcpage_visit(gcptr obj)
{
- if (!(obj->h_tid & GCFLAG_VISITED)) {
- obj->h_tid &= ~GCFLAG_PUBLIC_TO_PRIVATE; /* see fix_outdated() */
- obj->h_tid |= GCFLAG_VISITED;
- gcptrlist_insert(&objects_to_trace, obj);
-
- if (IS_POINTER(obj->h_revision)) {
- assert(!(obj->h_revision & 2));
- visit((gcptr *)&obj->h_revision);
- }
- keep_original_alive(obj);
+ if (!(obj->h_tid & GCFLAG_PUBLIC)) {
+ visit_nonpublic(obj, NULL);
}
+ else {
+ obj = visit_public(obj, NULL);
+ }
+ return obj;
}
static void visit_all_objects(void)
{
while (gcptrlist_size(&objects_to_trace) > 0) {
+ visit_protected_gcp =
+ (struct tx_public_descriptor *)gcptrlist_pop(&objects_to_trace);
gcptr obj = gcptrlist_pop(&objects_to_trace);
- stmgc_trace(obj, &visit);
+ stmgc_trace(obj, &visit_take_protected);
}
+ visit_protected_gcp = NULL;
}
static void mark_prebuilt_roots(void)
@@ -371,18 +443,20 @@
/* Note about prebuilt roots: 'stm_prebuilt_gcroots' is a list that
contains all the ones that have been modified. Because they are
themselves not in any page managed by this file, their
- GCFLAG_VISITED will not be removed at the end of the current
- collection. This is fine because the base object cannot contain
- references to the heap. So we decided to systematically set
- GCFLAG_VISITED on prebuilt objects. */
+ GCFLAG_VISITED is not removed at the end of the current
+ collection. That's why we remove it here. GCFLAG_MARKED is not
+ relevant for prebuilt objects, but we avoid objects with MARKED
+ but not VISITED, which trigger some asserts. */
gcptr *pobj = stm_prebuilt_gcroots.items;
gcptr *pend = stm_prebuilt_gcroots.items + stm_prebuilt_gcroots.size;
- gcptr obj;
+ gcptr obj, obj2;
for (; pobj != pend; pobj++) {
obj = *pobj;
+ obj->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED);
assert(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL);
- assert(IS_POINTER(obj->h_revision));
- visit((gcptr *)&obj->h_revision);
+
+ obj2 = visit_public(obj, NULL);
+ assert(obj2 == obj); /* it is its own original */
}
}
@@ -396,7 +470,7 @@
if (((revision_t)item) & ~((revision_t)END_MARKER_OFF |
(revision_t)END_MARKER_ON)) {
/* 'item' is a regular, non-null pointer */
- visit(root);
+ visit_take_protected(root);
dprintf(("visit stack root: %p -> %p\n", item, *root));
}
else if (item == END_MARKER_OFF) {
@@ -409,15 +483,19 @@
static void mark_all_stack_roots(void)
{
struct tx_descriptor *d;
+ struct GcPtrList new_public_to_private;
+ memset(&new_public_to_private, 0, sizeof(new_public_to_private));
+
for (d = stm_tx_head; d; d = d->tx_next) {
assert(!stm_has_got_any_lock(d));
+ visit_protected_gcp = d->public_descriptor;
/* the roots pushed on the shadowstack */
mark_roots(d->shadowstack, *d->shadowstack_end_ref);
/* the thread-local object */
- visit(d->thread_local_obj_ref);
- visit(&d->old_thread_local_obj);
+ visit_take_protected(d->thread_local_obj_ref);
+ visit_take_protected(&d->old_thread_local_obj);
/* the current transaction's private copies of public objects */
wlog_t *item;
@@ -427,37 +505,50 @@
gcptr R = item->addr;
gcptr L = item->val;
- /* Objects that were not visited yet must have the PUB_TO_PRIV
- flag. Except if that transaction will abort anyway, then it
- may be removed from a previous major collection that didn't
- fix the PUB_TO_PRIV because the transaction was going to
- abort anyway:
- 1. minor_collect before major collect (R->L, R is outdated, abort)
- 2. major collect removes flag
- 3. major collect again, same thread, no time to abort
- 4. flag still removed
- */
- assert(IMPLIES(!(R->h_tid & GCFLAG_VISITED) && d->active > 0,
- R->h_tid & GCFLAG_PUBLIC_TO_PRIVATE));
- visit_keep(R);
+ /* we visit the public object R. Must keep a public object
+ here, so we pass NULL as second argument. */
+ gcptr new_R = visit_public(R, NULL);
+ assert(new_R->h_tid & GCFLAG_PUBLIC);
+
+ if (new_R != R) {
+ /* we have to update the key in public_to_private, which
+ can only be done by deleting the existing key and
+ (after the loop) re-inserting the new key. */
+ G2L_LOOP_DELETE(item);
+ gcptrlist_insert2(&new_public_to_private, new_R, L);
+ }
+
+ /* we visit the private copy L --- which at this point
+ should be private, possibly private_from_protected,
+ so visit() should return the same private copy */
if (L != NULL) {
- /* minor collection found R->L in public_to_young
- and R was modified. It then sets item->val to NULL and wants
- to abort later. */
- revision_t v = L->h_revision;
- visit_keep(L);
- /* a bit of custom logic here: if L->h_revision used to
- point exactly to R, as set by stealing, then we must
- keep this property, even though visit_keep(L) might
- decide it would be better to make it point to a more
- recent copy. */
- if (v == (revision_t)R) {
- assert(L->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
- L->h_revision = v; /* restore */
- }
+ visit_nonpublic(L, visit_protected_gcp);
}
+
} G2L_LOOP_END;
+ /* reinsert to real pub_to_priv */
+ long i, size = new_public_to_private.size;
+ gcptr *items = new_public_to_private.items;
+ for (i = 0; i < size; i += 2) {
+ g2l_insert(&d->public_to_private, items[i], items[i + 1]);
+ }
+ gcptrlist_clear(&new_public_to_private);
+
+ /* the current transaction's private copies of protected objects */
+ items = d->private_from_protected.items;
+ for (i = d->private_from_protected.size - 1; i >= 0; i--) {
+ gcptr obj = items[i];
+ assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
+ visit_nonpublic(obj, visit_protected_gcp);
+
+ gcptr backup_obj = (gcptr)obj->h_revision;
+ if (!(backup_obj->h_tid & GCFLAG_PUBLIC))
+ visit_nonpublic(backup_obj, visit_protected_gcp);
+ else
+ obj->h_revision = (revision_t)visit_public(backup_obj, NULL);
+ }
+
/* make sure that the other lists are empty */
assert(gcptrlist_size(&d->public_with_young_copy) == 0);
assert(gcptrlist_size(&d->public_descriptor->stolen_objects) == 0);
@@ -473,27 +564,16 @@
assert(gcptrlist_size(&d->private_from_protected) ==
d->num_private_from_protected_known_old);
}
+
+ visit_protected_gcp = NULL;
+ gcptrlist_delete(&new_public_to_private);
}
static void cleanup_for_thread(struct tx_descriptor *d)
{
long i;
gcptr *items;
-
- /* It can occur that 'private_from_protected' contains an object that
- * has not been visited at all (maybe only in inevitable
- * transactions).
- */
- items = d->private_from_protected.items;
- for (i = d->private_from_protected.size - 1; i >= 0; i--) {
- gcptr obj = items[i];
- assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
-
- if (!(obj->h_tid & GCFLAG_VISITED)) {
- /* forget 'obj' */
- items[i] = items[--d->private_from_protected.size];
- }
- }
+ assert(d->old_objects_to_trace.size == 0);
/* If we're aborting this transaction anyway, we don't need to do
* more here.
@@ -516,21 +596,29 @@
items = d->list_of_read_objects.items;
for (i = d->list_of_read_objects.size - 1; i >= 0; --i) {
gcptr obj = items[i];
- assert(!(obj->h_tid & GCFLAG_STUB));
- /* Warning: in case the object listed is outdated and has been
- replaced with a more recent revision, then it might be the
- case that obj->h_revision doesn't have GCFLAG_VISITED, but
- just removing it is very wrong --- we want 'd' to abort.
- */
- if (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) {
+ if (obj->h_tid & GCFLAG_MOVED) {
+ assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
+ assert(IS_POINTER(obj->h_original));
+ obj = (gcptr)obj->h_original;
+ items[i] = obj;
+ }
+ else if (obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED) {
+ /* Warning: in case the object listed is outdated and has been
+ replaced with a more recent revision, then it might be the
+ case that obj->h_revision doesn't have GCFLAG_VISITED, but
+ just removing it is very wrong --- we want 'd' to abort.
+ */
/* follow obj to its backup */
assert(IS_POINTER(obj->h_revision));
obj = (gcptr)obj->h_revision;
+
+ /* the backup-ptr should already be updated: */
+ assert(!(obj->h_tid & GCFLAG_MOVED));
}
revision_t v = obj->h_revision;
- if (IS_POINTER(v)) {
+ if ((obj->h_tid & GCFLAG_STUB) || IS_POINTER(v)) {
/* has a more recent revision. Oups. */
dprintf(("ABRT_COLLECT_MAJOR %p: "
"%p was read but modified already\n", d, obj));
@@ -572,7 +660,7 @@
G2L_LOOP_FORWARD(d->public_to_private, item) {
assert(item->addr->h_tid & GCFLAG_VISITED);
assert(item->val->h_tid & GCFLAG_VISITED);
-
+ assert(!(item->addr->h_tid & GCFLAG_MOVED));
assert(item->addr->h_tid & GCFLAG_PUBLIC);
/* assert(is_private(item->val)); but in the other thread,
which becomes: */
@@ -611,7 +699,9 @@
and the flag is removed; other locations are marked as free. */
p = (gcptr)(lpage + 1);
for (j = 0; j < objs_per_page; j++) {
- if (p->h_tid & GCFLAG_VISITED)
+ assert(IMPLIES(p->h_tid & GCFLAG_VISITED,
+ p->h_tid & GCFLAG_MARKED));
+ if (p->h_tid & GCFLAG_MARKED)
break; /* first object that stays alive */
p = (gcptr)(((char *)p) + obj_size);
}
@@ -621,8 +711,10 @@
surviving_pages = lpage;
p = (gcptr)(lpage + 1);
for (j = 0; j < objs_per_page; j++) {
- if (p->h_tid & GCFLAG_VISITED) {
- p->h_tid &= ~GCFLAG_VISITED;
+ assert(IMPLIES(p->h_tid & GCFLAG_VISITED,
+ p->h_tid & GCFLAG_MARKED));
+ if (p->h_tid & GCFLAG_MARKED) {
+ p->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED);
mc_total_in_use += obj_size;
}
else {
@@ -648,6 +740,7 @@
p = (gcptr)(lpage + 1);
for (j = 0; j < objs_per_page; j++) {
assert(!(p->h_tid & GCFLAG_VISITED));
+ assert(!(p->h_tid & GCFLAG_MARKED));
if (p->h_tid != DEBUG_WORD(0xDD)) {
dprintf(("| freeing %p (with page %p)\n", p, lpage));
}
@@ -677,8 +770,10 @@
G2L_LOOP_FORWARD(gcp->nonsmall_objects, item) {
gcptr p = item->addr;
- if (p->h_tid & GCFLAG_VISITED) {
- p->h_tid &= ~GCFLAG_VISITED;
+ assert(IMPLIES(p->h_tid & GCFLAG_VISITED,
+ p->h_tid & GCFLAG_MARKED));
+ if (p->h_tid & GCFLAG_MARKED) {
+ p->h_tid &= ~(GCFLAG_VISITED | GCFLAG_MARKED);
}
else {
G2L_LOOP_DELETE(item);
diff --git a/rpython/translator/stm/src_stm/lists.c
b/rpython/translator/stm/src_stm/lists.c
--- a/rpython/translator/stm/src_stm/lists.c
+++ b/rpython/translator/stm/src_stm/lists.c
@@ -19,7 +19,7 @@
void g2l_delete(struct G2L *g2l)
{
- free(g2l->raw_start);
+ stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start);
memset(g2l, 0, sizeof(struct G2L));
}
@@ -57,7 +57,7 @@
long alloc = g2l->raw_end - g2l->raw_start;
long newalloc = (alloc + extra + (alloc >> 2) + 31) & ~15;
//fprintf(stderr, "growth: %ld\n", newalloc);
- char *newitems = malloc(newalloc);
+ char *newitems = stm_malloc(newalloc);
newg2l.raw_start = newitems;
newg2l.raw_current = newitems;
newg2l.raw_end = newitems + newalloc;
@@ -66,7 +66,7 @@
{
g2l_insert(&newg2l, item->addr, item->val);
} G2L_LOOP_END;
- free(g2l->raw_start);
+ stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start);
*g2l = newg2l;
}
@@ -152,7 +152,7 @@
//fprintf(stderr, "list %p deleted (%ld KB)\n",
//gcptrlist, gcptrlist->alloc * sizeof(gcptr) / 1024);
gcptrlist->size = 0;
- free(gcptrlist->items);
+ stm_free(gcptrlist->items, gcptrlist->alloc * sizeof(gcptr));
gcptrlist->items = NULL;
gcptrlist->alloc = 0;
}
@@ -163,7 +163,8 @@
return;
size_t nsize = gcptrlist->size * sizeof(gcptr);
- gcptr *newitems = realloc(gcptrlist->items, nsize);
+ gcptr *newitems = stm_realloc(gcptrlist->items, nsize,
+ gcptrlist->alloc * sizeof(gcptr));
if (newitems != NULL || nsize == 0)
{
gcptrlist->items = newitems;
@@ -178,11 +179,11 @@
//fprintf(stderr, "list %p growth to %ld items (%ld KB)\n",
// gcptrlist, newalloc, newalloc * sizeof(gcptr) / 1024);
- gcptr *newitems = malloc(newalloc * sizeof(gcptr));
+ gcptr *newitems = stm_malloc(newalloc * sizeof(gcptr));
long i;
for (i=0; i<gcptrlist->size; i++)
newitems[i] = gcptrlist->items[i];
- free(gcptrlist->items);
+ stm_free(gcptrlist->items, gcptrlist->alloc * sizeof(gcptr));
gcptrlist->items = newitems;
gcptrlist->alloc = newalloc;
}
diff --git a/rpython/translator/stm/src_stm/lists.h
b/rpython/translator/stm/src_stm/lists.h
--- a/rpython/translator/stm/src_stm/lists.h
+++ b/rpython/translator/stm/src_stm/lists.h
@@ -2,6 +2,8 @@
#ifndef _SRCSTM_LISTS_H
#define _SRCSTM_LISTS_H
+#include "dbgmem.h"
+
/************************************************************/
/* The g2l_xx functions ("global_to_local") are implemented as a tree,
@@ -37,7 +39,7 @@
void g2l_clear(struct G2L *g2l);
void g2l_delete(struct G2L *g2l);
static inline void g2l_delete_not_used_any_more(struct G2L *g2l) {
- free(g2l->raw_start);
+ stm_free(g2l->raw_start, g2l->raw_end - g2l->raw_start);
}
static inline int g2l_any_entry(struct G2L *g2l) {
diff --git a/rpython/translator/stm/src_stm/nursery.c
b/rpython/translator/stm/src_stm/nursery.c
--- a/rpython/translator/stm/src_stm/nursery.c
+++ b/rpython/translator/stm/src_stm/nursery.c
@@ -137,7 +137,7 @@
static inline gcptr create_old_object_copy(gcptr obj)
{
assert(!(obj->h_tid & GCFLAG_PUBLIC));
- assert(!(obj->h_tid & GCFLAG_NURSERY_MOVED));
+ assert(!(obj->h_tid & GCFLAG_MOVED));
assert(!(obj->h_tid & GCFLAG_VISITED));
assert(!(obj->h_tid & GCFLAG_WRITE_BARRIER));
assert(!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL));
@@ -160,9 +160,9 @@
}
else {
/* it's a nursery object. Was it already moved? */
- if (UNLIKELY(obj->h_tid & GCFLAG_NURSERY_MOVED)) {
+ if (UNLIKELY(obj->h_tid & GCFLAG_MOVED)) {
/* yes. Such an object can be a public object in the nursery
- too (such objects are always NURSERY_MOVED). For all cases,
+ too (such objects are always MOVED). For all cases,
we can just fix the ref.
Can be stolen objects or those we already moved.
*/
@@ -183,7 +183,7 @@
fresh_old_copy = create_old_object_copy(obj);
}
- obj->h_tid |= GCFLAG_NURSERY_MOVED;
+ obj->h_tid |= GCFLAG_MOVED;
obj->h_revision = (revision_t)fresh_old_copy;
/* fix the original reference */
@@ -233,8 +233,23 @@
assert(items[i]->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
assert(IS_POINTER(items[i]->h_revision));
+ /* if items[i] is young, move it, update the pointer, and
+ schedule the object for later consideration by
+ visit_all_outside_objects() (which will for example ensure
+ that the WRITE_BARRIER flag is added to it).
+ */
visit_if_young(&items[i]);
+ /* the backup copy is always allocated outside the nursery,
+ but we have to trace it as well, as it may contain its own
+ young pointers.
+
+ but only once: if the transaction was running for long enough
+ to have num_private_from_protected_known_old > 0, then the
+ backup copies of known-old objects have already been traced
+ in a previous minor collection, and as they are read-only,
+ they cannot contain young pointers any more.
+ */
stmgc_trace((gcptr)items[i]->h_revision, &visit_if_young);
}
@@ -386,13 +401,13 @@
/* non-young or visited young objects are kept */
continue;
}
- else if (obj->h_tid & GCFLAG_NURSERY_MOVED) {
+ else if (obj->h_tid & GCFLAG_MOVED) {
/* visited nursery objects are kept and updated */
items[i] = (gcptr)obj->h_revision;
assert(!(items[i]->h_tid & GCFLAG_STUB));
continue;
}
- /* Sanity check: a nursery object without the NURSERY_MOVED flag
+ /* Sanity check: a nursery object without the MOVED flag
is necessarily a private-without-backup object, or a protected
object; it cannot be a public object. */
assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
@@ -432,7 +447,7 @@
setup_minor_collect(d);
/* first do this, which asserts that some objects are private ---
- which fails if they have already been GCFLAG_NURSERY_MOVED */
+ which fails if they have already been GCFLAG_MOVED */
mark_public_to_young(d);
mark_young_roots(d);
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 @@
-4cad3aa5a20b
+c528da482152
diff --git a/rpython/translator/stm/src_stm/steal.c
b/rpython/translator/stm/src_stm/steal.c
--- a/rpython/translator/stm/src_stm/steal.c
+++ b/rpython/translator/stm/src_stm/steal.c
@@ -31,7 +31,7 @@
assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
if (obj->h_tid & GCFLAG_PUBLIC) {
/* young public, replace with stolen old copy */
- assert(obj->h_tid & GCFLAG_NURSERY_MOVED);
+ assert(obj->h_tid & GCFLAG_MOVED);
assert(IS_POINTER(obj->h_revision));
stub = (gcptr)obj->h_revision;
assert(!IS_POINTER(stub->h_revision)); /* not outdated */
@@ -56,7 +56,7 @@
if (!(obj->h_original))
obj->h_original = (revision_t)O;
}
- obj->h_tid |= (GCFLAG_NURSERY_MOVED | GCFLAG_PUBLIC);
+ obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC);
obj->h_revision = (revision_t)O;
O->h_tid |= GCFLAG_PUBLIC;
@@ -105,6 +105,8 @@
stub->h_tid = (obj->h_tid & STM_USER_TID_MASK) | GCFLAG_PUBLIC
| GCFLAG_STUB
| GCFLAG_OLD;
+ if (size == 0)
+ stub->h_tid |= GCFLAG_SMALLSTUB;
stub->h_revision = ((revision_t)obj) | 2;
if (obj->h_original) {
stub->h_original = obj->h_original;
@@ -206,7 +208,7 @@
/* note that we should follow h_revision at least one more
step: it is necessary if L is public but young (and then
- has GCFLAG_NURSERY_MOVED), but it is fine to do it more
+ has GCFLAG_MOVED), but it is fine to do it more
generally. */
v = ACCESS_ONCE(L->h_revision);
if (IS_POINTER(v)) {
@@ -239,7 +241,7 @@
}
L->h_revision = (revision_t)O;
- L->h_tid |= GCFLAG_PUBLIC | GCFLAG_NURSERY_MOVED;
+ L->h_tid |= GCFLAG_PUBLIC | GCFLAG_MOVED;
/* subtle: we need to remove L from the fxcache of the target
thread, otherwise its read barrier might not trigger on it.
It is mostly fine because it is anyway identical to O. But
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
@@ -24,7 +24,8 @@
#define STM_SIZE_OF_USER_TID (sizeof(revision_t) / 2) /* in bytes */
#define STM_FIRST_GCFLAG (1L << (8 * STM_SIZE_OF_USER_TID))
#define STM_USER_TID_MASK (STM_FIRST_GCFLAG - 1)
-#define PREBUILT_FLAGS (STM_FIRST_GCFLAG * (1 + 2 + 4 + 8))
+#define PREBUILT_FLAGS (STM_FIRST_GCFLAG * ((1<<0) | (1<<1) | \
+ (1<<2) | (1<<3) | (1<<13)))
#define PREBUILT_REVISION 1
diff --git a/rpython/translator/stm/src_stm/stmsync.c
b/rpython/translator/stm/src_stm/stmsync.c
--- a/rpython/translator/stm/src_stm/stmsync.c
+++ b/rpython/translator/stm/src_stm/stmsync.c
@@ -53,7 +53,7 @@
static void init_shadowstack(void)
{
struct tx_descriptor *d = thread_descriptor;
- d->shadowstack = malloc(sizeof(gcptr) * LENGTH_SHADOW_STACK);
+ d->shadowstack = stm_malloc(sizeof(gcptr) * LENGTH_SHADOW_STACK);
if (!d->shadowstack) {
stm_fatalerror("out of memory: shadowstack\n");
}
@@ -69,7 +69,7 @@
assert(x == END_MARKER_ON);
assert(stm_shadowstack == d->shadowstack);
stm_shadowstack = NULL;
- free(d->shadowstack);
+ stm_free(d->shadowstack, sizeof(gcptr) * LENGTH_SHADOW_STACK);
}
void stm_set_max_aborts(int max_aborts)
diff --git a/rpython/translator/stm/src_stm/weakref.c
b/rpython/translator/stm/src_stm/weakref.c
--- a/rpython/translator/stm/src_stm/weakref.c
+++ b/rpython/translator/stm/src_stm/weakref.c
@@ -28,7 +28,7 @@
*/
while (gcptrlist_size(&d->young_weakrefs) > 0) {
gcptr weakref = gcptrlist_pop(&d->young_weakrefs);
- if (!(weakref->h_tid & GCFLAG_NURSERY_MOVED))
+ if (!(weakref->h_tid & GCFLAG_MOVED))
continue; /* the weakref itself dies */
weakref = (gcptr)weakref->h_revision;
@@ -37,7 +37,7 @@
assert(pointing_to != NULL);
if (stmgc_is_in_nursery(d, pointing_to)) {
- if (pointing_to->h_tid & GCFLAG_NURSERY_MOVED) {
+ if (pointing_to->h_tid & GCFLAG_MOVED) {
dprintf(("weakref ptr moved %p->%p\n",
WEAKREF_PTR(weakref, size),
(gcptr)pointing_to->h_revision));
@@ -69,49 +69,25 @@
static _Bool is_partially_visited(gcptr obj)
{
- /* Based on gcpage.c:visit(). Check the code here if we simplify
- visit(). Returns True or False depending on whether we find any
- version of 'obj' to be VISITED or not.
+ /* Based on gcpage.c:visit_public(). Check the code here if we change
+ visit_public(). Returns True or False depending on whether we find any
+ version of 'obj' to be MARKED or not.
*/
- restart:
- if (obj->h_tid & GCFLAG_VISITED)
+ assert(IMPLIES(obj->h_tid & GCFLAG_VISITED,
+ obj->h_tid & GCFLAG_MARKED));
+ if (obj->h_tid & GCFLAG_MARKED)
return 1;
- if (obj->h_revision & 1) {
- assert(!(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED));
- assert(!(obj->h_tid & GCFLAG_STUB));
+ if (!(obj->h_tid & GCFLAG_PUBLIC))
return 0;
- }
- else if (obj->h_tid & GCFLAG_PUBLIC) {
- /* h_revision is a ptr: we have a more recent version */
- if (!(obj->h_revision & 2)) {
- /* go visit the more recent version */
- obj = (gcptr)obj->h_revision;
- }
- else {
- /* it's a stub */
- assert(obj->h_tid & GCFLAG_STUB);
- obj = (gcptr)(obj->h_revision - 2);
- }
- goto restart;
- }
- else {
- assert(obj->h_tid & GCFLAG_PRIVATE_FROM_PROTECTED);
- gcptr B = (gcptr)obj->h_revision;
- assert(B->h_tid & (GCFLAG_PUBLIC | GCFLAG_BACKUP_COPY));
- if (B->h_tid & GCFLAG_VISITED)
+
+ if (obj->h_original != 0 &&
+ !(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL)) {
+ gcptr original = (gcptr)obj->h_original;
+ assert(IMPLIES(original->h_tid & GCFLAG_VISITED,
+ original->h_tid & GCFLAG_MARKED));
+ if (original->h_tid & GCFLAG_MARKED)
return 1;
- assert(!(obj->h_tid & GCFLAG_STUB));
- assert(!(B->h_tid & GCFLAG_STUB));
-
- if (IS_POINTER(B->h_revision)) {
- assert(B->h_tid & GCFLAG_PUBLIC);
- assert(!(B->h_tid & GCFLAG_BACKUP_COPY));
- assert(!(B->h_revision & 2));
-
- obj = (gcptr)B->h_revision;
- goto restart;
- }
}
return 0;
}
diff --git a/rpython/translator/stm/test/test_ztranslated.py
b/rpython/translator/stm/test/test_ztranslated.py
--- a/rpython/translator/stm/test/test_ztranslated.py
+++ b/rpython/translator/stm/test/test_ztranslated.py
@@ -1,5 +1,5 @@
import py
-from rpython.rlib import rstm, rgc
+from rpython.rlib import rstm, rgc, objectmodel
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rclass
from rpython.rtyper.lltypesystem.lloperation import llop
from rpython.rtyper.annlowlevel import cast_instance_to_base_ptr
@@ -276,3 +276,30 @@
t, cbuilder = self.compile(main)
data = cbuilder.cmdexec('a b')
assert 'li102ee10:hi there 3e\n0\n' in data
+
+ def test_weakref(self):
+ import weakref
+ class Foo(object):
+ pass
+
+ def f(argv):
+ foo = Foo()
+ foo.n = argv
+ w = weakref.ref(foo)
+ assert w() is foo
+ objectmodel.keepalive_until_here(foo)
+ return w
+ f._dont_inline_ = True
+
+ def main(argv):
+ w = f(argv)
+ assert w() is not None
+ assert len(w().n) == len(argv)
+ rgc.collect()
+ assert w() is None
+ print 'test ok'
+ return 0
+
+ t, cbuilder = self.compile(main)
+ data = cbuilder.cmdexec('a b')
+ assert 'test ok\n' in data
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit