diff options
author | Armin Rigo <arigo@tunes.org> | 2016-01-03 13:59:17 +0100 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2016-01-03 13:59:17 +0100 |
commit | 7304a70b2d3a2cbf4b5feb7bc0ebed748b886b53 (patch) | |
tree | 085f5953c17c74f4fd91b1eef8a5af5dd45a33f4 /c/misc_thread_common.h | |
parent | 6afbc3b0c34f76aaa90b2d75e04a5c131d990052 (diff) | |
download | cffi-7304a70b2d3a2cbf4b5feb7bc0ebed748b886b53.tar.gz |
Generalize the code for 'local_thread_state' and move it to
misc_thread_common.h.
Diffstat (limited to 'c/misc_thread_common.h')
-rw-r--r-- | c/misc_thread_common.h | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h new file mode 100644 index 0000000..779de0b --- /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); +} |