Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r75444:05783c6d4ff1
Date: 2015-01-19 23:53 +0100
http://bitbucket.org/pypy/pypy/changeset/05783c6d4ff1/

Log:    hg merge errno-again

        Changes how errno, GetLastError, and WSAGetLastError are handled.
        The idea is to tie reading the error status as close as possible to
        the external function call. This fixes some bugs, both of the very
        rare kind (e.g. errno on Linux might in theory be overwritten by
        mmap(), called rarely during major GCs, if such a major GC occurs at
        exactly the wrong time), and some of the less rare kind
        (particularly on Windows tests).

        Now the rffi.llexternal() declaration must specify what kind of
        errno support it needs ('save_err' argument), and the
        rposix.get_errno() function has been consequently renamed to
        get_saved_errno(). We need to adapt all RPython libraries that use
        rposix.get_errno(), which is why the function was renamed. Same
        with rwin32.GetLastError().

diff too long, truncating to 2000 out of 6200 lines

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -486,10 +486,10 @@
                              w_exception_class=w_exception_class)
 wrap_oserror._annspecialcase_ = 'specialize:arg(3)'
 
-def exception_from_errno(space, w_type):
-    from rpython.rlib.rposix import get_errno
+def exception_from_saved_errno(space, w_type):
+    from rpython.rlib.rposix import get_saved_errno
 
-    errno = get_errno()
+    errno = get_saved_errno()
     msg = os.strerror(errno)
     w_error = space.call_function(w_type, space.wrap(errno), space.wrap(msg))
     return OperationError(w_type, w_error)
diff --git a/pypy/module/__pypy__/interp_time.py 
b/pypy/module/__pypy__/interp_time.py
--- a/pypy/module/__pypy__/interp_time.py
+++ b/pypy/module/__pypy__/interp_time.py
@@ -1,7 +1,7 @@
 from __future__ import with_statement
 import sys
 
-from pypy.interpreter.error import exception_from_errno
+from pypy.interpreter.error import exception_from_saved_errno
 from pypy.interpreter.gateway import unwrap_spec
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rtyper.tool import rffi_platform
@@ -48,11 +48,13 @@
 
     c_clock_gettime = rffi.llexternal("clock_gettime",
         [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
-        compilation_info=CConfig._compilation_info_, releasegil=False
+        compilation_info=CConfig._compilation_info_, releasegil=False,
+        save_err=rffi.RFFI_SAVE_ERRNO
     )
     c_clock_getres = rffi.llexternal("clock_getres",
         [lltype.Signed, lltype.Ptr(TIMESPEC)], rffi.INT,
-        compilation_info=CConfig._compilation_info_, releasegil=False
+        compilation_info=CConfig._compilation_info_, releasegil=False,
+        save_err=rffi.RFFI_SAVE_ERRNO
     )
 
     @unwrap_spec(clk_id="c_int")
@@ -60,7 +62,7 @@
         with lltype.scoped_alloc(TIMESPEC) as tp:
             ret = c_clock_gettime(clk_id, tp)
             if ret != 0:
-                raise exception_from_errno(space, space.w_IOError)
+                raise exception_from_saved_errno(space, space.w_IOError)
             return space.wrap(int(tp.c_tv_sec) + 1e-9 * int(tp.c_tv_nsec))
 
     @unwrap_spec(clk_id="c_int")
@@ -68,5 +70,5 @@
         with lltype.scoped_alloc(TIMESPEC) as tp:
             ret = c_clock_getres(clk_id, tp)
             if ret != 0:
-                raise exception_from_errno(space, space.w_IOError)
+                raise exception_from_saved_errno(space, space.w_IOError)
             return space.wrap(int(tp.c_tv_sec) + 1e-9 * int(tp.c_tv_nsec))
diff --git a/pypy/module/_cffi_backend/ccallback.py 
b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -160,7 +160,7 @@
 
 
 @jit.jit_callback("CFFI")
-def invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata):
+def _invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata):
     """ Callback specification.
     ffi_cif - something ffi specific, don't care
     ll_args - rffi.VOIDPP - pointer to array of pointers to args
@@ -168,7 +168,6 @@
     ll_userdata - a special structure which holds necessary information
                   (what the real callback is for example), casted to VOIDP
     """
-    e = cerrno.get_real_errno()
     ll_res = rffi.cast(rffi.CCHARP, ll_res)
     unique_id = rffi.cast(lltype.Signed, ll_userdata)
     callback = global_callback_mapping.get(unique_id)
@@ -185,12 +184,9 @@
         return
     #
     must_leave = False
-    ec = None
     space = callback.space
     try:
         must_leave = space.threadlocals.try_enter_thread(space)
-        ec = cerrno.get_errno_container(space)
-        cerrno.save_errno_into(ec, e)
         extra_line = ''
         try:
             w_res = callback.invoke(ll_args)
@@ -212,5 +208,8 @@
         callback.write_error_return_value(ll_res)
     if must_leave:
         space.threadlocals.leave_thread(space)
-    if ec is not None:
-        cerrno.restore_errno_from(ec)
+
+def invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata):
+    cerrno._errno_after(rffi.RFFI_ERR_ALL)
+    _invoke_callback(ffi_cif, ll_res, ll_args, ll_userdata)
+    cerrno._errno_before(rffi.RFFI_ERR_ALL)
diff --git a/pypy/module/_cffi_backend/cerrno.py 
b/pypy/module/_cffi_backend/cerrno.py
--- a/pypy/module/_cffi_backend/cerrno.py
+++ b/pypy/module/_cffi_backend/cerrno.py
@@ -2,7 +2,6 @@
 
 from rpython.rlib import rposix
 
-from pypy.interpreter.executioncontext import ExecutionContext
 from pypy.interpreter.gateway import unwrap_spec
 
 WIN32 = sys.platform == 'win32'
@@ -10,43 +9,22 @@
     from rpython.rlib import rwin32
 
 
-ExecutionContext._cffi_saved_errno = 0
-ExecutionContext._cffi_saved_LastError = 0
-
-
-def get_errno_container(space):
-    return space.getexecutioncontext()
-
-get_real_errno = rposix.get_errno
-
-
-def restore_errno_from(ec):
-    if WIN32:
-        rwin32.SetLastError(ec._cffi_saved_LastError)
-    rposix.set_errno(ec._cffi_saved_errno)
-
-def save_errno_into(ec, errno):
-    ec._cffi_saved_errno = errno
-    if WIN32:
-        ec._cffi_saved_LastError = rwin32.GetLastError()
-
+_errno_before = rposix._errno_before
+_errno_after  = rposix._errno_after
 
 def get_errno(space):
-    ec = get_errno_container(space)
-    return space.wrap(ec._cffi_saved_errno)
+    return space.wrap(rposix.get_saved_errno())
 
 @unwrap_spec(errno=int)
 def set_errno(space, errno):
-    ec = get_errno_container(space)
-    ec._cffi_saved_errno = errno
+    rposix.set_saved_errno(errno)
 
 # ____________________________________________________________
 
 @unwrap_spec(code=int)
 def getwinerror(space, code=-1):
-    from rpython.rlib.rwin32 import FormatError
+    from rpython.rlib.rwin32 import GetLastError_saved, FormatError
     if code == -1:
-        ec = get_errno_container(space)
-        code = ec._cffi_saved_LastError
+        code = GetLastError_saved()
     message = FormatError(code)
     return space.newtuple([space.wrap(code), space.wrap(message)])
diff --git a/pypy/module/_cffi_backend/ctypefunc.py 
b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -155,13 +155,9 @@
                     # argtype is a pointer type, and w_obj a list/tuple/str
                     mustfree_max_plus_1 = i + 1
 
-            ec = cerrno.get_errno_container(space)
-            cerrno.restore_errno_from(ec)
             jit_libffi.jit_ffi_call(cif_descr,
                                     rffi.cast(rffi.VOIDP, funcaddr),
                                     buffer)
-            e = cerrno.get_real_errno()
-            cerrno.save_errno_into(ec, e)
 
             resultdata = rffi.ptradd(buffer, cif_descr.exchange_result)
             w_res = self.ctitem.copy_and_convert_to_object(resultdata)
diff --git a/pypy/module/_cffi_backend/ctypeptr.py 
b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -347,7 +347,8 @@
 # ____________________________________________________________
 
 
-rffi_fdopen = rffi.llexternal("fdopen", [rffi.INT, rffi.CCHARP], rffi.CCHARP)
+rffi_fdopen = rffi.llexternal("fdopen", [rffi.INT, rffi.CCHARP], rffi.CCHARP,
+                              save_err=rffi.RFFI_SAVE_ERRNO)
 rffi_setbuf = rffi.llexternal("setbuf", [rffi.CCHARP, rffi.CCHARP], 
lltype.Void)
 rffi_fclose = rffi.llexternal("fclose", [rffi.CCHARP], rffi.INT)
 
@@ -357,7 +358,7 @@
     def __init__(self, fd, mode):
         self.llf = rffi_fdopen(fd, mode)
         if not self.llf:
-            raise OSError(rposix.get_errno(), "fdopen failed")
+            raise OSError(rposix.get_saved_errno(), "fdopen failed")
         rffi_setbuf(self.llf, lltype.nullptr(rffi.CCHARP.TO))
 
     def close(self):
diff --git a/pypy/module/_io/interp_bufferedio.py 
b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -19,10 +19,16 @@
 
 
 def make_write_blocking_error(space, written):
+    # XXX CPython reads 'errno' here.  I *think* it doesn't make sense,
+    # because we might reach this point after calling a write() method
+    # that may be overridden by the user, if that method returns None.
+    # In that case what we get is a potentially nonsense errno.  But
+    # we'll use get_saved_errno() anyway, and hope (like CPython does)
+    # that we're getting a reasonable value at this point.
     w_type = space.gettypeobject(W_BlockingIOError.typedef)
     w_value = space.call_function(
         w_type,
-        space.wrap(rposix.get_errno()),
+        space.wrap(rposix.get_saved_errno()),
         space.wrap("write could not complete without blocking"),
         space.wrap(written))
     return OperationError(w_type, w_value)
diff --git a/pypy/module/_locale/interp_locale.py 
b/pypy/module/_locale/interp_locale.py
--- a/pypy/module/_locale/interp_locale.py
+++ b/pypy/module/_locale/interp_locale.py
@@ -300,7 +300,8 @@
         return space.wrap(result)
 
     _bindtextdomain = rlocale.external('bindtextdomain', [rffi.CCHARP, 
rffi.CCHARP],
-                                                                rffi.CCHARP)
+                                                                rffi.CCHARP,
+                                       save_err=rffi.RFFI_SAVE_ERRNO)
 
     @unwrap_spec(domain=str)
     def bindtextdomain(space, domain, w_dir):
@@ -325,7 +326,7 @@
                 rffi.free_charp(dir_c)
 
         if not dirname:
