Skip to content

Commit 9ccd5bb

Browse files
gh-153531: fix thread safety of setting func.__doc__ and func.__module__ (#154851)
1 parent 9c91fd9 commit 9ccd5bb

2 files changed

Lines changed: 64 additions & 32 deletions

File tree

Lib/test/test_free_threading/test_functions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ def test_annotate(self):
5858
def test_type_params(self):
5959
self.stress_attribute("__type_params__", lambda: (random_string(),))
6060

61+
def test_doc(self):
62+
self.stress_attribute("__doc__", random_string)
63+
64+
def test_module(self):
65+
self.stress_attribute("__module__", random_string)
66+
6167
def test_annotations_and_annotate(self):
6268
# The __annotations__ and __annotate__ setters clear each other.
6369
def target(): pass

Objects/funcobject.c

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,7 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
628628

629629
static PyMemberDef func_memberlist[] = {
630630
{"__closure__", _Py_T_OBJECT, OFF(func_closure), Py_READONLY},
631-
{"__doc__", _Py_T_OBJECT, OFF(func_doc), 0},
632631
{"__globals__", _Py_T_OBJECT, OFF(func_globals), Py_READONLY},
633-
{"__module__", _Py_T_OBJECT, OFF(func_module), 0},
634632
{"__builtins__", _Py_T_OBJECT, OFF(func_builtins), Py_READONLY},
635633
{NULL} /* Sentinel */
636634
};
@@ -762,6 +760,56 @@ func_set_qualname(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
762760
return 0;
763761
}
764762

763+
static PyObject *
764+
func_get_doc(PyObject *self, void *Py_UNUSED(ignored))
765+
{
766+
PyFunctionObject *op = _PyFunction_CAST(self);
767+
PyObject *doc = op->func_doc;
768+
if (doc == NULL) {
769+
doc = Py_None;
770+
}
771+
return Py_NewRef(doc);
772+
}
773+
774+
static int
775+
func_set_doc(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
776+
{
777+
/* Legal to del f.__doc__ or to set it to any object. */
778+
PyFunctionObject *op = _PyFunction_CAST(self);
779+
PyInterpreterState *interp = _PyInterpreterState_GET();
780+
_PyEval_StopTheWorld(interp);
781+
PyObject *old_doc = op->func_doc;
782+
op->func_doc = Py_XNewRef(value);
783+
_PyEval_StartTheWorld(interp);
784+
Py_XDECREF(old_doc);
785+
return 0;
786+
}
787+
788+
static PyObject *
789+
func_get_module(PyObject *self, void *Py_UNUSED(ignored))
790+
{
791+
PyFunctionObject *op = _PyFunction_CAST(self);
792+
PyObject *module = op->func_module;
793+
if (module == NULL) {
794+
module = Py_None;
795+
}
796+
return Py_NewRef(module);
797+
}
798+
799+
static int
800+
func_set_module(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
801+
{
802+
/* Legal to del f.__module__ or to set it to any object. */
803+
PyFunctionObject *op = _PyFunction_CAST(self);
804+
PyInterpreterState *interp = _PyInterpreterState_GET();
805+
_PyEval_StopTheWorld(interp);
806+
PyObject *old_module = op->func_module;
807+
op->func_module = Py_XNewRef(value);
808+
_PyEval_StartTheWorld(interp);
809+
Py_XDECREF(old_module);
810+
return 0;
811+
}
812+
765813
static PyObject *
766814
func_get_defaults(PyObject *self, void *Py_UNUSED(ignored))
767815
{
@@ -891,24 +939,12 @@ function___annotate___set_impl(PyFunctionObject *self, PyObject *value)
891939
return -1;
892940
}
893941
if (Py_IsNone(value)) {
894-
PyInterpreterState *interp = _PyInterpreterState_GET();
895-
_PyEval_StopTheWorld(interp);
896-
PyObject *old_annotate = self->func_annotate;
897-
self->func_annotate = Py_NewRef(value);
898-
_PyEval_StartTheWorld(interp);
899-
Py_XDECREF(old_annotate);
942+
Py_XSETREF(self->func_annotate, Py_NewRef(value));
900943
return 0;
901944
}
902945
else if (PyCallable_Check(value)) {
903-
PyInterpreterState *interp = _PyInterpreterState_GET();
904-
_PyEval_StopTheWorld(interp);
905-
PyObject *old_annotate = self->func_annotate;
906-
self->func_annotate = Py_NewRef(value);
907-
PyObject *old_annotations = self->func_annotations;
908-
self->func_annotations = NULL;
909-
_PyEval_StartTheWorld(interp);
910-
Py_XDECREF(old_annotate);
911-
Py_XDECREF(old_annotations);
946+
Py_XSETREF(self->func_annotate, Py_NewRef(value));
947+
Py_CLEAR(self->func_annotations);
912948
return 0;
913949
}
914950
else {
@@ -961,15 +997,8 @@ function___annotations___set_impl(PyFunctionObject *self, PyObject *value)
961997
"__annotations__ must be set to a dict object");
962998
return -1;
963999
}
964-
PyInterpreterState *interp = _PyInterpreterState_GET();
965-
_PyEval_StopTheWorld(interp);
966-
PyObject *old_annotations = self->func_annotations;
967-
self->func_annotations = Py_XNewRef(value);
968-
PyObject *old_annotate = self->func_annotate;
969-
self->func_annotate = NULL;
970-
_PyEval_StartTheWorld(interp);
971-
Py_XDECREF(old_annotations);
972-
Py_XDECREF(old_annotate);
1000+
Py_XSETREF(self->func_annotations, Py_XNewRef(value));
1001+
Py_CLEAR(self->func_annotate);
9731002
return 0;
9741003
}
9751004

@@ -1010,12 +1039,7 @@ function___type_params___set_impl(PyFunctionObject *self, PyObject *value)
10101039
"__type_params__ must be set to a tuple");
10111040
return -1;
10121041
}
1013-
PyInterpreterState *interp = _PyInterpreterState_GET();
1014-
_PyEval_StopTheWorld(interp);
1015-
PyObject *old_typeparams = self->func_typeparams;
1016-
self->func_typeparams = Py_NewRef(value);
1017-
_PyEval_StartTheWorld(interp);
1018-
Py_XDECREF(old_typeparams);
1042+
Py_XSETREF(self->func_typeparams, Py_NewRef(value));
10191043
return 0;
10201044
}
10211045

@@ -1037,6 +1061,8 @@ static PyGetSetDef func_getsetlist[] = {
10371061
FUNCTION___ANNOTATIONS___GETSETDEF
10381062
FUNCTION___ANNOTATE___GETSETDEF
10391063
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
1064+
{"__doc__", func_get_doc, func_set_doc},
1065+
{"__module__", func_get_module, func_set_module},
10401066
{"__name__", func_get_name, func_set_name},
10411067
{"__qualname__", func_get_qualname, func_set_qualname},
10421068
FUNCTION___TYPE_PARAMS___GETSETDEF

0 commit comments

Comments
 (0)