summaryrefslogtreecommitdiffstats
path: root/Python/pystate.c
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2023-09-19 21:01:34 (GMT)
committerGitHub <noreply@github.com>2023-09-19 21:01:34 (GMT)
commitfd7e08a6f35581e1189b9bf12feb51f7167a86c5 (patch)
tree0844ef241ff291d4d50d293e185532b1284b276b /Python/pystate.c
parent754519a9f8c2bb06d85ff9b3e9fe6f967ac46d5c (diff)
downloadcpython-fd7e08a6f35581e1189b9bf12feb51f7167a86c5.zip
cpython-fd7e08a6f35581e1189b9bf12feb51f7167a86c5.tar.gz
cpython-fd7e08a6f35581e1189b9bf12feb51f7167a86c5.tar.bz2
gh-76785: Use Pending Calls When Releasing Cross-Interpreter Data (gh-109556)
This fixes some crashes in the _xxinterpchannels module, due to a race between interpreters.
Diffstat (limited to 'Python/pystate.c')
-rw-r--r--Python/pystate.c91
1 files changed, 58 insertions, 33 deletions
diff --git a/Python/pystate.c b/Python/pystate.c
index 71ff3e5..dcc6c11 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -2309,10 +2309,16 @@ _xidata_init(_PyCrossInterpreterData *data)
static inline void
_xidata_clear(_PyCrossInterpreterData *data)
{
- if (data->free != NULL) {
- data->free(data->data);
+ // _PyCrossInterpreterData only has two members that need to be
+ // cleaned up, if set: "data" must be freed and "obj" must be decref'ed.
+ // In both cases the original (owning) interpreter must be used,
+ // which is the caller's responsibility to ensure.
+ if (data->data != NULL) {
+ if (data->free != NULL) {
+ data->free(data->data);
+ }
+ data->data = NULL;
}
- data->data = NULL;
Py_CLEAR(data->obj);
}
@@ -2457,40 +2463,32 @@ _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data)
return data->new_object(data);
}
-typedef void (*releasefunc)(PyInterpreterState *, void *);
-
-static void
-_call_in_interpreter(PyInterpreterState *interp, releasefunc func, void *arg)
+static int
+_release_xidata_pending(void *data)
{
- /* We would use Py_AddPendingCall() if it weren't specific to the
- * main interpreter (see bpo-33608). In the meantime we take a
- * naive approach.
- */
- _PyRuntimeState *runtime = interp->runtime;
- PyThreadState *save_tstate = NULL;
- if (interp != current_fast_get(runtime)->interp) {
- // XXX Using the "head" thread isn't strictly correct.
- PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
- // XXX Possible GILState issues?
- save_tstate = _PyThreadState_Swap(runtime, tstate);
- }
-
- // XXX Once the GIL is per-interpreter, this should be called with the
- // calling interpreter's GIL released and the target interpreter's held.
- func(interp, arg);
+ _xidata_clear((_PyCrossInterpreterData *)data);
+ return 0;
+}
- // Switch back.
- if (save_tstate != NULL) {
- _PyThreadState_Swap(runtime, save_tstate);
- }
+static int
+_xidata_release_and_rawfree_pending(void *data)
+{
+ _xidata_clear((_PyCrossInterpreterData *)data);
+ PyMem_RawFree(data);
+ return 0;
}
-int
-_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
+static int
+_xidata_release(_PyCrossInterpreterData *data, int rawfree)
{
- if (data->free == NULL && data->obj == NULL) {
+ if ((data->data == NULL || data->free == NULL) && data->obj == NULL) {
// Nothing to release!
- data->data = NULL;
+ if (rawfree) {
+ PyMem_RawFree(data);
+ }
+ else {
+ data->data = NULL;
+ }
return 0;
}
@@ -2501,15 +2499,42 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
// This function shouldn't have been called.
// XXX Someone leaked some memory...
assert(PyErr_Occurred());
+ if (rawfree) {
+ PyMem_RawFree(data);
+ }
return -1;
}
// "Release" the data and/or the object.
- _call_in_interpreter(interp,
- (releasefunc)_PyCrossInterpreterData_Clear, data);
+ if (interp == current_fast_get(interp->runtime)->interp) {
+ _xidata_clear(data);
+ if (rawfree) {
+ PyMem_RawFree(data);
+ }
+ }
+ else {
+ _Py_pending_call_func func = _release_xidata_pending;
+ if (rawfree) {
+ func = _xidata_release_and_rawfree_pending;
+ }
+ // XXX Emit a warning if this fails?
+ _PyEval_AddPendingCall(interp, func, data, 0);
+ }
return 0;
}
+int
+_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
+{
+ return _xidata_release(data, 0);
+}
+
+int
+_PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *data)
+{
+ return _xidata_release(data, 1);
+}
+
/* registry of {type -> crossinterpdatafunc} */
/* For now we use a global registry of shareable classes. An