Skip to content

Commit aaa516e

Browse files
committed
Move some tests to _testinternalcapi.
1 parent 211edf1 commit aaa516e

2 files changed

Lines changed: 103 additions & 108 deletions

File tree

Modules/_testinternalcapi.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3007,6 +3007,108 @@ _pyerr_setkeyerror(PyObject *self, PyObject *arg)
30073007
return NULL;
30083008
}
30093009

3010+
static PyObject *
3011+
test_thread_state_ensure_from_view_interp_switch(PyObject *self, PyObject *unused)
3012+
{
3013+
/* The main tstate is already attached and was NOT created by
3014+
* PyThreadState_Ensure, so delete_on_release == 0. */
3015+
PyInterpreterState *interp = _PyInterpreterState_GET();
3016+
assert(interp != NULL);
3017+
PyInterpreterView *view = PyInterpreterView_FromCurrent();
3018+
assert(view != NULL);
3019+
3020+
/* First Ensure/Release pair on this pre-existing tstate. */
3021+
assert(_PyThreadState_GET() != NULL);
3022+
PyThreadStateToken *t1 = PyThreadState_EnsureFromView(view);
3023+
assert(t1 != NULL);
3024+
assert(_PyInterpreterState_GuardCountdown(interp) == 1);
3025+
PyThreadState_Release(t1);
3026+
assert(_PyInterpreterState_GuardCountdown(interp) == 0);
3027+
assert(_PyThreadState_GET() != NULL);
3028+
3029+
/* tstate->ensure.owned_guard now points at the freed guard. */
3030+
3031+
/* Re-attach: Bug B detaches us as a side effect (separate repro). */
3032+
PyThreadState *save = PyThreadState_Swap(NULL);
3033+
3034+
PyThreadStateToken *t2 = PyThreadState_EnsureFromView(view);
3035+
assert(_PyInterpreterState_GuardCountdown(interp) == 1);
3036+
assert(t2 != NULL);
3037+
PyThreadState_Release(t2);
3038+
assert(_PyInterpreterState_GuardCountdown(interp) == 0);
3039+
assert(_PyThreadState_GET() == NULL);
3040+
3041+
PyThreadState_Swap(save);
3042+
3043+
/* In a release build (no assertion) the second Ensure silently
3044+
* skipped storing its guard and Release decremented the global
3045+
* counter from 0, wrapping it to GUARDS_NOT_ALLOWED. All future
3046+
* guard acquisitions then fail: */
3047+
PyInterpreterGuard *g = PyInterpreterGuard_FromCurrent();
3048+
assert(g != NULL);
3049+
assert(_PyInterpreterState_GuardCountdown(interp) == 1);
3050+
PyInterpreterGuard_Close(g);
3051+
assert(_PyInterpreterState_GuardCountdown(interp) == 0);
3052+
3053+
PyInterpreterView_Close(view);
3054+
Py_RETURN_NONE;
3055+
}
3056+
3057+
/* A capsule destructor that calls Ensure/Release while the tstate is being
3058+
* cleared by PyThreadState_Release. */
3059+
static void
3060+
capsule_destructor(PyObject *capsule)
3061+
{
3062+
assert(capsule != NULL);
3063+
PyInterpreterGuard *guard = PyCapsule_GetPointer(capsule, "x");
3064+
PyThreadStateToken *token = PyThreadState_Ensure(guard);
3065+
assert(token != NULL);
3066+
PyThreadState_Release(token);
3067+
}
3068+
3069+
static void
3070+
thread_with_tstate_destructor(void *arg)
3071+
{
3072+
PyInterpreterGuard *guard = (PyInterpreterGuard *)arg;
3073+
3074+
/* Triggers fresh tstate path */
3075+
PyThreadStateToken *token = PyThreadState_Ensure(guard);
3076+
assert(token != NULL);
3077+
3078+
/* Stash a capsule whose destructor will run during PyThreadState_Clear. */
3079+
PyObject *capsule = PyCapsule_New(guard, "x", capsule_destructor);
3080+
assert(capsule != NULL);
3081+
3082+
/* We need to put it somewhere it gets cleaned up at PyThreadState_Clear.
3083+
* tstate->dict is cleared during PyThreadState_Clear. */
3084+
PyObject *dict = PyThreadState_GetDict();
3085+
assert(dict != NULL);
3086+
int res = PyDict_SetItemString(dict, "key", capsule);
3087+
assert(res == 0);
3088+
Py_DECREF(capsule);
3089+
3090+
PyThreadState_Release(token);
3091+
PyInterpreterGuard_Close(guard);
3092+
}
3093+
3094+
static PyObject *
3095+
test_thread_state_release_with_destructor(PyObject *self, PyObject *unused)
3096+
{
3097+
PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent();
3098+
assert(guard != NULL);
3099+
3100+
PyThread_handle_t handle;
3101+
PyThread_ident_t ident;
3102+
if (PyThread_start_joinable_thread(thread_with_tstate_destructor,
3103+
guard, &ident, &handle) < 0) {
3104+
PyInterpreterGuard_Close(guard);
3105+
return PyErr_NoMemory();
3106+
}
3107+
3108+
PyThread_join_thread(handle);
3109+
Py_RETURN_NONE;
3110+
}
3111+
30103112

