[pypy-commit] cffi default: hg merge windows-tls
arigo
pypy.commits at gmail.com
Thu Jan 7 10:37:24 EST 2016
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r2537:c1adfab5014d
Date: 2016-01-07 16:37 +0100
http://bitbucket.org/cffi/cffi/changeset/c1adfab5014d/
Log: hg merge windows-tls
Port to Windows the code to avoid creating and destroying
PyThreadStates all the time
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,136 @@
+#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;
+
+#ifndef USE__THREAD
+ /* The saved errno. If the C compiler supports '__thread', then
+ we use that instead. */
+ int saved_errno;
+#endif
+
+#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,15 +1,37 @@
#include <malloc.h> /* for alloca() */
+
/************************************************************/
/* errno and GetLastError support */
-struct cffi_errno_s {
- int saved_errno;
- int saved_lasterror;
-};
+#include "misc_thread_common.h"
static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES;
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+ DWORD reason_for_call,
+ LPVOID reserved)
+{
+ LPVOID p;
+
+ switch (reason_for_call) {
+
+ case DLL_THREAD_DETACH:
+ if (cffi_tls_index != TLS_OUT_OF_INDEXES) {
+ p = TlsGetValue(cffi_tls_index);
+ if (p != NULL) {
+ TlsSetValue(cffi_tls_index, NULL);
+ cffi_thread_shutdown(p);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return TRUE;
+}
+
static void init_cffi_tls(void)
{
if (cffi_tls_index == TLS_OUT_OF_INDEXES) {
@@ -19,28 +41,29 @@
}
}
-static struct cffi_errno_s *_geterrno_object(void)
+static struct cffi_tls_s *get_cffi_tls(void)
{
LPVOID p = TlsGetValue(cffi_tls_index);
if (p == NULL) {
- /* XXX this malloc() leaks */
- p = malloc(sizeof(struct cffi_errno_s));
+ p = malloc(sizeof(struct cffi_tls_s));
if (p == NULL)
return NULL;
- memset(p, 0, sizeof(struct cffi_errno_s));
+ memset(p, 0, sizeof(struct cffi_tls_s));
TlsSetValue(cffi_tls_index, p);
}
- return (struct cffi_errno_s *)p;
+ return (struct cffi_tls_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;
@@ -48,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;
@@ -72,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)
@@ -96,8 +97,7 @@
return NULL;
if (err == -1) {
- struct cffi_errno_s *p;
- p = _geterrno_object();
+ struct cffi_tls_s *p = get_cffi_tls();
if (p == NULL)
return PyErr_NoMemory();
err = p->saved_lasterror;
@@ -138,7 +138,7 @@
int len;
char *s;
char *s_buf = NULL; /* Free via LocalFree */
- char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */
+ char s_small_buf[40]; /* Room for "Windows Error 0xFFFFFFFFFFFFFFFF" */
PyObject *v;
static char *keywords[] = {"code", NULL};
@@ -146,8 +146,7 @@
return NULL;
if (err == -1) {
- struct cffi_errno_s *p;
- p = _geterrno_object();
+ struct cffi_tls_s *p = get_cffi_tls();
if (p == NULL)
return PyErr_NoMemory();
err = p->saved_lasterror;
@@ -183,16 +182,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