summaryrefslogtreecommitdiff
path: root/c/misc_thread_common.h
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2016-01-03 13:59:17 +0100
committerArmin Rigo <arigo@tunes.org>2016-01-03 13:59:17 +0100
commit7304a70b2d3a2cbf4b5feb7bc0ebed748b886b53 (patch)
tree085f5953c17c74f4fd91b1eef8a5af5dd45a33f4 /c/misc_thread_common.h
parent6afbc3b0c34f76aaa90b2d75e04a5c131d990052 (diff)
downloadcffi-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.h134
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);
+}