gh-101659: Avoid Allocation for Shared Exceptions in the _xxsubinterpreters Module (gh-102659)

https://github.com/python/cpython/issues/101659
This commit is contained in:
Eric Snow 2023-03-13 16:01:44 -06:00 committed by GitHub
parent 74885a08db
commit 959ea2f9e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -15,14 +15,14 @@
#define MODULE_NAME "_xxsubinterpreters" #define MODULE_NAME "_xxsubinterpreters"
static char * static const char *
_copy_raw_string(PyObject *strobj) _copy_raw_string(PyObject *strobj)
{ {
const char *str = PyUnicode_AsUTF8(strobj); const char *str = PyUnicode_AsUTF8(strobj);
if (str == NULL) { if (str == NULL) {
return NULL; return NULL;
} }
char *copied = PyMem_Malloc(strlen(str)+1); char *copied = PyMem_RawMalloc(strlen(str)+1);
if (copied == NULL) { if (copied == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; return NULL;
@ -128,7 +128,7 @@ clear_module_state(module_state *state)
/* data-sharing-specific code ***********************************************/ /* data-sharing-specific code ***********************************************/
struct _sharednsitem { struct _sharednsitem {
char *name; const char *name;
_PyCrossInterpreterData data; _PyCrossInterpreterData data;
}; };
@ -152,7 +152,7 @@ static void
_sharednsitem_clear(struct _sharednsitem *item) _sharednsitem_clear(struct _sharednsitem *item)
{ {
if (item->name != NULL) { if (item->name != NULL) {
PyMem_Free(item->name); PyMem_RawFree((void *)item->name);
item->name = NULL; item->name = NULL;
} }
(void)_release_xid_data(&item->data, 1); (void)_release_xid_data(&item->data, 1);
@ -258,96 +258,74 @@ _sharedns_apply(_sharedns *shared, PyObject *ns)
// of the exception in the calling interpreter. // of the exception in the calling interpreter.
typedef struct _sharedexception { typedef struct _sharedexception {
char *name; const char *name;
char *msg; const char *msg;
} _sharedexception; } _sharedexception;
static _sharedexception * static const struct _sharedexception no_exception = {
_sharedexception_new(void) .name = NULL,
{ .msg = NULL,
_sharedexception *err = PyMem_NEW(_sharedexception, 1); };
if (err == NULL) {
PyErr_NoMemory();
return NULL;
}
err->name = NULL;
err->msg = NULL;
return err;
}
static void static void
_sharedexception_clear(_sharedexception *exc) _sharedexception_clear(_sharedexception *exc)
{ {
if (exc->name != NULL) { if (exc->name != NULL) {
PyMem_Free(exc->name); PyMem_RawFree((void *)exc->name);
} }
if (exc->msg != NULL) { if (exc->msg != NULL) {
PyMem_Free(exc->msg); PyMem_RawFree((void *)exc->msg);
} }
} }
static void static const char *
_sharedexception_free(_sharedexception *exc) _sharedexception_bind(PyObject *exc, _sharedexception *sharedexc)
{
_sharedexception_clear(exc);
PyMem_Free(exc);
}
static _sharedexception *
_sharedexception_bind(PyObject *exc)
{ {
assert(exc != NULL); assert(exc != NULL);
char *failure = NULL; const char *failure = NULL;
_sharedexception *err = _sharedexception_new(); PyObject *nameobj = PyUnicode_FromFormat("%S", Py_TYPE(exc));
if (err == NULL) { if (nameobj == NULL) {
goto finally;
}
PyObject *name = PyUnicode_FromFormat("%S", Py_TYPE(exc));
if (name == NULL) {
failure = "unable to format exception type name"; failure = "unable to format exception type name";
goto finally; goto error;
} }
err->name = _copy_raw_string(name); sharedexc->name = _copy_raw_string(nameobj);
Py_DECREF(name); Py_DECREF(nameobj);
if (err->name == NULL) { if (sharedexc->name == NULL) {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) { if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
failure = "out of memory copying exception type name"; failure = "out of memory copying exception type name";
} else { } else {
failure = "unable to encode and copy exception type name"; failure = "unable to encode and copy exception type name";
} }
goto finally; goto error;
} }
if (exc != NULL) { if (exc != NULL) {
PyObject *msg = PyUnicode_FromFormat("%S", exc); PyObject *msgobj = PyUnicode_FromFormat("%S", exc);
if (msg == NULL) { if (msgobj == NULL) {
failure = "unable to format exception message"; failure = "unable to format exception message";
goto finally; goto error;
} }
err->msg = _copy_raw_string(msg); sharedexc->msg = _copy_raw_string(msgobj);
Py_DECREF(msg); Py_DECREF(msgobj);
if (err->msg == NULL) { if (sharedexc->msg == NULL) {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) { if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
failure = "out of memory copying exception message"; failure = "out of memory copying exception message";
} else { } else {
failure = "unable to encode and copy exception message"; failure = "unable to encode and copy exception message";
} }
goto finally; goto error;
} }
} }
finally: return NULL;
if (failure != NULL) {
PyErr_Clear(); error:
if (err->name != NULL) { assert(failure != NULL);
PyMem_Free(err->name); PyErr_Clear();
err->name = NULL; _sharedexception_clear(sharedexc);
} *sharedexc = no_exception;
err->msg = failure; return failure;
}
return err;
} }
static void static void
@ -430,7 +408,7 @@ _ensure_not_running(PyInterpreterState *interp)
static int static int
_run_script(PyInterpreterState *interp, const char *codestr, _run_script(PyInterpreterState *interp, const char *codestr,
_sharedns *shared, _sharedexception **exc) _sharedns *shared, _sharedexception *sharedexc)
{ {
PyObject *excval = NULL; PyObject *excval = NULL;
PyObject *main_mod = _PyInterpreterState_GetMainModule(interp); PyObject *main_mod = _PyInterpreterState_GetMainModule(interp);
@ -462,22 +440,20 @@ _run_script(PyInterpreterState *interp, const char *codestr,
Py_DECREF(result); // We throw away the result. Py_DECREF(result); // We throw away the result.
} }
*exc = NULL; *sharedexc = no_exception;
return 0; return 0;
error: error:
excval = PyErr_GetRaisedException(); excval = PyErr_GetRaisedException();
_sharedexception *sharedexc = _sharedexception_bind(excval); const char *failure = _sharedexception_bind(excval, sharedexc);
Py_XDECREF(excval); if (failure != NULL) {
if (sharedexc == NULL) { fprintf(stderr,
fprintf(stderr, "RunFailedError: script raised an uncaught exception"); "RunFailedError: script raised an uncaught exception (%s)",
failure);
PyErr_Clear(); PyErr_Clear();
sharedexc = NULL;
} }
else { Py_XDECREF(excval);
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
}
*exc = sharedexc;
return -1; return -1;
} }
@ -505,7 +481,7 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp,
} }
// Run the script. // Run the script.
_sharedexception *exc = NULL; _sharedexception exc;
int result = _run_script(interp, codestr, shared, &exc); int result = _run_script(interp, codestr, shared, &exc);
// Switch back. // Switch back.
@ -514,10 +490,9 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp,
} }
// Propagate any exception out to the caller. // Propagate any exception out to the caller.
if (exc != NULL) { if (exc.name != NULL) {
assert(state != NULL); assert(state != NULL);
_sharedexception_apply(exc, state->RunFailedError); _sharedexception_apply(&exc, state->RunFailedError);
_sharedexception_free(exc);
} }
else if (result != 0) { else if (result != 0) {
// We were unable to allocate a shared exception. // We were unable to allocate a shared exception.