-            errno = rposix.get_errno()
+            errno = rposix.get_saved_errno()
             raise OperationError(space.w_OSError, space.wrap(errno))
         return space.wrap(rffi.charp2str(dirname))
 
diff --git a/pypy/module/_multiprocessing/interp_connection.py 
b/pypy/module/_multiprocessing/interp_connection.py
--- a/pypy/module/_multiprocessing/interp_connection.py
+++ b/pypy/module/_multiprocessing/interp_connection.py
@@ -406,7 +406,7 @@
                 size, written_ptr, rffi.NULL)
 
             if (result == 0 and
-                rwin32.GetLastError() == ERROR_NO_SYSTEM_RESOURCES):
+                rwin32.GetLastError_saved() == ERROR_NO_SYSTEM_RESOURCES):
                 raise oefmt(space.w_ValueError,
                             "Cannot send %d bytes over connection", size)
         finally:
@@ -430,7 +430,7 @@
             if result:
                 return intmask(read_ptr[0]), lltype.nullptr(rffi.CCHARP.TO)
 
-            err = rwin32.GetLastError()
+            err = rwin32.GetLastError_saved()
             if err == ERROR_BROKEN_PIPE:
                 raise OperationError(space.w_EOFError, space.w_None)
             elif err != ERROR_MORE_DATA:
@@ -441,7 +441,7 @@
                                   lltype.nullptr(rwin32.LPDWORD.TO),
                                   lltype.nullptr(rwin32.LPDWORD.TO),
                                   left_ptr):
-                raise wrap_windowserror(space, rwin32.lastWindowsError())
+                raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
 
             length = intmask(read_ptr[0] + left_ptr[0])
             if length > maxlength: # bad message, close connection
@@ -460,7 +460,7 @@
                                read_ptr, rffi.NULL)
             if not result:
                 rffi.free_charp(newbuf)
-                raise wrap_windowserror(space, rwin32.lastWindowsError())
+                raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
 
             assert read_ptr[0] == left_ptr[0]
             return length, newbuf
@@ -480,7 +480,7 @@
                                   lltype.nullptr(rwin32.LPDWORD.TO),
                                   bytes_ptr,
                                   lltype.nullptr(rwin32.LPDWORD.TO)):
-                raise wrap_windowserror(space, rwin32.lastWindowsError())
+                raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
             bytes = bytes_ptr[0]
         finally:
             lltype.free(bytes_ptr, flavor='raw')
@@ -506,7 +506,8 @@
                                       lltype.nullptr(rwin32.LPDWORD.TO),
                                       bytes_ptr,
                                       lltype.nullptr(rwin32.LPDWORD.TO)):
-                    raise wrap_windowserror(space, rwin32.lastWindowsError())
+                    raise wrap_windowserror(space,
+                                            rwin32.lastSavedWindowsError())
                 bytes = bytes_ptr[0]
             finally:
                 lltype.free(bytes_ptr, flavor='raw')
diff --git a/pypy/module/_multiprocessing/interp_semaphore.py 
b/pypy/module/_multiprocessing/interp_semaphore.py
--- a/pypy/module/_multiprocessing/interp_semaphore.py
+++ b/pypy/module/_multiprocessing/interp_semaphore.py
@@ -26,12 +26,14 @@
 
     _CreateSemaphore = rwin32.winexternal(
         'CreateSemaphoreA', [rffi.VOIDP, rffi.LONG, rffi.LONG, rwin32.LPCSTR],
-        rwin32.HANDLE)
-    _CloseHandle = rwin32.winexternal('CloseHandle', [rwin32.HANDLE],
+        rwin32.HANDLE,
+        save_err=rffi.RFFI_FULL_LASTERROR)
+    _CloseHandle_no_errno = rwin32.winexternal('CloseHandle', [rwin32.HANDLE],
         rwin32.BOOL, releasegil=False)
     _ReleaseSemaphore = rwin32.winexternal(
         'ReleaseSemaphore', [rwin32.HANDLE, rffi.LONG, rffi.LONGP],
-        rwin32.BOOL)
+        rwin32.BOOL,
+        save_err=rffi.RFFI_SAVE_LASTERROR)
 
 else:
     from rpython.rlib import rposix
@@ -81,51 +83,61 @@
 
     _sem_open = external('sem_open',
                          [rffi.CCHARP, rffi.INT, rffi.INT, rffi.UINT],
-                         SEM_T)
+                         SEM_T, save_err=rffi.RFFI_SAVE_ERRNO)
     # sem_close is releasegil=False to be able to use it in the __del__
-    _sem_close = external('sem_close', [SEM_T], rffi.INT, releasegil=False)
-    _sem_unlink = external('sem_unlink', [rffi.CCHARP], rffi.INT)
-    _sem_wait = external('sem_wait', [SEM_T], rffi.INT)
-    _sem_trywait = external('sem_trywait', [SEM_T], rffi.INT)
-    _sem_post = external('sem_post', [SEM_T], rffi.INT)
-    _sem_getvalue = external('sem_getvalue', [SEM_T, rffi.INTP], rffi.INT)
+    _sem_close_no_errno = external('sem_close', [SEM_T], rffi.INT,
+                                   releasegil=False)
+    _sem_close = external('sem_close', [SEM_T], rffi.INT, releasegil=False,
+                          save_err=rffi.RFFI_SAVE_ERRNO)
+    _sem_unlink = external('sem_unlink', [rffi.CCHARP], rffi.INT,
+                           save_err=rffi.RFFI_SAVE_ERRNO)
+    _sem_wait = external('sem_wait', [SEM_T], rffi.INT,
+                         save_err=rffi.RFFI_SAVE_ERRNO)
+    _sem_trywait = external('sem_trywait', [SEM_T], rffi.INT,
+                            save_err=rffi.RFFI_SAVE_ERRNO)
+    _sem_post = external('sem_post', [SEM_T], rffi.INT,
+                         save_err=rffi.RFFI_SAVE_ERRNO)
+    _sem_getvalue = external('sem_getvalue', [SEM_T, rffi.INTP], rffi.INT,
+                             save_err=rffi.RFFI_SAVE_ERRNO)
 
-    _gettimeofday = external('gettimeofday', [TIMEVALP, rffi.VOIDP], rffi.INT)
+    _gettimeofday = external('gettimeofday', [TIMEVALP, rffi.VOIDP], rffi.INT,
+                             save_err=rffi.RFFI_SAVE_ERRNO)
 
     _select = external('select', [rffi.INT, rffi.VOIDP, rffi.VOIDP, rffi.VOIDP,
-                                                          TIMEVALP], rffi.INT)
+                                                          TIMEVALP], rffi.INT,
+                       save_err=rffi.RFFI_SAVE_ERRNO)
 
     @jit.dont_look_inside
     def sem_open(name, oflag, mode, value):
         res = _sem_open(name, oflag, mode, value)
         if res == rffi.cast(SEM_T, SEM_FAILED):
-            raise OSError(rposix.get_errno(), "sem_open failed")
+            raise OSError(rposix.get_saved_errno(), "sem_open failed")
         return res
 
     def sem_close(handle):
         res = _sem_close(handle)
         if res < 0:
-            raise OSError(rposix.get_errno(), "sem_close failed")
+            raise OSError(rposix.get_saved_errno(), "sem_close failed")
 
     def sem_unlink(name):
         res = _sem_unlink(name)
         if res < 0:
-            raise OSError(rposix.get_errno(), "sem_unlink failed")
+            raise OSError(rposix.get_saved_errno(), "sem_unlink failed")
 
     def sem_wait(sem):
         res = _sem_wait(sem)
         if res < 0:
-            raise OSError(rposix.get_errno(), "sem_wait failed")
+            raise OSError(rposix.get_saved_errno(), "sem_wait failed")
 
     def sem_trywait(sem):
         res = _sem_trywait(sem)
         if res < 0:
-            raise OSError(rposix.get_errno(), "sem_trywait failed")
+            raise OSError(rposix.get_saved_errno(), "sem_trywait failed")
 
     def sem_timedwait(sem, deadline):
         res = _sem_timedwait(sem, deadline)
         if res < 0:
-            raise OSError(rposix.get_errno(), "sem_timedwait failed")
+            raise OSError(rposix.get_saved_errno(), "sem_timedwait failed")
 
     def _sem_timedwait_save(sem, deadline):
         delay = 0
@@ -135,7 +147,7 @@
                 # poll
                 if _sem_trywait(sem) == 0:
                     return 0
-                elif rposix.get_errno() != errno.EAGAIN:
+                elif rposix.get_saved_errno() != errno.EAGAIN:
                     return -1
 
                 now = gettimeofday()
@@ -143,7 +155,7 @@
                 c_tv_nsec = rffi.getintfield(deadline[0], 'c_tv_nsec')
                 if (c_tv_sec < now[0] or
                     (c_tv_sec == now[0] and c_tv_nsec <= now[1])):
-                    rposix.set_errno(errno.ETIMEDOUT)
+                    rposix.set_saved_errno(errno.ETIMEDOUT)
                     return -1
 
 
@@ -166,21 +178,21 @@
 
     if SEM_TIMED_WAIT:
         _sem_timedwait = external('sem_timedwait', [SEM_T, TIMESPECP],
-                                  rffi.INT)
+                                  rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO)
     else:
         _sem_timedwait = _sem_timedwait_save
 
     def sem_post(sem):
         res = _sem_post(sem)
         if res < 0:
-            raise OSError(rposix.get_errno(), "sem_post failed")
+            raise OSError(rposix.get_saved_errno(), "sem_post failed")
 
     def sem_getvalue(sem):
         sval_ptr = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
         try:
             res = _sem_getvalue(sem, sval_ptr)
             if res < 0:
-                raise OSError(rposix.get_errno(), "sem_getvalue failed")
+                raise OSError(rposix.get_saved_errno(), "sem_getvalue failed")
             return rffi.cast(lltype.Signed, sval_ptr[0])
         finally:
             lltype.free(sval_ptr, flavor='raw')
@@ -190,7 +202,7 @@
         try:
             res = _gettimeofday(now, None)
             if res < 0:
-                raise OSError(rposix.get_errno(), "gettimeofday failed")
+                raise OSError(rposix.get_saved_errno(), "gettimeofday failed")
             return (rffi.getintfield(now[0], 'c_tv_sec'),
                     rffi.getintfield(now[0], 'c_tv_usec'))
         finally:
@@ -216,18 +228,16 @@
 
 if sys.platform == 'win32':
     def create_semaphore(space, name, val, max):
-        rwin32.SetLastError(0)
+        rwin32.SetLastError_saved(0)
         handle = _CreateSemaphore(rffi.NULL, val, max, rffi.NULL)
         # On Windows we should fail on ERROR_ALREADY_EXISTS
