[pypy-commit] pypy fix-2198: hg merge default

rlamy pypy.commits at gmail.com
Fri Dec 18 10:35:33 EST 2015


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: fix-2198
Changeset: r81376:86b5972fa57a
Date: 2015-12-18 16:33 +0100
http://bitbucket.org/pypy/pypy/changeset/86b5972fa57a/

Log:	hg merge default

diff too long, truncating to 2000 out of 2340 lines

diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO
--- a/lib_pypy/cffi.egg-info/PKG-INFO
+++ b/lib_pypy/cffi.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: cffi
-Version: 1.4.0
+Version: 1.4.1
 Summary: Foreign Function Interface for Python calling C code.
 Home-page: http://cffi.readthedocs.org
 Author: Armin Rigo, Maciej Fijalkowski
diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -4,8 +4,8 @@
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
 
-__version__ = "1.4.0"
-__version_info__ = (1, 4, 0)
+__version__ = "1.4.1"
+__version_info__ = (1, 4, 1)
 
 # The verifier module file names are based on the CRC32 of a string that
 # contains the following version number.  It may be older than __version__
diff --git a/pypy/doc/stm.rst b/pypy/doc/stm.rst
--- a/pypy/doc/stm.rst
+++ b/pypy/doc/stm.rst
@@ -83,30 +83,27 @@
 
 **pypy-stm requires 64-bit Linux for now.**
 
-Development is done in the branch `stmgc-c7`_.  If you are only
-interested in trying it out, you can download a Ubuntu binary here__
-(``pypy-stm-2.*.tar.bz2``, for Ubuntu 12.04-14.04).  The current version
-supports four "segments", which means that it will run up to four
-threads in parallel.  (Development recently switched to `stmgc-c8`_,
-but that is not ready for trying out yet.)
+Development is done in the branch `stmgc-c8`_.  If you are only
+interested in trying it out, please pester us until we upload a recent
+prebuilt binary.  The current version supports four "segments", which
+means that it will run up to four threads in parallel.
 
 To build a version from sources, you first need to compile a custom
-version of clang(!); we recommend downloading `llvm and clang like
-described here`__, but at revision 201645 (use ``svn co -r 201645 <path>``
-for all checkouts).  Then apply all the patches in `this directory`__:
-they are fixes for a clang-only feature that hasn't been used so heavily
-in the past (without the patches, you get crashes of clang).  Then get
-the branch `stmgc-c7`_ of PyPy and run::
+version of gcc(!).  See the instructions here:
+https://bitbucket.org/pypy/stmgc/src/default/gcc-seg-gs/
+(Note that these patches are being incorporated into gcc.  It is likely
+that future versions of gcc will not need to be patched any more.)
+
+Then get the branch `stmgc-c8`_ of PyPy and run::
 
    cd pypy/goal
    ../../rpython/bin/rpython -Ojit --stm
-   PYTHONPATH=. ./pypy-c pypy/tool/build_cffi_imports.py
 
-.. _`stmgc-c7`: https://bitbucket.org/pypy/pypy/src/stmgc-c7/
+At the end, this will try to compile the generated C code by calling
+``gcc-seg-gs``, which must be the script you installed in the
+instructions above.
+
 .. _`stmgc-c8`: https://bitbucket.org/pypy/pypy/src/stmgc-c8/
-.. __: https://bitbucket.org/pypy/pypy/downloads/
-.. __: http://clang.llvm.org/get_started.html
-.. __: https://bitbucket.org/pypy/stmgc/src/default/c7/llvmfix/
 
 
 .. _caveats:
@@ -114,6 +111,12 @@
 Current status (stmgc-c7)
 -------------------------
 
+.. warning::
+    
+    THIS PAGE IS OLD, THE REST IS ABOUT STMGC-C7 WHEREAS THE CURRENT
+    DEVELOPMENT WORK IS DONE ON STMGC-C8
+
+
 * **NEW:** It seems to work fine, without crashing any more.  Please `report
   any crash`_ you find (or other bugs).
 
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -81,3 +81,15 @@
 Move wrappers for OS functions from `rpython/rtyper` to `rpython/rlib` and 
 turn them into regular RPython functions. Most RPython-compatible `os.*` 
 functions are now directly accessible as `rpython.rposix.*`.
+
+.. branch: always-enable-gil
+
+Simplify a bit the GIL handling in non-jitted code.  Fixes issue #2205.
+
+.. branch: flowspace-cleanups
+
+Trivial cleanups in flowspace.operation : fix comment & duplicated method
+
+.. branch: test-AF_NETLINK
+.. branch: small-cleanups-misc
+.. branch: cpyext-slotdefs
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -2,7 +2,7 @@
 from pypy.interpreter.mixedmodule import MixedModule
 from rpython.rlib import rdynload, clibffi
 
-VERSION = "1.4.0"
+VERSION = "1.4.1"
 
 FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI
 try:
diff --git a/pypy/module/_cffi_backend/call_python.py b/pypy/module/_cffi_backend/call_python.py
--- a/pypy/module/_cffi_backend/call_python.py
+++ b/pypy/module/_cffi_backend/call_python.py
@@ -40,10 +40,9 @@
        at least 8 bytes in size.
     """
     from pypy.module._cffi_backend.ccallback import reveal_callback
+    from rpython.rlib import rgil
 
-    after = rffi.aroundstate.after
-    if after:
-        after()
+    rgil.acquire()
     rffi.stackcounter.stacks_counter += 1
     llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
 
@@ -71,9 +70,7 @@
     cerrno._errno_before(rffi.RFFI_ERR_ALL | rffi.RFFI_ALT_ERRNO)
 
     rffi.stackcounter.stacks_counter -= 1
-    before = rffi.aroundstate.before
-    if before:
-        before()
+    rgil.release()
 
 
 def get_ll_cffi_call_python():
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1,7 +1,7 @@
 # ____________________________________________________________
 
 import sys
-assert __version__ == "1.4.0", ("This test_c.py file is for testing a version"
+assert __version__ == "1.4.1", ("This test_c.py file is for testing a version"
                                 " of cffi that differs from the one that we"
                                 " get from 'import _cffi_backend'")
 if sys.version_info < (3,):
diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py
--- a/pypy/module/_socket/test/test_sock_app.py
+++ b/pypy/module/_socket/test/test_sock_app.py
@@ -251,7 +251,7 @@
     from pypy.module._socket.interp_socket import addr_as_object
     if not hasattr(rsocket._c, 'sockaddr_ll'):
         py.test.skip("posix specific test")
-    # HACK: To get the correct interface numer of lo, which in most cases is 1,
+    # HACK: To get the correct interface number of lo, which in most cases is 1,
     # but can be anything (i.e. 39), we need to call the libc function
     # if_nametoindex to get the correct index
     import ctypes
@@ -513,7 +513,7 @@
     def test_getsetsockopt(self):
         import _socket as socket
         import struct
-        # A socket sould start with reuse == 0
+        # A socket should start with reuse == 0
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         reuse = s.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
         assert reuse == 0
@@ -627,6 +627,26 @@
         self.foo = _socket.socket()
 
 
+class AppTestNetlink:
+    def setup_class(cls):
+        if not hasattr(os, 'getpid'):
+            py.test.skip("AF_NETLINK needs os.getpid()")
+        w_ok = space.appexec([], "(): import _socket; " +
+                                 "return hasattr(_socket, 'AF_NETLINK')")
+        if not space.is_true(w_ok):
+            py.test.skip("no AF_NETLINK on this platform")
+        cls.space = space
+
+    def test_connect_to_kernel_netlink_routing_socket(self):
+        import _socket, os
+        s = _socket.socket(_socket.AF_NETLINK, _socket.SOCK_DGRAM, _socket.NETLINK_ROUTE)
+        assert s.getsockname() == (0L, 0L)
+        s.bind((0, 0))
+        a, b = s.getsockname()
+        assert a == os.getpid()
+        assert b == 0
+ 
+
 class AppTestPacket:
     def setup_class(cls):
         if not hasattr(os, 'getuid') or os.getuid() != 0:
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -124,7 +124,7 @@
 METH_COEXIST METH_STATIC METH_CLASS
 METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O
 Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS
-Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE
+Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES
 """.split()
 for name in constant_names:
     setattr(CConfig_constants, name, rffi_platform.ConstantInteger(name))
@@ -602,6 +602,7 @@
 # Make the wrapper for the cases (1) and (2)
 def make_wrapper(space, callable, gil=None):
     "NOT_RPYTHON"
+    from rpython.rlib import rgil
     names = callable.api_func.argnames
     argtypes_enum_ui = unrolling_iterable(enumerate(zip(callable.api_func.argtypes,
         [name.startswith("w_") for name in names])))
@@ -617,9 +618,7 @@
         # we hope that malloc removal removes the newtuple() that is
         # inserted exactly here by the varargs specializer
         if gil_acquire:
-            after = rffi.aroundstate.after
-            if after:
-                after()
+            rgil.acquire()
         rffi.stackcounter.stacks_counter += 1
         llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
         retval = fatal_value
@@ -692,9 +691,7 @@
                 pypy_debug_catch_fatal_exception()
         rffi.stackcounter.stacks_counter -= 1
         if gil_release:
