gh-76785: Use Pending Calls When Releasing Cross-Interpreter Data by ericsnowcurrently · Pull Request #109556 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Include/cpython/pystate.h
2 changes: 1 addition & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ extern void _PyEval_SignalReceived(PyInterpreterState *interp);
// Export for '_testinternalcapi' shared extension
PyAPI_FUNC(int) _PyEval_AddPendingCall(
PyInterpreterState *interp,
int (*func)(void *),
_Py_pending_call_func func,
void *arg,
int mainthreadonly);

Expand Down
4 changes: 3 additions & 1 deletion Include/internal/pycore_ceval_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ extern "C" {
#include "pycore_gil.h" // struct _gil_runtime_state


typedef int (*_Py_pending_call_func)(void *);

struct _pending_calls {
int busy;
PyThread_type_lock lock;
Expand All @@ -22,7 +24,7 @@ struct _pending_calls {
int async_exc;
#define NPENDINGCALLS 32
struct _pending_call {
int (*func)(void *);
_Py_pending_call_func func;
void *arg;
} calls[NPENDINGCALLS];
int first;
Expand Down
30 changes: 20 additions & 10 deletions Modules/_xxinterpchannelsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,21 +160,34 @@ add_new_type(PyObject *mod, PyType_Spec *spec, crossinterpdatafunc shared)
return cls;
}

#define XID_IGNORE_EXC 1
#define XID_FREE 2

static int
_release_xid_data(_PyCrossInterpreterData *data, int ignoreexc)
_release_xid_data(_PyCrossInterpreterData *data, int flags)
{
int ignoreexc = flags & XID_IGNORE_EXC;
PyObject *exc;
if (ignoreexc) {
exc = PyErr_GetRaisedException();
}
int res = _PyCrossInterpreterData_Release(data);
int res;
if (flags & XID_FREE) {
res = _PyCrossInterpreterData_ReleaseAndRawFree(data);
}
else {
res = _PyCrossInterpreterData_Release(data);
}
if (res < 0) {
/* The owning interpreter is already destroyed. */
if (ignoreexc) {
// XXX Emit a warning?
PyErr_Clear();
}
}
if (flags & XID_FREE) {
/* Either way, we free the data. */
}
if (ignoreexc) {
PyErr_SetRaisedException(exc);
}
Expand Down Expand Up @@ -366,9 +379,8 @@ static void
_channelitem_clear(_channelitem *item)
{
if (item->data != NULL) {
(void)_release_xid_data(item->data, 1);
// It was allocated in _channel_send().
GLOBAL_FREE(item->data);
(void)_release_xid_data(item->data, XID_IGNORE_EXC & XID_FREE);
item->data = NULL;
}
item->next = NULL;
Expand Down Expand Up @@ -1439,14 +1451,12 @@ _channel_recv(_channels *channels, int64_t id, PyObject **res)
PyObject *obj = _PyCrossInterpreterData_NewObject(data);
if (obj == NULL) {
assert(PyErr_Occurred());
(void)_release_xid_data(data, 1);
// It was allocated in _channel_send().
GLOBAL_FREE(data);
// It was allocated in _channel_send(), so we free it.
(void)_release_xid_data(data, XID_IGNORE_EXC | XID_FREE);
return -1;
}
int release_res = _release_xid_data(data, 0);
// It was allocated in _channel_send().
GLOBAL_FREE(data);
// It was allocated in _channel_send(), so we free it.
int release_res = _release_xid_data(data, XID_FREE);
if (release_res < 0) {
// The source interpreter has been destroyed already.
assert(PyErr_Occurred());
Expand Down
29 changes: 11 additions & 18 deletions Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,17 @@ add_new_exception(PyObject *mod, const char *name, PyObject *base)
add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE)

static int
_release_xid_data(_PyCrossInterpreterData *data, int ignoreexc)
_release_xid_data(_PyCrossInterpreterData *data)
{
PyObject *exc;
if (ignoreexc) {
exc = PyErr_GetRaisedException();
}
PyObject *exc = PyErr_GetRaisedException();
int res = _PyCrossInterpreterData_Release(data);
if (res < 0) {
/* The owning interpreter is already destroyed. */
_PyCrossInterpreterData_Clear(NULL, data);
if (ignoreexc) {
// XXX Emit a warning?
PyErr_Clear();
}
}
if (ignoreexc) {
PyErr_SetRaisedException(exc);
// XXX Emit a warning?
PyErr_Clear();
}
PyErr_SetRaisedException(exc);
return res;
}

Expand Down Expand Up @@ -139,7 +132,7 @@ _sharednsitem_clear(struct _sharednsitem *item)
PyMem_RawFree((void *)item->name);
item->name = NULL;
}
(void)_release_xid_data(&item->data, 1);
(void)_release_xid_data(&item->data);
}

static int
Expand Down Expand Up @@ -168,16 +161,16 @@ typedef struct _sharedns {
static _sharedns *
_sharedns_new(Py_ssize_t len)
{
_sharedns *shared = PyMem_NEW(_sharedns, 1);
_sharedns *shared = PyMem_RawCalloc(sizeof(_sharedns), 1);
if (shared == NULL) {
PyErr_NoMemory();
return NULL;
}
shared->len = len;
shared->items = PyMem_NEW(struct _sharednsitem, len);
shared->items = PyMem_RawCalloc(sizeof(struct _sharednsitem), len);
if (shared->items == NULL) {
PyErr_NoMemory();
PyMem_Free(shared);
PyMem_RawFree(shared);
return NULL;
}
return shared;
Expand All @@ -189,8 +182,8 @@ _sharedns_free(_sharedns *shared)
for (Py_ssize_t i=0; i < shared->len; i++) {
_sharednsitem_clear(&shared->items[i]);
}
PyMem_Free(shared->items);
PyMem_Free(shared);
PyMem_RawFree(shared->items);
PyMem_RawFree(shared);
}

static _sharedns *
Expand Down
8 changes: 4 additions & 4 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ _PyEval_SignalReceived(PyInterpreterState *interp)
/* Push one item onto the queue while holding the lock. */
static int
_push_pending_call(struct _pending_calls *pending,
int (*func)(void *), void *arg)
_Py_pending_call_func func, void *arg)
{
int i = pending->last;
int j = (i + 1) % NPENDINGCALLS;
Expand Down Expand Up @@ -841,7 +841,7 @@ _pop_pending_call(struct _pending_calls *pending,

int
_PyEval_AddPendingCall(PyInterpreterState *interp,
int (*func)(void *), void *arg,
_Py_pending_call_func func, void *arg,
int mainthreadonly)
{
assert(!mainthreadonly || _Py_IsMainInterpreter(interp));
Expand All @@ -865,7 +865,7 @@ _PyEval_AddPendingCall(PyInterpreterState *interp,
}

int
Py_AddPendingCall(int (*func)(void *), void *arg)
Py_AddPendingCall(_Py_pending_call_func func, void *arg)
{
/* Legacy users of this API will continue to target the main thread
(of the main interpreter). */
Expand Down Expand Up @@ -909,7 +909,7 @@ _make_pending_calls(struct _pending_calls *pending)
{
/* perform a bounded number of calls, in case of recursion */
for (int i=0; i<NPENDINGCALLS; i++) {
int (*func)(void *) = NULL;
_Py_pending_call_func func = NULL;
void *arg = NULL;

/* pop one item off the queue while holding the lock */
Expand Down
91 changes: 58 additions & 33 deletions Python/pystate.c