[pypy-commit] pypy reverse-debugger: hg merge boehm-rawrefcount

arigo pypy.commits at gmail.com
Wed Sep 7 08:44:51 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r86928:ce52ed81e2ba
Date: 2016-09-07 14:43 +0200
http://bitbucket.org/pypy/pypy/changeset/ce52ed81e2ba/

Log:	hg merge boehm-rawrefcount

diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py
--- a/pypy/module/cpyext/state.py
+++ b/pypy/module/cpyext/state.py
@@ -1,7 +1,7 @@
 from rpython.rlib.objectmodel import we_are_translated
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.interpreter.error import OperationError, oefmt
-from pypy.interpreter.executioncontext import AsyncAction
+from pypy.interpreter import executioncontext
 from rpython.rtyper.lltypesystem import lltype
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib.rdynload import DLLHANDLE
@@ -14,8 +14,9 @@
         self.reset()
         self.programname = lltype.nullptr(rffi.CCHARP.TO)
         self.version = lltype.nullptr(rffi.CCHARP.TO)
-        pyobj_dealloc_action = PyObjDeallocAction(space)
-        self.dealloc_trigger = lambda: pyobj_dealloc_action.fire()
+        if space.config.translation.gc != "boehm":
+            pyobj_dealloc_action = PyObjDeallocAction(space)
+            self.dealloc_trigger = lambda: pyobj_dealloc_action.fire()
 
     def reset(self):
         from pypy.module.cpyext.modsupport import PyMethodDef
@@ -67,6 +68,11 @@
             state.api_lib = str(api.build_bridge(self.space))
         else:
             api.setup_library(self.space)