-            before = rffi.aroundstate.before
-            if before:
-                before()
+            rgil.release()
         return retval
     callable._always_inline_ = 'try'
     wrapper.__name__ = "wrapper for %r" % (callable, )
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -4,14 +4,14 @@
 
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (
-    cpython_api, generic_cpy_call, PyObject, Py_ssize_t)
+    cpython_api, generic_cpy_call, PyObject, Py_ssize_t, Py_TPFLAGS_CHECKTYPES)
 from pypy.module.cpyext.typeobjectdefs import (
     unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc,
     getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry,
     ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc,
     cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc,
     readbufferproc)
-from pypy.module.cpyext.pyobject import from_ref
+from pypy.module.cpyext.pyobject import from_ref, make_ref, Py_DecRef
 from pypy.module.cpyext.pyerrors import PyErr_Occurred
 from pypy.module.cpyext.state import State
 from pypy.interpreter.error import OperationError, oefmt
@@ -65,22 +65,24 @@
     func_binary = rffi.cast(binaryfunc, func)
     check_num_args(space, w_args, 1)
     args_w = space.fixedview(w_args)
-
-    if not space.is_true(space.issubtype(space.type(args_w[0]),
-                                         space.type(w_self))):
+    ref = make_ref(space, w_self)
+    if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+        not space.is_true(space.issubtype(space.type(args_w[0]),
+                                         space.type(w_self)))):
         return space.w_NotImplemented
- 
+    Py_DecRef(space, ref)
     return generic_cpy_call(space, func_binary, w_self, args_w[0])
 
 def wrap_binaryfunc_r(space, w_self, w_args, func):
     func_binary = rffi.cast(binaryfunc, func)
     check_num_args(space, w_args, 1)
     args_w = space.fixedview(w_args)
-
-    if not space.is_true(space.issubtype(space.type(args_w[0]),
-                                         space.type(w_self))):
+    ref = make_ref(space, w_self)
+    if (not ref.c_ob_type.c_tp_flags & Py_TPFLAGS_CHECKTYPES and
+        not space.is_true(space.issubtype(space.type(args_w[0]),
+                                         space.type(w_self)))):
         return space.w_NotImplemented
-
+    Py_DecRef(space, ref)
     return generic_cpy_call(space, func_binary, args_w[0], w_self)
 
 def wrap_inquirypred(space, w_self, w_args, func):
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -591,45 +591,92 @@
 
     def test_binaryfunc(self):
         module = self.import_extension('foo', [
-            ("new_obj", "METH_NOARGS",
+            ("newInt", "METH_VARARGS",
              """
-                FooObject *fooObj;
+                IntLikeObject *intObj;
+                long intval;
 
-                Foo_Type.tp_as_number = &foo_as_number;
-                foo_as_number.nb_add = foo_nb_add_call;
-                if (PyType_Ready(&Foo_Type) < 0) return NULL;
-                fooObj = PyObject_New(FooObject, &Foo_Type);
-                if (!fooObj) {
+                if (!PyArg_ParseTuple(args, "i", &intval))
+                    return NULL;
+
+                IntLike_Type.tp_as_number = &intlike_as_number;
+                IntLike_Type.tp_flags |= Py_TPFLAGS_CHECKTYPES;
+                intlike_as_number.nb_add = intlike_nb_add;
+                if (PyType_Ready(&IntLike_Type) < 0) return NULL;
+                intObj = PyObject_New(IntLikeObject, &IntLike_Type);
+                if (!intObj) {
                     return NULL;
                 }
 
-                return (PyObject *)fooObj;
+                intObj->ival = intval;
+                return (PyObject *)intObj;
+             """),
+             ("newIntNoOp", "METH_VARARGS",
+             """
+                IntLikeObjectNoOp *intObjNoOp;
+                long intval;
+
+                if (!PyArg_ParseTuple(args, "i", &intval))
+                    return NULL;
+
+                IntLike_Type_NoOp.tp_flags |= Py_TPFLAGS_CHECKTYPES;
+                if (PyType_Ready(&IntLike_Type_NoOp) < 0) return NULL;
+                intObjNoOp = PyObject_New(IntLikeObjectNoOp, &IntLike_Type_NoOp);
+                if (!intObjNoOp) {
+                    return NULL;
+                }
+
+                intObjNoOp->ival = intval;
+                return (PyObject *)intObjNoOp;
              """)],
             """
             typedef struct
             {
                 PyObject_HEAD
-            } FooObject;
+                long ival;
+            } IntLikeObject;
 
             static PyObject * 
-            foo_nb_add_call(PyObject *self, PyObject *other)
+            intlike_nb_add(PyObject *self, PyObject *other)
             {
-                return PyInt_FromLong(42); 
+                long val1 = ((IntLikeObject *)(self))->ival;
+                if (PyInt_Check(other)) {
+                  long val2 = PyInt_AsLong(other);
+                  return PyInt_FromLong(val1+val2);
+                }
+
+                long val2 = ((IntLikeObject *)(other))->ival;
+                return PyInt_FromLong(val1+val2);
             }
 
-            PyTypeObject Foo_Type = {
+            PyTypeObject IntLike_Type = {
                 PyObject_HEAD_INIT(0)
                 /*ob_size*/             0,
-                /*tp_name*/             "Foo",
-                /*tp_basicsize*/        sizeof(FooObject),
+                /*tp_name*/             "IntLike",
+                /*tp_basicsize*/        sizeof(IntLikeObject),
             };
-            static PyNumberMethods foo_as_number;
+            static PyNumberMethods intlike_as_number;
+
+            typedef struct
+            {
+                PyObject_HEAD
+                long ival;
+            } IntLikeObjectNoOp;
+
+            PyTypeObject IntLike_Type_NoOp = {
+                PyObject_HEAD_INIT(0)
+                /*ob_size*/             0,
+                /*tp_name*/             "IntLikeNoOp",
+                /*tp_basicsize*/        sizeof(IntLikeObjectNoOp),
+            };
             """)
-        a = module.new_obj()
-        b = module.new_obj() 
+        a = module.newInt(1)
+        b = module.newInt(2)
         c = 3
-        assert (a + b) == 42 
-        raises(TypeError, "b + c")
+        d = module.newIntNoOp(4)
+        assert (a + b) == 3
+        assert (b + c) == 5
+        assert (d + a) == 5
 
     def test_tp_new_in_subclass_of_type(self):
         skip("BROKEN")
diff --git a/pypy/module/signal/__init__.py b/pypy/module/signal/__init__.py
--- a/pypy/module/signal/__init__.py
+++ b/pypy/module/signal/__init__.py
@@ -48,3 +48,6 @@
                                                   use_bytecode_counter=False)
         space.actionflag.__class__ = interp_signal.SignalActionFlag
         # xxx yes I know the previous line is a hack
+
+    def startup(self, space):
+        space.check_signal_action.startup(space)
diff --git a/pypy/module/signal/interp_signal.py b/pypy/module/signal/interp_signal.py
--- a/pypy/module/signal/interp_signal.py
+++ b/pypy/module/signal/interp_signal.py
@@ -63,19 +63,25 @@
         AsyncAction.__init__(self, space)
         self.pending_signal = -1
         self.fire_in_another_thread = False
-        if self.space.config.objspace.usemodules.thread:
-            from pypy.module.thread import gil
-            gil.after_thread_switch = self._after_thread_switch
+        #
+        @rgc.no_collect
+        def _after_thread_switch():
+            if self.fire_in_another_thread:
+                if self.space.threadlocals.signals_enabled():
+                    self.fire_in_another_thread = False
+                    self.space.actionflag.rearm_ticker()
+                    # this occurs when we just switched to the main thread
+                    # and there is a signal pending: we force the ticker to
+                    # -1, which should ensure perform() is called quickly.
+        self._after_thread_switch = _after_thread_switch
+        # ^^^ so that 'self._after_thread_switch' can be annotated as a
+        # constant
 
-    @rgc.no_collect
-    def _after_thread_switch(self):
-        if self.fire_in_another_thread:
-            if self.space.threadlocals.signals_enabled():
-                self.fire_in_another_thread = False
-                self.space.actionflag.rearm_ticker()
-                # this occurs when we just switched to the main thread
-                # and there is a signal pending: we force the ticker to
-                # -1, which should ensure perform() is called quickly.
+    def startup(self, space):
+        # this is translated
+        if space.config.objspace.usemodules.thread:
+            from rpython.rlib import rgil
+            rgil.invoke_after_thread_switch(self._after_thread_switch)
 
     def perform(self, executioncontext, frame):
         self._poll_for_signals()
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py
@@ -1353,8 +1353,8 @@
         ffi = FFI(backend=self.Backend())
         ffi.cdef("enum foo;")
         from cffi import __version_info__