30113113
static PyMethodDef module_functions[] = {
30123114
{"get_configs", get_configs, METH_NOARGS},
@@ -3134,6 +3236,7 @@ static PyMethodDef module_functions[] = {
31343236
{"_pyerr_setkeyerror", _pyerr_setkeyerror, METH_O},
31353237
{"test_interp_guard_countdown", test_interp_guard_countdown, METH_NOARGS},
31363238
{"test_interp_view_countdown", test_interp_view_countdown, METH_NOARGS},
3239+
{"test_thread_state_ensure_from_view_interp_switch", test_thread_state_ensure_from_view_interp_switch, METH_NOARGS},
31373240
{NULL, NULL} /* sentinel */
31383241
};
31393242

Programs/_testembed.c

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,112 +2878,6 @@ test_concurrent_finalization_stress(void)
28782878

28792879
#undef NUM_THREADS
28802880

2881-
/* A capsule destructor that calls Ensure/Release while the tstate is being
2882-
* cleared by PyThreadState_Release. */
2883-
static void
2884-
capsule_destructor(PyObject *capsule)
2885-
{
2886-
assert(capsule != NULL);
2887-
PyInterpreterGuard *guard = PyCapsule_GetPointer(capsule, "x");
2888-
PyThreadStateToken *token = PyThreadState_Ensure(guard);
2889-
assert(token != NULL);
2890-
PyThreadState_Release(token);
2891-
}
2892-
2893-
static void
2894-
thread_with_tstate_destructor(void *arg)
2895-
{
2896-
PyInterpreterGuard *guard = (PyInterpreterGuard *)arg;
2897-
2898-
/* Triggers fresh tstate path */
2899-
PyThreadStateToken *token = PyThreadState_Ensure(guard);
2900-
assert(token != NULL);
2901-
2902-
/* Stash a capsule whose destructor will run during PyThreadState_Clear. */
2903-
PyObject *capsule = PyCapsule_New(guard, "x", capsule_destructor);
2904-
assert(capsule != NULL);
2905-
2906-
/* We need to put it somewhere it gets cleaned up at PyThreadState_Clear.
2907-
* tstate->dict is cleared during PyThreadState_Clear. */
2908-
PyObject *dict = PyThreadState_GetDict();
2909-
assert(dict != NULL);
2910-
int res = PyDict_SetItemString(dict, "key", capsule);
2911-
assert(res == 0);
2912-
Py_DECREF(capsule);
2913-
2914-
PyThreadState_Release(token);
2915-
PyInterpreterGuard_Close(guard);
2916-
}
2917-
2918-
static int
2919-
test_thread_state_release_with_destructor(void)
2920-
{
2921-
_testembed_initialize();
2922-
PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent();
2923-
assert(guard != NULL);
2924-
2925-
PyThread_handle_t handle;
2926-
PyThread_ident_t ident;
2927-
if (PyThread_start_joinable_thread(thread_with_tstate_destructor,
2928-
guard, &ident, &handle) < 0) {
2929-
PyInterpreterGuard_Close(guard);
2930-
return -1;
2931-
}
2932-
2933-
PyThread_join_thread(handle);
2934-
Py_Finalize();
2935-
return 0;
2936-
}
2937-
2938-
static int
2939-
test_thread_state_ensure_from_view_interp_switch(void)
2940-
{
2941-
_testembed_initialize();
2942-
2943-
/* The main tstate is already attached and was NOT created by
2944-
* PyThreadState_Ensure, so delete_on_release == 0. */
2945-
PyInterpreterState *interp = _PyInterpreterState_GET();
2946-
assert(interp != NULL);
2947-
PyInterpreterView *view = PyInterpreterView_FromCurrent();
2948-
assert(view != NULL);
2949-
2950-
/* First Ensure/Release pair on this pre-existing tstate. */
2951-
assert(_PyThreadState_GET() != NULL);
2952-
PyThreadStateToken *t1 = PyThreadState_EnsureFromView(view);
2953-
assert(t1 != NULL);
2954-
assert(_PyInterpreterState_GuardCountdown(interp) == 1);
2955-
PyThreadState_Release(t1);
2956-
assert(_PyInterpreterState_GuardCountdown(interp) == 0);
2957-
assert(_PyThreadState_GET() != NULL);
2958-
2959-
/* tstate->ensure.owned_guard now points at the freed guard. */
2960-
2961-
/* Re-attach: Bug B detaches us as a side effect (separate repro). */
2962-
PyThreadState *save = PyThreadState_Swap(NULL);
2963-
2964-
PyThreadStateToken *t2 = PyThreadState_EnsureFromView(view);
2965-
assert(_PyInterpreterState_GuardCountdown(interp) == 1);
2966-
assert(t2 != NULL);
2967-
PyThreadState_Release(t2);
2968-
assert(_PyInterpreterState_GuardCountdown(interp) == 0);
2969-
assert(_PyThreadState_GET() == NULL);
2970-
2971-
PyThreadState_Swap(save);
2972-
2973-
/* In a release build (no assertion) the second Ensure silently
2974-
* skipped storing its guard and Release decremented the global
2975-
* counter from 0, wrapping it to GUARDS_NOT_ALLOWED. All future
2976-
* guard acquisitions then fail: */
2977-
PyInterpreterGuard *g = PyInterpreterGuard_FromCurrent();
2978-
assert(g != NULL);
2979-
assert(_PyInterpreterState_GuardCountdown(interp) == 1);
2980-
PyInterpreterGuard_Close(g);
2981-
assert(_PyInterpreterState_GuardCountdown(interp) == 0);
2982-
2983-
PyInterpreterView_Close(view);
2984-
Py_Finalize();
2985-
return 0;
2986-
}
29872881

29882882
/* *********************************************************
29892883
* List of test cases and the function that implements it.
@@ -3083,8 +2977,6 @@ static struct TestCase TestCases[] = {
30832977
{"test_main_interpreter_view", test_main_interpreter_view},
30842978
{"test_thread_state_ensure_from_view", test_thread_state_ensure_from_view},
30852979
{"test_concurrent_finalization_stress", test_concurrent_finalization_stress},
3086-
{"test_thread_state_release_with_destructor", test_thread_state_release_with_destructor},
3087-
{"test_thread_state_ensure_from_view_interp_switch", test_thread_state_ensure_from_view_interp_switch},
30882980
{NULL, NULL}
30892981
};
30902982

0 commit comments

Comments
 (0)