+            #
+            if self.space.config.translation.gc == "boehm":
+                action = BoehmPyObjDeallocAction(self.space)
+                self.space.actionflag.register_periodic_action(action,
+                    use_bytecode_counter=True)
 
     def install_dll(self, eci):
         """NOT_RPYTHON
@@ -84,8 +90,10 @@
         from pypy.module.cpyext.api import init_static_data_translated
 
         if we_are_translated():
-            rawrefcount.init(llhelper(rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER,
-                                      self.dealloc_trigger))
+            if space.config.translation.gc != "boehm":
+                rawrefcount.init(
+                    llhelper(rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER,
+                    self.dealloc_trigger))
             init_static_data_translated(space)
 
         setup_new_method_def(space)
@@ -143,15 +151,23 @@
         self.extensions[path] = w_copy
 
 
-class PyObjDeallocAction(AsyncAction):
+def _rawrefcount_perform(space):
+    from pypy.module.cpyext.pyobject import PyObject, decref
+    while True:
+        py_obj = rawrefcount.next_dead(PyObject)
+        if not py_obj:
+            break
+        decref(space, py_obj)
+
+class PyObjDeallocAction(executioncontext.AsyncAction):
     """An action that invokes _Py_Dealloc() on the dying PyObjects.
     """
+    def perform(self, executioncontext, frame):
+        _rawrefcount_perform(self.space)
 
+class BoehmPyObjDeallocAction(executioncontext.PeriodicAsyncAction):
+    # This variant is used with Boehm, which doesn't have the explicit
+    # callback.  Instead we must periodically check ourselves.
     def perform(self, executioncontext, frame):
-        from pypy.module.cpyext.pyobject import PyObject, decref
-
-        while True:
-            py_obj = rawrefcount.next_dead(PyObject)
-            if not py_obj:
-                break
-            decref(self.space, py_obj)
+        if we_are_translated():
+            _rawrefcount_perform(self.space)
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -4,10 +4,11 @@
 #  This is meant for pypy's cpyext module, but is a generally
 #  useful interface over our GC.  XXX "pypy" should be removed here
 #
-import sys, weakref
-from rpython.rtyper.lltypesystem import lltype, llmemory
+import sys, weakref, py
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rlib.objectmodel import we_are_translated, specialize
 from rpython.rtyper.extregistry import ExtRegistryEntry
+from rpython.translator.tool.cbuild import ExternalCompilationInfo
 from rpython.rlib import rgc
 
 
@@ -229,6 +230,11 @@
         v_p, v_ob = hop.inputargs(*hop.args_r)
         hop.exception_cannot_occur()
         hop.genop(name, [_unspec_p(hop, v_p), _unspec_ob(hop, v_ob)])
+        #
+        if hop.rtyper.annotator.translator.config.translation.gc == "boehm":
+            c_func = hop.inputconst(lltype.typeOf(func_boehm_eci),
+                                    func_boehm_eci)
+            hop.genop('direct_call', [c_func])
 
 
 class Entry(ExtRegistryEntry):
@@ -281,3 +287,10 @@
         v_ob = hop.genop('gc_rawrefcount_next_dead', [],
                          resulttype = llmemory.Address)
         return _spec_ob(hop, v_ob)
+
+src_dir = py.path.local(__file__).dirpath() / 'src'
+boehm_eci = ExternalCompilationInfo(
+    post_include_bits     = [(src_dir / 'boehm-rawrefcount.h').read()],
+    separate_module_files = [(src_dir / 'boehm-rawrefcount.c')],
+)
+func_boehm_eci = rffi.llexternal_use_eci(boehm_eci)
diff --git a/rpython/rlib/src/boehm-rawrefcount.c b/rpython/rlib/src/boehm-rawrefcount.c
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/src/boehm-rawrefcount.c
@@ -0,0 +1,281 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <gc/gc.h>
+#include <gc/gc_mark.h>
+
+#ifdef TEST_BOEHM_RAWREFCOUNT
+#  define RPY_EXTERN  /* nothing */
+#else
+#  include "common_header.h"
+#endif
+
+
+#define REFCNT_FROM_PYPY  (LONG_MAX / 4 + 1)
+
+typedef struct pypy_header0 gcobj_t;    /* opaque here */
+
+#ifndef _WIN32
+typedef intptr_t Py_ssize_t;
+#else
+typedef long Py_ssize_t;
+#endif
+
+/* this is the first two words of the PyObject structure used in
+   pypy/module/cpyext */
+typedef struct {
+    Py_ssize_t ob_refcnt;
+    Py_ssize_t ob_pypy_link;
+} pyobj_t;
+
+struct link_s {
+    pyobj_t *pyobj;    /* NULL if entry unused */
+    uintptr_t gcenc;
+    struct link_s *next_in_bucket;
+};
+
+#define MARKER_LIST_START  ((pyobj_t *)-1)
+
+static struct link_s **hash_buckets, *hash_list, *hash_free_list;
+static uintptr_t hash_mask_bucket;
+static intptr_t hash_list_walk_next = -1;
+
+static uintptr_t hash_get_hash(gcobj_t *gcobj)
+{
+    assert(gcobj != NULL);
+    uintptr_t h = (uintptr_t)gcobj;
+    assert((h & 1) == 0);
+    h -= (h >> 6);
+    return h & hash_mask_bucket;
+}
+
+static gcobj_t *decode_gcenc(uintptr_t gcenc)
+{
+    if (gcenc & 1)
+        gcenc = ~gcenc;
+    return (gcobj_t *)gcenc;
+}
+
+static void hash_link(struct link_s *lnk)
+{
+    uintptr_t h = hash_get_hash(decode_gcenc(lnk->gcenc));
+    lnk->next_in_bucket = hash_buckets[h];
+    hash_buckets[h] = lnk;
+}
+
+static void boehm_is_about_to_collect(void);
+
+static void hash_grow_table(void)
+{
+    static int rec = 0;
+    assert(!rec);   /* recursive hash_grow_table() */
+    rec = 1;
+
+    if (hash_buckets == NULL)
+        GC_set_start_callback(boehm_is_about_to_collect);
+
+    uintptr_t i, num_buckets = (hash_mask_bucket + 1) * 2;
+    if (num_buckets < 16) num_buckets = 16;
+    assert((num_buckets & (num_buckets - 1)) == 0);  /* power of two */
+
+    /* The new hash_buckets: an array of pointers to struct link_s, of
+       length a power of two, used as a dictionary hash table.  It is
+       not allocated with Boehm because there is no point in Boehm looking
+       in it.
+     */
+    struct link_s **new_buckets = calloc(num_buckets, sizeof(struct link_s *));
+    assert(new_buckets);
+
+    /* The new hash_list: the array of all struct link_s.  Their order
+       is irrelevant.  There is a GC_register_finalizer() on the 'gcenc'
+       field, so we don't move the array; instead we allocate a new array
+       to use in addition to the old one.  There are a total of 2 to 4
+       times as many 'struct link_s' as the length of 'buckets'.
+     */
+    uintptr_t num_list = num_buckets * 2;
+    struct link_s *new_list = GC_MALLOC(num_list * sizeof(struct link_s));
+    for (i = num_list; i-- > 1; ) {
+        new_list[i].next_in_bucket = hash_free_list;
+        hash_free_list = &new_list[i];
+    }
+    /* list[0] is abused to store a pointer to the previous list and
+       the length of the current list */
+    struct link_s *old_list = hash_list;
+    new_list[0].next_in_bucket = old_list;
+    new_list[0].gcenc = num_list;
+    new_list[0].pyobj = MARKER_LIST_START;
+
+    hash_list = new_list;
+    free(hash_buckets);
+    hash_buckets = new_buckets;
+    hash_mask_bucket = num_buckets - 1;
+    hash_list_walk_next = hash_mask_bucket;
+
+    /* re-add all old 'struct link_s' to the hash_buckets */
+    struct link_s *plist = old_list;
+    while (plist != NULL) {
+        uintptr_t count = plist[0].gcenc;
+        for (i = 1; i < count; i++) {
+            if (plist[i].gcenc != 0)
+                hash_link(&plist[i]);
+        }
+        plist = plist[0].next_in_bucket;
+    }
+    GC_reachable_here(old_list);
+
+    rec = 0;
+}
+
+static void hash_add_entry(gcobj_t *gcobj, pyobj_t *pyobj)
+{
+    if (hash_free_list == NULL) {
+        hash_grow_table();
+    }
+    assert(pyobj->ob_pypy_link == 0);
+
+    struct link_s *lnk = hash_free_list;
+    hash_free_list = lnk->next_in_bucket;
+    lnk->pyobj = pyobj;
+    lnk->gcenc = (uintptr_t)gcobj;
+    pyobj->ob_pypy_link = (Py_ssize_t)lnk;
+
+    hash_link(lnk);
+
+    if (GC_base(gcobj) == NULL) {
+        /* 'gcobj' is probably a prebuilt object - it makes no */
+        /* sense to register it then, and it crashes Boehm in */
+        /* quite obscure ways */
+    }
+    else {
+        int j = GC_general_register_disappearing_link(
+                                    (void **)&lnk->gcenc, gcobj);
+        assert(j == GC_SUCCESS);
+    }
+}
+
+static pyobj_t *hash_get_entry(gcobj_t *gcobj)
+{
+    if (hash_buckets == NULL)
+        return NULL;
+    uintptr_t h = hash_get_hash(gcobj);
+    struct link_s *lnk = hash_buckets[h];
+    while (lnk != NULL) {
+        assert(lnk->pyobj != NULL);
+        if (decode_gcenc(lnk->gcenc) == gcobj)
+            return lnk->pyobj;
+        lnk = lnk->next_in_bucket;
+    }
+    return NULL;
+}
+
+
+RPY_EXTERN
+/*pyobj_t*/void *gc_rawrefcount_next_dead(void)
+{
+    while (hash_list_walk_next >= 0) {
+        struct link_s *p, **pp = &hash_buckets[hash_list_walk_next];
+        while (1) {
+            p = *pp;
+            if (p == NULL)
+                break;
+            assert(p->pyobj != NULL);
+            if (p->gcenc == 0) {
+                /* quadratic time on the number of links from the same
+                   bucket chain, but it should be small with very high
+                   probability */
+                pyobj_t *result = p->pyobj;
+#ifdef TEST_BOEHM_RAWREFCOUNT
+                printf("next_dead: %p\n", result);
+#endif
+                assert(result->ob_refcnt == REFCNT_FROM_PYPY);
+                p->pyobj = NULL;
+                *pp = p->next_in_bucket;
+                p->next_in_bucket = hash_free_list;
+                hash_free_list = p;
+                return result;
+            }
+            else {
+                assert(p->gcenc != ~(uintptr_t)0);
+                pp = &p->next_in_bucket;
+            }
+        }
+        hash_list_walk_next--;
+    }
+    return NULL;
+}
+
+RPY_EXTERN
+void gc_rawrefcount_create_link_pypy(/*gcobj_t*/void *gcobj, 
+                                     /*pyobj_t*/void *pyobj)
+{
+    gcobj_t *gcobj1 = (gcobj_t *)gcobj;
+    pyobj_t *pyobj1 = (pyobj_t *)pyobj;
+
+    assert(pyobj1->ob_pypy_link == 0);
+    /*assert(pyobj1->ob_refcnt >= REFCNT_FROM_PYPY);*/
+    /*^^^ could also be fixed just after the call to create_link_pypy()*/
+
+    hash_add_entry(gcobj1, pyobj1);
+}
+
+RPY_EXTERN
+/*pyobj_t*/void *gc_rawrefcount_from_obj(/*gcobj_t*/void *gcobj)
+{
+    return hash_get_entry((gcobj_t *)gcobj);
+}
+
+RPY_EXTERN
+/*gcobj_t*/void *gc_rawrefcount_to_obj(/*pyobj_t*/void *pyobj)
+{
+    pyobj_t *pyobj1 = (pyobj_t *)pyobj;
+
+    if (pyobj1->ob_pypy_link == 0)
+        return NULL;
+
+    struct link_s *lnk = (struct link_s *)pyobj1->ob_pypy_link;
+    assert(lnk->pyobj == pyobj1);
+    
+    gcobj_t *g = decode_gcenc(lnk->gcenc);
+    assert(g != NULL);
+    return g;
+}
+
+static void boehm_is_about_to_collect(void)
+{
+    struct link_s *plist = hash_list;
+    uintptr_t gcenc_union = 0;
+    while (plist != NULL) {
+        uintptr_t i, count = plist[0].gcenc;
+        for (i = 1; i < count; i++) {
+            if (plist[i].gcenc == 0)
+                continue;
+
+            pyobj_t *p = plist[i].pyobj;
+            assert(p != NULL);
+            assert(p->ob_refcnt >= REFCNT_FROM_PYPY);
+
+#ifdef TEST_BOEHM_RAWREFCOUNT
+            printf("plist[%d].gcenc: %p ", (int)i, plist[i].gcenc);
+#endif
+
+            if ((plist[i].gcenc & 1) ^ (p->ob_refcnt == REFCNT_FROM_PYPY)) {
+                /* ob_refcnt > FROM_PYPY: non-zero regular refcnt, 
+                   the gc obj must stay alive.  decode gcenc.
+                   ---OR---
+                   ob_refcnt == FROM_PYPY: no refs from C code, the
+                   gc obj must not (necessarily) stay alive.  encode gcenc.
+                */
+                plist[i].gcenc = ~plist[i].gcenc;
+            }
+            gcenc_union |= plist[i].gcenc;
+#ifdef TEST_BOEHM_RAWREFCOUNT
+            printf("-> %p\n", plist[i].gcenc);
+#endif
+    }
+        plist = plist[0].next_in_bucket;
+    }
+    if (gcenc_union & 1)   /* if there is at least one item potentially dead */
+        hash_list_walk_next = hash_mask_bucket;
+}
diff --git a/rpython/rlib/src/boehm-rawrefcount.h b/rpython/rlib/src/boehm-rawrefcount.h
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/src/boehm-rawrefcount.h
@@ -0,0 +1,24 @@
+
+/* Missing:
+   OP_GC_RAWREFCOUNT_INIT(callback, r): the callback is not supported here
+   OP_GC_RAWREFCOUNT_CREATE_LINK_PYOBJ(): not implemented, maybe not needed
+*/
+
+#define OP_GC_RAWREFCOUNT_CREATE_LINK_PYPY(gcobj, pyobj, r)   \
+    gc_rawrefcount_create_link_pypy(gcobj, pyobj)
+
+#define OP_GC_RAWREFCOUNT_FROM_OBJ(gcobj, r)   \
+    r = gc_rawrefcount_from_obj(gcobj)
+
+#define OP_GC_RAWREFCOUNT_TO_OBJ(pyobj, r)   \
+    r = gc_rawrefcount_to_obj(pyobj)
+
+#define OP_GC_RAWREFCOUNT_NEXT_DEAD(r)   \
+    r = gc_rawrefcount_next_dead()
+
+
+RPY_EXTERN void gc_rawrefcount_create_link_pypy(/*gcobj_t*/void *gcobj, 
+                                                /*pyobj_t*/void *pyobj);
+RPY_EXTERN /*pyobj_t*/void *gc_rawrefcount_from_obj(/*gcobj_t*/void *gcobj);
+RPY_EXTERN /*gcobj_t*/void *gc_rawrefcount_to_obj(/*pyobj_t*/void *pyobj);
+RPY_EXTERN /*pyobj_t*/void *gc_rawrefcount_next_dead(void);
diff --git a/rpython/rlib/test/test_rawrefcount.py b/rpython/rlib/test/test_rawrefcount.py
--- a/rpython/rlib/test/test_rawrefcount.py
+++ b/rpython/rlib/test/test_rawrefcount.py
@@ -266,3 +266,52 @@
         t, cbuilder = self.compile(entry_point)
         data = cbuilder.cmdexec('hi there')
         assert data.startswith('OK!\n')
+
+
+class TestBoehmTranslated(StandaloneTests):
+
+    def test_full_translation(self):
+
+        def make_ob():
+            p = W_Root(42)
+            ob = lltype.malloc(PyObjectS, flavor='raw', zero=True)
+            rawrefcount.create_link_pypy(p, ob)
+            ob.c_ob_refcnt += REFCNT_FROM_PYPY
+            assert rawrefcount.from_obj(PyObject, p) == ob
+            assert rawrefcount.to_obj(W_Root, ob) == p
+            return ob
+
+        prebuilt_p = W_Root(-42)
+        prebuilt_ob = lltype.malloc(PyObjectS, flavor='raw', zero=True,
+                                    immortal=True)
+
+        def entry_point(argv):
+            rawrefcount.create_link_pypy(prebuilt_p, prebuilt_ob)
+            prebuilt_ob.c_ob_refcnt += REFCNT_FROM_PYPY
+            oblist = [make_ob() for i in range(50)]
+            rgc.collect()
+            deadlist = []
+            while True:
+                ob = rawrefcount.next_dead(PyObject)
+                if not ob: break
+                deadlist.append(ob)
+            if len(deadlist) == 0:
+                print "no dead object"
+                return 1
+            if len(deadlist) < 30:
+                print "not enough dead objects"
+                return 1
+            for ob in deadlist:
+                if ob not in oblist:
+                    print "unexpected value for dead pointer"
+                    return 1
+                oblist.remove(ob)
+            print "OK!"
+            lltype.free(ob, flavor='raw')
+            return 0
+
+        self.config = get_combined_translation_config(translating=True)
+        self.config.translation.gc = "boehm"
+        t, cbuilder = self.compile(entry_point)
+        data = cbuilder.cmdexec('hi there')
+        assert data.startswith('OK!\n')
diff --git a/rpython/rlib/test/test_rawrefcount_boehm.py b/rpython/rlib/test/test_rawrefcount_boehm.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/test/test_rawrefcount_boehm.py
@@ -0,0 +1,230 @@
+import itertools, os, subprocess
+from hypothesis import given, strategies
+from rpython.tool.udir import udir
+
+
+TEST_CODE = r"""
+#define TEST_BOEHM_RAWREFCOUNT
+#include "boehm-rawrefcount.c"
+
+static gcobj_t *alloc_gcobj(void)   /* for tests */
+{
+    gcobj_t *g = GC_MALLOC(1000);
+    printf("gc obj: %p\n", g);
+    return g;
+}
+
+static pyobj_t *alloc_pyobj(void)   /* for tests */
+{
+    pyobj_t *p = malloc(1000);
+    p->ob_refcnt = 1;
+    p->ob_pypy_link = 0;
+    printf("py obj: %p\n", p);
+    return p;
+}
+
+static void decref(pyobj_t *p)      /* for tests */
+{
+    p->ob_refcnt--;
+    if (p->ob_refcnt == 0) {
+        printf("decref to zero: %p\n", p);
+        free(p);
+    }
+    assert(p->ob_refcnt >= REFCNT_FROM_PYPY ||
+           p->ob_refcnt < REFCNT_FROM_PYPY * 0.99);
+}
+
+void run_test(void);     /* forward declaration, produced by the test */
+
+int main(void)
+{
+    run_test();
+    while (gc_rawrefcount_next_dead() != NULL)
+        ;
+    return 0;
+}
+"""
+
+
+operations = strategies.sampled_from([
+    'new_pyobj',
+    'new_gcobj',
+    'create_link',
+    'from_obj',
+    'to_obj',
+    'forget_pyobj',
+    'forget_gcobj',
+    'collect',
+    'dead',
+    ])
+
+
+ at strategies.composite
+def make_code(draw):
+    code = []
+    pyobjs = []
+    gcobjs = []
+    num_gcobj = itertools.count()
+    num_pyobj = itertools.count()
+    links_g2p = {}
+    links_p2g = {}
+
+    def new_gcobj():
+        varname = 'g%d' % next(num_gcobj)
+        code.append('gcobj_t *volatile %s = alloc_gcobj();' % varname)
+        gcobjs.append(varname)
+        return varname
+
+    def new_pyobj():
+        varname = 'p%d' % next(num_pyobj)
+        code.append('pyobj_t *%s = alloc_pyobj();' % varname)
+        pyobjs.append(varname)
+        return varname
+
+    for op in draw(strategies.lists(operations, average_size=250)):
+        if op == 'new_gcobj':
+            new_gcobj()
+        elif op == 'new_pyobj':
+            new_pyobj()
+        elif op == 'create_link':
+            gvars = [varname for varname in gcobjs if varname not in links_g2p]
+            if gvars == []:
+                gvars.append(new_gcobj())
+            pvars = [varname for varname in pyobjs if varname not in links_p2g]
+            if pvars == []:
+                pvars.append(new_pyobj())
+            gvar = draw(strategies.sampled_from(gvars))
+            pvar = draw(strategies.sampled_from(pvars))
+            code.append(r'printf("create_link %%p-%%p\n", %s, %s); '
+                            % (gvar, pvar) +
+                        "%s->ob_refcnt += REFCNT_FROM_PYPY; " % pvar +
+                        "gc_rawrefcount_create_link_pypy(%s, %s);"
+                            % (gvar, pvar))
+            links_g2p[gvar] = pvar
+            links_p2g[pvar] = gvar
+        elif op == 'from_obj':
+            if gcobjs:
+                prnt = False
+                gvar = draw(strategies.sampled_from(gcobjs))
+                if gvar not in links_g2p:
+                    check = "== NULL"
+                elif links_g2p[gvar] in pyobjs:
+                    check = "== %s" % (links_g2p[gvar],)
+                else:
+                    check = "!= NULL"
+                    prnt = True
+                code.append("assert(gc_rawrefcount_from_obj(%s) %s);"
+                            % (gvar, check))
+                if prnt:
+                    code.append(r'printf("link %%p-%%p\n", %s, '
+                        'gc_rawrefcount_from_obj(%s));' % (gvar, gvar))
+        elif op == 'to_obj':
+            if pyobjs:
+                prnt = False
+                pvar = draw(strategies.sampled_from(pyobjs))
+                if pvar not in links_p2g:
+                    check = "== NULL"
+                elif links_p2g[pvar] in gcobjs:
+                    check = "== %s" % (links_p2g[pvar],)
+                else:
+                    check = "!= NULL"
+                    prnt = True
+                code.append("assert(gc_rawrefcount_to_obj(%s) %s);"
+                            % (pvar, check))
+                if prnt:
+                    code.append(r'printf("link %%p-%%p\n", '
+                        'gc_rawrefcount_to_obj(%s), %s);' % (pvar, pvar))
+        elif op == 'forget_pyobj':
+            if pyobjs:
+                index = draw(strategies.sampled_from(range(len(pyobjs))))
+                pvar = pyobjs.pop(index)
+                code.append(r'printf("-p%%p\n", %s); ' % pvar +
+                            "decref(%s); %s = NULL;" % (pvar, pvar))
+        elif op == 'forget_gcobj':
+            if gcobjs:
+                index = draw(strategies.sampled_from(range(len(gcobjs))))
+                gvar = gcobjs.pop(index)
+                code.append(r'printf("-g%%p\n", %s); ' % gvar +
+                            "%s = NULL;" % (gvar,))
+        elif op == 'collect':
+            code.append("GC_gcollect();")
+        elif op == 'dead':
+            code.append('gc_rawrefcount_next_dead();')
+        else:
+            assert False, op
+
+    return '\n'.join(code)
+
+
+ at given(make_code())
+def test_random(code):
+    filename = str(udir.join("test-rawrefcount-boehm.c"))
+    with open(filename, "w") as f:
+        print >> f, TEST_CODE
+        print >> f, 'void run_test(void) {'
+        print >> f, code
+        print >> f, '}'
+
+    srcdir = os.path.dirname(os.path.dirname(
+        os.path.abspath(os.path.join(__file__))))
+    srcdir = os.path.join(srcdir, 'src')
+
+    err = os.system("cd '%s' && gcc -Werror -lgc -I%s -o test-rawrefcount-boehm"
+                    " test-rawrefcount-boehm.c" % (udir, srcdir))
+    assert err == 0
+    p = subprocess.Popen("./test-rawrefcount-boehm", stdout=subprocess.PIPE,
+                         cwd=str(udir))
+    stdout, _ = p.communicate()
+    assert p.wait() == 0
+
+    gcobjs = {}
+    pyobjs = {}
+    links_p2g = {}
+    links_g2p = {}
+    for line in stdout.splitlines():
+        if line.startswith('py obj: '):
+            p = line[8:]
+            assert not pyobjs.get(p)
+            pyobjs[p] = True
+            assert p not in links_p2g
+        elif line.startswith('gc obj: '):
+            g = line[8:]
+            assert not gcobjs.get(g)
+            gcobjs[g] = True
+            if g in links_g2p: del links_g2p[g]
+        elif line.startswith('-p'):
+            p = line[2:]
+            assert pyobjs[p] == True
+            pyobjs[p] = False
+        elif line.startswith('-g'):
+            g = line[2:]
+            assert gcobjs[g] == True
+            gcobjs[g] = False
+        elif line.startswith('decref to zero: '):
+            p = line[16:]
+            assert pyobjs[p] == False
+            assert p not in links_p2g
+            del pyobjs[p]
+        elif line.startswith('create_link '):
+            g, p = line[12:].split('-')
+            assert g in gcobjs
+            assert p in pyobjs
+            assert g not in links_g2p
+            assert p not in links_p2g
+            links_g2p[g] = p
+            links_p2g[p] = g
+        elif line.startswith('link '):
+            g, p = line[5:].split('-')
+            assert g in gcobjs
+            assert p in pyobjs
+            assert links_g2p[g] == p
+            assert links_p2g[p] == g
+        elif line.startswith('plist['):
+            pass
+        elif line.startswith('next_dead: '):
+            p = line[11:]
+            assert pyobjs[p] == False
+            del pyobjs[p]
+            del links_p2g[p]
+        else:
+            assert False, repr(line)
diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -497,6 +497,7 @@
     'gc_rawrefcount_create_link_pyobj': LLOp(),
     'gc_rawrefcount_from_obj':          LLOp(sideeffects=False),
     'gc_rawrefcount_to_obj':            LLOp(sideeffects=False),
+    'gc_rawrefcount_next_dead':         LLOp(),
 
     # ------- JIT & GC interaction, only for some GCs ----------
 


More information about the pypy-commit mailing list