Skip to content

Commit 62b137e

Browse files
committed
Add a test stressing a lot of threads against finalization.
1 parent 5d28e1a commit 62b137e

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

Lib/test/test_embed.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,6 +2005,9 @@ def test_main_interpreter_view(self):
20052005
def test_thread_state_ensure_from_view(self):
20062006
self.run_embedded_interpreter("test_thread_state_ensure_from_view")
20072007

2008+
def test_concurrent_finalization_stress(self):
2009+
self.run_embedded_interpreter("test_concurrent_finalization_stress")
2010+
20082011

20092012
class MiscTests(EmbeddingTestsMixin, unittest.TestCase):
20102013
def test_unicode_id_init(self):

Programs/_testembed.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2801,6 +2801,81 @@ test_thread_state_ensure_from_view(void)
28012801
return 0;
28022802
}
28032803

2804+
#define NUM_THREADS 4
2805+
2806+
static void
2807+
stress_func(void *arg)
2808+
{
2809+
PyInterpreterGuard *guard = (PyInterpreterGuard *)arg;
2810+
2811+
for (int i = 0; i < 1000; ++i) {
2812+
assert(guard != NULL);
2813+
PyThreadStateToken *token = PyThreadState_Ensure(guard);
2814+
assert(token != NULL);
2815+
2816+
PyGILState_STATE gstate = PyGILState_Ensure();
2817+
2818+
PyInterpreterView *view = PyInterpreterView_FromCurrent();
2819+
assert(view != NULL);
2820+
2821+
PyThreadStateToken *token2 = PyThreadState_EnsureFromView(view);
2822+
assert(token2 != NULL);
2823+
PyThreadState_Release(token2);
2824+
2825+
PyGILState_Release(gstate);
2826+
2827+
PyThreadState_Release(token);
2828+
2829+
PyInterpreterGuard_Close(guard);
2830+
2831+
_Py_yield();
2832+
2833+
guard = PyInterpreterGuard_FromView(view);
2834+
PyInterpreterView_Close(view);
2835+
2836+
if (guard == NULL) {
2837+
// The interpreter is shutting down. Bail out now.
2838+
return;
2839+
}
2840+
}
2841+
2842+
PyInterpreterGuard_Close(guard);
2843+
}
2844+
2845+
static int
2846+
test_concurrent_finalization_stress(void)
2847+
{
2848+
for (int j = 0; j < 50; ++j) {
2849+
_testembed_initialize();
2850+
PyThread_handle_t handles[NUM_THREADS];
2851+
PyThread_ident_t idents[NUM_THREADS];
2852+
PyInterpreterGuard *guards[NUM_THREADS];
2853+
2854+
for (int i = 0; i < NUM_THREADS; ++i) {
2855+
guards[i] = PyInterpreterGuard_FromCurrent();
2856+
assert(guards[i] != NULL);
2857+
if (PyThread_start_joinable_thread(stress_func, guards[i], &idents[i], &handles[i]) < 0) {
2858+
for (int x = 0; x < i; ++x) {
2859+
PyInterpreterGuard_Close(guards[x]);
2860+
PyThread_detach_thread(handles[x]);
2861+
}
2862+
return -1;
2863+
}
2864+
}
2865+
2866+
_Py_yield();
2867+
Py_Finalize();
2868+
2869+
for (int i = 0; i < NUM_THREADS; ++i) {
2870+
PyThread_join_thread(handles[i]);
2871+
}
2872+
}
2873+
2874+
return 0;
2875+
}
2876+
2877+
#undef NUM_THREADS
2878+
28042879
/* *********************************************************
28052880
* List of test cases and the function that implements it.
28062881
*
@@ -2898,6 +2973,7 @@ static struct TestCase TestCases[] = {
28982973
{"test_thread_state_ensure", test_thread_state_ensure},
28992974
{"test_main_interpreter_view", test_main_interpreter_view},
29002975
{"test_thread_state_ensure_from_view", test_thread_state_ensure_from_view},
2976+
{"test_concurrent_finalization_stress", test_concurrent_finalization_stress},
29012977
{NULL, NULL}
29022978
};
29032979

0 commit comments

Comments
 (0)