bpo-20891: Fix PyGILState_Ensure() (#4650) (#4655) · python/cpython@e10c9de (original) (raw)
1
1
`#include <Python.h>
`
``
2
`+
#include "pythread.h"
`
2
3
`#include <stdio.h>
`
3
4
``
4
5
`/*********************************************************
`
`@@ -33,7 +34,7 @@ static void print_subinterp(void)
`
33
34
` );
`
34
35
`}
`
35
36
``
36
``
`-
static void test_repeated_init_and_subinterpreters(void)
`
``
37
`+
static int test_repeated_init_and_subinterpreters(void)
`
37
38
`{
`
38
39
`PyThreadState *mainstate, *substate;
`
39
40
`#ifdef WITH_THREAD
`
`@@ -70,6 +71,7 @@ static void test_repeated_init_and_subinterpreters(void)
`
70
71
`PyEval_RestoreThread(mainstate);
`
71
72
`Py_Finalize();
`
72
73
` }
`
``
74
`+
return 0;
`
73
75
`}
`
74
76
``
75
77
`/*****************************************************
`
`@@ -103,7 +105,7 @@ static void check_stdio_details(const char *encoding, const char * errors)
`
103
105
`Py_Finalize();
`
104
106
`}
`
105
107
``
106
``
`-
static void test_forced_io_encoding(void)
`
``
108
`+
static int test_forced_io_encoding(void)
`
107
109
`{
`
108
110
`/* Check various combinations */
`
109
111
`printf("--- Use defaults ---\n");
`
`@@ -122,19 +124,123 @@ static void test_forced_io_encoding(void)
`
122
124
`printf("Unexpected success calling Py_SetStandardStreamEncoding");
`
123
125
` }
`
124
126
`Py_Finalize();
`
``
127
`+
return 0;
`
125
128
`}
`
126
129
``
127
``
`-
/* Different embedding tests */
`
128
``
`-
int main(int argc, char *argv[])
`
``
130
+
``
131
`+
/*********************************************************
`
``
132
`+
- Test parts of the C-API that work before initialization
`
``
133
`+
*********************************************************/
`
``
134
+
``
135
`+
static int test_pre_initialization_api(void)
`
129
136
`{
`
``
137
`+
/* Leading "./" ensures getpath.c can still find the standard library */
`
``
138
`+
wchar_t *program = Py_DecodeLocale("./spam", NULL);
`
``
139
`+
if (program == NULL) {
`
``
140
`+
fprintf(stderr, "Fatal error: cannot decode program name\n");
`
``
141
`+
return 1;
`
``
142
`+
}
`
``
143
`+
Py_SetProgramName(program);
`
130
144
``
131
``
`-
/* TODO: Check the argument string to allow for more test cases */
`
132
``
`-
if (argc > 1) {
`
133
``
`-
/* For now: assume "forced_io_encoding */
`
134
``
`-
test_forced_io_encoding();
`
135
``
`-
} else {
`
136
``
`-
/* Run the original embedding test case by default */
`
137
``
`-
test_repeated_init_and_subinterpreters();
`
``
145
`+
Py_Initialize();
`
``
146
`+
Py_Finalize();
`
``
147
+
``
148
`+
PyMem_RawFree(program);
`
``
149
`+
return 0;
`
``
150
`+
}
`
``
151
+
``
152
`+
static void bpo20891_thread(void *lockp)
`
``
153
`+
{
`
``
154
`+
PyThread_type_lock lock = ((PyThread_type_lock)lockp);
`
``
155
+
``
156
`+
PyGILState_STATE state = PyGILState_Ensure();
`
``
157
`+
if (!PyGILState_Check()) {
`
``
158
`+
fprintf(stderr, "PyGILState_Check failed!");
`
``
159
`+
abort();
`
``
160
`+
}
`
``
161
+
``
162
`+
PyGILState_Release(state);
`
``
163
+
``
164
`+
PyThread_release_lock(lock);
`
``
165
+
``
166
`+
PyThread_exit_thread();
`
``
167
`+
}
`
``
168
+
``
169
`+
static int test_bpo20891(void)
`
``
170
`+
{
`
``
171
`+
/* bpo-20891: Calling PyGILState_Ensure in a non-Python thread before
`
``
172
`+
calling PyEval_InitThreads() must not crash. PyGILState_Ensure() must
`
``
173
`+
call PyEval_InitThreads() for us in this case. */
`
``
174
`+
PyThread_type_lock lock = PyThread_allocate_lock();
`
``
175
`+
if (!lock) {
`
``
176
`+
fprintf(stderr, "PyThread_allocate_lock failed!");
`
``
177
`+
return 1;
`
``
178
`+
}
`
``
179
+
``
180
`+
_testembed_Py_Initialize();
`
``
181
+
``
182
`+
long thrd = PyThread_start_new_thread(bpo20891_thread, &lock);
`
``
183
`+
if (thrd == -1) {
`
``
184
`+
fprintf(stderr, "PyThread_start_new_thread failed!");
`
``
185
`+
return 1;
`
138
186
` }
`
``
187
`+
PyThread_acquire_lock(lock, WAIT_LOCK);
`
``
188
+
``
189
`+
Py_BEGIN_ALLOW_THREADS
`
``
190
`+
/* wait until the thread exit */
`
``
191
`+
PyThread_acquire_lock(lock, WAIT_LOCK);
`
``
192
`+
Py_END_ALLOW_THREADS
`
``
193
+
``
194
`+
PyThread_free_lock(lock);
`
``
195
+
139
196
`return 0;
`
140
197
`}
`
``
198
+
``
199
+
``
200
`+
/* *********************************************************
`
``
201
`+
- List of test cases and the function that implements it.
`
``
202
`+
`
``
203
`+
- Names are compared case-sensitively with the first
`
``
204
`+
- argument. If no match is found, or no first argument was
`
``
205
`+
- provided, the names of all test cases are printed and
`
``
206
`+
- the exit code will be -1.
`
``
207
`+
`
``
208
`+
- The int returned from test functions is used as the exit
`
``
209
`+
- code, and test_capi treats all non-zero exit codes as a
`
``
210
`+
- failed test.
`
``
211
`+
*********************************************************/
`
``
212
`+
struct TestCase
`
``
213
`+
{
`
``
214
`+
const char *name;
`
``
215
`+
int (*func)(void);
`
``
216
`+
};
`
``
217
+
``
218
`+
static struct TestCase TestCases[] = {
`
``
219
`+
{ "forced_io_encoding", test_forced_io_encoding },
`
``
220
`+
{ "repeated_init_and_subinterpreters", test_repeated_init_and_subinterpreters },
`
``
221
`+
{ "pre_initialization_api", test_pre_initialization_api },
`
``
222
`+
{ "bpo20891", test_bpo20891 },
`
``
223
`+
{ NULL, NULL }
`
``
224
`+
};
`
``
225
+
``
226
`+
int main(int argc, char *argv[])
`
``
227
`+
{
`
``
228
`+
if (argc > 1) {
`
``
229
`+
for (struct TestCase *tc = TestCases; tc && tc->name; tc++) {
`
``
230
`+
if (strcmp(argv[1], tc->name) == 0)
`
``
231
`+
return (*tc->func)();
`
``
232
`+
}
`
``
233
`+
}
`
``
234
+
``
235
`+
/* No match found, or no test name provided, so display usage */
`
``
236
`+
printf("Python " PY_VERSION " _testembed executable for embedded interpreter tests\n"
`
``
237
`+
"Normally executed via 'EmbeddingTests' in Lib/test/test_capi.py\n\n"
`
``
238
`+
"Usage: %s TESTNAME\n\nAll available tests:\n", argv[0]);
`
``
239
`+
for (struct TestCase *tc = TestCases; tc && tc->name; tc++) {
`
``
240
`+
printf(" %s\n", tc->name);
`
``
241
`+
}
`
``
242
+
``
243
`+
/* Non-zero exit code will cause test_capi.py tests to fail.
`
``
244
`+
This is intentional. */
`
``
245
`+
return -1;
`
``
246
`+
}
`