summaryrefslogtreecommitdiffstats
path: root/Programs
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2017-11-30 22:36:49 (GMT)
committerGitHub <noreply@github.com>2017-11-30 22:36:49 (GMT)
commite10c9de9d74fd4c26b32e6719d96f04a5be6987d (patch)
treed52af6d26b53ff529cfab22ea50bb6b5faea7a03 /Programs
parent29cb50ba347d9dc18e0720bef8e9caedd012a3cd (diff)
downloadcpython-e10c9de9d74fd4c26b32e6719d96f04a5be6987d.zip
cpython-e10c9de9d74fd4c26b32e6719d96f04a5be6987d.tar.gz
cpython-e10c9de9d74fd4c26b32e6719d96f04a5be6987d.tar.bz2
bpo-20891: Fix PyGILState_Ensure() (#4650) (#4655)
When PyGILState_Ensure() is called in a non-Python thread before PyEval_InitThreads(), only call PyEval_InitThreads() after calling PyThreadState_New() to fix a crash. Add an unit test in test_embed. Enhance also embedded tests, backport from master: * Add test_pre_initialization_api() * Set PYTHONIOENCODING environment variable in test_forced_io_encoding() (cherry picked from commit b4d1e1f7c1af6ae33f0e371576c8bcafedb099db)
Diffstat (limited to 'Programs')
-rw-r--r--Programs/_testembed.c128
1 files changed, 117 insertions, 11 deletions
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 3968399..b0f9087 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -1,4 +1,5 @@
#include <Python.h>
+#include "pythread.h"
#include <stdio.h>
/*********************************************************
@@ -33,7 +34,7 @@ static void print_subinterp(void)
);
}
-static void test_repeated_init_and_subinterpreters(void)
+static int test_repeated_init_and_subinterpreters(void)
{
PyThreadState *mainstate, *substate;
#ifdef WITH_THREAD
@@ -70,6 +71,7 @@ static void test_repeated_init_and_subinterpreters(void)
PyEval_RestoreThread(mainstate);
Py_Finalize();
}
+ return 0;
}
/*****************************************************
@@ -103,7 +105,7 @@ static void check_stdio_details(const char *encoding, const char * errors)
Py_Finalize();
}
-static void test_forced_io_encoding(void)
+static int test_forced_io_encoding(void)
{
/* Check various combinations */
printf("--- Use defaults ---\n");
@@ -122,19 +124,123 @@ static void test_forced_io_encoding(void)
printf("Unexpected success calling Py_SetStandardStreamEncoding");
}
Py_Finalize();
+ return 0;
}
-/* Different embedding tests */
-int main(int argc, char *argv[])
+
+/*********************************************************
+ * Test parts of the C-API that work before initialization
+ *********************************************************/
+
+static int test_pre_initialization_api(void)
{
+ /* Leading "./" ensures getpath.c can still find the standard library */
+ wchar_t *program = Py_DecodeLocale("./spam", NULL);
+ if (program == NULL) {
+ fprintf(stderr, "Fatal error: cannot decode program name\n");
+ return 1;
+ }
+ Py_SetProgramName(program);
- /* TODO: Check the argument string to allow for more test cases */
- if (argc > 1) {
- /* For now: assume "forced_io_encoding */
- test_forced_io_encoding();
- } else {
- /* Run the original embedding test case by default */
- test_repeated_init_and_subinterpreters();
+ Py_Initialize();
+ Py_Finalize();
+
+ PyMem_RawFree(program);
+ return 0;
+}
+
+static void bpo20891_thread(void *lockp)
+{
+ PyThread_type_lock lock = *((PyThread_type_lock*)lockp);
+
+ PyGILState_STATE state = PyGILState_Ensure();
+ if (!PyGILState_Check()) {
+ fprintf(stderr, "PyGILState_Check failed!");
+ abort();
+ }
+
+ PyGILState_Release(state);
+
+ PyThread_release_lock(lock);
+
+ PyThread_exit_thread();
+}
+
+static int test_bpo20891(void)
+{
+ /* bpo-20891: Calling PyGILState_Ensure in a non-Python thread before
+ calling PyEval_InitThreads() must not crash. PyGILState_Ensure() must
+ call PyEval_InitThreads() for us in this case. */
+ PyThread_type_lock lock = PyThread_allocate_lock();
+ if (!lock) {
+ fprintf(stderr, "PyThread_allocate_lock failed!");
+ return 1;
+ }
+
+ _testembed_Py_Initialize();
+
+ long thrd = PyThread_start_new_thread(bpo20891_thread, &lock);
+ if (thrd == -1) {
+ fprintf(stderr, "PyThread_start_new_thread failed!");
+ return 1;
}
+ PyThread_acquire_lock(lock, WAIT_LOCK);
+
+ Py_BEGIN_ALLOW_THREADS
+ /* wait until the thread exit */
+ PyThread_acquire_lock(lock, WAIT_LOCK);
+ Py_END_ALLOW_THREADS
+
+ PyThread_free_lock(lock);
+
return 0;
}
+
+
+/* *********************************************************
+ * List of test cases and the function that implements it.
+ *
+ * Names are compared case-sensitively with the first
+ * argument. If no match is found, or no first argument was
+ * provided, the names of all test cases are printed and
+ * the exit code will be -1.
+ *
+ * The int returned from test functions is used as the exit
+ * code, and test_capi treats all non-zero exit codes as a
+ * failed test.
+ *********************************************************/
+struct TestCase
+{
+ const char *name;
+ int (*func)(void);
+};
+
+static struct TestCase TestCases[] = {
+ { "forced_io_encoding", test_forced_io_encoding },
+ { "repeated_init_and_subinterpreters", test_repeated_init_and_subinterpreters },
+ { "pre_initialization_api", test_pre_initialization_api },
+ { "bpo20891", test_bpo20891 },
+ { NULL, NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1) {
+ for (struct TestCase *tc = TestCases; tc && tc->name; tc++) {
+ if (strcmp(argv[1], tc->name) == 0)
+ return (*tc->func)();
+ }
+ }
+
+ /* No match found, or no test name provided, so display usage */
+ printf("Python " PY_VERSION " _testembed executable for embedded interpreter tests\n"
+ "Normally executed via 'EmbeddingTests' in Lib/test/test_capi.py\n\n"
+ "Usage: %s TESTNAME\n\nAll available tests:\n", argv[0]);
+ for (struct TestCase *tc = TestCases; tc && tc->name; tc++) {
+ printf(" %s\n", tc->name);
+ }
+
+ /* Non-zero exit code will cause test_capi.py tests to fail.
+ This is intentional. */
+ return -1;
+}