-        err = rwin32.GetLastError()
+        err = rwin32.GetLastError_saved()
         if err != 0:
             raise WindowsError(err, "CreateSemaphore")
         return handle
 
     def delete_semaphore(handle):
-        if not _CloseHandle(handle):
-            err = rwin32.GetLastError()
-            raise WindowsError(err, "CloseHandle")
+        _CloseHandle_no_errno(handle)
 
     def semlock_acquire(self, space, block, w_timeout):
         if not block:
@@ -286,7 +296,7 @@
     def semlock_release(self, space):
         if not _ReleaseSemaphore(self.handle, 1,
                                  lltype.nullptr(rffi.LONGP.TO)):
-            err = rwin32.GetLastError()
+            err = rwin32.GetLastError_saved()
             if err == 0x0000012a: # ERROR_TOO_MANY_POSTS
                 raise OperationError(
                     space.w_ValueError,
@@ -300,7 +310,7 @@
         previous_ptr = lltype.malloc(rffi.LONGP.TO, 1, flavor='raw')
         try:
             if not _ReleaseSemaphore(self.handle, 1, previous_ptr):
-                raise rwin32.lastWindowsError("ReleaseSemaphore")
+                raise rwin32.lastSavedWindowsError("ReleaseSemaphore")
             return previous_ptr[0] + 1
         finally:
             lltype.free(previous_ptr, flavor='raw')
@@ -320,7 +330,7 @@
         return sem
 
     def delete_semaphore(handle):
-        sem_close(handle)
+        _sem_close_no_errno(handle)
 
     def semlock_acquire(self, space, block, w_timeout):
         if not block:
diff --git a/pypy/module/_multiprocessing/interp_win32.py 
b/pypy/module/_multiprocessing/interp_win32.py
--- a/pypy/module/_multiprocessing/interp_win32.py
+++ b/pypy/module/_multiprocessing/interp_win32.py
@@ -41,20 +41,24 @@
         rwin32.DWORD, rwin32.DWORD, rwin32.DWORD,
         rwin32.DWORD, rwin32.DWORD, rwin32.DWORD,
         rffi.VOIDP],
-    rwin32.HANDLE)
+    rwin32.HANDLE,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _ConnectNamedPipe = rwin32.winexternal(
-    'ConnectNamedPipe', [rwin32.HANDLE, rffi.VOIDP], rwin32.BOOL)
+    'ConnectNamedPipe', [rwin32.HANDLE, rffi.VOIDP], rwin32.BOOL,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _SetNamedPipeHandleState = rwin32.winexternal(
     'SetNamedPipeHandleState', [
         rwin32.HANDLE,
         rwin32.LPDWORD, rwin32.LPDWORD, rwin32.LPDWORD],
-    rwin32.BOOL)
+    rwin32.BOOL,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _WaitNamedPipe = rwin32.winexternal(
     'WaitNamedPipeA', [rwin32.LPCSTR, rwin32.DWORD],
-    rwin32.BOOL)
+    rwin32.BOOL,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _PeekNamedPipe = rwin32.winexternal(
     'PeekNamedPipe', [
@@ -62,31 +66,36 @@
         rffi.VOIDP,
         rwin32.DWORD,
         rwin32.LPDWORD, rwin32.LPDWORD, rwin32.LPDWORD],
-    rwin32.BOOL) 
+    rwin32.BOOL,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _CreateFile = rwin32.winexternal(
     'CreateFileA', [
         rwin32.LPCSTR,
         rwin32.DWORD, rwin32.DWORD, rffi.VOIDP,
         rwin32.DWORD, rwin32.DWORD, rwin32.HANDLE],
-    rwin32.HANDLE)
+    rwin32.HANDLE,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _WriteFile = rwin32.winexternal(
     'WriteFile', [
         rwin32.HANDLE,
         rffi.VOIDP, rwin32.DWORD,
         rwin32.LPDWORD, rffi.VOIDP],
-    rwin32.BOOL)
+    rwin32.BOOL,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _ReadFile = rwin32.winexternal(
     'ReadFile', [
         rwin32.HANDLE,
         rffi.VOIDP, rwin32.DWORD,
         rwin32.LPDWORD, rffi.VOIDP],
-    rwin32.BOOL)
+    rwin32.BOOL,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _ExitProcess = rwin32.winexternal(
-    'ExitProcess', [rffi.UINT], lltype.Void)
+    'ExitProcess', [rffi.UINT], lltype.Void,
+    save_err=rffi.RFFI_SAVE_LASTERROR)
 
 _GetTickCount = rwin32.winexternal(
     'GetTickCount', [], rwin32.DWORD)
@@ -97,10 +106,10 @@
 def CloseHandle(space, w_handle):
     handle = handle_w(space, w_handle)
     if not rwin32.CloseHandle(handle):
-        raise wrap_windowserror(space, rwin32.lastWindowsError())
+        raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
 
 def GetLastError(space):
-    return space.wrap(rwin32.GetLastError())
+    return space.wrap(rwin32.GetLastError_saved())
 
 # __________________________________________________________
 # functions for the "win32" namespace
@@ -118,7 +127,7 @@
         outputsize, inputsize, timeout, rffi.NULL)
 
     if handle == rwin32.INVALID_HANDLE_VALUE:
-        raise wrap_windowserror(space, rwin32.lastWindowsError())
+        raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
 
     return w_handle(space, handle)
 
@@ -129,7 +138,7 @@
         raise OperationError(space.w_NotImplementedError,
                              space.wrap("expected a NULL pointer"))
     if not _ConnectNamedPipe(handle, rffi.NULL):
-        raise wrap_windowserror(space, rwin32.lastWindowsError())
+        raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
 
 def SetNamedPipeHandleState(space, w_handle, w_pipemode, w_maxinstances,
                             w_timeout):
@@ -149,7 +158,7 @@
             statep[2] = rffi.ptradd(state, 2)
         if not _SetNamedPipeHandleState(handle, statep[0], statep[1],
                                         statep[2]):
-            raise wrap_windowserror(space, rwin32.lastWindowsError())
+            raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
     finally:
         lltype.free(state, flavor='raw')
         lltype.free(statep, flavor='raw')
@@ -158,7 +167,7 @@
 def WaitNamedPipe(space, name, timeout):
     # Careful: zero means "default value specified by CreateNamedPipe()"
     if not _WaitNamedPipe(name, timeout):
-        raise wrap_windowserror(space, rwin32.lastWindowsError())
+        raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
 
 @unwrap_spec(filename=str, access=r_uint, share=r_uint,
              disposition=r_uint, flags=r_uint)
@@ -174,7 +183,7 @@
                          disposition, flags, rwin32.NULL_HANDLE)
 
     if handle == rwin32.INVALID_HANDLE_VALUE:
-        raise wrap_windowserror(space, rwin32.lastWindowsError())
+        raise wrap_windowserror(space, rwin32.lastSavedWindowsError())
 
     return w_handle(space, handle)
 
diff --git a/pypy/module/_rawffi/alt/interp_funcptr.py 
b/pypy/module/_rawffi/alt/interp_funcptr.py
--- a/pypy/module/_rawffi/alt/interp_funcptr.py
+++ b/pypy/module/_rawffi/alt/interp_funcptr.py
@@ -14,7 +14,6 @@
 from rpython.rlib.objectmodel import we_are_translated
 from pypy.module._rawffi.alt.type_converter import FromAppLevelConverter, 
ToAppLevelConverter
 from pypy.module._rawffi.interp_rawffi import got_libffi_error, 
wrap_dlopenerror
-from pypy.module._rawffi import lasterror
 
 import os
 if os.name == 'nt':
@@ -202,23 +201,11 @@
         self.func = func
         self.argchain = argchain
 
-    def before(self):
-        lasterror.restore_last_error(self.space)
-
-    def after(self):
-        lasterror.save_last_error(self.space)
-
     def get_longlong(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, rffi.LONGLONG)
-        self.after()
-        return x
+        return self.func.call(self.argchain, rffi.LONGLONG)
 
     def get_ulonglong(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, rffi.ULONGLONG)
-        self.after()
-        return x
+        return self.func.call(self.argchain, rffi.ULONGLONG)
 
     def get_signed(self, w_ffitype):
         # if the declared return type of the function is smaller than LONG,
@@ -229,7 +216,6 @@
         # to space.wrap in order to get a nice applevel <int>.
         #
         restype = w_ffitype.get_ffitype()
-        self.before()
         call = self.func.call
         if restype is libffi.types.slong:
             x = call(self.argchain, rffi.LONG)
@@ -241,19 +227,14 @@
             x = rffi.cast(rffi.LONG, call(self.argchain, rffi.SIGNEDCHAR))
         else:
             raise self.error(w_ffitype)
-        self.after()
         return x
 
     def get_unsigned(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, rffi.ULONG)
-        self.after()
-        return x
+        return self.func.call(self.argchain, rffi.ULONG)
 
     def get_unsigned_which_fits_into_a_signed(self, w_ffitype):
         # the same comment as get_signed apply
         restype = w_ffitype.get_ffitype()
-        self.before()
         call = self.func.call
         if restype is libffi.types.uint:
             assert not libffi.IS_32_BIT
@@ -266,57 +247,35 @@
             x = rffi.cast(rffi.LONG, call(self.argchain, rffi.UCHAR))
         else:
             raise self.error(w_ffitype)
-        self.after()
         return x
 
 
     def get_pointer(self, w_ffitype):
-        self.before()
         ptrres = self.func.call(self.argchain, rffi.VOIDP)
-        self.after()
         return rffi.cast(rffi.ULONG, ptrres)
 
     def get_char(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, rffi.UCHAR)
-        self.after()
-        return x
+        return self.func.call(self.argchain, rffi.UCHAR)
 
     def get_unichar(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, rffi.WCHAR_T)
-        self.after()
-        return x
+        return self.func.call(self.argchain, rffi.WCHAR_T)
 
     def get_float(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, rffi.DOUBLE)
-        self.after()
-        return x
+        return self.func.call(self.argchain, rffi.DOUBLE)
 
     def get_singlefloat(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, rffi.FLOAT)
-        self.after()
-        return x
+        return self.func.call(self.argchain, rffi.FLOAT)
 
     def get_struct(self, w_ffitype, w_structdescr):
-        self.before()
         addr = self.func.call(self.argchain, rffi.LONG, is_struct=True)
-        self.after()
         return w_structdescr.fromaddress(self.space, addr)
 
     def get_struct_rawffi(self, w_ffitype, w_structdescr):
-        self.before()
         uintval = self.func.call(self.argchain, rffi.ULONG, is_struct=True)
-        self.after()
         return w_structdescr.fromaddress(self.space, uintval)
 
     def get_void(self, w_ffitype):
