Author: Armin Rigo <ar...@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); } _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit