[pypy-commit] stmgc nonmovable-int-ref: implement stm_allocate_public_integer_address(). They need to be explicitely unregistered (freed), otherwise they survive everything.
Raemi
noreply at buildbot.pypy.org
Thu Aug 15 13:08:34 CEST 2013
Author: Remi Meier <remi.meier at gmail.com>
Branch: nonmovable-int-ref
Changeset: r477:6a465701aaa1
Date: 2013-08-15 13:07 +0200
http://bitbucket.org/pypy/stmgc/changeset/6a465701aaa1/
Log: implement stm_allocate_public_integer_address(). They need to be
explicitely unregistered (freed), otherwise they survive everything.
diff --git a/c4/demo_random.c b/c4/demo_random.c
--- a/c4/demo_random.c
+++ b/c4/demo_random.c
@@ -20,6 +20,7 @@
#define MAXROOTS 1000
#define SHARED_ROOTS 5 // shared by threads
#define DO_MAJOR_COLLECTS 1
+#define MAX_PUBLIC_INTS 5
@@ -82,6 +83,8 @@
int interruptible;
int atomic;
char to_clear_on_abort[20];
+ intptr_t public_ints[MAX_PUBLIC_INTS];
+ int num_public_ints;
};
__thread struct thread_data td;
@@ -266,6 +269,38 @@
}
}
+void check_public_ints()
+{
+ int i;
+ for (i = 0; i < td.num_public_ints; i++) {
+ intptr_t ip = td.public_ints[i];
+ gcptr obj = (gcptr)ip;
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+ assert(obj->h_tid & GCFLAG_SMALLSTUB);
+ check(obj);
+ check((gcptr)(obj->h_revision - 2));
+ }
+}
+
+void add_as_public_int(gcptr p)
+{
+ if (!p || td.num_public_ints >= MAX_PUBLIC_INTS)
+ return;
+
+ push_roots();
+ intptr_t ip = stm_allocate_public_integer_address(p);
+ pop_roots();
+ td.public_ints[td.num_public_ints++] = ip;
+}
+
+void pop_public_int()
+{
+ if (td.num_public_ints == 0)
+ return;
+
+ stm_unregister_integer_address(td.public_ints[--td.num_public_ints]);
+}
+
gcptr read_barrier(gcptr p)
{
gcptr r = p;
@@ -401,6 +436,7 @@
gcptr rare_events(gcptr p, gcptr _r, gcptr _sr)
{
+ check_public_ints();
int k = get_rand(100);
if (k < 10) {
push_roots();
@@ -408,13 +444,22 @@
stm_become_inevitable("fun");
p = stm_pop_root();
pop_roots();
- }
+ }
else if (k < 40) {
push_roots();
stmgc_minor_collect();
pop_roots();
p = NULL;
- } else if (k < 41 && DO_MAJOR_COLLECTS) {
+ }
+ else if (k < 50) {
+ add_as_public_int(p);
+ p = NULL;
+ }
+ else if (k < 60) {
+ pop_public_int();
+ p = NULL;
+ }
+ else if (k < 61 && DO_MAJOR_COLLECTS) {
fprintf(stdout, "major collect\n");
push_roots();
stmgcpage_possibly_major_collect(1);
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -6,7 +6,6 @@
*/
#include "stmimpl.h"
-#ifdef _GC_DEBUG
char tmp_buf[128];
char* stm_dbg_get_hdr_str(gcptr obj)
{
@@ -26,7 +25,6 @@
cur += sprintf(cur, "tid=%ld", stm_get_tid(obj));
return tmp_buf;
}
-#endif
diff --git a/c4/extra.c b/c4/extra.c
--- a/c4/extra.c
+++ b/c4/extra.c
@@ -23,6 +23,51 @@
stm_bytes_to_clear_on_abort = bytes;
}
+
+intptr_t stm_allocate_public_integer_address(gcptr obj)
+{
+ struct tx_descriptor *d = thread_descriptor;
+ gcptr stub;
+ intptr_t result;
+ /* plan: we allocate a small stub whose reference
+ we never give to the caller except in the form
+ of an integer.
+ During major collections, we visit them and update
+ their references. */
+
+ /* we don't want to deal with young objs */
+ if (!(obj->h_tid & GCFLAG_OLD)) {
+ stm_push_root(obj);
+ stm_minor_collect();
+ obj = stm_pop_root();
+ }
+
+ spinlock_acquire(d->public_descriptor->collection_lock, 'P');
+
+ stub = stm_stub_malloc(d->public_descriptor, 0);
+ stub->h_tid = (obj->h_tid & STM_USER_TID_MASK)
+ | GCFLAG_PUBLIC | GCFLAG_STUB | GCFLAG_SMALLSTUB
+ | GCFLAG_OLD;
+
+ stub->h_revision = ((revision_t)obj) | 2;
+ if (!(obj->h_tid & GCFLAG_PREBUILT_ORIGINAL) && obj->h_original) {
+ stub->h_original = obj->h_original;
+ }
+ else {
+ stub->h_original = (revision_t)obj;
+ }
+
+ result = (intptr_t)stub;
+ spinlock_release(d->public_descriptor->collection_lock);
+ stm_register_integer_address(result);
+ return result;
+}
+
+
+
+
+
+
/************************************************************/
/* Each object has a h_original pointer to an old copy of
the same object (e.g. an old revision), the "original".
diff --git a/c4/gcpage.c b/c4/gcpage.c
--- a/c4/gcpage.c
+++ b/c4/gcpage.c
@@ -22,6 +22,9 @@
/* Only computed during a major collection */
static size_t mc_total_in_use, mc_total_reserved;
+/* keeps track of registered smallstubs that will survive unless unregistered */
+static struct G2L registered_stubs;
+
/* For tests */
long stmgcpage_count(int quantity)
{
@@ -62,6 +65,8 @@
nblocks_for_size[i] =
(GC_PAGE_SIZE - sizeof(page_header_t)) / (WORD * i);
}
+
+ memset(®istered_stubs, 0, sizeof(registered_stubs));
}
void stmgcpage_init_tls(void)
@@ -208,6 +213,34 @@
}
+/***** registering of small stubs as integer addresses *****/
+
+void stm_register_integer_address(intptr_t adr)
+{
+ gcptr obj = (gcptr)adr;
+ assert(obj->h_tid & GCFLAG_SMALLSTUB);
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+
+ stmgcpage_acquire_global_lock();
+ g2l_insert(®istered_stubs, obj, NULL);
+ stmgcpage_release_global_lock();
+ dprintf(("registered %p\n", obj));
+}
+
+void stm_unregister_integer_address(intptr_t adr)
+{
+ gcptr obj = (gcptr)adr;
+ assert(obj->h_tid & GCFLAG_SMALLSTUB);
+ assert(obj->h_tid & GCFLAG_PUBLIC);
+
+ stmgcpage_acquire_global_lock();
+ g2l_delete_item(®istered_stubs, obj);
+ stmgcpage_release_global_lock();
+ dprintf(("unregistered %p\n", obj));
+}
+
+
+
/***** Major collections: marking *****/
static struct GcPtrList objects_to_trace;
@@ -459,6 +492,27 @@
}
}
+static void mark_registered_stubs(void)
+{
+ wlog_t *item;
+ G2L_LOOP_FORWARD(registered_stubs, item) {
+ gcptr R = item->addr;
+ assert(R->h_tid & GCFLAG_SMALLSTUB);
+
+ R->h_tid |= (GCFLAG_MARKED | GCFLAG_VISITED);
+
+ gcptr L = (gcptr)(R->h_revision - 2);
+ L = stmgcpage_visit(L);
+ R->h_revision = ((revision_t)L) | 2;
+
+ /* h_original will be kept up-to-date because
+ it is either == L or L's h_original. And
+ h_originals don't move */
+ } G2L_LOOP_END;
+
+}
+
+
static void mark_roots(gcptr *root, gcptr *end)
{
assert(*root == END_MARKER_ON);
@@ -889,6 +943,7 @@
assert(gcptrlist_size(&objects_to_trace) == 0);
mark_prebuilt_roots();
+ mark_registered_stubs();
mark_all_stack_roots();
do {
visit_all_objects();
diff --git a/c4/steal.c b/c4/steal.c
--- a/c4/steal.c
+++ b/c4/steal.c
@@ -19,6 +19,59 @@
};
static __thread struct tx_steal_data *steal_data;
+static void replace_ptr_to_immutable_with_stub(gcptr * pobj)
+{
+ gcptr stub, obj = *pobj;
+ assert(obj->h_tid & GCFLAG_IMMUTABLE);
+ 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_MOVED);
+ assert(IS_POINTER(obj->h_revision));
+ stub = (gcptr)obj->h_revision;
+ assert(!IS_POINTER(stub->h_revision)); /* not outdated */
+ goto done;
+ }
+
+ /* old or young protected! mark as PUBLIC */
+ if (!(obj->h_tid & GCFLAG_OLD)) {
+ /* young protected */
+ gcptr O;
+
+ if (obj->h_tid & GCFLAG_HAS_ID) {
+ /* use id-copy for us */
+ O = (gcptr)obj->h_original;
+ obj->h_tid &= ~GCFLAG_HAS_ID;
+ stm_copy_to_old_id_copy(obj, O);
+ O->h_original = 0;
+ } else {
+ O = stmgc_duplicate_old(obj);
+
+ /* young and without original? */
+ if (!(obj->h_original))
+ obj->h_original = (revision_t)O;
+ }
+ obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC);
+ obj->h_revision = (revision_t)O;
+
+ O->h_tid |= GCFLAG_PUBLIC;
+ /* here it is fine if it stays in read caches because
+ the object is immutable anyway and there are no
+ write_barriers allowed. */
+ dprintf(("steal prot immutable -> public: %p -> %p\n", obj, O));
+ stub = O;
+ goto done;
+ }
+ /* old protected: */
+ dprintf(("prot immutable -> public: %p\n", obj));
+ obj->h_tid |= GCFLAG_PUBLIC;
+
+ return;
+ done:
+ *pobj = stub;
+ dprintf((" stolen: fixing *%p: %p -> %p\n", pobj, obj, stub));
+}
+
static void replace_ptr_to_protected_with_stub(gcptr *pobj)
{
gcptr stub, obj = *pobj;
@@ -27,49 +80,7 @@
return;
if (obj->h_tid & GCFLAG_IMMUTABLE) {
- 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_MOVED);
- assert(IS_POINTER(obj->h_revision));
- stub = (gcptr)obj->h_revision;
- assert(!IS_POINTER(stub->h_revision)); /* not outdated */
- goto done;
- }
-
- /* old or young protected! mark as PUBLIC */
- if (!(obj->h_tid & GCFLAG_OLD)) {
- /* young protected */
- gcptr O;
-
- if (obj->h_tid & GCFLAG_HAS_ID) {
- /* use id-copy for us */
- O = (gcptr)obj->h_original;
- obj->h_tid &= ~GCFLAG_HAS_ID;
- stm_copy_to_old_id_copy(obj, O);
- O->h_original = 0;
- } else {
- O = stmgc_duplicate_old(obj);
-
- /* young and without original? */
- if (!(obj->h_original))
- obj->h_original = (revision_t)O;
- }
- obj->h_tid |= (GCFLAG_MOVED | GCFLAG_PUBLIC);
- obj->h_revision = (revision_t)O;
-
- O->h_tid |= GCFLAG_PUBLIC;
- /* here it is fine if it stays in read caches because
- the object is immutable anyway and there are no
- write_barriers allowed. */
- dprintf(("steal prot immutable -> public: %p -> %p\n", obj, O));
- stub = O;
- goto done;
- }
- /* old protected: */
- dprintf(("prot immutable -> public: %p\n", obj));
- obj->h_tid |= GCFLAG_PUBLIC;
-
+ replace_ptr_to_immutable_with_stub(pobj);
return;
}
diff --git a/c4/stmgc.h b/c4/stmgc.h
--- a/c4/stmgc.h
+++ b/c4/stmgc.h
@@ -28,12 +28,21 @@
#define PREBUILT_REVISION 1
+/* push roots around allocating functions! */
+
/* allocate an object out of the local nursery */
gcptr stm_allocate(size_t size, unsigned long tid);
/* allocate an object that is be immutable. it cannot be changed with
a stm_write_barrier() or after the next commit */
gcptr stm_allocate_immutable(size_t size, unsigned long tid);
+/* allocates a public reference to the object that will
+ not be freed until stm_unregister_integer_address is
+ called on the result */
+intptr_t stm_allocate_public_integer_address(gcptr);
+void stm_unregister_integer_address(intptr_t);
+
+
/* returns a never changing hash for the object */
revision_t stm_hash(gcptr);
/* returns a number for the object which is unique during its lifetime */
@@ -166,6 +175,8 @@
extern __thread void *stm_to_clear_on_abort;
extern __thread size_t stm_bytes_to_clear_on_abort;
+/* only user currently is stm_allocate_public_integer_address() */
+void stm_register_integer_address(intptr_t);
/* macro functionality */
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -47,6 +47,9 @@
#define PREBUILT_REVISION ...
gcptr stm_allocate(size_t size, unsigned long tid);
+ gcptr stm_allocate_immutable(size_t size, unsigned long tid);
+ intptr_t stm_allocate_public_integer_address(gcptr adr);
+ void stm_unregister_integer_address(intptr_t adr);
revision_t stm_hash(gcptr);
revision_t stm_id(gcptr);
_Bool stm_pointer_equal(gcptr, gcptr);
diff --git a/c4/test/test_extra.py b/c4/test/test_extra.py
--- a/c4/test/test_extra.py
+++ b/c4/test/test_extra.py
@@ -156,3 +156,47 @@
0, 0,
0, 0,
0, 0]
+
+
+
+def test_allocate_public_integer_address():
+ p1 = palloc(HDR)
+ p2 = oalloc(HDR)
+ p3 = nalloc(HDR)
+ lib.stm_push_root(p3)
+ p3p = lib.stm_allocate_public_integer_address(p3)
+ p1p = lib.stm_allocate_public_integer_address(p1)
+ p2p = lib.stm_allocate_public_integer_address(p2)
+
+ # p3 stub points to p3o:
+ p3o = lib.stm_pop_root()
+ p3po = ffi.cast("gcptr", p3p)
+ assert ffi.cast("gcptr", p3po.h_revision - 2) == p3o
+
+ # we have stubs here:
+ assert ffi.cast("gcptr", p1p).h_tid & GCFLAG_PUBLIC
+ assert classify(ffi.cast("gcptr", p1p)) == 'stub'
+ assert classify(ffi.cast("gcptr", p2p)) == 'stub'
+ assert classify(ffi.cast("gcptr", p3p)) == 'stub'
+
+ major_collect()
+
+ # kept alive through stubs:
+ check_not_free(p3o)
+ check_not_free(p2)
+
+ check_not_free(ffi.cast("gcptr", p1p))
+ check_not_free(ffi.cast("gcptr", p2p))
+ check_not_free(ffi.cast("gcptr", p3p))
+
+ lib.stm_unregister_integer_address(p1p)
+ lib.stm_unregister_integer_address(p2p)
+ lib.stm_unregister_integer_address(p3p)
+
+ major_collect()
+ major_collect()
+
+ check_free_old(p3o)
+ check_free_old(p2)
+
+
More information about the pypy-commit
mailing list