[pypy-commit] cffi default: hg merge windows-tls

arigo pypy.commits at gmail.com
Thu Jan 7 10:37:24 EST 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2537:c1adfab5014d
Date: 2016-01-07 16:37 +0100
http://bitbucket.org/cffi/cffi/changeset/c1adfab5014d/

Log:	hg merge windows-tls

	Port to Windows the code to avoid creating and destroying
	PyThreadStates all the time

diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h
new file mode 100644
--- /dev/null
+++ b/c/misc_thread_common.h
@@ -0,0 +1,136 @@
+#ifndef WITH_THREAD
+# error "xxx no-thread configuration not tested, please report if you need that"
+#endif
+
+
+struct cffi_tls_s {
+    /* The locally-made thread state.  This is only non-null in case
+       we build the thread state here.  It remains null if this thread
+       had already a thread state provided by CPython. */
+    PyThreadState *local_thread_state;
+
+#ifndef USE__THREAD
+    /* The saved errno.  If the C compiler supports '__thread', then
+       we use that instead. */
+    int saved_errno;
+#endif
+
+#ifdef MS_WIN32
+    /* The saved lasterror, on Windows. */
+    int saved_lasterror;
+#endif
+};
+
+static struct cffi_tls_s *get_cffi_tls(void);   /* in misc_thread_posix.h 
+                                                   or misc_win32.h */
+
+static void cffi_thread_shutdown(void *p)
+{
+    struct cffi_tls_s *tls = (struct cffi_tls_s *)p;
+
+    if (tls->local_thread_state != NULL) {
+        /* We need to re-acquire the GIL temporarily to free the
+           thread state.  I hope it is not a problem to do it in
+           a thread-local destructor.
+        */
+        PyEval_RestoreThread(tls->local_thread_state);
+        PyThreadState_DeleteCurrent();
+    }
+    free(tls);
+}
+
+/* USE__THREAD is defined by setup.py if it finds that it is
+   syntactically valid to use "__thread" with this C compiler. */
+#ifdef USE__THREAD
+
+static __thread int cffi_saved_errno = 0;
+static void save_errno_only(void) { cffi_saved_errno = errno; }
+static void restore_errno_only(void) { errno = cffi_saved_errno; }
+
+#else
+
+static void save_errno_only(void)
+{
+    int saved = errno;
+    struct cffi_tls_s *tls = get_cffi_tls();
+    if (tls != NULL)
+        tls->saved_errno = saved;
+}
+
+static void restore_errno_only(void)
+{
+    struct cffi_tls_s *tls = get_cffi_tls();
+    if (tls != NULL)
+        errno = tls->saved_errno;
+}
+
+#endif
+
+
+/* Seems that CPython 3.5.1 made our job harder.  Did not find out how
+   to do that without these hacks.  We can't use PyThreadState_GET(),
+   because that calls PyThreadState_Get() which fails an assert if the
+   result is NULL. */
+#if PY_MAJOR_VERSION >= 3 && !defined(_Py_atomic_load_relaxed)
+                             /* this was abruptly un-defined in 3.5.1 */
+void *volatile _PyThreadState_Current;
+   /* XXX simple volatile access is assumed atomic */
+#  define _Py_atomic_load_relaxed(pp)  (*(pp))
+#endif
+
+static PyThreadState *get_current_ts(void)
+{
+#if PY_MAJOR_VERSION >= 3
+    return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
+#else
+    return _PyThreadState_Current;
+#endif
+}
+
+static PyGILState_STATE gil_ensure(void)
+{
+    /* Called at the start of a callback.  Replacement for
+       PyGILState_Ensure().
+    */
+    PyGILState_STATE result;
+    struct cffi_tls_s *tls;
+    PyThreadState *ts = PyGILState_GetThisThreadState();
+
+    if (ts != NULL) {
+        ts->gilstate_counter++;
+        if (ts != get_current_ts()) {
+            /* common case: 'ts' is our non-current thread state and
+               we have to make it current and acquire the GIL */
+            PyEval_RestoreThread(ts);
+            return PyGILState_UNLOCKED;
+        }
+        else {
+            return PyGILState_LOCKED;
+        }
+    }
+    else {
+        /* no thread state here so far. */
+        result = PyGILState_Ensure();
+        assert(result == PyGILState_UNLOCKED);
+
+        ts = PyGILState_GetThisThreadState();
+        assert(ts != NULL);
+        assert(ts == get_current_ts());
+        assert(ts->gilstate_counter >= 1);
+
+        /* Save the now-current thread state inside our 'local_thread_state'
+           field, to be removed at thread shutdown */
+        tls = get_cffi_tls();
+        if (tls != NULL) {
+            tls->local_thread_state = ts;
+            ts->gilstate_counter++;
+        }
+
+        return result;
+    }
+}
+
+static void gil_release(PyGILState_STATE oldstate)
+{
+    PyGILState_Release(oldstate);
+}
diff --git a/c/misc_thread_posix.h b/c/misc_thread_posix.h
--- a/c/misc_thread_posix.h
+++ b/c/misc_thread_posix.h
@@ -13,41 +13,15 @@
   shut down, using a destructor on the tls key.
 */
 