-        if __version_info__ < (1, 4):
-            py.test.skip("re-enable me in version 1.4")
+        if __version_info__ < (1, 5):
+            py.test.skip("re-enable me in version 1.5")
         e = py.test.raises(CDefError, ffi.cast, "enum foo", -1)
         assert str(e.value) == (
             "'enum foo' has no values explicitly defined: refusing to guess "
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -11,7 +11,6 @@
 from pypy.module.thread.error import wrap_thread_error
 from pypy.interpreter.executioncontext import PeriodicAsyncAction
 from pypy.module.thread.threadlocals import OSThreadLocals
-from rpython.rlib.objectmodel import invoke_around_extcall
 
 class GILThreadLocals(OSThreadLocals):
     """A version of OSThreadLocals that enforces a GIL."""
@@ -23,34 +22,21 @@
         space.actionflag.register_periodic_action(GILReleaseAction(space),
                                                   use_bytecode_counter=True)
 
-    def _initialize_gil(self, space):
-        rgil.gil_allocate()
-
     def setup_threads(self, space):
         """Enable threads in the object space, if they haven't already been."""
         if not self.gil_ready:
-            self._initialize_gil(space)
+            # Note: this is a quasi-immutable read by module/pypyjit/interp_jit
+            # It must be changed (to True) only if it was really False before
+            rgil.allocate()
             self.gil_ready = True
             result = True
         else:
             result = False      # already set up
-
-        # add the GIL-releasing callback around external function calls.
-        #
-        # XXX we assume a single space, but this is not quite true during
-        # testing; for example, if you run the whole of test_lock you get
-        # a deadlock caused by the first test's space being reused by
-        # test_lock_again after the global state was cleared by
-        # test_compile_lock.  As a workaround, we repatch these global
-        # fields systematically.
-        invoke_around_extcall(before_external_call, after_external_call)
         return result
 
-    def reinit_threads(self, space):
-        "Called in the child process after a fork()"
-        OSThreadLocals.reinit_threads(self, space)
-        if self.gil_ready:     # re-initialize the gil if needed
-            self._initialize_gil(space)
+    ## def reinit_threads(self, space):
+    ##     "Called in the child process after a fork()"
+    ##     OSThreadLocals.reinit_threads(self, space)
 
 
 class GILReleaseAction(PeriodicAsyncAction):
@@ -59,43 +45,4 @@
     """
 
     def perform(self, executioncontext, frame):
-        do_yield_thread()
-
-
-after_thread_switch = lambda: None     # hook for signal.py
-
-def before_external_call():
-    # this function must not raise, in such a way that the exception
-    # transformer knows that it cannot raise!
-    rgil.gil_release()
-before_external_call._gctransformer_hint_cannot_collect_ = True
-before_external_call._dont_reach_me_in_del_ = True
-
-def after_external_call():
-    rgil.gil_acquire()
-    rthread.gc_thread_run()
-    after_thread_switch()
-after_external_call._gctransformer_hint_cannot_collect_ = True
-after_external_call._dont_reach_me_in_del_ = True
-
-# The _gctransformer_hint_cannot_collect_ hack is needed for
-# translations in which the *_external_call() functions are not inlined.
-# They tell the gctransformer not to save and restore the local GC
-# pointers in the shadow stack.  This is necessary because the GIL is
-# not held after the call to before_external_call() or before the call
-# to after_external_call().
-
-def do_yield_thread():
-    # explicitly release the gil, in a way that tries to give more
-    # priority to other threads (as opposed to continuing to run in
-    # the same thread).
-    if rgil.gil_yield_thread():
-        rthread.gc_thread_run()
-        after_thread_switch()
-do_yield_thread._gctransformer_hint_close_stack_ = True
-do_yield_thread._dont_reach_me_in_del_ = True
-do_yield_thread._dont_inline_ = True
-
-# do_yield_thread() needs a different hint: _gctransformer_hint_close_stack_.
-# The *_external_call() functions are themselves called only from the rffi
-# module from a helper function that also has this hint.
+        rgil.yield_thread()
diff --git a/pypy/module/thread/test/support.py b/pypy/module/thread/test/support.py
--- a/pypy/module/thread/test/support.py
+++ b/pypy/module/thread/test/support.py
@@ -5,7 +5,7 @@
 import errno
 
 from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.module.thread import gil
+from rpython.rlib import rgil
 
 
 NORMAL_TIMEOUT = 300.0   # 5 minutes
@@ -15,9 +15,9 @@
     adaptivedelay = 0.04
     limit = time.time() + delay * NORMAL_TIMEOUT
     while time.time() <= limit:
-        gil.before_external_call()
+        rgil.release()
         time.sleep(adaptivedelay)
-        gil.after_external_call()
+        rgil.acquire()
         gc.collect()
         if space.is_true(space.call_function(w_condition)):
             return
diff --git a/pypy/module/thread/test/test_gil.py b/pypy/module/thread/test/test_gil.py
--- a/pypy/module/thread/test/test_gil.py
+++ b/pypy/module/thread/test/test_gil.py
@@ -1,5 +1,6 @@
 import time
 from pypy.module.thread import gil
+from rpython.rlib import rgil
 from rpython.rlib.test import test_rthread
 from rpython.rlib import rthread as thread
 from rpython.rlib.objectmodel import we_are_translated
@@ -55,7 +56,7 @@
                 assert state.datalen3 == len(state.data)
                 assert state.datalen4 == len(state.data)
                 debug_print(main, i, state.datalen4)
-                gil.do_yield_thread()
+                rgil.yield_thread()
                 assert i == j
                 j += 1
         def bootstrap():
@@ -82,9 +83,9 @@
                 if not still_waiting:
                     raise ValueError("time out")
                 still_waiting -= 1
-                if not we_are_translated(): gil.before_external_call()
+                if not we_are_translated(): rgil.release()
                 time.sleep(0.01)
-                if not we_are_translated(): gil.after_external_call()
+                if not we_are_translated(): rgil.acquire()
             debug_print("leaving!")
             i1 = i2 = 0
             for tid, i in state.data:
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -521,7 +521,6 @@
 
     def descr_getitem(self, space, w_index):
         if isinstance(w_index, W_SliceObject):
-            # XXX consider to extend rlist's functionality?
             length = self.length()
             start, stop, step, slicelength = w_index.indices4(space, length)
             assert slicelength >= 0
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -441,7 +441,7 @@
 def dict_contains(s_dct, s_element, position):
     s_dct.dictdef.generalize_key(s_element)
     if s_dct._is_empty(position):
-        s_bool =SomeBool()
+        s_bool = SomeBool()
         s_bool.const = False
         return s_bool
     return s_Bool
diff --git a/rpython/flowspace/operation.py b/rpython/flowspace/operation.py
--- a/rpython/flowspace/operation.py
+++ b/rpython/flowspace/operation.py
@@ -1,5 +1,5 @@
 """
-This module defines all the SpaceOeprations used in rpython.flowspace.
+This module defines all the SpaceOperations used in rpython.flowspace.
 """
 
 import __builtin__
@@ -196,21 +196,6 @@
             return cls._dispatch(type(s_arg))
 
     @classmethod
-    def get_specialization(cls, s_arg, *_ignored):
-        try:
-            impl = getattr(s_arg, cls.opname)
-
-            def specialized(annotator, arg, *other_args):
-                return impl(*[annotator.annotation(x) for x in other_args])
-            try:
-                specialized.can_only_throw = impl.can_only_throw
-            except AttributeError:
-                pass
-            return specialized
-        except AttributeError:
-            return cls._dispatch(type(s_arg))
-
-    @classmethod
     def register_transform(cls, Some_cls):
         def decorator(func):
             cls._transform[Some_cls] = func
diff --git a/rpython/jit/backend/llsupport/assembler.py b/rpython/jit/backend/llsupport/assembler.py
--- a/rpython/jit/backend/llsupport/assembler.py
+++ b/rpython/jit/backend/llsupport/assembler.py
@@ -380,6 +380,8 @@
         # the call that it is no longer equal to css.  See description
         # in translator/c/src/thread_pthread.c.
 
+        # XXX some duplicated logic here, but note that rgil.acquire()
+        # does more than just RPyGilAcquire()
         if old_rpy_fastgil == 0:
             # this case occurs if some other thread stole the GIL but
             # released it again.  What occurred here is that we changed
@@ -390,9 +392,8 @@
         elif old_rpy_fastgil == 1:
             # 'rpy_fastgil' was (and still is) locked by someone else.
             # We need to wait for the regular mutex.
-            after = rffi.aroundstate.after
-            if after:
-                after()
+            from rpython.rlib import rgil
+            rgil.acquire()
         else:
             # stole the GIL from a different thread that is also
             # currently in an external call from the jit.  Attach
@@ -421,9 +422,8 @@
         # 'rpy_fastgil' contains only zero or non-zero, and this is only
         # called when the old value stored in 'rpy_fastgil' was non-zero
         # (i.e. still locked, must wait with the regular mutex)
-        after = rffi.aroundstate.after
-        if after:
-            after()
+        from rpython.rlib import rgil
+        rgil.acquire()
 
     _REACQGIL0_FUNC = lltype.Ptr(lltype.FuncType([], lltype.Void))
     _REACQGIL2_FUNC = lltype.Ptr(lltype.FuncType([rffi.CCHARP, lltype.Signed],
diff --git a/rpython/jit/backend/llsupport/test/test_gc_integration.py b/rpython/jit/backend/llsupport/test/test_gc_integration.py
--- a/rpython/jit/backend/llsupport/test/test_gc_integration.py
+++ b/rpython/jit/backend/llsupport/test/test_gc_integration.py
@@ -17,7 +17,6 @@
 from rpython.jit.backend.llsupport.test.test_regalloc_integration import BaseTestRegalloc
 from rpython.jit.codewriter.effectinfo import EffectInfo
 from rpython.jit.codewriter import longlong
-from rpython.rlib.objectmodel import invoke_around_extcall
 
 CPU = getcpuclass()
 
@@ -625,9 +624,6 @@
         self.S = S
         self.cpu = cpu
 
-    def teardown_method(self, meth):
-        rffi.aroundstate._cleanup_()
-
     def test_shadowstack_call(self):
         cpu = self.cpu
         cpu.gc_ll_descr.init_nursery(100)
diff --git a/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py b/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py
--- a/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py
+++ b/rpython/jit/backend/llsupport/test/zrpy_releasegil_test.py
@@ -1,6 +1,5 @@
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rlib.jit import dont_look_inside
-from rpython.rlib.objectmodel import invoke_around_extcall
 from rpython.jit.metainterp.optimizeopt import ALL_OPTS_NAMES
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 from rpython.rlib import rposix
@@ -16,20 +15,10 @@
     compile_kwds = dict(enable_opts=ALL_OPTS_NAMES, thread=True)
 
     def define_simple(self):
-        class Glob:
-            def __init__(self):
-                self.event = 0
-        glob = Glob()
-        #
-
         c_strchr = rffi.llexternal('strchr', [rffi.CCHARP, lltype.Signed],
                                    rffi.CCHARP)
 
-        def func():
-            glob.event += 1
-
         def before(n, x):
-            invoke_around_extcall(func, func)
             return (n, None, None, None, None, None,
                     None, None, None, None, None, None)
         #
@@ -73,7 +62,8 @@
         def f42(n):
             length = len(glob.lst)
             raw = alloc1()
-            fn = llhelper(CALLBACK, rffi._make_wrapper_for(CALLBACK, callback))
+            wrapper = rffi._make_wrapper_for(CALLBACK, callback, None, True)
+            fn = llhelper(CALLBACK, wrapper)
             if n & 1:    # to create a loop and a bridge, and also
                 pass     # to run the qsort() call in the blackhole interp
             c_qsort(rffi.cast(rffi.VOIDP, raw), rffi.cast(rffi.SIZE_T, 2),
diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -1170,10 +1170,11 @@
     'GC_LOAD/3/rfi',
     # parameters GC_LOAD_INDEXED
     # 1: pointer to complex object
-    # 2: integer describing the offset
+    # 2: integer describing the index
     # 3: constant integer scale factor
-    # 4: constant integer offset
+    # 4: constant integer base offset   (final offset is 'base + scale * index')
     # 5: constant integer. byte size of datatype to load (negative if it is signed)
+    # (GC_LOAD is equivalent to GC_LOAD_INDEXED with arg3==1, arg4==0)
     'GC_LOAD_INDEXED/5/rfi',
 
     '_RAW_LOAD_FIRST',
@@ -1204,8 +1205,9 @@
 
     # same paramters as GC_LOAD, but one additional for the value to store
     # note that the itemsize is not signed!
+    # (gcptr, index, value, [scale, base_offset,] itemsize)
     'GC_STORE/4d/n',
-    'GC_STORE_INDEXED/5d/n',
+    'GC_STORE_INDEXED/6d/n',
 
     'INCREMENT_DEBUG_COUNTER/1/n',
     '_RAW_STORE_FIRST',
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4044,7 +4044,7 @@
         self.interp_operations(f, [])
 
     def test_external_call(self):
-        from rpython.rlib.objectmodel import invoke_around_extcall
+        from rpython.rlib import rgil
 
         TIME_T = lltype.Signed
         # ^^^ some 32-bit platforms have a 64-bit rffi.TIME_T, but we
@@ -4058,11 +4058,6 @@
             pass
         state = State()
 
-        def before():
-            if we_are_jitted():
-                raise Oups
-            state.l.append("before")
-
         def after():
             if we_are_jitted():
                 raise Oups
@@ -4070,14 +4065,14 @@
 
         def f():
             state.l = []
-            invoke_around_extcall(before, after)
+            rgil.invoke_after_thread_switch(after)
             external(lltype.nullptr(T.TO))
             return len(state.l)
 
         res = self.interp_operations(f, [])
-        assert res == 2
+        assert res == 1
         res = self.interp_operations(f, [])
-        assert res == 2
+        assert res == 1
         self.check_operations_history(call_release_gil_i=1, call_may_force_i=0)
 
     def test_unescaped_write_zero(self):
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -1124,8 +1124,8 @@
                   resultvar=op.result)
 
     def gct_gc_thread_run(self, hop):
-        assert self.translator.config.translation.thread
-        if hasattr(self.root_walker, 'thread_run_ptr'):
+        if (self.translator.config.translation.thread and
+                hasattr(self.root_walker, 'thread_run_ptr')):
             livevars = self.push_roots(hop)
             assert not livevars, "live GC var around %s!" % (hop.spaceop,)
             hop.genop("direct_call", [self.root_walker.thread_run_ptr])
diff --git a/rpython/memory/gcwrapper.py b/rpython/memory/gcwrapper.py
--- a/rpython/memory/gcwrapper.py
+++ b/rpython/memory/gcwrapper.py
@@ -184,6 +184,9 @@
                 hdr.tid |= self.gc.gcflag_extra
         return (hdr.tid & self.gc.gcflag_extra) != 0
 
+    def thread_run(self):
+        pass
+
 # ____________________________________________________________
 
 class LLInterpRootWalker:
diff --git a/rpython/rlib/_rposix_repr.py b/rpython/rlib/_rposix_repr.py
deleted file mode 100644
--- a/rpython/rlib/_rposix_repr.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""
-RTyping support for os.stat_result objects.
-They are rtyped just like a tuple of the correct length supporting
-only indexing and the st_xxx attributes.  We need a custom StatResultRepr
-because when rtyping for LL backends we have extra platform-dependent
-items at the end of the tuple, but for OO backends we only want the
-portable items.  This allows the OO backends to assume a fixed shape for
-the tuples returned by os.stat().
-"""
-from rpython.annotator import model as annmodel
-from rpython.rtyper.llannotation import lltype_to_annotation
-from rpython.flowspace.model import Constant
-from rpython.flowspace.operation import op
-from rpython.tool.pairtype import pairtype
-from rpython.rtyper.rmodel import Repr
-from rpython.rtyper.rint import IntegerRepr
-from rpython.rtyper.error import TyperError
-from rpython.rlib import rposix_stat
-
-
-class StatResultRepr(Repr):
-
-    def __init__(self, rtyper):
-        self.rtyper = rtyper
-        self.stat_fields = rposix_stat.STAT_FIELDS
-
-        self.stat_field_indexes = {}
-        for i, (name, TYPE) in enumerate(self.stat_fields):
-            self.stat_field_indexes[name] = i
-
-        self.s_tuple = annmodel.SomeTuple([lltype_to_annotation(TYPE)
-                                           for name, TYPE in self.stat_fields])
-        self.r_tuple = rtyper.getrepr(self.s_tuple)
-        self.lowleveltype = self.r_tuple.lowleveltype
-
-    def redispatch_getfield(self, hop, index):
-        rtyper = self.rtyper
-        s_index = rtyper.annotator.bookkeeper.immutablevalue(index)
-        hop2 = hop.copy()
-        spaceop = op.getitem(hop.args_v[0], Constant(index))
-        spaceop.result = hop.spaceop.result
-        hop2.spaceop = spaceop
-        hop2.args_v = spaceop.args
-        hop2.args_s = [self.s_tuple, s_index]
-        hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)]
-        return hop2.dispatch()
-
-    def rtype_getattr(self, hop):
-        s_attr = hop.args_s[1]
-        attr = s_attr.const
-        try:
-            index = self.stat_field_indexes[attr]
-        except KeyError:
-            raise TyperError("os.stat().%s: field not available" % (attr,))
-        return self.redispatch_getfield(hop, index)
-
-
-class __extend__(pairtype(StatResultRepr, IntegerRepr)):
-
-    def rtype_getitem((r_sta, r_int), hop):
-        s_int = hop.args_s[1]
-        index = s_int.const
-        return r_sta.redispatch_getfield(hop, index)
-
-
-def specialize_make_stat_result(hop):
-    r_StatResult = hop.rtyper.getrepr(rposix_stat.s_StatResult)
-    [v_result] = hop.inputargs(r_StatResult.r_tuple)
-    # no-op conversion from r_StatResult.r_tuple to r_StatResult
-    hop.exception_cannot_occur()
-    return v_result
-
-
-class StatvfsResultRepr(Repr):
-
-    def __init__(self, rtyper):
-        self.rtyper = rtyper
-        self.statvfs_fields = rposix_stat.STATVFS_FIELDS
-
-        self.statvfs_field_indexes = {}
-        for i, (name, TYPE) in enumerate(self.statvfs_fields):
-            self.statvfs_field_indexes[name] = i
-
-        self.s_tuple = annmodel.SomeTuple([lltype_to_annotation(TYPE)
-                                           for name, TYPE in self.statvfs_fields])
-        self.r_tuple = rtyper.getrepr(self.s_tuple)
-        self.lowleveltype = self.r_tuple.lowleveltype
-
-    def redispatch_getfield(self, hop, index):
-        rtyper = self.rtyper
-        s_index = rtyper.annotator.bookkeeper.immutablevalue(index)
-        hop2 = hop.copy()
-        spaceop = op.getitem(hop.args_v[0], Constant(index))
-        spaceop.result = hop.spaceop.result
-        hop2.spaceop = spaceop
-        hop2.args_v = spaceop.args
-        hop2.args_s = [self.s_tuple, s_index]
-        hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)]
-        return hop2.dispatch()
-
-    def rtype_getattr(self, hop):
-        s_attr = hop.args_s[1]
-        attr = s_attr.const
-        try:
-            index = self.statvfs_field_indexes[attr]
-        except KeyError:
-            raise TyperError("os.statvfs().%s: field not available" % (attr,))
-        return self.redispatch_getfield(hop, index)
-
-
-class __extend__(pairtype(StatvfsResultRepr, IntegerRepr)):
-    def rtype_getitem((r_sta, r_int), hop):
-        s_int = hop.args_s[1]
-        index = s_int.const
-        return r_sta.redispatch_getfield(hop, index)
-
-
-def specialize_make_statvfs_result(hop):
-    r_StatvfsResult = hop.rtyper.getrepr(rposix_stat.s_StatvfsResult)
-    [v_result] = hop.inputargs(r_StatvfsResult.r_tuple)
-    hop.exception_cannot_occur()
-    return v_result
diff --git a/rpython/rlib/entrypoint.py b/rpython/rlib/entrypoint.py
--- a/rpython/rlib/entrypoint.py
+++ b/rpython/rlib/entrypoint.py
@@ -56,10 +56,11 @@
     """
     def deco(func):
         source = py.code.Source("""
