https://github.com/python/cpython/commit/e10c9de9d74fd4c26b32e6719d96f04a5b…
commit: e10c9de9d74fd4c26b32e6719d96f04a5be6987d
branch: 3.6
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-11-30T23:36:49+01:00
summary:
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)
files:
A Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
M Lib/test/test_capi.py
M Programs/_testembed.c
M Python/pystate.c
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 3a29b69f957..6e4286ed881 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -401,23 +401,30 @@ def setUp(self):
def tearDown(self):
os.chdir(self.oldcwd)
- def run_embedded_interpreter(self, *args):
+ def run_embedded_interpreter(self, *args, env=None):
"""Runs a test in the embedded interpreter"""
cmd = [self.test_exe]
cmd.extend(args)
+ if env is not None and sys.platform == 'win32':
+ # Windows requires at least the SYSTEMROOT environment variable to
+ # start Python.
+ env = env.copy()
+ env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
+
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
- universal_newlines=True)
+ universal_newlines=True,
+ env=env)
(out, err) = p.communicate()
self.assertEqual(p.returncode, 0,
"bad returncode %d, stderr is %r" %
(p.returncode, err))
return out, err
- def test_subinterps(self):
+ def test_repeated_init_and_subinterpreters(self):
# This is just a "don't crash" test
- out, err = self.run_embedded_interpreter()
+ out, err = self.run_embedded_interpreter('repeated_init_and_subinterpreters')
if support.verbose:
print()
print(out)
@@ -435,13 +442,14 @@ def _get_default_pipe_encoding():
def test_forced_io_encoding(self):
# Checks forced configuration of embedded interpreter IO streams
- out, err = self.run_embedded_interpreter("forced_io_encoding")
+ env = dict(os.environ, PYTHONIOENCODING="utf-8:surrogateescape")
+ out, err = self.run_embedded_interpreter("forced_io_encoding", env=env)
if support.verbose:
print()
print(out)
print(err)
- expected_errors = sys.__stdout__.errors
- expected_stdin_encoding = sys.__stdin__.encoding
+ expected_stream_encoding = "utf-8"
+ expected_errors = "surrogateescape"
expected_pipe_encoding = self._get_default_pipe_encoding()
expected_output = '\n'.join([
"--- Use defaults ---",
@@ -469,13 +477,33 @@ def test_forced_io_encoding(self):
"stdout: latin-1:replace",
"stderr: latin-1:backslashreplace"])
expected_output = expected_output.format(
- in_encoding=expected_stdin_encoding,
- out_encoding=expected_pipe_encoding,
+ in_encoding=expected_stream_encoding,
+ out_encoding=expected_stream_encoding,
errors=expected_errors)
# This is useful if we ever trip over odd platform behaviour
self.maxDiff = None
self.assertEqual(out.strip(), expected_output)
+ def test_pre_initialization_api(self):
+ """
+ Checks the few parts of the C-API that work before the runtine
+ is initialized (via Py_Initialize()).
+ """
+ env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
+ out, err = self.run_embedded_interpreter("pre_initialization_api", env=env)
+ self.assertEqual(out, '')
+ self.assertEqual(err, '')
+
+ def test_bpo20891(self):
+ """
+ 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.
+ """
+ out, err = self.run_embedded_interpreter("bpo20891")
+ self.assertEqual(out, '')
+ self.assertEqual(err, '')
+
class SkipitemTest(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst b/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
new file mode 100644
index 00000000000..e89cf1292af
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
@@ -0,0 +1,3 @@
+Fix PyGILState_Ensure(). 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.
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 39683993ea0..b0f9087115b 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;
+}
diff --git a/Python/pystate.c b/Python/pystate.c
index 65f9b7ea05d..c0e088055a6 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -865,6 +865,8 @@ PyGILState_Ensure(void)
{
int current;
PyThreadState *tcur;
+ int need_init_threads = 0;
+
/* Note that we do not auto-init Python here - apart from
potential races with 2 threads auto-initializing, pep-311
spells out other issues. Embedders are expected to have
@@ -873,10 +875,7 @@ PyGILState_Ensure(void)
assert(autoInterpreterState); /* Py_Initialize() hasn't been called! */
tcur = (PyThreadState *)PyThread_get_key_value(autoTLSkey);
if (tcur == NULL) {
- /* At startup, Python has no concrete GIL. If PyGILState_Ensure() is
- called from a new thread for the first time, we need the create the
- GIL. */
- PyEval_InitThreads();
+ need_init_threads = 1;
/* Create a new thread state for this thread */
tcur = PyThreadState_New(autoInterpreterState);
@@ -887,16 +886,28 @@ PyGILState_Ensure(void)
tcur->gilstate_counter = 0;
current = 0; /* new thread state is never current */
}
- else
+ else {
current = PyThreadState_IsCurrent(tcur);
- if (current == 0)
+ }
+
+ if (current == 0) {
PyEval_RestoreThread(tcur);
+ }
+
/* Update our counter in the thread-state - no need for locks:
- tcur will remain valid as we hold the GIL.
- the counter is safe as we are the only thread "allowed"
to modify this value
*/
++tcur->gilstate_counter;
+
+ if (need_init_threads) {
+ /* At startup, Python has no concrete GIL. If PyGILState_Ensure() is
+ called from a new thread for the first time, we need the create the
+ GIL. */
+ PyEval_InitThreads();
+ }
+
return current ? PyGILState_LOCKED : PyGILState_UNLOCKED;
}
https://github.com/python/cpython/commit/be6b74c0795b709c7a04e2187a7e32d08f…
commit: be6b74c0795b709c7a04e2187a7e32d08f5155f6
branch: 2.7
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-11-30T23:35:14+01:00
summary:
bpo-20891: Fix PyGILState_Ensure() (#4650) (#4657)
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.
(cherry picked from commit b4d1e1f7c1af6ae33f0e371576c8bcafedb099db)
files:
A Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
M Python/pystate.c
diff --git a/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst b/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
new file mode 100644
index 00000000000..e89cf1292af
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
@@ -0,0 +1,3 @@
+Fix PyGILState_Ensure(). 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.
diff --git a/Python/pystate.c b/Python/pystate.c
index eb992c17801..f33f1820236 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -588,6 +588,8 @@ PyGILState_Ensure(void)
{
int current;
PyThreadState *tcur;
+ int need_init_threads = 0;
+
/* Note that we do not auto-init Python here - apart from
potential races with 2 threads auto-initializing, pep-311
spells out other issues. Embedders are expected to have
@@ -596,6 +598,8 @@ PyGILState_Ensure(void)
assert(autoInterpreterState); /* Py_Initialize() hasn't been called! */
tcur = (PyThreadState *)PyThread_get_key_value(autoTLSkey);
if (tcur == NULL) {
+ need_init_threads = 1;
+
/* Create a new thread state for this thread */
tcur = PyThreadState_New(autoInterpreterState);
if (tcur == NULL)
@@ -605,16 +609,28 @@ PyGILState_Ensure(void)
tcur->gilstate_counter = 0;
current = 0; /* new thread state is never current */
}
- else
+ else {
current = PyThreadState_IsCurrent(tcur);
- if (current == 0)
+ }
+
+ if (current == 0) {
PyEval_RestoreThread(tcur);
+ }
+
/* Update our counter in the thread-state - no need for locks:
- tcur will remain valid as we hold the GIL.
- the counter is safe as we are the only thread "allowed"
to modify this value
*/
++tcur->gilstate_counter;
+
+ if (need_init_threads) {
+ /* At startup, Python has no concrete GIL. If PyGILState_Ensure() is
+ called from a new thread for the first time, we need the create the
+ GIL. */
+ PyEval_InitThreads();
+ }
+
return current ? PyGILState_LOCKED : PyGILState_UNLOCKED;
}
https://github.com/python/cpython/commit/29cb50ba347d9dc18e0720bef8e9caedd0…
commit: 29cb50ba347d9dc18e0720bef8e9caedd012a3cd
branch: 3.6
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-11-30T23:34:21+01:00
summary:
[3.6] make tags: index also Modules/_ctypes/ (#4648) (#4659)
* `make tags` fixes (GH-717)
* Fix `make tags` warnings
`make tags` target tries to find C sources and headers in "Grammar" and
"Mac" folders and generates these warnings:
ctags: Warning: cannot open source file "Grammar/*.[ch]" : No such file or directory
ctags: Warning: cannot open source file "Mac/*.[ch]" : No such file or directory
This commit changes $SRCDIRS variable in configure.ac to remote these
directories. This variable is used only for tags generation.
Also, "configure" was regenerated with `autoreconf`.
* Fix `make tags` fail on non-default tag names
When ctags overrides default tags filename (e.g. `-f .tags`) `make tags`
is failed because it assumes to see default `tags` filename:
sort: cannot read: tags: No such file or directory
This commit explicitly specifies "tags" filename for tags generation.
(cherry picked from commit 8a543c0bc7347d5b333f334d157bf4a7cd33c14a)
* make tags: index also Modules/_ctypes/ (#4648)
Avoid also "cd $(srcdir)" to not change the current directory.
(cherry picked from commit 3be3b97a9709d3cd5303175ddbffa7dcca57ac3e)
files:
M Makefile.pre.in
M configure
M configure.ac
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 2b647a8e588..5e88e3f6aa1 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1599,10 +1599,9 @@ autoconf:
# Create a tags file for vi
tags::
- cd $(srcdir); \
- ctags -w Include/*.h; \
- for i in $(SRCDIRS); do ctags -w -a $$i/*.[ch]; \
- done; \
+ ctags -w $(srcdir)/Include/*.h
+ for i in $(SRCDIRS); do ctags -f tags -w -a $(srcdir)/$$i/*.[ch]; done
+ ctags -f tags -w -a $(srcdir)/Modules/_ctypes/*.[ch]
LC_ALL=C sort -o tags tags
# Create a tags file for GNU Emacs
diff --git a/configure b/configure
index 24d86f59ebc..ea1baef55d3 100755
--- a/configure
+++ b/configure
@@ -16553,7 +16553,7 @@ do
done
-SRCDIRS="Parser Grammar Objects Python Modules Mac Programs"
+SRCDIRS="Parser Objects Python Modules Programs"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for build directories" >&5
$as_echo_n "checking for build directories... " >&6; }
for dir in $SRCDIRS; do
diff --git a/configure.ac b/configure.ac
index 9a784e4636f..fc1dba6b029 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5302,7 +5302,7 @@ do
done
AC_SUBST(SRCDIRS)
-SRCDIRS="Parser Grammar Objects Python Modules Mac Programs"
+SRCDIRS="Parser Objects Python Modules Programs"
AC_MSG_CHECKING(for build directories)
for dir in $SRCDIRS; do
if test ! -d $dir; then
https://github.com/python/cpython/commit/7d1cfeef6486af07de832b3eae27e3f635…
commit: 7d1cfeef6486af07de832b3eae27e3f63532c270
branch: 2.7
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-11-30T23:28:01+01:00
summary:
make tags: index also Modules/_ctypes/ (#4648) (#4660)
Avoid also "cd $(srcdir)" to not change the current directory.
(cherry picked from commit 3be3b97a9709d3cd5303175ddbffa7dcca57ac3e)
files:
M Makefile.pre.in
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 19e8f55b2ab..9297e7fc89c 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1378,10 +1378,9 @@ autoconf:
# Create a tags file for vi
tags::
- cd $(srcdir); \
- ctags -w Include/*.h; \
- for i in $(SRCDIRS); do ctags -f tags -w -a $$i/*.[ch]; \
- done; \
+ ctags -w $(srcdir)/Include/*.h
+ for i in $(SRCDIRS); do ctags -f tags -w -a $(srcdir)/$$i/*.[ch]; done
+ ctags -f tags -w -a $(srcdir)/Modules/_ctypes/*.[ch]
LC_ALL=C sort -o tags tags
# Create a tags file for GNU Emacs
https://github.com/python/cpython/commit/c319eeeb45043ee45384b8064c53ddbfde…
commit: c319eeeb45043ee45384b8064c53ddbfde1673cd
branch: master
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-11-30T23:03:47+01:00
summary:
Fix CID-1420310: cast PY_TIMEOUT_MAX to _Py_time_t (#4646)
Fix the following false-alarm Coverity warning:
Result is not floating-point
(UNINTENDED_INTEGER_DIVISION)integer_division: Dividing integer
expressions 9223372036854775807LL and 1000LL, and then converting
the integer quotient to type double. Any remainder, or fractional
part of the quotient, is ignored.
To compute and use a non-integer quotient, change or cast either
operand to type double. If integer division is intended, consider
indicating that by casting the result to type long long .
files:
M Modules/_threadmodule.c
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index aaa13da890b..c9171f52e38 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -1363,7 +1363,7 @@ PyInit__thread(void)
if (m == NULL)
return NULL;
- timeout_max = (double)PY_TIMEOUT_MAX * 1e-6;
+ timeout_max = (_PyTime_t)PY_TIMEOUT_MAX * 1e-6;
time_max = _PyTime_AsSecondsDouble(_PyTime_MAX);
timeout_max = Py_MIN(timeout_max, time_max);
/* Round towards minus infinity */
https://github.com/python/cpython/commit/3be3b97a9709d3cd5303175ddbffa7dcca…
commit: 3be3b97a9709d3cd5303175ddbffa7dcca57ac3e
branch: master
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-11-30T22:49:10+01:00
summary:
make tags: index also Modules/_ctypes/ (#4648)
Avoid also "cd $(srcdir)" to not change the current directory.
files:
M Makefile.pre.in
diff --git a/Makefile.pre.in b/Makefile.pre.in
index d196d5f838e..f425a89173a 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1605,10 +1605,9 @@ autoconf:
# Create a tags file for vi
tags::
- cd $(srcdir); \
- ctags -w Include/*.h Include/internal/*.h; \
- for i in $(SRCDIRS); do ctags -f tags -w -a $$i/*.[ch]; \
- done; \
+ ctags -w $(srcdir)/Include/*.h $(srcdir)/Include/internal/*.h
+ for i in $(SRCDIRS); do ctags -f tags -w -a $(srcdir)/$$i/*.[ch]; done
+ ctags -f tags -w -a $(srcdir)/Modules/_ctypes/*.[ch]
LC_ALL=C sort -o tags tags
# Create a tags file for GNU Emacs
https://github.com/python/cpython/commit/b4d1e1f7c1af6ae33f0e371576c8bcafed…
commit: b4d1e1f7c1af6ae33f0e371576c8bcafedb099db
branch: master
author: Victor Stinner <victor.stinner(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2017-11-30T22:05:00+01:00
summary:
bpo-20891: Fix PyGILState_Ensure() (#4650)
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.
files:
A Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
M Doc/c-api/init.rst
M Lib/test/test_embed.py
M Programs/_testembed.c
M Python/pystate.c
diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst
index 2f77bb359d2..a9927aba5e1 100644
--- a/Doc/c-api/init.rst
+++ b/Doc/c-api/init.rst
@@ -58,8 +58,9 @@ The following functions can be safely called before Python is initialized:
The following functions **should not be called** before
:c:func:`Py_Initialize`: :c:func:`Py_EncodeLocale`, :c:func:`Py_GetPath`,
- :c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix` and
- :c:func:`Py_GetProgramFullPath` and :c:func:`Py_GetPythonHome`.
+ :c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`,
+ :c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome` and
+ :c:func:`PyEval_InitThreads`.
.. _global-conf-vars:
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 8d44543ffd6..c7f45b59acc 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -198,6 +198,16 @@ def test_pre_initialization_api(self):
self.assertEqual(out, '')
self.assertEqual(err, '')
+ def test_bpo20891(self):
+ """
+ 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.
+ """
+ out, err = self.run_embedded_interpreter("bpo20891")
+ self.assertEqual(out, '')
+ self.assertEqual(err, '')
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst b/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
new file mode 100644
index 00000000000..e89cf1292af
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2017-11-30-18-13-45.bpo-20891.wBnMdF.rst
@@ -0,0 +1,3 @@
+Fix PyGILState_Ensure(). 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.
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 21aa76e9de3..a528f3e3aa0 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -1,4 +1,5 @@
#include <Python.h>
+#include "pythread.h"
#include <inttypes.h>
#include <stdio.h>
@@ -147,6 +148,53 @@ static int test_pre_initialization_api(void)
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();
+
+ unsigned long thrd = PyThread_start_new_thread(bpo20891_thread, &lock);
+ if (thrd == PYTHREAD_INVALID_THREAD_ID) {
+ 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.
@@ -170,6 +218,7 @@ 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 }
};
diff --git a/Python/pystate.c b/Python/pystate.c
index 0fb8ed07195..500f9676875 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -922,6 +922,8 @@ PyGILState_Ensure(void)
{
int current;
PyThreadState *tcur;
+ int need_init_threads = 0;
+
/* Note that we do not auto-init Python here - apart from
potential races with 2 threads auto-initializing, pep-311
spells out other issues. Embedders are expected to have
@@ -929,12 +931,10 @@ PyGILState_Ensure(void)
*/
/* Py_Initialize() hasn't been called! */
assert(_PyRuntime.gilstate.autoInterpreterState);
+
tcur = (PyThreadState *)PyThread_tss_get(&_PyRuntime.gilstate.autoTSSkey);
if (tcur == NULL) {
- /* At startup, Python has no concrete GIL. If PyGILState_Ensure() is
- called from a new thread for the first time, we need the create the
- GIL. */
- PyEval_InitThreads();
+ need_init_threads = 1;
/* Create a new thread state for this thread */
tcur = PyThreadState_New(_PyRuntime.gilstate.autoInterpreterState);
@@ -945,16 +945,28 @@ PyGILState_Ensure(void)
tcur->gilstate_counter = 0;
current = 0; /* new thread state is never current */
}
- else
+ else {
current = PyThreadState_IsCurrent(tcur);
- if (current == 0)
+ }
+
+ if (current == 0) {
PyEval_RestoreThread(tcur);
+ }
+
/* Update our counter in the thread-state - no need for locks:
- tcur will remain valid as we hold the GIL.
- the counter is safe as we are the only thread "allowed"
to modify this value
*/
++tcur->gilstate_counter;
+
+ if (need_init_threads) {
+ /* At startup, Python has no concrete GIL. If PyGILState_Ensure() is
+ called from a new thread for the first time, we need the create the
+ GIL. */
+ PyEval_InitThreads();
+ }
+
return current ? PyGILState_LOCKED : PyGILState_UNLOCKED;
}