-#ifdef WITH_THREAD
 #include <pthread.h>
+#include "misc_thread_common.h"
 
 
 static pthread_key_t cffi_tls_key;
 
-struct cffi_tls_s {
-    /* The locally-made thread state.  This is only non-null in case
-       we build the thread state here.  It remains null if this thread
-       had already a thread state provided by CPython. */
-    PyThreadState *local_thread_state;
-
-    /* The saved errno.  If the C compiler supports '__thread', then
-       we use that instead; this value is not used at all in this case. */
-    int saved_errno;
-};
-
-static void _tls_destructor(void *p)
-{
-    struct cffi_tls_s *tls = (struct cffi_tls_s *)p;
-
-    if (tls->local_thread_state != NULL) {
-        /* We need to re-acquire the GIL temporarily to free the
-           thread state.  I hope it is not a problem to do it in
-           a thread-local destructor.
-        */
-        PyEval_RestoreThread(tls->local_thread_state);
-        PyThreadState_DeleteCurrent();
-    }
-    free(tls);
-}
-
 static void init_cffi_tls(void)
 {
-    if (pthread_key_create(&cffi_tls_key, _tls_destructor) != 0)
+    if (pthread_key_create(&cffi_tls_key, &cffi_thread_shutdown) != 0)
         PyErr_SetString(PyExc_OSError, "pthread_key_create() failed");
 }
 
@@ -71,116 +45,5 @@
     return (struct cffi_tls_s *)p;
 }
 
-
-/* USE__THREAD is defined by setup.py if it finds that it is
-   syntactically valid to use "__thread" with this C compiler. */
-#ifdef USE__THREAD
-
-static __thread int cffi_saved_errno = 0;
-static void save_errno(void) { cffi_saved_errno = errno; }
-static void restore_errno(void) { errno = cffi_saved_errno; }
-
-#else
-
-static void save_errno(void)
-{
-    int saved = errno;
-    struct cffi_tls_s *tls = get_cffi_tls();
-    if (tls != NULL)
-        tls->saved_errno = saved;
-}
-
-static void restore_errno(void)
-{
-    struct cffi_tls_s *tls = get_cffi_tls();
-    if (tls != NULL)
-        errno = tls->saved_errno;
-}
-
-#endif
-
-
-/* Seems that CPython 3.5.1 made our job harder.  Did not find out how
-   to do that without these hacks.  We can't use PyThreadState_GET(),
-   because that calls PyThreadState_Get() which fails an assert if the
-   result is NULL. */
-#if PY_MAJOR_VERSION >= 3 && !defined(_Py_atomic_load_relaxed)
-                             /* this was abruptly un-defined in 3.5.1 */
-void *volatile _PyThreadState_Current;
-   /* XXX simple volatile access is assumed atomic */
-#  define _Py_atomic_load_relaxed(pp)  (*(pp))
-#endif
-
-
-static PyThreadState *get_current_ts(void)
-{
-#if PY_MAJOR_VERSION >= 3
-    return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current);
-#else
-    return _PyThreadState_Current;
-#endif
-}
-
-static PyGILState_STATE gil_ensure(void)
-{
-    /* Called at the start of a callback.  Replacement for
-       PyGILState_Ensure().
-    */
-    PyGILState_STATE result;
-    struct cffi_tls_s *tls;
-    PyThreadState *ts = PyGILState_GetThisThreadState();
-
-    if (ts != NULL) {
-        ts->gilstate_counter++;
-        if (ts != get_current_ts()) {
-            /* common case: 'ts' is our non-current thread state and
-               we have to make it current and acquire the GIL */
-            PyEval_RestoreThread(ts);
-            return PyGILState_UNLOCKED;
-        }
-        else {
-            return PyGILState_LOCKED;
-        }
-    }
-    else {
-        /* no thread state here so far. */
-        result = PyGILState_Ensure();
-        assert(result == PyGILState_UNLOCKED);
-
-        ts = PyGILState_GetThisThreadState();
-        assert(ts != NULL);
-        assert(ts == get_current_ts());
-        assert(ts->gilstate_counter >= 1);
-
-        /* Save the now-current thread state inside our 'local_thread_state'
-           field, to be removed at thread shutdown */
-        tls = get_cffi_tls();
-        if (tls != NULL) {
-            tls->local_thread_state = ts;
-            ts->gilstate_counter++;
-        }
-
-        return result;
-    }
-}
-
-static void gil_release(PyGILState_STATE oldstate)
-{
-    PyGILState_Release(oldstate);
-}
-
-
-#else   /* !WITH_THREAD */
-
-static int cffi_saved_errno = 0;
-static void save_errno(void) { cffi_saved_errno = errno; }
-static void restore_errno(void) { errno = cffi_saved_errno; }
-
-static PyGILState_STATE gil_ensure(void) { return -1; }
-static void gil_release(PyGILState_STATE oldstate) { }
-
-#endif  /* !WITH_THREAD */
-
-
-#define save_errno_only      save_errno
-#define restore_errno_only   restore_errno
+#define save_errno      save_errno_only
+#define restore_errno   restore_errno_only
diff --git a/c/misc_win32.h b/c/misc_win32.h
--- a/c/misc_win32.h
+++ b/c/misc_win32.h
@@ -1,15 +1,37 @@
 #include <malloc.h>   /* for alloca() */
 