+        from rpython.rlib import rgil
+
         def wrapper(%(args)s):
             # acquire the GIL
-            after = rffi.aroundstate.after
-            if after: after()
+            rgil.acquire()
             #
             rffi.stackcounter.stacks_counter += 1
             llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
@@ -78,8 +79,7 @@
                     assert 0 # dead code
             rffi.stackcounter.stacks_counter -= 1
             # release the GIL
-            before = rffi.aroundstate.before
-            if before: before()
+            rgil.release()
             #
             return res
         """ % {'args': ', '.join(['arg%d' % i for i in range(len(argtypes))])})
diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py
--- a/rpython/rlib/objectmodel.py
+++ b/rpython/rlib/objectmodel.py
@@ -599,22 +599,10 @@
 def hlinvoke(repr, llcallable, *args):
     raise TypeError("hlinvoke is meant to be rtyped and not called direclty")
 
-def invoke_around_extcall(before, after):
-    """Call before() before any external function call, and after() after.
-    At the moment only one pair before()/after() can be registered at a time.
+def is_in_callback():
+    """Returns True if we're currently in a callback *or* if there are
+    multiple threads around.
     """
-    # NOTE: the hooks are cleared during translation!  To be effective
-    # in a compiled program they must be set at run-time.
-    from rpython.rtyper.lltypesystem import rffi
-    rffi.aroundstate.before = before
-    rffi.aroundstate.after = after
-    # the 'aroundstate' contains regular function and not ll pointers to them,
-    # but let's call llhelper() anyway to force their annotation
-    from rpython.rtyper.annlowlevel import llhelper
-    llhelper(rffi.AroundFnPtr, before)
-    llhelper(rffi.AroundFnPtr, after)
-
-def is_in_callback():
     from rpython.rtyper.lltypesystem import rffi
     return rffi.stackcounter.stacks_counter > 1
 
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -186,7 +186,13 @@
     """
     if not obj:
         return False
-    return can_move(obj)
+    # XXX returning can_move() here might acidentally work for the use
+    # cases (see issue #2212), but this is not really safe.  Now we
+    # just return True for any non-NULL pointer, and too bad for the
+    # few extra 'cond_call_gc_wb'.  It could be improved e.g. to return
+    # False if 'obj' is a static prebuilt constant, or if we're not
+    # running incminimark...
+    return True #can_move(obj)
 
 def _heap_stats():
     raise NotImplementedError # can't be run directly
diff --git a/rpython/rlib/rgil.py b/rpython/rlib/rgil.py
--- a/rpython/rlib/rgil.py
+++ b/rpython/rlib/rgil.py
@@ -2,6 +2,7 @@
 from rpython.translator import cdir
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rtyper.extregistry import ExtRegistryEntry
 
 # these functions manipulate directly the GIL, whose definition does not
 # escape the C code itself
@@ -10,27 +11,135 @@
 eci = ExternalCompilationInfo(
     includes = ['src/thread.h'],
     separate_module_files = [translator_c_dir / 'src' / 'thread.c'],
-    include_dirs = [translator_c_dir])
+    include_dirs = [translator_c_dir],
+    post_include_bits = ['#define RPY_WITH_GIL'])
 
 llexternal = rffi.llexternal
 
 
-gil_allocate      = llexternal('RPyGilAllocate', [], lltype.Void,
+_gil_allocate = llexternal('RPyGilAllocate', [], lltype.Void,
+                           _nowrapper=True, sandboxsafe=True,
+                           compilation_info=eci)
+
+_gil_yield_thread = llexternal('RPyGilYieldThread', [], lltype.Signed,
                                _nowrapper=True, sandboxsafe=True,
                                compilation_info=eci)
 
-gil_yield_thread  = llexternal('RPyGilYieldThread', [], lltype.Signed,
+_gil_release      = llexternal('RPyGilRelease', [], lltype.Void,
                                _nowrapper=True, sandboxsafe=True,
                                compilation_info=eci)
 
-gil_release       = llexternal('RPyGilRelease', [], lltype.Void,
-                               _nowrapper=True, sandboxsafe=True,
-                               compilation_info=eci)
-
-gil_acquire       = llexternal('RPyGilAcquire', [], lltype.Void,
+_gil_acquire      = llexternal('RPyGilAcquire', [], lltype.Void,
                               _nowrapper=True, sandboxsafe=True,
                               compilation_info=eci)
 
 gil_fetch_fastgil = llexternal('RPyFetchFastGil', [], llmemory.Address,
                                _nowrapper=True, sandboxsafe=True,
                                compilation_info=eci)
+
+# ____________________________________________________________
+
+
+def invoke_after_thread_switch(callback):
+    """Invoke callback() after a thread switch.
+
+    This is a hook used by pypy.module.signal.  Several callbacks should
+    be easy to support (but not right now).
+
+    This function should be called from the translated RPython program
+    (i.e. *not* at module level!), but registers the callback
+    statically.  The exact point at which invoke_after_thread_switch()
+    is called has no importance: the callback() will be called anyway.
+    """
+    print "NOTE: invoke_after_thread_switch() is meant to be translated "
+    print "and not called directly.  Using some emulation."
+    global _emulated_after_thread_switch
+    _emulated_after_thread_switch = callback
+
+_emulated_after_thread_switch = None
+
+def _after_thread_switch():
+    """NOT_RPYTHON"""
+    if _emulated_after_thread_switch is not None:
+        _emulated_after_thread_switch()
+
+
+class Entry(ExtRegistryEntry):
+    _about_ = invoke_after_thread_switch
+
+    def compute_result_annotation(self, s_callback):
+        assert s_callback.is_constant()
+        callback = s_callback.const
+        bk = self.bookkeeper
+        translator = bk.annotator.translator
+        if hasattr(translator, '_rgil_invoke_after_thread_switch'):
+            assert translator._rgil_invoke_after_thread_switch == callback, (
+                "not implemented yet: several invoke_after_thread_switch()")
+        else:
+            translator._rgil_invoke_after_thread_switch = callback
+        bk.emulate_pbc_call("rgil.invoke_after_thread_switch", s_callback, [])
+
+    def specialize_call(self, hop):
+        # the actual call is not done here
+        hop.exception_cannot_occur()
+
+class Entry(ExtRegistryEntry):
+    _about_ = _after_thread_switch
+
+    def compute_result_annotation(self):
+        # the call has been emulated already in invoke_after_thread_switch()
+        pass
+
+    def specialize_call(self, hop):
+        translator = hop.rtyper.annotator.translator
+        if hasattr(translator, '_rgil_invoke_after_thread_switch'):
+            func = translator._rgil_invoke_after_thread_switch
+            graph = translator._graphof(func)
+            llfn = hop.rtyper.getcallable(graph)
+            c_callback = hop.inputconst(lltype.typeOf(llfn), llfn)
+            hop.exception_is_here()
+            hop.genop("direct_call", [c_callback])
+        else:
+            hop.exception_cannot_occur()
+
+
+def allocate():
+    _gil_allocate()
+
+def release():
+    # this function must not raise, in such a way that the exception
+    # transformer knows that it cannot raise!
+    _gil_release()
+release._gctransformer_hint_cannot_collect_ = True
+release._dont_reach_me_in_del_ = True
+
+def acquire():
+    from rpython.rlib import rthread
+    _gil_acquire()
+    rthread.gc_thread_run()
+    _after_thread_switch()
+acquire._gctransformer_hint_cannot_collect_ = True
+acquire._dont_reach_me_in_del_ = True
+
+# The _gctransformer_hint_cannot_collect_ hack is needed for
+# translations in which the *_external_call() functions are not inlined.
+# They tell the gctransformer not to save and restore the local GC
+# pointers in the shadow stack.  This is necessary because the GIL is
+# not held after the call to gil.release() or before the call
+# to gil.acquire().
+
+def yield_thread():
+    # explicitly release the gil, in a way that tries to give more
+    # priority to other threads (as opposed to continuing to run in
+    # the same thread).
+    if _gil_yield_thread():
+        from rpython.rlib import rthread
+        rthread.gc_thread_run()
+        _after_thread_switch()
+yield_thread._gctransformer_hint_close_stack_ = True
+yield_thread._dont_reach_me_in_del_ = True
+yield_thread._dont_inline_ = True
+
+# yield_thread() needs a different hint: _gctransformer_hint_close_stack_.
+# The *_external_call() functions are themselves called only from the rffi
+# module from a helper function that also has this hint.
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -234,9 +234,16 @@
     includes = ['io.h', 'sys/utime.h', 'sys/types.h']
     libraries = []
 else:
+    if sys.platform.startswith(('darwin', 'netbsd', 'openbsd')):
+        _ptyh = 'util.h'
+    elif sys.platform.startswith('freebsd'):
+        _ptyh = 'libutil.h'
+    else:
+        _ptyh = 'pty.h'
     includes = ['unistd.h',  'sys/types.h', 'sys/wait.h',
                 'utime.h', 'sys/time.h', 'sys/times.h',
-                'grp.h', 'dirent.h']
+                'grp.h', 'dirent.h', 'sys/stat.h', 'fcntl.h',
+                'signal.h', 'sys/utsname.h', _ptyh]
     libraries = ['util']
 eci = ExternalCompilationInfo(
     includes=includes,
@@ -1269,7 +1276,8 @@
 if not _WIN32:
     TMSP = lltype.Ptr(TMS)
     c_times = external('times', [TMSP], CLOCK_T,
-                        save_err=rffi.RFFI_SAVE_ERRNO)
+                        save_err=rffi.RFFI_SAVE_ERRNO |
+                                 rffi.RFFI_ZERO_ERRNO_BEFORE)
 
     # Here is a random extra platform parameter which is important.
     # Strictly speaking, this should probably be retrieved at runtime, not
@@ -1291,7 +1299,13 @@
     if not _WIN32:
         l_tmsbuf = lltype.malloc(TMSP.TO, flavor='raw')
         try:
-            result = handle_posix_error('times', c_times(l_tmsbuf))
+            # note: times() can return a negative value (or even -1)
+            # even if there is no error
+            result = widen(c_times(l_tmsbuf))
+            if result == -1:
+                errno = get_saved_errno()
+                if errno != 0:
+                    raise OSError(errno, 'times() failed')
             return (
                 rffi.cast(lltype.Signed, l_tmsbuf.c_tms_utime)
                                                / CLOCK_TICKS_PER_SECOND,
@@ -1607,7 +1621,8 @@
 #___________________________________________________________________
 
 c_chroot = external('chroot', [rffi.CCHARP], rffi.INT,
-                    save_err=rffi.RFFI_SAVE_ERRNO)
+                    save_err=rffi.RFFI_SAVE_ERRNO,
+                    macro=_MACRO_ON_POSIX)
 
 @replace_os_function('chroot')
 def chroot(path):
diff --git a/rpython/rlib/rposix_stat.py b/rpython/rlib/rposix_stat.py
--- a/rpython/rlib/rposix_stat.py
+++ b/rpython/rlib/rposix_stat.py
@@ -5,11 +5,16 @@
 
 import os, sys
 
+from rpython.flowspace.model import Constant
+from rpython.flowspace.operation import op
 from rpython.annotator import model as annmodel
 from rpython.rtyper import extregistry
 from rpython.tool.pairtype import pairtype
 from rpython.rtyper.tool import rffi_platform as platform
 from rpython.rtyper.llannotation import lltype_to_annotation
+from rpython.rtyper.rmodel import Repr
+from rpython.rtyper.rint import IntegerRepr
+from rpython.rtyper.error import TyperError
 
 from rpython.rlib.objectmodel import specialize
 from rpython.rtyper.lltypesystem import lltype, rffi
@@ -23,7 +28,7 @@
 _LINUX = sys.platform.startswith('linux')
 
 if _WIN32:
-    from rpython.rlib import rwin32 
+    from rpython.rlib import rwin32
     from rpython.rlib.rwin32file import make_win32_traits
 
 # Support for float times is here.
@@ -84,8 +89,7 @@
     knowntype = os.stat_result
 
     def rtyper_makerepr(self, rtyper):
-        from rpython.rlib import _rposix_repr
-        return _rposix_repr.StatResultRepr(rtyper)
+        return StatResultRepr(rtyper)
 
     def rtyper_makekey(self):
         return self.__class__,
@@ -111,6 +115,83 @@
         return s_reduced, stat_result_reduce, stat_result_recreate
 
 
+class __extend__(pairtype(SomeStatResult, annmodel.SomeInteger)):
+    def getitem((s_sta, s_int)):
+        assert s_int.is_constant(), "os.stat()[index]: index must be constant"
+        index = s_int.const
+        assert 0 <= index < N_INDEXABLE_FIELDS, "os.stat()[index] out of range"
+        name, TYPE = STAT_FIELDS[index]
+        return lltype_to_annotation(TYPE)
+
+
+class StatResultRepr(Repr):
+
+    def __init__(self, rtyper):
+        self.rtyper = rtyper
+        self.stat_field_indexes = {}
+        for i, (name, TYPE) in enumerate(STAT_FIELDS):
+            self.stat_field_indexes[name] = i
+
+        self.s_tuple = annmodel.SomeTuple(
+            [lltype_to_annotation(TYPE) for name, TYPE in STAT_FIELDS])
+        self.r_tuple = rtyper.getrepr(self.s_tuple)
+        self.lowleveltype = self.r_tuple.lowleveltype
+
+    def redispatch_getfield(self, hop, index):
+        rtyper = self.rtyper
+        s_index = rtyper.annotator.bookkeeper.immutablevalue(index)
+        hop2 = hop.copy()
+        spaceop = op.getitem(hop.args_v[0], Constant(index))
+        spaceop.result = hop.spaceop.result
+        hop2.spaceop = spaceop
+        hop2.args_v = spaceop.args
+        hop2.args_s = [self.s_tuple, s_index]
+        hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)]
+        return hop2.dispatch()
+
+    def rtype_getattr(self, hop):
+        s_attr = hop.args_s[1]
+        attr = s_attr.const
+        try:
+            index = self.stat_field_indexes[attr]
+        except KeyError:
+            raise TyperError("os.stat().%s: field not available" % (attr,))
+        return self.redispatch_getfield(hop, index)
+
+
+class __extend__(pairtype(StatResultRepr, IntegerRepr)):
+    def rtype_getitem((r_sta, r_int), hop):
+        s_int = hop.args_s[1]
+        index = s_int.const
+        return r_sta.redispatch_getfield(hop, index)
+
+s_StatResult = SomeStatResult()
+
+def make_stat_result(tup):
+    """Turn a tuple into an os.stat_result object."""
+    positional = tuple(
+        lltype.cast_primitive(TYPE, value) for value, (name, TYPE) in
+            zip(tup, STAT_FIELDS)[:N_INDEXABLE_FIELDS])
+    kwds = {}
+    for value, (name, TYPE) in zip(tup, STAT_FIELDS)[N_INDEXABLE_FIELDS:]:
+        kwds[name] = lltype.cast_primitive(TYPE, value)
+    return os.stat_result(positional, kwds)
+
+
+class MakeStatResultEntry(extregistry.ExtRegistryEntry):
+    _about_ = make_stat_result
+
+    def compute_result_annotation(self, s_tup):
+        return s_StatResult
+
+    def specialize_call(self, hop):
+        r_StatResult = hop.rtyper.getrepr(s_StatResult)
+        [v_result] = hop.inputargs(r_StatResult.r_tuple)
+        # no-op conversion from r_StatResult.r_tuple to r_StatResult
+        hop.exception_cannot_occur()
+        return v_result
+
+
 class SomeStatvfsResult(annmodel.SomeObject):
     if hasattr(os, 'statvfs_result'):
         knowntype = os.statvfs_result
@@ -118,8 +199,7 @@
         knowntype = None # will not be used
 
     def rtyper_makerepr(self, rtyper):
-        from rpython.rlib import _rposix_repr
-        return _rposix_repr.StatvfsResultRepr(rtyper)
+        return StatvfsResultRepr(rtyper)
 
     def rtyper_makekey(self):
         return self.__class__,
@@ -130,15 +210,6 @@
         return lltype_to_annotation(TYPE)
 
 
-class __extend__(pairtype(SomeStatResult, annmodel.SomeInteger)):
-    def getitem((s_sta, s_int)):
-        assert s_int.is_constant(), "os.stat()[index]: index must be constant"
-        index = s_int.const
-        assert 0 <= index < N_INDEXABLE_FIELDS, "os.stat()[index] out of range"
-        name, TYPE = STAT_FIELDS[index]
-        return lltype_to_annotation(TYPE)
-
-
 class __extend__(pairtype(SomeStatvfsResult, annmodel.SomeInteger)):
     def getitem((s_stat, s_int)):
         assert s_int.is_constant()
@@ -146,33 +217,55 @@
         return lltype_to_annotation(TYPE)
 
 
-s_StatResult = SomeStatResult()
 s_StatvfsResult = SomeStatvfsResult()
 
 
-def make_stat_result(tup):
-    """Turn a tuple into an os.stat_result object."""
-    positional = tup[:N_INDEXABLE_FIELDS]
-    kwds = {}
-    for i, name in enumerate(STAT_FIELD_NAMES[N_INDEXABLE_FIELDS:]):
-        kwds[name] = tup[N_INDEXABLE_FIELDS + i]
-    return os.stat_result(positional, kwds)
+class StatvfsResultRepr(Repr):
+    def __init__(self, rtyper):
+        self.rtyper = rtyper
+        self.statvfs_field_indexes = {}
+        for i, (name, TYPE) in enumerate(STATVFS_FIELDS):
+            self.statvfs_field_indexes[name] = i
+
+        self.s_tuple = annmodel.SomeTuple(
+            [lltype_to_annotation(TYPE) for name, TYPE in STATVFS_FIELDS])
+        self.r_tuple = rtyper.getrepr(self.s_tuple)
+        self.lowleveltype = self.r_tuple.lowleveltype
+
+    def redispatch_getfield(self, hop, index):
+        rtyper = self.rtyper
+        s_index = rtyper.annotator.bookkeeper.immutablevalue(index)
+        hop2 = hop.copy()
+        spaceop = op.getitem(hop.args_v[0], Constant(index))
+        spaceop.result = hop.spaceop.result
+        hop2.spaceop = spaceop
+        hop2.args_v = spaceop.args
+        hop2.args_s = [self.s_tuple, s_index]
+        hop2.args_r = [self.r_tuple, rtyper.getrepr(s_index)]
+        return hop2.dispatch()
+
+    def rtype_getattr(self, hop):
+        s_attr = hop.args_s[1]
+        attr = s_attr.const
+        try:
+            index = self.statvfs_field_indexes[attr]
+        except KeyError:
+            raise TyperError("os.statvfs().%s: field not available" % (attr,))
+        return self.redispatch_getfield(hop, index)
+
+
+class __extend__(pairtype(StatvfsResultRepr, IntegerRepr)):
+    def rtype_getitem((r_sta, r_int), hop):
+        s_int = hop.args_s[1]
+        index = s_int.const
+        return r_sta.redispatch_getfield(hop, index)
 
 
 def make_statvfs_result(tup):
-    return os.statvfs_result(tup)
-
-
-class MakeStatResultEntry(extregistry.ExtRegistryEntry):
-    _about_ = make_stat_result
-
-    def compute_result_annotation(self, s_tup):
-        return s_StatResult
-
-    def specialize_call(self, hop):
-        from rpython.rlib import _rposix_repr
-        return _rposix_repr.specialize_make_stat_result(hop)
-
+    args = tuple(
+        lltype.cast_primitive(TYPE, value) for value, (name, TYPE) in
+            zip(tup, STATVFS_FIELDS))
+    return os.statvfs_result(args)
 
 class MakeStatvfsResultEntry(extregistry.ExtRegistryEntry):
     _about_ = make_statvfs_result
@@ -181,8 +274,10 @@
         return s_StatvfsResult
 
     def specialize_call(self, hop):
-        from rpython.rlib import _rposix_repr
-        return _rposix_repr.specialize_make_statvfs_result(hop)
+        r_StatvfsResult = hop.rtyper.getrepr(s_StatvfsResult)
+        [v_result] = hop.inputargs(r_StatvfsResult.r_tuple)
+        hop.exception_cannot_occur()
+        return v_result
 
 # ____________________________________________________________
 #
diff --git a/rpython/rlib/rthread.py b/rpython/rlib/rthread.py
--- a/rpython/rlib/rthread.py
+++ b/rpython/rlib/rthread.py
@@ -79,7 +79,12 @@
 
 @specialize.arg(0)
 def ll_start_new_thread(func):
+    from rpython.rlib import rgil
     _check_thread_enabled()
+    rgil.allocate()
+    # ^^^ convenience: any RPython program which uses explicitly
+    # rthread.start_new_thread() will initialize the GIL at that
+    # point.
     ident = c_thread_start(func)
     if ident == -1:
         raise error("can't start new thread")
diff --git a/rpython/rlib/rtime.py b/rpython/rlib/rtime.py
--- a/rpython/rlib/rtime.py
+++ b/rpython/rlib/rtime.py
@@ -165,9 +165,11 @@
     globals().update(rffi_platform.configure(CConfigForClockGetTime))
     TIMESPEC = TIMESPEC
     CLOCK_PROCESS_CPUTIME_ID = CLOCK_PROCESS_CPUTIME_ID
+    eci_with_lrt = eci.merge(ExternalCompilationInfo(libraries=['rt']))
     c_clock_gettime = external('clock_gettime',
                                [lltype.Signed, lltype.Ptr(TIMESPEC)],
-                               rffi.INT, releasegil=False)
+                               rffi.INT, releasegil=False,
+                               compilation_info=eci_with_lrt)
 else:
     RUSAGE = RUSAGE
     RUSAGE_SELF = RUSAGE_SELF or 0
diff --git a/rpython/rlib/test/test_rgil.py b/rpython/rlib/test/test_rgil.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/test/test_rgil.py
@@ -0,0 +1,47 @@
+from rpython.rlib import rgil
+from rpython.translator.c.test.test_standalone import StandaloneTests
+
+
+class BaseTestGIL(StandaloneTests):
+
+    def test_simple(self):
+        def main(argv):
+            rgil.release()
+            # don't have the GIL here
+            rgil.acquire()
+            rgil.yield_thread()
+            print "OK"   # there is also a release/acquire pair here
+            return 0
+
+        main([])
+
+        t, cbuilder = self.compile(main)
+        data = cbuilder.cmdexec('')
+        assert data == "OK\n"
+
+    def test_after_thread_switch(self):
+        class Foo:
+            pass
+        foo = Foo()
+        foo.counter = 0
+        def seeme():
+            foo.counter += 1
+        def main(argv):
+            rgil.invoke_after_thread_switch(seeme)
+            print "Test"     # one release/acquire pair here
+            print foo.counter
+            print foo.counter
+            return 0
+
+        t, cbuilder = self.compile(main)
+        data = cbuilder.cmdexec('')
+        assert data == "Test\n1\n2\n"
+
+
+class TestGILAsmGcc(BaseTestGIL):
+    gc = 'minimark'
+    gcrootfinder = 'asmgcc'
+
+class TestGILShadowStack(BaseTestGIL):
+    gc = 'minimark'
+    gcrootfinder = 'shadowstack'
diff --git a/rpython/rlib/test/test_rposix_stat.py b/rpython/rlib/test/test_rposix_stat.py
--- a/rpython/rlib/test/test_rposix_stat.py
+++ b/rpython/rlib/test/test_rposix_stat.py
@@ -32,7 +32,11 @@
         fname = udir.join('test_stat_large_number.txt')
         fname.ensure()
         t1 = 5000000000.0
-        os.utime(str(fname), (t1, t1))
+        try:
+            os.utime(str(fname), (t1, t1))
+        except OverflowError:
+            py.test.skip("This platform doesn't support setting stat times "
+                         "to large values")
         assert rposix_stat.stat(str(fname)).st_mtime == t1
 
     @py.test.mark.skipif(not hasattr(os, 'statvfs'),
diff --git a/rpython/rlib/test/test_rsocket.py b/rpython/rlib/test/test_rsocket.py
--- a/rpython/rlib/test/test_rsocket.py
+++ b/rpython/rlib/test/test_rsocket.py
@@ -143,7 +143,7 @@
 
 
 def test_simple_tcp():
-    import thread
+    from rpython.rlib import rthread
     sock = RSocket()
     try_ports = [1023] + range(20000, 30000, 437)
     for port in try_ports:
@@ -169,14 +169,14 @@
             connected[0] = True
         finally:
             lock.release()
-    lock = thread.allocate_lock()
-    lock.acquire()
-    thread.start_new_thread(connecting, ())
+    lock = rthread.allocate_lock()
+    lock.acquire(True)
+    rthread.start_new_thread(connecting, ())
     print 'waiting for connection'
     fd1, addr2 = sock.accept()
     s1 = RSocket(fd=fd1)
     print 'connection accepted'
-    lock.acquire()
+    lock.acquire(True)
     assert connected[0]
     print 'connecting side knows that the connection was accepted too'
     assert addr.eq(s2.getpeername())
@@ -188,7 +188,9 @@
     buf = s2.recv(100)
     assert buf == '?'
     print 'received ok'
-    thread.start_new_thread(s2.sendall, ('x'*50000,))
+    def sendstuff():
+        s2.sendall('x'*50000)
+    rthread.start_new_thread(sendstuff, ())
     buf = ''
     while len(buf) < 50000:
         data = s1.recv(50100)
diff --git a/rpython/rlib/test/test_rthread.py b/rpython/rlib/test/test_rthread.py
--- a/rpython/rlib/test/test_rthread.py
+++ b/rpython/rlib/test/test_rthread.py
@@ -5,13 +5,6 @@
 from rpython.rtyper.lltypesystem import lltype, rffi
 import py
 
-def setup_module(mod):
-    # Hack to avoid a deadlock if the module is run after other test files :-(
-    # In this module, we assume that rthread.start_new_thread() is not
-    # providing us with a GIL equivalent, except in test_gc_locking
-    # which installs its own aroundstate.
-    rffi.aroundstate._cleanup_()
-
 def test_lock():
     l = allocate_lock()
     ok1 = l.acquire(True)
@@ -31,6 +24,7 @@
         py.test.fail("Did not raise")
 
 def test_tlref_untranslated():
+    import thread
     class FooBar(object):
         pass
     t = ThreadLocalReference(FooBar)
@@ -43,7 +37,7 @@
         time.sleep(0.2)
         results.append(t.get() is x)
     for i in range(5):
-        start_new_thread(subthread, ())
+        thread.start_new_thread(subthread, ())
     time.sleep(0.5)
     assert results == [True] * 15
 
@@ -99,7 +93,6 @@
 
     def test_gc_locking(self):
         import time
-        from rpython.rlib.objectmodel import invoke_around_extcall
         from rpython.rlib.debug import ll_assert
 
         class State:
@@ -123,17 +116,6 @@
                 ll_assert(j == self.j, "2: bad j")
             run._dont_inline_ = True
 
-        def before_extcall():
-            release_NOAUTO(state.gil)
-        before_extcall._gctransformer_hint_cannot_collect_ = True
-        # ^^^ see comments in gil.py about this hint
-
-        def after_extcall():
-            acquire_NOAUTO(state.gil, True)
-            gc_thread_run()
-        after_extcall._gctransformer_hint_cannot_collect_ = True
-        # ^^^ see comments in gil.py about this hint
-
         def bootstrap():
             # after_extcall() is called before we arrive here.
             # We can't just acquire and release the GIL manually here,
@@ -154,14 +136,9 @@
             start_new_thread(bootstrap, ())
 
         def f():
-            state.gil = allocate_ll_lock()
-            acquire_NOAUTO(state.gil, True)
             state.bootstrapping = allocate_lock()
             state.answers = []
             state.finished = 0
-            # the next line installs before_extcall() and after_extcall()
-            # to be called automatically around external function calls.
-            invoke_around_extcall(before_extcall, after_extcall)
 
             g(10, 1)
             done = False
@@ -179,10 +156,7 @@
             return len(state.answers)
 
         expected = 89
-        try:
-            fn = self.getcompiled(f, [])
-        finally:
-            rffi.aroundstate._cleanup_()
+        fn = self.getcompiled(f, [])
         answers = fn()
         assert answers == expected
 
diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -169,9 +169,9 @@
 
         argnames = ', '.join(['a%d' % i for i in range(len(args))])
         source = py.code.Source("""
