diff options
Diffstat (limited to 'Modules/_testcapimodule.c')
-rw-r--r-- | Modules/_testcapimodule.c | 54 |
1 files changed, 42 insertions, 12 deletions
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 263c61e..60c71d7 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -10,7 +10,6 @@ #ifdef WITH_THREAD #include "pythread.h" #endif /* WITH_THREAD */ - static PyObject *TestError; /* set to exception object in init */ /* Raise TestError with test_name + ": " + msg, and return NULL. */ @@ -482,7 +481,7 @@ static PyObject *codec_incrementalencoder(PyObject *self, PyObject *args) { const char *encoding, *errors = NULL; - if (!PyArg_ParseTuple(args, "s|s:test_incrementalencoder", + if (!PyArg_ParseTuple(args, "s|s:test_incrementalencoder", &encoding, &errors)) return NULL; return PyCodec_IncrementalEncoder(encoding, errors); @@ -492,7 +491,7 @@ static PyObject *codec_incrementaldecoder(PyObject *self, PyObject *args) { const char *encoding, *errors = NULL; - if (!PyArg_ParseTuple(args, "s|s:test_incrementaldecoder", + if (!PyArg_ParseTuple(args, "s|s:test_incrementaldecoder", &encoding, &errors)) return NULL; return PyCodec_IncrementalDecoder(encoding, errors); @@ -583,7 +582,17 @@ raise_exception(PyObject *self, PyObject *args) #ifdef WITH_THREAD -void _make_call(void *callable) +/* test_thread_state spawns a thread of its own, and that thread releases + * `thread_done` when it's finished. The driver code has to know when the + * thread finishes, because the thread uses a PyObject (the callable) that + * may go away when the driver finishes. The former lack of this explicit + * synchronization caused rare segfaults, so rare that they were seen only + * on a Mac buildbot (although they were possible on any box). + */ +static PyThread_type_lock thread_done = NULL; + +static void +_make_call(void *callable) { PyObject *rc; PyGILState_STATE s = PyGILState_Ensure(); @@ -592,32 +601,53 @@ void _make_call(void *callable) PyGILState_Release(s); } +/* Same thing, but releases `thread_done` when it returns. This variant + * should be called only from threads spawned by test_thread_state(). + */ +static void +_make_call_from_thread(void *callable) +{ + _make_call(callable); + PyThread_release_lock(thread_done); +} + static PyObject * test_thread_state(PyObject *self, PyObject *args) { PyObject *fn; + if (!PyArg_ParseTuple(args, "O:test_thread_state", &fn)) return NULL; - /* Ensure Python is setup for threading */ + + /* Ensure Python is set up for threading */ PyEval_InitThreads(); - /* Start a new thread for our callback. */ - PyThread_start_new_thread( _make_call, fn); + thread_done = PyThread_allocate_lock(); + if (thread_done == NULL) + return PyErr_NoMemory(); + PyThread_acquire_lock(thread_done, 1); + + /* Start a new thread with our callback. */ + PyThread_start_new_thread(_make_call_from_thread, fn); /* Make the callback with the thread lock held by this thread */ _make_call(fn); /* Do it all again, but this time with the thread-lock released */ Py_BEGIN_ALLOW_THREADS _make_call(fn); + PyThread_acquire_lock(thread_done, 1); /* wait for thread to finish */ Py_END_ALLOW_THREADS + /* And once more with and without a thread - XXX - should use a lock and work out exactly what we are trying - to test <wink> + XXX - should use a lock and work out exactly what we are trying + to test <wink> */ Py_BEGIN_ALLOW_THREADS - PyThread_start_new_thread( _make_call, fn); + PyThread_start_new_thread(_make_call_from_thread, fn); _make_call(fn); + PyThread_acquire_lock(thread_done, 1); /* wait for thread to finish */ Py_END_ALLOW_THREADS - Py_INCREF(Py_None); - return Py_None; + + PyThread_free_lock(thread_done); + Py_RETURN_NONE; } #endif |