+
 /************************************************************/
 /* errno and GetLastError support */
 
-struct cffi_errno_s {
-    int saved_errno;
-    int saved_lasterror;
-};
+#include "misc_thread_common.h"
 
 static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES;
 
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+                    DWORD     reason_for_call,
+                    LPVOID    reserved)
+{
+    LPVOID p;
+
+    switch (reason_for_call) {
+
+    case DLL_THREAD_DETACH:
+        if (cffi_tls_index != TLS_OUT_OF_INDEXES) {
+            p = TlsGetValue(cffi_tls_index);
+            if (p != NULL) {
+                TlsSetValue(cffi_tls_index, NULL);
+                cffi_thread_shutdown(p);
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+    return TRUE;
+}
+
 static void init_cffi_tls(void)
 {
     if (cffi_tls_index == TLS_OUT_OF_INDEXES) {
@@ -19,28 +41,29 @@
     }
 }
 
-static struct cffi_errno_s *_geterrno_object(void)
+static struct cffi_tls_s *get_cffi_tls(void)
 {
     LPVOID p = TlsGetValue(cffi_tls_index);
 
     if (p == NULL) {
-        /* XXX this malloc() leaks */
-        p = malloc(sizeof(struct cffi_errno_s));
+        p = malloc(sizeof(struct cffi_tls_s));
         if (p == NULL)
             return NULL;
-        memset(p, 0, sizeof(struct cffi_errno_s));
+        memset(p, 0, sizeof(struct cffi_tls_s));
         TlsSetValue(cffi_tls_index, p);
     }
-    return (struct cffi_errno_s *)p;
+    return (struct cffi_tls_s *)p;
 }
 
+#ifdef USE__THREAD
+# error "unexpected USE__THREAD on Windows"
+#endif
+
 static void save_errno(void)
 {
     int current_err = errno;
     int current_lasterr = GetLastError();
-    struct cffi_errno_s *p;
-
-    p = _geterrno_object();
+    struct cffi_tls_s *p = get_cffi_tls();
     if (p != NULL) {
         p->saved_errno = current_err;
         p->saved_lasterror = current_lasterr;
@@ -48,23 +71,9 @@
     /* else: cannot report the error */
 }
 
-static void save_errno_only(void)
-{
-    int current_err = errno;
-    struct cffi_errno_s *p;
-
-    p = _geterrno_object();
-    if (p != NULL) {
-        p->saved_errno = current_err;
-    }
-    /* else: cannot report the error */
-}
-
 static void restore_errno(void)
 {
-    struct cffi_errno_s *p;
-
-    p = _geterrno_object();
+    struct cffi_tls_s *p = get_cffi_tls();
     if (p != NULL) {
         SetLastError(p->saved_lasterror);
         errno = p->saved_errno;
@@ -72,16 +81,8 @@
     /* else: cannot report the error */
 }
 
-static void restore_errno_only(void)
-{
-    struct cffi_errno_s *p;
+/************************************************************/
 
-    p = _geterrno_object();
-    if (p != NULL) {
-        errno = p->saved_errno;
-    }
-    /* else: cannot report the error */
-}
 
 #if PY_MAJOR_VERSION >= 3
 static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds)
@@ -96,8 +97,7 @@
         return NULL;
 
     if (err == -1) {
-        struct cffi_errno_s *p;
-        p = _geterrno_object();
+        struct cffi_tls_s *p = get_cffi_tls();
         if (p == NULL)
             return PyErr_NoMemory();
         err = p->saved_lasterror;
@@ -138,7 +138,7 @@
     int len;
     char *s;
     char *s_buf = NULL; /* Free via LocalFree */
-    char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */
+    char s_small_buf[40]; /* Room for "Windows Error 0xFFFFFFFFFFFFFFFF" */
     PyObject *v;
     static char *keywords[] = {"code", NULL};
 
@@ -146,8 +146,7 @@
         return NULL;
 
     if (err == -1) {
-        struct cffi_errno_s *p;
-        p = _geterrno_object();
+        struct cffi_tls_s *p = get_cffi_tls();
         if (p == NULL)
             return PyErr_NoMemory();
         err = p->saved_lasterror;
@@ -183,16 +182,6 @@
 #endif
 
 
-#ifdef WITH_THREAD
-/* XXX should port the code from misc_thread_posix.h */
-static PyGILState_STATE gil_ensure(void) { return PyGILState_Ensure(); }
-static void gil_release(PyGILState_STATE oldst) { PyGILState_Release(oldst); }
-#else
-static PyGILState_STATE gil_ensure(void) { return -1; }
-static void gil_release(PyGILState_STATE oldstate) { }
-#endif
-
-
 /************************************************************/
 /* Emulate dlopen()&co. from the Windows API */
 


More information about the pypy-commit mailing list