Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-116664: Make module state Py_SETREF's in _warnings thread-safe #116959

Merged
merged 13 commits into from Mar 28, 2024
80 changes: 56 additions & 24 deletions Python/_warnings.c
@@ -1,4 +1,5 @@
#include "Python.h"
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_interp.h" // PyInterpreterState.warnings
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
Expand Down Expand Up @@ -233,16 +234,9 @@ get_warnings_attr(PyInterpreterState *interp, PyObject *attr, int try_import)


static PyObject *
get_once_registry(PyInterpreterState *interp)
get_once_registry_impl(PyInterpreterState *interp, WarningsState *st)
{
PyObject *registry;

WarningsState *st = warnings_get_state(interp);
if (st == NULL) {
return NULL;
}

registry = GET_WARNINGS_ATTR(interp, onceregistry, 0);
PyObject *registry = GET_WARNINGS_ATTR(interp, onceregistry, 0);
if (registry == NULL) {
if (PyErr_Occurred())
return NULL;
Expand All @@ -263,16 +257,26 @@ get_once_registry(PyInterpreterState *interp)


static PyObject *
get_default_action(PyInterpreterState *interp)
get_once_registry(PyInterpreterState *interp)
{
PyObject *default_action;

WarningsState *st = warnings_get_state(interp);
if (st == NULL) {
return NULL;
}

default_action = GET_WARNINGS_ATTR(interp, defaultaction, 0);
PyObject *registry;
Py_BEGIN_CRITICAL_SECTION(st->once_registry);
erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved
registry = get_once_registry_impl(interp, st);
Py_END_CRITICAL_SECTION();

return registry;
}


static PyObject *
get_default_action_impl(PyInterpreterState *interp, WarningsState *st)
{
PyObject *default_action = GET_WARNINGS_ATTR(interp, defaultaction, 0);
if (default_action == NULL) {
if (PyErr_Occurred()) {
return NULL;
Expand All @@ -293,21 +297,30 @@ get_default_action(PyInterpreterState *interp)
}


/* The item is a new reference. */
static PyObject*
get_filter(PyInterpreterState *interp, PyObject *category,
PyObject *text, Py_ssize_t lineno,
PyObject *module, PyObject **item)
static PyObject *
get_default_action(PyInterpreterState *interp)
{
PyObject *action;
Py_ssize_t i;
PyObject *warnings_filters;
WarningsState *st = warnings_get_state(interp);
if (st == NULL) {
return NULL;
}

warnings_filters = GET_WARNINGS_ATTR(interp, filters, 0);
PyObject *default_action;
Py_BEGIN_CRITICAL_SECTION(st->default_action);
default_action = get_default_action_impl(interp, st);
Py_END_CRITICAL_SECTION();

return default_action;
}


/* The item is a new reference. */
static PyObject *
get_filter_impl(PyInterpreterState *interp, WarningsState *st,
PyObject *category, PyObject *text, Py_ssize_t lineno,
PyObject *module, PyObject **item)
{
PyObject *warnings_filters = GET_WARNINGS_ATTR(interp, filters, 0);
if (warnings_filters == NULL) {
if (PyErr_Occurred())
return NULL;
Expand All @@ -324,7 +337,7 @@ get_filter(PyInterpreterState *interp, PyObject *category,
}

/* WarningsState.filters could change while we are iterating over it. */
for (i = 0; i < PyList_GET_SIZE(filters); i++) {
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(filters); i++) {
PyObject *tmp_item, *action, *msg, *cat, *mod, *ln_obj;
Py_ssize_t ln;
int is_subclass, good_msg, good_mod;
Expand Down Expand Up @@ -384,7 +397,7 @@ get_filter(PyInterpreterState *interp, PyObject *category,
Py_DECREF(tmp_item);
}

action = get_default_action(interp);
PyObject *action = get_default_action(interp);
if (action != NULL) {
*item = Py_NewRef(Py_None);
return action;
Expand All @@ -394,6 +407,25 @@ get_filter(PyInterpreterState *interp, PyObject *category,
}


static PyObject *
get_filter(PyInterpreterState *interp, PyObject *category,
PyObject *text, Py_ssize_t lineno,
PyObject *module, PyObject **item)
{
WarningsState *st = warnings_get_state(interp);
if (st == NULL) {
return NULL;
}

PyObject *filters;
Py_BEGIN_CRITICAL_SECTION(st->filters);
filters = get_filter_impl(interp, st, category, text, lineno, module, item);
Py_END_CRITICAL_SECTION();

return filters;
}


static int
already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key,
int should_set)
Expand Down