[pypy-commit] cffi default: Issue #362

arigo pypy.commits at gmail.com
Thu Mar 15 03:36:54 EDT 2018


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r3115:e5f8ac3b8e6b
Date: 2018-03-15 08:36 +0100
http://bitbucket.org/cffi/cffi/changeset/e5f8ac3b8e6b/

Log:	Issue #362

	Py_Finalize() will free any threadstate around, so in that case we
	must not call PyThreadState_Delete() any more on them from
	cffi_thread_shutdown().

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -7490,6 +7490,9 @@
     init_cffi_tls();
     if (PyErr_Occurred())
         INITERROR;
+    init_cffi_tls_delete();
+    if (PyErr_Occurred())
+        INITERROR;
 
     if (init_ffi_lib(m) < 0)
         INITERROR;
diff --git a/c/ffi_obj.c b/c/ffi_obj.c
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -941,16 +941,6 @@
 #define ffi_memmove  b_memmove     /* ffi_memmove() => b_memmove()
                                       from _cffi_backend.c */
 
-#ifdef WITH_THREAD
-# include "pythread.h"
-#else
-typedef void *PyThread_type_lock;
-# define PyThread_allocate_lock()        ((void *)-1)
-# define PyThread_free_lock(lock)        ((void)(lock))
-# define PyThread_acquire_lock(lock, _)  ((void)(lock))
-# define PyThread_release_lock(lock)     ((void)(lock))
-#endif
-
 PyDoc_STRVAR(ffi_init_once_doc,
 "init_once(function, tag): run function() once.  More precisely,\n"
 "'function()' is called the first time we see a given 'tag'.\n"
diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h
--- a/c/misc_thread_common.h
+++ b/c/misc_thread_common.h
@@ -1,6 +1,7 @@
 #ifndef WITH_THREAD
 # error "xxx no-thread configuration not tested, please report if you need that"
 #endif
+#include "pythread.h"
 
 
 struct cffi_tls_s {
@@ -24,12 +25,79 @@
 static struct cffi_tls_s *get_cffi_tls(void);   /* in misc_thread_posix.h 
                                                    or misc_win32.h */
 
+
+/* issue #362: Py_Finalize() will free any threadstate around, so in
+ * that case we must not call PyThreadState_Delete() any more on them
+ * from cffi_thread_shutdown().  The following mess is to give a
+ * thread-safe way to know that Py_Finalize() started.
+ */
+#define TLS_DEL_LOCK() PyThread_acquire_lock(cffi_tls_delete_lock, WAIT_LOCK)
+#define TLS_DEL_UNLOCK() PyThread_release_lock(cffi_tls_delete_lock)
+static PyThread_type_lock cffi_tls_delete_lock = NULL;
+static int cffi_tls_delete;
+static PyObject *old_exitfunc;
+
+static PyObject *cffi_tls_shutdown(PyObject *self, PyObject *args)
+{
+    /* the lock here will wait until any parallel cffi_thread_shutdown()
+       is done.  Future cffi_thread_shutdown() won't touch their
+       PyThreadState any more, which are all supposed to be freed anyway
+       very soon after the present cffi_tls_shutdown() function is called.
+     */
+    TLS_DEL_LOCK();
+    cffi_tls_delete = 0;   /* Py_Finalize() called */
+    TLS_DEL_UNLOCK();
+
+    PyObject *ofn = old_exitfunc;
+    if (ofn == NULL)
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    else
+    {
+        old_exitfunc = NULL;
+        return PyObject_CallFunction(ofn, "");
+    }
+}
+
+static void init_cffi_tls_delete(void)
+{
+    static PyMethodDef mdef = {
+        "cffi_tls_shutdown", cffi_tls_shutdown, METH_NOARGS,
+    };
+    PyObject *shutdown_fn;
+
+    cffi_tls_delete_lock = PyThread_allocate_lock();
+    if (cffi_tls_delete_lock == NULL)
+    {
+        PyErr_SetString(PyExc_SystemError,
+                        "can't allocate cffi_tls_delete_lock");
+        return;
+    }
+
+    shutdown_fn = PyCFunction_New(&mdef, NULL);
+    if (shutdown_fn == NULL)
+        return;
+
+    old_exitfunc = PySys_GetObject("exitfunc");
+    if (PySys_SetObject("exitfunc", shutdown_fn) == 0)
+        cffi_tls_delete = 1;    /* all ready */
+    Py_DECREF(shutdown_fn);
+}
+
 static void cffi_thread_shutdown(void *p)
 {
     struct cffi_tls_s *tls = (struct cffi_tls_s *)p;
 
     if (tls->local_thread_state != NULL) {
-        PyThreadState_Delete(tls->local_thread_state);
+        /*
+         *  issue #362: see comments above
+         */
+        TLS_DEL_LOCK();
+        if (cffi_tls_delete)
+            PyThreadState_Delete(tls->local_thread_state);
+        TLS_DEL_UNLOCK();
     }
     free(tls);
 }


More information about the pypy-commit mailing list