+            from rpython.rlib import rgil
             def call_external_function(%(argnames)s):
-                before = aroundstate.before
-                if before: before()
+                rgil.release()
                 # NB. it is essential that no exception checking occurs here!
                 if %(save_err)d:
                     from rpython.rlib import rposix
@@ -180,12 +180,10 @@
                 if %(save_err)d:
                     from rpython.rlib import rposix
                     rposix._errno_after(%(save_err)d)
-                after = aroundstate.after
-                if after: after()
+                rgil.acquire()
                 return res
         """ % locals())
-        miniglobals = {'aroundstate': aroundstate,
-                       'funcptr':     funcptr,
+        miniglobals = {'funcptr':     funcptr,
                        '__name__':    __name__, # for module name propagation
                        }
         exec source.compile() in miniglobals
@@ -205,7 +203,7 @@
         # don't inline, as a hack to guarantee that no GC pointer is alive
         # anywhere in call_external_function
     else:
-        # if we don't have to invoke the aroundstate, we can just call
+        # if we don't have to invoke the GIL handling, we can just call
         # the low-level function pointer carelessly
         if macro is None and save_err == RFFI_ERR_NONE:
             call_external_function = funcptr
@@ -270,13 +268,10 @@
                     freeme = arg
             elif _isfunctype(TARGET) and not _isllptr(arg):
                 # XXX pass additional arguments
-                if invoke_around_handlers:
-                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
-                                                             callbackholder,
-                                                             aroundstate))
-                else:
-                    arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
-                                                             callbackholder))
+                use_gil = invoke_around_handlers
+                arg = llhelper(TARGET, _make_wrapper_for(TARGET, arg,
+                                                         callbackholder,
+                                                         use_gil))
             else:
                 SOURCE = lltype.typeOf(arg)
                 if SOURCE != TARGET:
@@ -315,7 +310,7 @@
     def __init__(self):
         self.callbacks = {}
 
-def _make_wrapper_for(TP, callable, callbackholder=None, aroundstate=None):
+def _make_wrapper_for(TP, callable, callbackholder, use_gil):
     """ Function creating wrappers for callbacks. Note that this is
     cheating as we assume constant callbacks and we just memoize wrappers
     """
@@ -330,11 +325,13 @@
         callbackholder.callbacks[callable] = True
     args = ', '.join(['a%d' % i for i in range(len(TP.TO.ARGS))])
     source = py.code.Source(r"""
