[pypy-commit] cffi windows-tls: Generalize the code for 'local_thread_state' and move it to

arigo pypy.commits at gmail.com
Sun Jan 3 07:59:26 EST 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: windows-tls
Changeset: r2524:345a268931c3
Date: 2016-01-03 13:59 +0100
http://bitbucket.org/cffi/cffi/changeset/345a268931c3/

Log:	Generalize the code for 'local_thread_state' and move it to
	misc_thread_common.h.

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,134 @@
+#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;
+
+    /* 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;
+
+#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,18 +1,10 @@
 #include <malloc.h>   /* for alloca() */
 
+
 /************************************************************/
 /* errno and GetLastError support */
 
-struct cffi_errno_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 and lasterror. */
-    int saved_errno;
-    int saved_lasterror;
-};
+#include "misc_thread_common.h"
 
 static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES;
 
@@ -21,7 +13,6 @@
                     LPVOID    reserved)
 {
     LPVOID p;
-    struct cffi_errno_s *tls;
 
     switch (reason_for_call) {
 
@@ -29,20 +20,8 @@
         if (cffi_tls_index != TLS_OUT_OF_INDEXES) {
             p = TlsGetValue(cffi_tls_index);
             if (p != NULL) {
-                tls = (struct cffi_errno_s *)p;
-                fprintf(stderr, "thread shutting down! %p\n",
-                        tls->local_thread_state);
                 TlsSetValue(cffi_tls_index, NULL);
-
-                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
-                       DLL_THREAD_DETACH.
-                    */
-                    PyEval_RestoreThread(tls->local_thread_state);
-                    PyThreadState_DeleteCurrent();
-                }
-                free(tls);
+                cffi_thread_shutdown(p);
             }
         }
         break;
@@ -62,7 +41,7 @@
     }
 }
 
-static struct cffi_errno_s *_geterrno_object(void)
+static struct cffi_tls_s *get_cffi_tls(void)
 {
     LPVOID p = TlsGetValue(cffi_tls_index);
 
@@ -76,13 +55,15 @@
     return (struct cffi_errno_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;
@@ -90,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;
@@ -114,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)
@@ -225,16 +184,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