-        self.before()
-        x = self.func.call(self.argchain, lltype.Void)
-        self.after()
-        return x
+        return self.func.call(self.argchain, lltype.Void)
 
 
 def unpack_argtypes(space, w_argtypes, w_restype):
diff --git a/pypy/module/_rawffi/interp_rawffi.py 
b/pypy/module/_rawffi/interp_rawffi.py
--- a/pypy/module/_rawffi/interp_rawffi.py
+++ b/pypy/module/_rawffi/interp_rawffi.py
@@ -18,7 +18,6 @@
 from rpython.rlib.rarithmetic import intmask, r_uint
 from pypy.module._rawffi.buffer import RawFFIBuffer
 from pypy.module._rawffi.tracker import tracker
-from pypy.module._rawffi import lasterror
 
 TYPEMAP = {
     # XXX A mess with unsigned/signed/normal chars :-/
@@ -496,14 +495,10 @@
         try:
             if self.resshape is not None:
                 result = self.resshape.allocate(space, 1, autofree=True)
-                lasterror.restore_last_error(space)
                 self.ptr.call(args_ll, result.ll_buffer)
-                lasterror.save_last_error(space)
                 return space.wrap(result)
             else:
-                lasterror.restore_last_error(space)
                 self.ptr.call(args_ll, lltype.nullptr(rffi.VOIDP.TO))
-                lasterror.save_last_error(space)
                 return space.w_None
         except StackCheckError, e:
             raise OperationError(space.w_ValueError, space.wrap(e.message))
@@ -613,17 +608,19 @@
     return space.wrap(W_CDLL(space, name, cdll))
 
 def get_errno(space):
-    return space.wrap(rposix.get_errno())
+    return space.wrap(rposix.get_saved_errno())
 
 def set_errno(space, w_errno):
-    rposix.set_errno(space.int_w(w_errno))
+    rposix.set_saved_errno(space.int_w(w_errno))
 
 if sys.platform == 'win32':
+    # see also
+    # https://bitbucket.org/pypy/pypy/issue/1944/ctypes-on-windows-getlasterror
     def get_last_error(space):
-        return space.wrap(lasterror.fetch_last_error(space))
+        return space.wrap(rwin32.GetLastError_saved())
     @unwrap_spec(error=int)
     def set_last_error(space, error):
-        lasterror.store_last_error(space, error)
+        rwin32.SetLastError_saved(error)
 else:
     # always have at least a dummy version of these functions
     # (https://bugs.pypy.org/issue1242)
diff --git a/pypy/module/_rawffi/lasterror.py b/pypy/module/_rawffi/lasterror.py
deleted file mode 100644
--- a/pypy/module/_rawffi/lasterror.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# For Windows only.
-# https://bitbucket.org/pypy/pypy/issue/1944/ctypes-on-windows-getlasterror
-
-import os
-
-_MS_WINDOWS = os.name == "nt"
-
-
-if _MS_WINDOWS:
-    from rpython.rlib import rwin32
-    from pypy.interpreter.executioncontext import ExecutionContext
-
-
-    ExecutionContext._rawffi_last_error = 0
-
-    def fetch_last_error(space):
-        ec = space.getexecutioncontext()
-        return ec._rawffi_last_error
-
-    def store_last_error(space, last_error):
-        ec = space.getexecutioncontext()
-        ec._rawffi_last_error = last_error
-
-    def restore_last_error(space):
-        ec = space.getexecutioncontext()
-        lasterror = ec._rawffi_last_error
-        rwin32.SetLastError(lasterror)
-
-    def save_last_error(space):
-        lasterror = rwin32.GetLastError()
-        ec = space.getexecutioncontext()
-        ec._rawffi_last_error = lasterror
-
-else:
-
-    def restore_last_error(space):
-        pass
-
-    def save_last_error(space):
-        pass
diff --git a/pypy/module/cpyext/pyerrors.py b/pypy/module/cpyext/pyerrors.py
--- a/pypy/module/cpyext/pyerrors.py
+++ b/pypy/module/cpyext/pyerrors.py
@@ -9,7 +9,7 @@
     PyObject, PyObjectP, make_ref, from_ref, Py_DecRef, borrow_from)
 from pypy.module.cpyext.state import State
 from pypy.module.cpyext.import_ import PyImport_Import
-from rpython.rlib.rposix import get_errno
+from rpython.rlib import rposix, jit
 
 @cpython_api([PyObject, PyObject], lltype.Void)
 def PyErr_SetObject(space, w_type, w_value):
@@ -159,6 +159,7 @@
     PyErr_SetFromErrnoWithFilenameObject(space, w_type, filename)
 
 @cpython_api([PyObject, PyObject], PyObject)
[email protected]_look_inside       # direct use of _get_errno()
 def PyErr_SetFromErrnoWithFilenameObject(space, w_type, w_value):
     """Similar to PyErr_SetFromErrno(), with the additional behavior that if
     w_value is not NULL, it is passed to the constructor of type as a
@@ -166,7 +167,7 @@
     this is used to define the filename attribute of the exception instance.
     Return value: always NULL."""
     # XXX Doesn't actually do anything with PyErr_CheckSignals.