+        rgil = None
+        if use_gil:
+            from rpython.rlib import rgil
+
         def wrapper(%(args)s):    # no *args - no GIL for mallocing the tuple
-            if aroundstate is not None:
-                after = aroundstate.after
-                if after:
-                    after()
+            if rgil is not None:
+                rgil.acquire()
             # from now on we hold the GIL
             stackcounter.stacks_counter += 1
             llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
@@ -349,13 +346,11 @@
                     traceback.print_exc()
                 result = errorcode
             stackcounter.stacks_counter -= 1
-            if aroundstate is not None:
-                before = aroundstate.before
-                if before:
-                    before()
+            if rgil is not None:
+                rgil.release()
             # here we don't hold the GIL any more. As in the wrapper() produced
             # by llexternal, it is essential that no exception checking occurs
-            # after the call to before().
+            # after the call to rgil.release().
             return result
     """ % locals())
     miniglobals = locals().copy()
@@ -369,13 +364,6 @@
 
 AroundFnPtr = lltype.Ptr(lltype.FuncType([], lltype.Void))
 
-class AroundState:
-    def _cleanup_(self):
-        self.before = None        # or a regular RPython function
-        self.after = None         # or a regular RPython function
-aroundstate = AroundState()
-aroundstate._cleanup_()
-
 class StackCounter:
     def _cleanup_(self):
         self.stacks_counter = 0     # number of "stack pieces": callbacks
diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py b/rpython/rtyper/lltypesystem/test/test_rffi.py
--- a/rpython/rtyper/lltypesystem/test/test_rffi.py
+++ b/rpython/rtyper/lltypesystem/test/test_rffi.py
@@ -688,42 +688,6 @@
 
         assert interpret(f, []) == 4
 
-    def test_around_extcall(self):
-        if sys.platform == "win32":
-            py.test.skip('No pipes on windows')
-        import os
-        from rpython.annotator import model as annmodel
-        from rpython.rlib.objectmodel import invoke_around_extcall
-        from rpython.rtyper.extfuncregistry import register_external
-        read_fd, write_fd = os.pipe()
-        try:
-            # we need an external function that is not going to get wrapped around
-            # before()/after() calls, in order to call it from before()/after()...
-            def mywrite(s):
-                os.write(write_fd, s)
-            def llimpl(s):
-                s = ''.join(s.chars)
-                os.write(write_fd, s)
-            register_external(mywrite, [str], annmodel.s_None, 'll_mywrite',
-                              llfakeimpl=llimpl, sandboxsafe=True)
-
-            def before():
-                mywrite("B")
-            def after():
-                mywrite("A")
-            def f():
-                os.write(write_fd, "-")
-                invoke_around_extcall(before, after)
-                os.write(write_fd, "E")
-
-            interpret(f, [])
-            data = os.read(read_fd, 99)
-            assert data == "-BEA"
-
-        finally:
-            os.close(write_fd)
-            os.close(read_fd)
-
     def test_external_callable(self):
         """ Try to call some llexternal function with llinterp
         """
diff --git a/rpython/translator/backendopt/test/test_malloc.py b/rpython/translator/backendopt/test/test_malloc.py
--- a/rpython/translator/backendopt/test/test_malloc.py
+++ b/rpython/translator/backendopt/test/test_malloc.py
@@ -159,7 +159,7 @@
 
             def __del__(self):
                 delcalls[0] += 1
-                os.write(1, "__del__\n")
+                #os.write(1, "__del__\n")
 
         def f(x=int):
             a = A()
diff --git a/rpython/translator/c/src/entrypoint.c b/rpython/translator/c/src/entrypoint.c
--- a/rpython/translator/c/src/entrypoint.c
+++ b/rpython/translator/c/src/entrypoint.c
@@ -33,6 +33,10 @@
 #  include <io.h>
 #endif
 
+#ifdef RPY_WITH_GIL
+# include <src/thread.h>
+#endif
+
 
 RPY_EXTERN
 int pypy_main_function(int argc, char *argv[])
@@ -46,6 +50,14 @@
     _setmode(1, _O_BINARY);
 #endif
 
+#ifdef RPY_WITH_GIL
+    /* Note that the GIL's mutexes are not automatically made; if the
+       program starts threads, it needs to call rgil.gil_allocate().
+       RPyGilAcquire() still works without that, but crash if it finds
+       that it really needs to wait on a mutex. */
+    RPyGilAcquire();
+#endif
+
 #ifdef PYPY_USE_ASMGCC
     pypy_g_rpython_rtyper_lltypesystem_rffi_StackCounter.sc_inst_stacks_counter++;
 #endif
@@ -82,6 +94,10 @@
 
     pypy_malloc_counters_results();
 
+#ifdef RPY_WITH_GIL
+    RPyGilRelease();
+#endif
+
     return exitcode;
 
  memory_out:
diff --git a/rpython/translator/c/src/mem.c b/rpython/translator/c/src/mem.c
--- a/rpython/translator/c/src/mem.c
+++ b/rpython/translator/c/src/mem.c
@@ -120,11 +120,8 @@
         got += 1;
         fd = ((void* *) (((char *)fd) + sizeof(void*)))[0];
     }
-    if (rpy_fastgil != 1) {
-        RPyAssert(rpy_fastgil != 0,
-                          "pypy_check_stack_count doesn't have the GIL");
-        got++;  /* <= the extra one currently stored in rpy_fastgil */
-    }
+    RPyAssert(rpy_fastgil == 1,
+              "pypy_check_stack_count doesn't have the GIL");
     RPyAssert(got == stacks_counter - 1,
               "bad stacks_counter or non-closed stacks around");
 # endif
diff --git a/rpython/translator/c/src/thread.h b/rpython/translator/c/src/thread.h
--- a/rpython/translator/c/src/thread.h
+++ b/rpython/translator/c/src/thread.h
@@ -28,7 +28,8 @@
 
 RPY_EXTERN void RPyGilAllocate(void);
 RPY_EXTERN long RPyGilYieldThread(void);
-RPY_EXTERN void RPyGilAcquire(void);
+RPY_EXTERN void RPyGilAcquireSlowPath(long);


More information about the pypy-commit mailing list