-    errno = get_errno()
+    errno = rffi.cast(lltype.Signed, rposix._get_errno())
     msg = os.strerror(errno)
     if w_value:
         w_error = space.call_function(w_type,
diff --git a/pypy/module/cpyext/pystrtod.py b/pypy/module/cpyext/pystrtod.py
--- a/pypy/module/cpyext/pystrtod.py
+++ b/pypy/module/cpyext/pystrtod.py
@@ -4,12 +4,13 @@
 from pypy.module.cpyext.pyobject import PyObject
 from rpython.rlib import rdtoa
 from rpython.rlib import rfloat
-from rpython.rlib import rposix
+from rpython.rlib import rposix, jit
 from rpython.rtyper.lltypesystem import lltype
 from rpython.rtyper.lltypesystem import rffi
 
 
 @cpython_api([rffi.CCHARP, rffi.CCHARPP, PyObject], rffi.DOUBLE, error=-1.0)
[email protected]_look_inside       # direct use of _get_errno()
 def PyOS_string_to_double(space, s, endptr, w_overflow_exception):
     """Convert a string s to a double, raising a Python
     exception on failure.  The set of accepted strings corresponds to
@@ -52,8 +53,9 @@
             raise OperationError(
                 space.w_ValueError,
                 space.wrap('invalid input at position %s' % endpos))
-        if rposix.get_errno() == errno.ERANGE:
-            rposix.set_errno(0)
+        err = rffi.cast(lltype.Signed, rposix._get_errno())
+        if err == errno.ERANGE:
+            rposix._set_errno(rffi.cast(rffi.INT, 0))
             if w_overflow_exception is None:
                 if result > 0:
                     return rfloat.INFINITY
diff --git a/pypy/module/fcntl/interp_fcntl.py 
b/pypy/module/fcntl/interp_fcntl.py
--- a/pypy/module/fcntl/interp_fcntl.py
+++ b/pypy/module/fcntl/interp_fcntl.py
@@ -55,22 +55,30 @@
         constants[name] = value
 locals().update(constants)
 
-def external(name, args, result):
-    return rffi.llexternal(name, args, result, 
compilation_info=CConfig._compilation_info_)
+def external(name, args, result, **kwds):
+    return rffi.llexternal(name, args, result,
+                           compilation_info=CConfig._compilation_info_,
+                           **kwds)
 
 _flock = lltype.Ptr(cConfig.flock)
-fcntl_int = external('fcntl', [rffi.INT, rffi.INT, rffi.INT], rffi.INT)
-fcntl_str = external('fcntl', [rffi.INT, rffi.INT, rffi.CCHARP], rffi.INT)
-fcntl_flock = external('fcntl', [rffi.INT, rffi.INT, _flock], rffi.INT)
-ioctl_int = external('ioctl', [rffi.INT, rffi.UINT, rffi.INT], rffi.INT)
-ioctl_str = external('ioctl', [rffi.INT, rffi.UINT, rffi.CCHARP], rffi.INT)
+fcntl_int = external('fcntl', [rffi.INT, rffi.INT, rffi.INT], rffi.INT,
+                     save_err=rffi.RFFI_SAVE_ERRNO)
+fcntl_str = external('fcntl', [rffi.INT, rffi.INT, rffi.CCHARP], rffi.INT,
+                     save_err=rffi.RFFI_SAVE_ERRNO)
+fcntl_flock = external('fcntl', [rffi.INT, rffi.INT, _flock], rffi.INT,
+                       save_err=rffi.RFFI_SAVE_ERRNO)
+ioctl_int = external('ioctl', [rffi.INT, rffi.UINT, rffi.INT], rffi.INT,
+                     save_err=rffi.RFFI_SAVE_ERRNO)
+ioctl_str = external('ioctl', [rffi.INT, rffi.UINT, rffi.CCHARP], rffi.INT,
+                     save_err=rffi.RFFI_SAVE_ERRNO)
 
 has_flock = cConfig.has_flock
 if has_flock:
-    c_flock = external('flock', [rffi.INT, rffi.INT], rffi.INT)
+    c_flock = external('flock', [rffi.INT, rffi.INT], rffi.INT,
+                       save_err=rffi.RFFI_SAVE_ERRNO)
 
 def _get_error(space, funcname):
-    errno = rposix.get_errno()
+    errno = rposix.get_saved_errno()
     return wrap_oserror(space, OSError(errno, funcname),
                         exception_name = 'w_IOError')
 
diff --git a/pypy/module/micronumpy/test/test_zjit.py 
b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -123,10 +123,9 @@
         assert result == 3 ** 2
         self.check_trace_count(1)
         self.check_simple_loop({
-            'call': 1,
+            'call': 2,        # ccall_pow / _ll_1_threadlocalref_get(rpy_errno)
             'float_eq': 2,
             'float_mul': 2,
-            'getarrayitem_raw': 1,     # read the errno
             'guard_false': 2,
             'guard_not_invalidated': 1,
             'guard_true': 2,
@@ -136,7 +135,6 @@
             'jump': 1,
             'raw_load': 1,
             'raw_store': 1,
-            'setarrayitem_raw': 1,     # write the errno
         })
 
     def define_pow_int():
diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py 
b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
--- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py
@@ -200,14 +200,11 @@
         assert res == 8.0 * 300
         loop, = log.loops_by_filename(self.filepath)
         assert loop.match_by_id('cfficall', """
-            setarrayitem_raw(i69, 0, i95, descr=<ArrayS 4>)    # write 'errno'
             p96 = force_token()
             setfield_gc(p0, p96, descr=<FieldP 
pypy.interpreter.pyframe.PyFrame.vable_token .>)
-            f97 = call_release_gil(i59, 1.0, 3, descr=<Callf 8 fi EF=6 OS=62>)
+            f97 = call_release_gil(27, i59, 1.0, 3, descr=<Callf 8 fi EF=6 
OS=62>)
             guard_not_forced(descr=...)
             guard_no_exception(descr=...)
-            i98 = getarrayitem_raw(i69, 0, descr=<ArrayS 4>)   # read 'errno'
-            setfield_gc(p65, i98, descr=<FieldS 
pypy.interpreter.executioncontext.ExecutionContext.inst__cffi_saved_errno .>)
         """, ignore_ops=['guard_not_invalidated'])
 
     def test_cffi_call_guard_not_forced_fails(self):
diff --git a/pypy/module/pypyjit/test_pypy_c/test_thread.py 
b/pypy/module/pypyjit/test_pypy_c/test_thread.py
--- a/pypy/module/pypyjit/test_pypy_c/test_thread.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_thread.py
@@ -64,7 +64,7 @@
         guard_true(i56, descr=...)
         p57 = force_token()
         setfield_gc(p0, p57, descr=<FieldP 
pypy.interpreter.pyframe.PyFrame.vable_token 8>)
-        i58 = call_release_gil(_, i37, 1, descr=<Calli 4 ii EF=6>)
+        i58 = call_release_gil(0, _, i37, 1, descr=<Calli 4 ii EF=6>)
         guard_not_forced(descr=...)
         guard_no_exception(descr=...)
         i59 = int_is_true(i58)
@@ -72,14 +72,14 @@
         i60 = int_sub(i44, 1)
         p62 = force_token()
         setfield_gc(p0, p62, descr=<FieldP 
pypy.interpreter.pyframe.PyFrame.vable_token 8>)
-        i63 = call_release_gil(_, i37, 0, descr=<Calli 4 ii EF=6>)
+        i63 = call_release_gil(0, _, i37, 0, descr=<Calli 4 ii EF=6>)
         guard_not_forced(descr=...)
         guard_no_exception(descr=...)
         i64 = int_is_true(i63)
         guard_false(i64, descr=...)
         p65 = force_token()
         setfield_gc(p0, p65, descr=<FieldP 
pypy.interpreter.pyframe.PyFrame.vable_token 8>)
-        call_release_gil(_, i37, descr=<Callv 0 i EF=6>)
+        call_release_gil(0, _, i37, descr=<Callv 0 i EF=6>)
         guard_not_forced(descr=...)
         guard_no_exception(descr=...)
         guard_not_invalidated(descr=...)
diff --git a/pypy/module/select/interp_epoll.py 
b/pypy/module/select/interp_epoll.py
--- a/pypy/module/select/interp_epoll.py
+++ b/pypy/module/select/interp_epoll.py
@@ -4,12 +4,13 @@
 
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.error import OperationError, exception_from_errno, oefmt
+from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.error import exception_from_saved_errno
 from pypy.interpreter.typedef import TypeDef, GetSetProperty
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.tool import rffi_platform
 from rpython.rlib._rsocket_rffi import socketclose, FD_SETSIZE
-from rpython.rlib.rposix import get_errno
+from rpython.rlib.rposix import get_saved_errno
 from rpython.rlib.rarithmetic import intmask
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
 
@@ -53,19 +54,22 @@
 EPOLL_CTL_DEL = cconfig["EPOLL_CTL_DEL"]
 
 epoll_create = rffi.llexternal(
-    "epoll_create", [rffi.INT], rffi.INT, compilation_info=eci
+    "epoll_create", [rffi.INT], rffi.INT, compilation_info=eci,
+    save_err=rffi.RFFI_SAVE_ERRNO
 )
 epoll_ctl = rffi.llexternal(
     "epoll_ctl",
     [rffi.INT, rffi.INT, rffi.INT, lltype.Ptr(epoll_event)],
     rffi.INT,
-    compilation_info=eci
+    compilation_info=eci,
+    save_err=rffi.RFFI_SAVE_ERRNO
 )
 epoll_wait = rffi.llexternal(
     "epoll_wait",
     [rffi.INT, rffi.CArrayPtr(epoll_event), rffi.INT, rffi.INT],
     rffi.INT,
     compilation_info=eci,
+    save_err=rffi.RFFI_SAVE_ERRNO
 )
 
 
@@ -82,7 +86,7 @@
                         "sizehint must be greater than zero, got %d", sizehint)
         epfd = epoll_create(sizehint)
         if epfd < 0:
-            raise exception_from_errno(space, space.w_IOError)
+            raise exception_from_saved_errno(space, space.w_IOError)
 
         return space.wrap(W_Epoll(space, epfd))
 
@@ -114,10 +118,10 @@
             rffi.setintfield(ev.c_data, 'c_fd', fd)
 
             result = epoll_ctl(self.epfd, ctl, fd, ev)
-            if ignore_ebadf and get_errno() == errno.EBADF:
+            if ignore_ebadf and get_saved_errno() == errno.EBADF:
                 result = 0
             if result < 0:
-                raise exception_from_errno(space, space.w_IOError)
+                raise exception_from_saved_errno(space, space.w_IOError)
 
     def descr_get_closed(self, space):
         return space.wrap(self.get_closed())
@@ -160,7 +164,7 @@
         with lltype.scoped_alloc(rffi.CArray(epoll_event), maxevents) as evs:
             nfds = epoll_wait(self.epfd, evs, maxevents, int(timeout))
             if nfds < 0:
-                raise exception_from_errno(space, space.w_IOError)
+                raise exception_from_saved_errno(space, space.w_IOError)
 
             elist_w = [None] * nfds
             for i in xrange(nfds):
diff --git a/pypy/module/select/interp_kqueue.py 
b/pypy/module/select/interp_kqueue.py
--- a/pypy/module/select/interp_kqueue.py
+++ b/pypy/module/select/interp_kqueue.py
@@ -1,8 +1,9 @@
 from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter.error import OperationError, exception_from_errno, oefmt
+from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.error import exception_from_saved_errno
 from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
 from pypy.interpreter.typedef import TypeDef, generic_new_descr, GetSetProperty
-from rpython.rlib._rsocket_rffi import socketclose
+from rpython.rlib._rsocket_rffi import socketclose_no_errno
 from rpython.rlib.rarithmetic import r_uint
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rtyper.tool import rffi_platform
@@ -86,7 +87,8 @@
     "kqueue",
     [],
     rffi.INT,
-    compilation_info=eci
+    compilation_info=eci,
+    save_err=rffi.RFFI_SAVE_ERRNO
 )
 
 syscall_kevent = rffi.llexternal(
@@ -99,7 +101,8 @@
      lltype.Ptr(timespec)
     ],
     rffi.INT,
-    compilation_info=eci
+    compilation_info=eci,
+    save_err=rffi.RFFI_SAVE_ERRNO
 )
 
 
@@ -110,7 +113,7 @@
     def descr__new__(space, w_subtype):
         kqfd = syscall_kqueue()
         if kqfd < 0:
-            raise exception_from_errno(space, space.w_IOError)
+            raise exception_from_saved_errno(space, space.w_IOError)
         return space.wrap(W_Kqueue(space, kqfd))
 
     @unwrap_spec(fd=int)
@@ -127,7 +130,7 @@
         if not self.get_closed():
             kqfd = self.kqfd
             self.kqfd = -1
-            socketclose(kqfd)
+            socketclose_no_errno(kqfd)
 
     def check_closed(self, space):
         if self.get_closed():
@@ -198,7 +201,7 @@
                                           max_events,
                                           ptimeout)
                     if nfds < 0:
-                        raise exception_from_errno(space, space.w_IOError)
+                        raise exception_from_saved_errno(space, 
space.w_IOError)
                     else:
                         elist_w = [None] * nfds
                         for i in xrange(nfds):
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
@@ -5,7 +5,7 @@
 import os
 import errno
 
-from pypy.interpreter.error import OperationError, exception_from_errno
+from pypy.interpreter.error import OperationError, exception_from_saved_errno
 from pypy.interpreter.executioncontext import (AsyncAction, AbstractActionFlag,
     PeriodicAsyncAction)
 from pypy.interpreter.gateway import unwrap_spec
@@ -258,7 +258,7 @@
 def siginterrupt(space, signum, flag):
     check_signum_in_range(space, signum)
     if rffi.cast(lltype.Signed, c_siginterrupt(signum, flag)) < 0:
-        errno = rposix.get_errno()
+        errno = rposix.get_saved_errno()
         raise OperationError(space.w_RuntimeError, space.wrap(errno))
 
 
@@ -311,7 +311,7 @@
 
             ret = c_setitimer(which, new, old)
             if ret != 0:
-                raise exception_from_errno(space, get_itimer_error(space))
+                raise exception_from_saved_errno(space, 
get_itimer_error(space))
 
             return itimer_retval(space, old[0])
 
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
@@ -7,12 +7,11 @@
 # all but one will be blocked.  The other threads get a chance to run
 # from time to time, using the periodic action GILReleaseAction.
 
-from rpython.rlib import rthread, rgil, rwin32
+from rpython.rlib import rthread, rgil
 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
-from rpython.rlib.rposix import get_errno, set_errno
 
 class GILThreadLocals(OSThreadLocals):
     """A version of OSThreadLocals that enforces a GIL."""
@@ -75,16 +74,9 @@
 before_external_call._dont_reach_me_in_del_ = True
 
 def after_external_call():
-    e = get_errno()
-    e2 = 0
-    if rwin32.WIN32:
-        e2 = rwin32.GetLastError()
     rgil.gil_acquire()
     rthread.gc_thread_run()
     after_thread_switch()
-    if rwin32.WIN32:
-        rwin32.SetLastError(e2)
-    set_errno(e)
 after_external_call._gctransformer_hint_cannot_collect_ = True
 after_external_call._dont_reach_me_in_del_ = True
 
diff --git a/pypy/module/thread/threadlocals.py 
b/pypy/module/thread/threadlocals.py
--- a/pypy/module/thread/threadlocals.py
+++ b/pypy/module/thread/threadlocals.py
@@ -17,7 +17,8 @@
         "NOT_RPYTHON"
         self._valuedict = {}   # {thread_ident: ExecutionContext()}
         self._cleanup_()
-        self.raw_thread_local = rthread.ThreadLocalReference(ExecutionContext)
+        self.raw_thread_local = rthread.ThreadLocalReference(ExecutionContext,
+                                                            
loop_invariant=True)
 
     def _cleanup_(self):
         self._valuedict.clear()
diff --git a/pypy/module/time/interp_time.py b/pypy/module/time/interp_time.py
--- a/pypy/module/time/interp_time.py
+++ b/pypy/module/time/interp_time.py
@@ -62,7 +62,8 @@
     _setCtrlHandlerRoutine = rffi.llexternal(
         'pypy_timemodule_setCtrlHandler',
         [rwin32.HANDLE], rwin32.BOOL,
-        compilation_info=eci)
+        compilation_info=eci,
+        save_err=rffi.RFFI_SAVE_LASTERROR)
 
     class GlobalState:
         def __init__(self):
@@ -79,8 +80,8 @@
             except WindowsError, e:
                 raise wrap_windowserror(space, e)
             if not _setCtrlHandlerRoutine(globalState.interrupt_event):
-                raise wrap_windowserror(
-                    space, rwin32.lastWindowsError("SetConsoleCtrlHandler"))
+                raise wrap_windowserror(space,
+                    rwin32.lastSavedWindowsError("SetConsoleCtrlHandler"))
 
     globalState = GlobalState()
 
@@ -142,7 +143,7 @@
     setattr(cConfig, k, v)
 cConfig.tm.__name__ = "_tm"
 
-def external(name, args, result, eci=CConfig._compilation_info_):
+def external(name, args, result, eci=CConfig._compilation_info_, **kwds):
     if _WIN and rffi.sizeof(rffi.TIME_T) == 8:
         # Recent Microsoft compilers use 64bit time_t and
         # the corresponding functions are named differently
@@ -152,7 +153,8 @@
     return rffi.llexternal(name, args, result,
                            compilation_info=eci,
                            calling_conv=calling_conv,
-                           releasegil=False)
+                           releasegil=False,
+                           **kwds)
 
 if _POSIX:
     cConfig.timeval.__name__ = "_timeval"
@@ -169,10 +171,12 @@
 c_clock = external('clock', [rffi.TIME_TP], clock_t)
 c_time = external('time', [rffi.TIME_TP], rffi.TIME_T)
 c_ctime = external('ctime', [rffi.TIME_TP], rffi.CCHARP)
-c_gmtime = external('gmtime', [rffi.TIME_TP], TM_P)
+c_gmtime = external('gmtime', [rffi.TIME_TP], TM_P,
+                    save_err=rffi.RFFI_SAVE_ERRNO)
 c_mktime = external('mktime', [TM_P], rffi.TIME_T)
 c_asctime = external('asctime', [TM_P], rffi.CCHARP)
-c_localtime = external('localtime', [rffi.TIME_TP], TM_P)
+c_localtime = external('localtime', [rffi.TIME_TP], TM_P,
+                       save_err=rffi.RFFI_SAVE_ERRNO)
 if _POSIX:
     c_tzset = external('tzset', [], lltype.Void)
 if _WIN:
@@ -304,7 +308,7 @@
     _set_module_object(space, 'altzone', space.wrap(altzone))
 
 def _get_error_msg():
-    errno = rposix.get_errno()
+    errno = rposix.get_saved_errno()
     return os.strerror(errno)
 
 if sys.platform != 'win32':
diff --git a/rpython/jit/backend/arm/callbuilder.py 
b/rpython/jit/backend/arm/callbuilder.py
--- a/rpython/jit/backend/arm/callbuilder.py
+++ b/rpython/jit/backend/arm/callbuilder.py
@@ -11,6 +11,8 @@
 from rpython.jit.backend.arm.helper.assembler import saved_registers
 from rpython.jit.backend.arm.helper.regalloc import check_imm_arg
 from rpython.jit.backend.arm.codebuilder import OverwritingBuilder
+from rpython.jit.backend.llsupport import llerrno
+from rpython.rtyper.lltypesystem import rffi
 
 
 class ARMCallbuilder(AbstractCallBuilder):
@@ -172,6 +174,41 @@
                 self.mc.LSL_ri(resloc.value, resloc.value, 16)
                 self.mc.ASR_ri(resloc.value, resloc.value, 16)
 
+    def write_real_errno(self, save_err):
+        if save_err & rffi.RFFI_READSAVED_ERRNO:
+            # Just before a call, read 'rpy_errno' and write it into the
+            # real 'errno'.  The r0-r3 registers contain arguments to the
+            # future call; the r5-r7 registers contain various stuff.
+            # We still have r8-r12.
+            rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu)
+            p_errno = llerrno.get_p_errno_offset(self.asm.cpu)
+            self.mc.LDR_ri(r.r9.value, r.sp.value,
+                           self.asm.saved_threadlocal_addr + self.current_sp)
+            self.mc.LDR_ri(r.ip.value, r.r9.value, p_errno)
+            self.mc.LDR_ri(r.r9.value, r.r9.value, rpy_errno)
+            self.mc.STR_ri(r.r9.value, r.ip.value)
+        elif save_err & rffi.RFFI_ZERO_ERRNO_BEFORE:
+            # Same, but write zero.
+            p_errno = llerrno.get_p_errno_offset(self.asm.cpu)
+            self.mc.LDR_ri(r.r9.value, r.sp.value,
+                           self.asm.saved_threadlocal_addr + self.current_sp)
+            self.mc.LDR_ri(r.ip.value, r.r9.value, p_errno)
+            self.mc.MOV_ri(r.r9.value, 0)
+            self.mc.STR_ri(r.r9.value, r.ip.value)
+
+    def read_real_errno(self, save_err):
+        if save_err & rffi.RFFI_SAVE_ERRNO:
+            # Just after a call, read the real 'errno' and save a copy of
+            # it inside our thread-local 'rpy_errno'.  Registers r8-r12
+            # are unused here, and registers r2-r3 never contain anything
+            # after the call.
+            rpy_errno = llerrno.get_rpy_errno_offset(self.asm.cpu)
+            p_errno = llerrno.get_p_errno_offset(self.asm.cpu)
+            self.mc.LDR_ri(r.r3.value, r.sp.value,
+                           self.asm.saved_threadlocal_addr)
+            self.mc.LDR_ri(r.ip.value, r.r3.value, p_errno)
+            self.mc.LDR_ri(r.ip.value, r.ip.value, 0)
+            self.mc.STR_ri(r.ip.value, r.r3.value, rpy_errno)
 
 
 class SoftFloatCallBuilder(ARMCallbuilder):
diff --git a/rpython/jit/backend/arm/opassembler.py 
b/rpython/jit/backend/arm/opassembler.py
--- a/rpython/jit/backend/arm/opassembler.py
+++ b/rpython/jit/backend/arm/opassembler.py
@@ -403,7 +403,9 @@
         # args = [resloc, size, sign, args...]
         from rpython.jit.backend.llsupport.descr import CallDescr
 
-        cb = callbuilder.get_callbuilder(self.cpu, self, arglocs[3], 
arglocs[4:], arglocs[0])
+        func_index = 3 + is_call_release_gil
+        cb = callbuilder.get_callbuilder(self.cpu, self, arglocs[func_index],
+                                         arglocs[func_index+1:], arglocs[0])
 
         descr = op.getdescr()
         assert isinstance(descr, CallDescr)
@@ -418,7 +420,9 @@
         cb.ressign = signloc.value
 
         if is_call_release_gil:
-            cb.emit_call_release_gil()
+            saveerrloc = arglocs[3]
+            assert saveerrloc.is_imm()
+            cb.emit_call_release_gil(saveerrloc.value)
         else:
             cb.emit()
         return fcond
@@ -1286,9 +1290,13 @@
         return fcond
 
     def emit_opx_threadlocalref_get(self, op, arglocs, regalloc, fcond):
-        ofs0, res = arglocs
-        assert ofs0.is_imm()
+        ofs_loc, size_loc, sign_loc, res_loc = arglocs
+        assert ofs_loc.is_imm()
+        assert size_loc.is_imm()
+        assert sign_loc.is_imm()
         ofs = self.saved_threadlocal_addr
-        self.load_reg(self.mc, res, r.sp, ofs)
-        self.load_reg(self.mc, res, res, ofs0.value)
+        self.load_reg(self.mc, res_loc, r.sp, ofs)
+        scale = get_scale(size_loc.value)
+        signed = (sign_loc.value != 0)
+        self._load_from_mem(res_loc, res_loc, ofs_loc, scale, signed, fcond)
         return fcond
diff --git a/rpython/jit/backend/arm/regalloc.py 
b/rpython/jit/backend/arm/regalloc.py
--- a/rpython/jit/backend/arm/regalloc.py
+++ b/rpython/jit/backend/arm/regalloc.py
@@ -573,11 +573,12 @@
             #    ...
         return self._prepare_call(op)
 
-    def _prepare_call(self, op, force_store=[], save_all_regs=False):
+    def _prepare_call(self, op, force_store=[], save_all_regs=False,
+                      first_arg_index=1):
         args = [None] * (op.numargs() + 3)
         calldescr = op.getdescr()
         assert isinstance(calldescr, CallDescr)
-        assert len(calldescr.arg_classes) == op.numargs() - 1
+        assert len(calldescr.arg_classes) == op.numargs() - first_arg_index
 
         for i in range(op.numargs()):
             args[i + 3] = self.loc(op.getarg(i))
@@ -626,9 +627,12 @@
         return [loc0, res]
 
     def _prepare_threadlocalref_get(self, op, fcond):
-        ofs0 = imm(op.getarg(1).getint())
-        res = self.force_allocate_reg(op.result)
-        return [ofs0, res]
+        ofs_loc = imm(op.getarg(1).getint())
+        calldescr = op.getdescr()
+        size_loc = imm(calldescr.get_result_size())
+        sign_loc = imm(calldescr.is_result_signed())
+        res_loc = self.force_allocate_reg(op.result)
+        return [ofs_loc, size_loc, sign_loc, res_loc]
 
     def _prepare_guard(self, op, args=None):
         if args is None:
@@ -1235,7 +1239,10 @@
     def prepare_guard_call_may_force(self, op, guard_op, fcond):
         args = self._prepare_call(op, save_all_regs=True)
         return self._prepare_guard(guard_op, args)
-    prepare_guard_call_release_gil = prepare_guard_call_may_force
+
+    def prepare_guard_call_release_gil(self, op, guard_op, fcond):
+        args = self._prepare_call(op, save_all_regs=True, first_arg_index=2)
+        return self._prepare_guard(guard_op, args)
 
     def prepare_guard_call_assembler(self, op, guard_op, fcond):
         locs = self.locs_for_call_assembler(op, guard_op)
diff --git a/rpython/jit/backend/llgraph/runner.py 
b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -935,7 +935,7 @@
         del self.force_guard_op
         return res
 
-    def execute_call_release_gil(self, descr, func, *args):
+    def execute_call_release_gil(self, descr, saveerr, func, *args):
         if hasattr(descr, '_original_func_'):
             func = descr._original_func_     # see pyjitpl.py
             # we want to call the function that does the aroundstate
diff --git a/rpython/jit/backend/llsupport/callbuilder.py 
b/rpython/jit/backend/llsupport/callbuilder.py
--- a/rpython/jit/backend/llsupport/callbuilder.py
+++ b/rpython/jit/backend/llsupport/callbuilder.py
@@ -42,16 +42,18 @@
         self.pop_gcmap()
         self.load_result()
 
-    def emit_call_release_gil(self):
+    def emit_call_release_gil(self, save_err):
         """Emit a CALL_RELEASE_GIL, including calls to releasegil_addr
-        and reacqgil_addr."""
+        and reacqgil_addr.  'save_err' is a combination of rffi.RFFI_*ERR*."""
         fastgil = rffi.cast(lltype.Signed, rgil.gil_fetch_fastgil())
         self.select_call_release_gil_mode()
         self.prepare_arguments()
         self.push_gcmap_for_call_release_gil()
         self.call_releasegil_addr_and_move_real_arguments(fastgil)
+        self.write_real_errno(save_err)
         self.emit_raw_call()
         self.restore_stack_pointer()
+        self.read_real_errno(save_err)
         self.move_real_result_and_call_reacqgil_addr(fastgil)
         self.pop_gcmap()
         self.load_result()
@@ -62,6 +64,12 @@
     def move_real_result_and_call_reacqgil_addr(self, fastgil):
         raise NotImplementedError
 
+    def write_real_errno(self, save_err):
+        raise NotImplementedError
+
+    def read_real_errno(self, save_err):
+        raise NotImplementedError
+
     def select_call_release_gil_mode(self):
         """Overridden in CallBuilder64"""
         self.is_call_release_gil = True
diff --git a/rpython/jit/backend/llsupport/llerrno.py 
b/rpython/jit/backend/llsupport/llerrno.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/llsupport/llerrno.py
@@ -0,0 +1,59 @@
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.translator.tool.cbuild import ExternalCompilationInfo
+from rpython.jit.backend.llsupport.symbolic import WORD
+
+
+def get_debug_saved_errno(cpu):
+    return cpu._debug_errno_container[3]
+
+def set_debug_saved_errno(cpu, nerrno):
+    assert nerrno >= 0
+    cpu._debug_errno_container[3] = nerrno
+
+def get_rpy_errno_offset(cpu):
+    if cpu.translate_support_code:
+        from rpython.rlib import rthread
+        return rthread.tlfield_rpy_errno.getoffset()
+    else:
+        return 3 * WORD
+
+
+def get_debug_saved_lasterror(cpu):
+    return cpu._debug_errno_container[4]
+
+def set_debug_saved_lasterror(cpu, nerrno):
+    assert nerrno >= 0
+    cpu._debug_errno_container[4] = nerrno
+
+def get_rpy_lasterror_offset(cpu):
+    if cpu.translate_support_code:
+        from rpython.rlib import rthread
+        return rthread.tlfield_rpy_lasterror.getoffset()
+    else:
+        return 4 * WORD
+
+
+def _fetch_addr_errno():
+    eci = ExternalCompilationInfo(
+        separate_module_sources=['''
+            #include <errno.h>
+            RPY_EXPORTED long fetch_addr_errno(void) {
+                return (long)(&errno);
+            }
+        '''])
+    func1_ptr = rffi.llexternal('fetch_addr_errno', [], lltype.Signed,
+                                compilation_info=eci, _nowrapper=True)
+    return func1_ptr()
+
+def get_p_errno_offset(cpu):
+    if cpu.translate_support_code:
+        from rpython.rlib import rthread
+        return rthread.tlfield_p_errno.getoffset()
+    else:
+        # fetch the real address of errno (in this thread), and store it
+        # at offset 2 in the _debug_errno_container
+        if cpu._debug_errno_container[2] == 0:
+            addr_errno = _fetch_addr_errno()
+            assert addr_errno != 0
+            cpu._debug_errno_container[2] = addr_errno
+        return 2 * WORD
diff --git a/rpython/jit/backend/llsupport/llmodel.py 
b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -62,6 +62,9 @@
             self.floatarraydescr = ArrayDescr(ad.basesize, ad.itemsize,
                                               ad.lendescr, FLAG_FLOAT)
         self.setup()
+        self._debug_errno_container = lltype.malloc(
+            rffi.CArray(lltype.Signed), 5, flavor='raw', zero=True,
+            track_allocation=False)
 
     def getarraydescr_for_frame(self, type):
         if type == history.FLOAT:
@@ -222,7 +225,8 @@
         # as arguments, and it returns the (possibly reallocated) jitframe.
         # The backend can optimize OS_THREADLOCALREF_GET calls to return a
         # field of this threadlocal_addr, but only if 'translate_support_code':
-        # in untranslated tests, threadlocal_addr is a dummy NULL.
+        # in untranslated tests, threadlocal_addr is a dummy container
+        # for errno tests only.
         FUNCPTR = lltype.Ptr(lltype.FuncType([llmemory.GCREF, 
llmemory.Address],
                                              llmemory.GCREF))
 
@@ -259,7 +263,8 @@
                     ll_threadlocal_addr = llop.threadlocalref_addr(
                         llmemory.Address)
                 else:
-                    ll_threadlocal_addr = llmemory.NULL
+                    ll_threadlocal_addr = rffi.cast(llmemory.Address,
+                        self._debug_errno_container)
                 llop.gc_writebarrier(lltype.Void, ll_frame)
                 ll_frame = func(ll_frame, ll_threadlocal_addr)
             finally:
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
@@ -2,6 +2,8 @@
 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
 
 from rpython.rtyper.annlowlevel import llhelper
 
@@ -95,3 +97,37 @@
     def test_close_stack(self):
         self.run('close_stack')
         assert 'call_release_gil' in 
udir.join('TestCompileFramework.log').read()
+
+    def define_get_set_errno(self):
+        eci = ExternalCompilationInfo(
+            post_include_bits=[r'''
+                #include <errno.h>
+                static int test_get_set_errno(void) {
+                    int r = errno;
+                    //fprintf(stderr, "read saved errno: %d\n", r);
+                    errno = 42;
+                    return r;
+                }
+            '''])
+
+        c_test = rffi.llexternal('test_get_set_errno', [], rffi.INT,
+                                 compilation_info=eci,
+                                 save_err=rffi.RFFI_FULL_ERRNO)
+
+        def before(n, x):
+            return (n, None, None, None, None, None,
+                    None, None, None, None, None, None)
+        #
+        def f(n, x, *args):
+            rposix.set_saved_errno(24)
+            result1 = c_test()
+            result2 = rposix.get_saved_errno()
+            assert result1 == 24
+            assert result2 == 42
+            n -= 1
+            return (n, x) + args
+        return before, f, None
+
+    def test_get_set_errno(self):
+        self.run('get_set_errno')
+        assert 'call_release_gil' in 
udir.join('TestCompileFramework.log').read()
diff --git a/rpython/jit/backend/llsupport/test/ztranslation_test.py 
b/rpython/jit/backend/llsupport/test/ztranslation_test.py
--- a/rpython/jit/backend/llsupport/test/ztranslation_test.py
+++ b/rpython/jit/backend/llsupport/test/ztranslation_test.py
@@ -5,7 +5,7 @@
 from rpython.rlib.jit import promote
 from rpython.rlib import jit_hooks, rposix
 from rpython.rlib.objectmodel import keepalive_until_here
-from rpython.rlib.rthread import ThreadLocalReference
+from rpython.rlib.rthread import ThreadLocalReference, ThreadLocalField
 from rpython.jit.backend.detect_cpu import getcpuclass
 from rpython.jit.backend.test.support import CCompiledMixin
 from rpython.jit.codewriter.policy import StopAtXPolicy
@@ -128,7 +128,8 @@
 
         class Foo(object):
             pass
-        t = ThreadLocalReference(Foo)
+        t = ThreadLocalReference(Foo, loop_invariant=True)
+        tf = ThreadLocalField(lltype.Char, "test_call_assembler_")
 
         def change(newthing):
             somewhere_else.frame.thing = newthing
@@ -156,6 +157,7 @@
                 frame.thing = Thing(nextval + 1)
                 i += 1
                 if t.get().nine != 9: raise ValueError
+                if ord(tf.getraw()) != 0x92: raise ValueError
             return frame.thing.val
 
         driver2 = JitDriver(greens = [], reds = ['n'])
@@ -181,6 +183,7 @@
             foo = Foo()
             foo.nine = value
             t.set(foo)
+            tf.setraw("\x92")
             return foo
 
         def mainall(codeno, bound):
diff --git a/rpython/jit/backend/test/runner_test.py 
b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -20,6 +20,7 @@
 from rpython.rlib.rarithmetic import intmask, is_valid_int
 from rpython.jit.backend.detect_cpu import autodetect
 from rpython.jit.backend.llsupport import jitframe
+from rpython.jit.backend.llsupport.llmodel import AbstractLLCPU
 
 
 IS_32_BIT = sys.maxint < 2**32
@@ -2512,7 +2513,7 @@
         tok = BoxInt()
         faildescr = BasicFailDescr(1)
         ops = [
-        ResOperation(rop.CALL_RELEASE_GIL, [funcbox, i1], i2,
+        ResOperation(rop.CALL_RELEASE_GIL, [ConstInt(0), funcbox, i1], i2,
                      descr=calldescr),
         ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
         ResOperation(rop.FINISH, [i2], None, descr=BasicFinalDescr(0))
@@ -2570,7 +2571,8 @@
         tok = BoxInt()
         faildescr = BasicFailDescr(1)
         ops = [
-        ResOperation(rop.CALL_RELEASE_GIL, [funcbox, i0, i1, i2, i3], None,
+        ResOperation(rop.CALL_RELEASE_GIL,
+                     [ConstInt(0), funcbox, i0, i1, i2, i3], None,
                      descr=calldescr),
         ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
         ResOperation(rop.FINISH, [], None, descr=BasicFinalDescr(0))
@@ -2625,7 +2627,8 @@
         for i in range(50):
             i3 = BoxInt()
             ops += [
-                ResOperation(rop.CALL_RELEASE_GIL, [funcbox, i1, i2], i3,
+                ResOperation(rop.CALL_RELEASE_GIL,
+                             [ConstInt(0), funcbox, i1, i2], i3,
                              descr=calldescr),
                 ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
                 ]
@@ -2697,7 +2700,7 @@
                 assert 0, kind
             #
             ops = [
-                ResOperation(rop.CALL_RELEASE_GIL, [funcbox], b3,
+                ResOperation(rop.CALL_RELEASE_GIL, [ConstInt(0), funcbox], b3,
                              descr=calldescr),
                 ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
                 ResOperation(rop.FINISH, [b3], None, descr=BasicFinalDescr(0))
@@ -2881,7 +2884,8 @@
             loadcodes = ''.join(loadcodes)
             print loadcodes
             ops += [
-                ResOperation(rop.CALL_RELEASE_GIL, insideboxes, None,
+                ResOperation(rop.CALL_RELEASE_GIL,
+                             [ConstInt(0)] + insideboxes, None,
                              descr=calldescr),
                 ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
                 ResOperation(rop.FINISH, [], None, descr=BasicFinalDescr(0))
@@ -2916,6 +2920,310 @@
             assert got == expected, '\n'.join(
                 ['bad args, signature %r' % codes[1:]] + different_values)
 
+    def test_call_release_gil_save_errno(self):
+        from rpython.translator.tool.cbuild import ExternalCompilationInfo
+        from rpython.rlib.libffi import types
+        from rpython.jit.backend.llsupport import llerrno
+        #
+        if not isinstance(self.cpu, AbstractLLCPU):
+            py.test.skip("not on LLGraph")
+        eci = ExternalCompilationInfo(
+            separate_module_sources=['''
+                #include <errno.h>
+                static long f1(long a, long b, long c, long d,
+                               long e, long f, long g) {
+                    errno = 42;
+                    return (a + 10*b + 100*c + 1000*d +
+                            10000*e + 100000*f + 1000000*g);
+                }
+                RPY_EXPORTED
+                long test_call_release_gil_save_errno(void) {
+                    return (long)&f1;
+                }
+            '''])
+        fn_name = 'test_call_release_gil_save_errno'
+        getter_ptr = rffi.llexternal(fn_name, [], lltype.Signed,
+                                     compilation_info=eci, _nowrapper=True)
+        func1_adr = getter_ptr()
+        calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7,
+                                                          types.slong)
+        #
+        for saveerr in [rffi.RFFI_ERR_NONE, rffi.RFFI_SAVE_ERRNO]:
+            faildescr = BasicFailDescr(1)
+            inputargs = [BoxInt() for i in range(7)]
+            i1 = BoxInt()
+            ops = [
+                ResOperation(rop.CALL_RELEASE_GIL,
+                             [ConstInt(saveerr), ConstInt(func1_adr)]
+                                 + inputargs, i1,
+                             descr=calldescr),
+                ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+                ResOperation(rop.FINISH, [i1], None, descr=BasicFinalDescr(0))
+            ]
+            ops[-2].setfailargs([])
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(inputargs, ops, looptoken)
+            #
+            llerrno.set_debug_saved_errno(self.cpu, 24)
+            deadframe = self.cpu.execute_token(looptoken, 9, 8, 7, 6, 5, 4, 3)
+            original_result = self.cpu.get_int_value(deadframe, 0)
+            result = llerrno.get_debug_saved_errno(self.cpu)
+            print 'saveerr =', saveerr, ': got result =', result
+            #
+            if saveerr == rffi.RFFI_SAVE_ERRNO:
+                assert result == 42      # from the C code
+            else:
+                assert result == 24      # not touched
+            assert original_result == 3456789
+
+    def test_call_release_gil_readsaved_errno(self):
+        from rpython.translator.tool.cbuild import ExternalCompilationInfo
+        from rpython.rlib.libffi import types
+        from rpython.jit.backend.llsupport import llerrno
+        #
+        if not isinstance(self.cpu, AbstractLLCPU):
+            py.test.skip("not on LLGraph")
+        eci = ExternalCompilationInfo(
+            separate_module_sources=[r'''
+                #include <stdio.h>
+                #include <errno.h>
+                static long f1(long a, long b, long c, long d,
+                               long e, long f, long g) {
+                    long r = errno;
+                    printf("read saved errno: %ld\n", r);
+                    r += 100 * (a + 10*b + 100*c + 1000*d +
+                                10000*e + 100000*f + 1000000*g);
+                    return r;
+                }
+                RPY_EXPORTED
+                long test_call_release_gil_readsaved_errno(void) {
+                    return (long)&f1;
+                }
+            '''])
+        fn_name = 'test_call_release_gil_readsaved_errno'
+        getter_ptr = rffi.llexternal(fn_name, [], lltype.Signed,
+                                     compilation_info=eci, _nowrapper=True)
+        func1_adr = getter_ptr()
+        calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7,
+                                                          types.slong)
+        #
+        for saveerr in [rffi.RFFI_READSAVED_ERRNO, 
rffi.RFFI_ZERO_ERRNO_BEFORE]:
+            faildescr = BasicFailDescr(1)
+            inputargs = [BoxInt() for i in range(7)]
+            i1 = BoxInt()
+            ops = [
+                ResOperation(rop.CALL_RELEASE_GIL,
+                             [ConstInt(saveerr), ConstInt(func1_adr)]
+                                 + inputargs, i1,
+                             descr=calldescr),
+                ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+                ResOperation(rop.FINISH, [i1], None, descr=BasicFinalDescr(0))
+            ]
+            ops[-2].setfailargs([])
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(inputargs, ops, looptoken)
+            #
+            llerrno.set_debug_saved_errno(self.cpu, 24)
+            deadframe = self.cpu.execute_token(looptoken, 9, 8, 7, 6, 5, 4, 3)
+            result = self.cpu.get_int_value(deadframe, 0)
+            assert llerrno.get_debug_saved_errno(self.cpu) == 24
+            #
+            if saveerr == rffi.RFFI_READSAVED_ERRNO:
+                assert result == 24 + 345678900
+            else:
+                assert result == 0  + 345678900
+
+    def test_call_release_gil_save_lasterror(self):
+        from rpython.translator.tool.cbuild import ExternalCompilationInfo
+        from rpython.rlib.libffi import types
+        from rpython.jit.backend.llsupport import llerrno
+        #
+        if not isinstance(self.cpu, AbstractLLCPU):
+            py.test.skip("not on LLGraph")
+        if sys.platform != 'win32':
+            py.test.skip("Windows test only")
+        eci = ExternalCompilationInfo(
+            separate_module_sources=['''
+                #include <windows.h>
+                static long f1(long a, long b, long c, long d,
+                               long e, long f, long g) {
+                    SetLastError(42);
+                    return (a + 10*b + 100*c + 1000*d +
+                            10000*e + 100000*f + 1000000*g);
+                }
+                RPY_EXPORTED
+                long test_call_release_gil_save_lasterror(void) {
+                    return (long)&f1;
+                }
+            '''])
+        fn_name = 'test_call_release_gil_save_lasterror'
+        getter_ptr = rffi.llexternal(fn_name, [], lltype.Signed,
+                                     compilation_info=eci, _nowrapper=True)
+        func1_adr = getter_ptr()
+        calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7,
+                                                          types.slong)
+        #
+        for saveerr in [rffi.RFFI_SAVE_ERRNO,  # but not _LASTERROR
+                        rffi.RFFI_SAVE_LASTERROR]:
+            faildescr = BasicFailDescr(1)
+            inputargs = [BoxInt() for i in range(7)]
+            i1 = BoxInt()
+            ops = [
+                ResOperation(rop.CALL_RELEASE_GIL,
+                             [ConstInt(saveerr), ConstInt(func1_adr)]
+                                 + inputargs, i1,
+                             descr=calldescr),
+                ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+                ResOperation(rop.FINISH, [i1], None, descr=BasicFinalDescr(0))
+            ]
+            ops[-2].setfailargs([])
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(inputargs, ops, looptoken)
+            #
+            llerrno.set_debug_saved_lasterror(self.cpu, 24)
+            deadframe = self.cpu.execute_token(looptoken, 9, 8, 7, 6, 5, 4, 3)
+            original_result = self.cpu.get_int_value(deadframe, 0)
+            result = llerrno.get_debug_saved_lasterror(self.cpu)
+            print 'saveerr =', saveerr, ': got result =', result
+            #
+            if saveerr == rffi.RFFI_SAVE_LASTERROR:
+                assert result == 42      # from the C code
+            else:
+                assert result == 24      # not touched
+            assert original_result == 3456789
+
+    def test_call_release_gil_readsaved_lasterror(self):
+        from rpython.translator.tool.cbuild import ExternalCompilationInfo
+        from rpython.rlib.libffi import types
+        from rpython.jit.backend.llsupport import llerrno
+        #
+        if not isinstance(self.cpu, AbstractLLCPU):
+            py.test.skip("not on LLGraph")
+        if sys.platform != 'win32':
+            py.test.skip("Windows test only")
+        eci = ExternalCompilationInfo(
+            separate_module_sources=[r'''
+                #include <windows.h>
+                static long f1(long a, long b, long c, long d,
+                               long e, long f, long g) {
+                    long r = GetLastError();
+                    printf("GetLastError() result: %ld\n", r);
+                    printf("%ld %ld %ld %ld %ld %ld %ld\n", a,b,c,d,e,f,g);
+                    r += 100 * (a + 10*b + 100*c + 1000*d +
+                                10000*e + 100000*f + 1000000*g);
+                    return r;
+                }
+                RPY_EXPORTED
+                long test_call_release_gil_readsaved_lasterror(void) {
+                    return (long)&f1;
+                }
+            '''])
+        fn_name = 'test_call_release_gil_readsaved_lasterror'
+        getter_ptr = rffi.llexternal(fn_name, [], lltype.Signed,
+                                     compilation_info=eci, _nowrapper=True)
+        func1_adr = getter_ptr()
+        calldescr = self.cpu._calldescr_dynamic_for_tests([types.slong]*7,
+                                                          types.slong)
+        #
+        for saveerr in [rffi.RFFI_READSAVED_LASTERROR]:
+            faildescr = BasicFailDescr(1)
+            inputargs = [BoxInt() for i in range(7)]
+            i1 = BoxInt()
+            ops = [
+                ResOperation(rop.CALL_RELEASE_GIL,
+                             [ConstInt(saveerr), ConstInt(func1_adr)]
+                                 + inputargs, i1,
+                             descr=calldescr),
+                ResOperation(rop.GUARD_NOT_FORCED, [], None, descr=faildescr),
+                ResOperation(rop.FINISH, [i1], None, descr=BasicFinalDescr(0))
+            ]
+            ops[-2].setfailargs([])
+            looptoken = JitCellToken()
+            self.cpu.compile_loop(inputargs, ops, looptoken)
+            #
+            llerrno.set_debug_saved_lasterror(self.cpu, 24)
+            deadframe = self.cpu.execute_token(looptoken, 9, 8, 7, 6, 5, 4, 3)
+            result = self.cpu.get_int_value(deadframe, 0)
+            assert llerrno.get_debug_saved_lasterror(self.cpu) == 24
+            #
+            assert result == 24 + 345678900
+
+    def test_call_release_gil_err_all(self):
+        from rpython.translator.tool.cbuild import ExternalCompilationInfo
+        from rpython.rlib.libffi import types
+        from rpython.jit.backend.llsupport import llerrno
+        #
+        if not isinstance(self.cpu, AbstractLLCPU):
+            py.test.skip("not on LLGraph")
+        if sys.platform != 'win32':
+            eci = ExternalCompilationInfo(
+                separate_module_sources=[r'''
+                    #include <errno.h>
+                    static long f1(long a, long b, long c, long d,
+                                   long e, long f, long g) {
+                        long r = errno;
+                        errno = 42;
+                        r += 100 * (a + 10*b + 100*c + 1000*d +
+                                    10000*e + 100000*f + 1000000*g);
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to