From de5123c0221e892bc1d8de38e46f88d7297cf498 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 14 Dec 2024 11:19:31 -0500 Subject: [PATCH 1/9] Use a critical section for argtypes and restype. --- Modules/_ctypes/_ctypes.c | 63 +++++++++++---- Modules/_ctypes/clinic/_ctypes.c.h | 111 ++++++++++++++++++++++++++- Tools/clinic/libclinic/dsl_parser.py | 1 + 3 files changed, 159 insertions(+), 16 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index bb4699884057ba..3f22d00b1181db 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -128,8 +128,9 @@ bytes(cdata) /*[clinic input] module _ctypes +class _ctypes.CFuncPtr "PyCFuncPtrObject *" "&PyCFuncPtr_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=476a19c49b31a75c]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=58e8c99474bc631e]*/ #define clinic_state() (get_module_state_by_class(cls)) #define clinic_state_sub() (get_module_state_by_class(cls->tp_base)) @@ -3451,11 +3452,18 @@ PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) Py_RETURN_NONE; } +/*[clinic input] +@setter +@critical_section +_ctypes.CFuncPtr.restype +[clinic start generated code]*/ + static int -PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_restype_set_impl(PyCFuncPtrObject *self, PyObject *value) +/*[clinic end generated code: output=0be0a086abbabf18 input=683c3bef4562ccc6]*/ { PyObject *checker, *oldchecker; - if (ob == NULL) { + if (value == NULL) { oldchecker = self->checker; self->checker = NULL; Py_CLEAR(self->restype); @@ -3464,27 +3472,36 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ign } ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self))); StgInfo *info; - if (PyStgInfo_FromType(st, ob, &info) < 0) { + if (PyStgInfo_FromType(st, value, &info) < 0) { return -1; } - if (ob != Py_None && !info && !PyCallable_Check(ob)) { + if (value != Py_None && !info && !PyCallable_Check(value)) { PyErr_SetString(PyExc_TypeError, "restype must be a type, a callable, or None"); return -1; } - if (PyObject_GetOptionalAttr(ob, &_Py_ID(_check_retval_), &checker) < 0) { + if (PyObject_GetOptionalAttr(value, &_Py_ID(_check_retval_), &checker) < 0) { return -1; } oldchecker = self->checker; self->checker = checker; - Py_INCREF(ob); - Py_XSETREF(self->restype, ob); + Py_INCREF(value); + Py_XSETREF(self->restype, value); Py_XDECREF(oldchecker); return 0; } +/*[clinic input] +@getter +@critical_section +_ctypes.CFuncPtr.restype + +specify the result type +[clinic start generated code]*/ + static PyObject * -PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_restype_get_impl(PyCFuncPtrObject *self) +/*[clinic end generated code: output=c8f44cd16f1dee5e input=5e3ed95116204fd2]*/ { if (self->restype) { return Py_NewRef(self->restype); @@ -3502,6 +3519,15 @@ PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) } } +/*[clinic input] +@setter +@critical_section +_ctypes.CFuncPtr.argtypes +[clinic start generated code]*/ + +static int +_ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value) +/*[clinic end generated code: output=596a36e2ae89d7d1 input=c4627573e980aa8b]*/ static int PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) { @@ -3522,6 +3548,17 @@ PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ig return 0; } +/*[clinic input] +@getter +@critical_section +_ctypes.CFuncPtr.argtypes + +specify the argument types +[clinic start generated code]*/ + +static PyObject * +_ctypes_CFuncPtr_argtypes_get_impl(PyCFuncPtrObject *self) +/*[clinic end generated code: output=c46b05a1b0f99172 input=37a8a545a56f8ae2]*/ static PyObject * PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) { @@ -3544,11 +3581,8 @@ PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) static PyGetSetDef PyCFuncPtr_getsets[] = { { "errcheck", (getter)PyCFuncPtr_get_errcheck, (setter)PyCFuncPtr_set_errcheck, "a function to check for errors", NULL }, - { "restype", (getter)PyCFuncPtr_get_restype, (setter)PyCFuncPtr_set_restype, - "specify the result type", NULL }, - { "argtypes", (getter)PyCFuncPtr_get_argtypes, - (setter)PyCFuncPtr_set_argtypes, - "specify the argument types", NULL }, + _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF + _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF { NULL, NULL } }; @@ -5068,7 +5102,6 @@ class _ctypes.Simple "PyObject *" "clinic_state()->Simple_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=016c476c7aa8b8a8]*/ - static int Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) { diff --git a/Modules/_ctypes/clinic/_ctypes.c.h b/Modules/_ctypes/clinic/_ctypes.c.h index 1332ba04cdfecd..640d0e9811508e 100644 --- a/Modules/_ctypes/clinic/_ctypes.c.h +++ b/Modules/_ctypes/clinic/_ctypes.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_runtime.h" // _Py_SINGLETON() #endif #include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_ctypes_CType_Type___sizeof____doc__, @@ -601,6 +602,114 @@ PyCData_reduce(PyObject *myself, PyTypeObject *cls, PyObject *const *args, Py_ss return PyCData_reduce_impl(myself, cls); } +#if !defined(_ctypes_CFuncPtr_restype_DOCSTR) +# define _ctypes_CFuncPtr_restype_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_RESTYPE_GETSETDEF) +# undef _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", (getter)_ctypes_CFuncPtr_restype_get, (setter)_ctypes_CFuncPtr_restype_set, _ctypes_CFuncPtr_restype_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", NULL, (setter)_ctypes_CFuncPtr_restype_set, NULL}, +#endif + +static int +_ctypes_CFuncPtr_restype_set_impl(PyCFuncPtrObject *self, PyObject *value); + +static int +_ctypes_CFuncPtr_restype_set(PyCFuncPtrObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_restype_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ctypes_CFuncPtr_restype__doc__, +"specify the result type"); +#define _ctypes_CFuncPtr_restype_DOCSTR _ctypes_CFuncPtr_restype__doc__ + +#if !defined(_ctypes_CFuncPtr_restype_DOCSTR) +# define _ctypes_CFuncPtr_restype_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_RESTYPE_GETSETDEF) +# undef _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", (getter)_ctypes_CFuncPtr_restype_get, (setter)_ctypes_CFuncPtr_restype_set, _ctypes_CFuncPtr_restype_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF {"restype", (getter)_ctypes_CFuncPtr_restype_get, NULL, _ctypes_CFuncPtr_restype_DOCSTR}, +#endif + +static PyObject * +_ctypes_CFuncPtr_restype_get_impl(PyCFuncPtrObject *self); + +static PyObject * +_ctypes_CFuncPtr_restype_get(PyCFuncPtrObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_restype_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(_ctypes_CFuncPtr_argtypes_DOCSTR) +# define _ctypes_CFuncPtr_argtypes_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", (getter)_ctypes_CFuncPtr_argtypes_get, (setter)_ctypes_CFuncPtr_argtypes_set, _ctypes_CFuncPtr_argtypes_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", NULL, (setter)_ctypes_CFuncPtr_argtypes_set, NULL}, +#endif + +static int +_ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value); + +static int +_ctypes_CFuncPtr_argtypes_set(PyCFuncPtrObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_argtypes_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ctypes_CFuncPtr_argtypes__doc__, +"specify the argument types"); +#define _ctypes_CFuncPtr_argtypes_DOCSTR _ctypes_CFuncPtr_argtypes__doc__ + +#if !defined(_ctypes_CFuncPtr_argtypes_DOCSTR) +# define _ctypes_CFuncPtr_argtypes_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", (getter)_ctypes_CFuncPtr_argtypes_get, (setter)_ctypes_CFuncPtr_argtypes_set, _ctypes_CFuncPtr_argtypes_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF {"argtypes", (getter)_ctypes_CFuncPtr_argtypes_get, NULL, _ctypes_CFuncPtr_argtypes_DOCSTR}, +#endif + +static PyObject * +_ctypes_CFuncPtr_argtypes_get_impl(PyCFuncPtrObject *self); + +static PyObject * +_ctypes_CFuncPtr_argtypes_get(PyCFuncPtrObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_argtypes_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(Simple_from_outparm__doc__, "__ctypes_from_outparam__($self, /)\n" "--\n" @@ -621,4 +730,4 @@ Simple_from_outparm(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py } return Simple_from_outparm_impl(self, cls); } -/*[clinic end generated code: output=52724c091e3a8b8d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=844834e45c4bf037 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/libclinic/dsl_parser.py b/Tools/clinic/libclinic/dsl_parser.py index 4b4a8b9969d142..31e74efbe58708 100644 --- a/Tools/clinic/libclinic/dsl_parser.py +++ b/Tools/clinic/libclinic/dsl_parser.py @@ -1529,6 +1529,7 @@ def format_docstring(self) -> str: lines = f.docstring.split('\n') if len(lines) >= 2: if lines[1]: + print(lines[1]) fail(f"Docstring for {f.full_name!r} does not have a summary line!\n" "Every non-blank function docstring must start with " "a single line summary followed by an empty line.") From 2b587097ccf19baa4361c1bfce38f1efe4b5fb0d Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 14 Dec 2024 11:20:20 -0500 Subject: [PATCH 2/9] Fix generated code. --- Modules/_ctypes/_ctypes.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 3f22d00b1181db..c2b1c72c95db2e 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3528,22 +3528,20 @@ _ctypes.CFuncPtr.argtypes static int _ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value) /*[clinic end generated code: output=596a36e2ae89d7d1 input=c4627573e980aa8b]*/ -static int -PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) { PyObject *converters; - if (ob == NULL || ob == Py_None) { + if (value == NULL || value == Py_None) { Py_CLEAR(self->converters); Py_CLEAR(self->argtypes); } else { ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self))); - converters = converters_from_argtypes(st, ob); + converters = converters_from_argtypes(st, value); if (!converters) return -1; Py_XSETREF(self->converters, converters); - Py_INCREF(ob); - Py_XSETREF(self->argtypes, ob); + Py_INCREF(value); + Py_XSETREF(self->argtypes, value); } return 0; } @@ -3559,8 +3557,6 @@ specify the argument types static PyObject * _ctypes_CFuncPtr_argtypes_get_impl(PyCFuncPtrObject *self) /*[clinic end generated code: output=c46b05a1b0f99172 input=37a8a545a56f8ae2]*/ -static PyObject * -PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) { if (self->argtypes) { return Py_NewRef(self->argtypes); From 797613d9fcdf21c1b404a931df592d56ebad3939 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sat, 14 Dec 2024 11:24:27 -0500 Subject: [PATCH 3/9] Switch errcheck to a critical section. --- Modules/_ctypes/_ctypes.c | 29 ++++++++++++---- Modules/_ctypes/clinic/_ctypes.c.h | 56 +++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index c2b1c72c95db2e..bd8be30c3c03e7 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3430,21 +3430,37 @@ generic_pycdata_new(ctypes_state *st, PyCFuncPtr_Type */ +/*[clinic input] +@critical_section +@setter +_ctypes.CFuncPtr.errcheck +[clinic start generated code]*/ + static int -PyCFuncPtr_set_errcheck(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_errcheck_set_impl(PyCFuncPtrObject *self, PyObject *value) +/*[clinic end generated code: output=6580cf1ffdf3b9fb input=84930bb16c490b33]*/ { - if (ob && !PyCallable_Check(ob)) { + if (value && !PyCallable_Check(value)) { PyErr_SetString(PyExc_TypeError, "the errcheck attribute must be callable"); return -1; } - Py_XINCREF(ob); - Py_XSETREF(self->errcheck, ob); + Py_XINCREF(value); + Py_XSETREF(self->errcheck, value); return 0; } +/*[clinic input] +@critical_section +@getter +_ctypes.CFuncPtr.errcheck + +a function to check for errors +[clinic start generated code]*/ + static PyObject * -PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) +_ctypes_CFuncPtr_errcheck_get_impl(PyCFuncPtrObject *self) +/*[clinic end generated code: output=dfa6fb5c6f90fd14 input=4672135fef37819f]*/ { if (self->errcheck) { return Py_NewRef(self->errcheck); @@ -3575,8 +3591,7 @@ _ctypes_CFuncPtr_argtypes_get_impl(PyCFuncPtrObject *self) } static PyGetSetDef PyCFuncPtr_getsets[] = { - { "errcheck", (getter)PyCFuncPtr_get_errcheck, (setter)PyCFuncPtr_set_errcheck, - "a function to check for errors", NULL }, + _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF _CTYPES_CFUNCPTR_RESTYPE_GETSETDEF _CTYPES_CFUNCPTR_ARGTYPES_GETSETDEF { NULL, NULL } diff --git a/Modules/_ctypes/clinic/_ctypes.c.h b/Modules/_ctypes/clinic/_ctypes.c.h index 640d0e9811508e..88366168d585e9 100644 --- a/Modules/_ctypes/clinic/_ctypes.c.h +++ b/Modules/_ctypes/clinic/_ctypes.c.h @@ -602,6 +602,60 @@ PyCData_reduce(PyObject *myself, PyTypeObject *cls, PyObject *const *args, Py_ss return PyCData_reduce_impl(myself, cls); } +#if !defined(_ctypes_CFuncPtr_errcheck_DOCSTR) +# define _ctypes_CFuncPtr_errcheck_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", (getter)_ctypes_CFuncPtr_errcheck_get, (setter)_ctypes_CFuncPtr_errcheck_set, _ctypes_CFuncPtr_errcheck_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", NULL, (setter)_ctypes_CFuncPtr_errcheck_set, NULL}, +#endif + +static int +_ctypes_CFuncPtr_errcheck_set_impl(PyCFuncPtrObject *self, PyObject *value); + +static int +_ctypes_CFuncPtr_errcheck_set(PyCFuncPtrObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_errcheck_set_impl(self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ctypes_CFuncPtr_errcheck__doc__, +"a function to check for errors"); +#define _ctypes_CFuncPtr_errcheck_DOCSTR _ctypes_CFuncPtr_errcheck__doc__ + +#if !defined(_ctypes_CFuncPtr_errcheck_DOCSTR) +# define _ctypes_CFuncPtr_errcheck_DOCSTR NULL +#endif +#if defined(_CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF) +# undef _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", (getter)_ctypes_CFuncPtr_errcheck_get, (setter)_ctypes_CFuncPtr_errcheck_set, _ctypes_CFuncPtr_errcheck_DOCSTR}, +#else +# define _CTYPES_CFUNCPTR_ERRCHECK_GETSETDEF {"errcheck", (getter)_ctypes_CFuncPtr_errcheck_get, NULL, _ctypes_CFuncPtr_errcheck_DOCSTR}, +#endif + +static PyObject * +_ctypes_CFuncPtr_errcheck_get_impl(PyCFuncPtrObject *self); + +static PyObject * +_ctypes_CFuncPtr_errcheck_get(PyCFuncPtrObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ctypes_CFuncPtr_errcheck_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + #if !defined(_ctypes_CFuncPtr_restype_DOCSTR) # define _ctypes_CFuncPtr_restype_DOCSTR NULL #endif @@ -730,4 +784,4 @@ Simple_from_outparm(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py } return Simple_from_outparm_impl(self, cls); } -/*[clinic end generated code: output=844834e45c4bf037 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=67e66bba915fe5b6 input=a9049054013a1b77]*/ From b6efa0dcbb51a8659d595961105a163af00511a0 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 19 Dec 2024 20:41:39 -0500 Subject: [PATCH 4/9] Rerun clinic to fix compiler warnings. --- Modules/_ctypes/clinic/_ctypes.c.h | 11 ++++++++++- a.zip | Bin 0 -> 216 bytes 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 a.zip diff --git a/Modules/_ctypes/clinic/_ctypes.c.h b/Modules/_ctypes/clinic/_ctypes.c.h index 88366168d585e9..405a3c9238d77d 100644 --- a/Modules/_ctypes/clinic/_ctypes.c.h +++ b/Modules/_ctypes/clinic/_ctypes.c.h @@ -629,6 +629,9 @@ _ctypes_CFuncPtr_errcheck_set(PyCFuncPtrObject *self, PyObject *value, void *Py_ PyDoc_STRVAR(_ctypes_CFuncPtr_errcheck__doc__, "a function to check for errors"); +#if defined(_ctypes_CFuncPtr_errcheck_DOCSTR) +# undef _ctypes_CFuncPtr_errcheck_DOCSTR +#endif #define _ctypes_CFuncPtr_errcheck_DOCSTR _ctypes_CFuncPtr_errcheck__doc__ #if !defined(_ctypes_CFuncPtr_errcheck_DOCSTR) @@ -683,6 +686,9 @@ _ctypes_CFuncPtr_restype_set(PyCFuncPtrObject *self, PyObject *value, void *Py_U PyDoc_STRVAR(_ctypes_CFuncPtr_restype__doc__, "specify the result type"); +#if defined(_ctypes_CFuncPtr_restype_DOCSTR) +# undef _ctypes_CFuncPtr_restype_DOCSTR +#endif #define _ctypes_CFuncPtr_restype_DOCSTR _ctypes_CFuncPtr_restype__doc__ #if !defined(_ctypes_CFuncPtr_restype_DOCSTR) @@ -737,6 +743,9 @@ _ctypes_CFuncPtr_argtypes_set(PyCFuncPtrObject *self, PyObject *value, void *Py_ PyDoc_STRVAR(_ctypes_CFuncPtr_argtypes__doc__, "specify the argument types"); +#if defined(_ctypes_CFuncPtr_argtypes_DOCSTR) +# undef _ctypes_CFuncPtr_argtypes_DOCSTR +#endif #define _ctypes_CFuncPtr_argtypes_DOCSTR _ctypes_CFuncPtr_argtypes__doc__ #if !defined(_ctypes_CFuncPtr_argtypes_DOCSTR) @@ -784,4 +793,4 @@ Simple_from_outparm(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py } return Simple_from_outparm_impl(self, cls); } -/*[clinic end generated code: output=67e66bba915fe5b6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cb3583522a2c5ce5 input=a9049054013a1b77]*/ diff --git a/a.zip b/a.zip new file mode 100644 index 0000000000000000000000000000000000000000..3d7f6cd0443cc7af3f43e73ce507a776ad03af65 GIT binary patch literal 216 zcmWIWW@Zs#0D<+n{gJJLUcB5uHVCrp|g6xgl+Z(_CR0kwcF4De=U1Lz>% literal 0 HcmV?d00001 From 56218e7d168ea5993639341786de313e52b1189b Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 19 Dec 2024 20:41:50 -0500 Subject: [PATCH 5/9] Remove test artifact. --- a.zip | Bin 216 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.zip diff --git a/a.zip b/a.zip deleted file mode 100644 index 3d7f6cd0443cc7af3f43e73ce507a776ad03af65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216 zcmWIWW@Zs#0D<+n{gJJLUcB5uHVCrp|g6xgl+Z(_CR0kwcF4De=U1Lz>% From ec5d9f88d8514a31355f423c2d201f73ab3fe208 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 19 Dec 2024 20:43:40 -0500 Subject: [PATCH 6/9] Remove debugging artifact. --- Tools/clinic/libclinic/dsl_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tools/clinic/libclinic/dsl_parser.py b/Tools/clinic/libclinic/dsl_parser.py index 31e74efbe58708..4b4a8b9969d142 100644 --- a/Tools/clinic/libclinic/dsl_parser.py +++ b/Tools/clinic/libclinic/dsl_parser.py @@ -1529,7 +1529,6 @@ def format_docstring(self) -> str: lines = f.docstring.split('\n') if len(lines) >= 2: if lines[1]: - print(lines[1]) fail(f"Docstring for {f.full_name!r} does not have a summary line!\n" "Every non-blank function docstring must start with " "a single line summary followed by an empty line.") From 80c120247470276acd4922440512178da56aa49b Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 19 Dec 2024 20:46:06 -0500 Subject: [PATCH 7/9] Add blurb. --- .../next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst diff --git a/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst b/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst new file mode 100644 index 00000000000000..faf1ec042bc2b9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-19-20-46-01.gh-issue-127946.4lM3Op.rst @@ -0,0 +1,2 @@ +Fix crash when modifying :class:`ctypes._CFuncPtr` objects concurrently on +the :term:`free threaded ` build. From 277a959440321cac26ca84381abc7ae9714c58e2 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 19 Dec 2024 20:51:11 -0500 Subject: [PATCH 8/9] Add a test. --- Lib/test/test_ctypes/test_cfuncs.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ctypes/test_cfuncs.py b/Lib/test/test_ctypes/test_cfuncs.py index 48330c4b0a763b..7bcb64824b3b6e 100644 --- a/Lib/test/test_ctypes/test_cfuncs.py +++ b/Lib/test/test_ctypes/test_cfuncs.py @@ -5,7 +5,8 @@ c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double, c_longdouble) -from test.support import import_helper +from test import support +from test.support import import_helper, threading_helper _ctypes_test = import_helper.import_module("_ctypes_test") @@ -191,6 +192,20 @@ def test_void(self): self.assertEqual(self._dll.tv_i(-42), None) self.assertEqual(self.S(), -42) + @threading_helper.requires_working_threading() + @support.requires_resource("cpu") + @unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful on free-threading") + def test_thread_safety(self): + from threading import Thread + + def concurrent(): + for _ in range(100): + self._dll.tf_b.restype = c_byte + self._dll.tf_b.argtypes = (c_byte,) + + with threading_helper.start_threads((Thread(target=concurrent) for _ in range(10))): + pass + # The following repeats the above tests with stdcall functions (where # they are available) From f682074b2092937a6108b11554fe0a814b1e1b9a Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 19 Dec 2024 20:52:19 -0500 Subject: [PATCH 9/9] Wrap test with catch_threading_exception() --- Lib/test/test_ctypes/test_cfuncs.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ctypes/test_cfuncs.py b/Lib/test/test_ctypes/test_cfuncs.py index 7bcb64824b3b6e..e0c124607cb2e9 100644 --- a/Lib/test/test_ctypes/test_cfuncs.py +++ b/Lib/test/test_ctypes/test_cfuncs.py @@ -203,8 +203,11 @@ def concurrent(): self._dll.tf_b.restype = c_byte self._dll.tf_b.argtypes = (c_byte,) - with threading_helper.start_threads((Thread(target=concurrent) for _ in range(10))): - pass + with threading_helper.catch_threading_exception() as exc: + with threading_helper.start_threads((Thread(target=concurrent) for _ in range(10))): + pass + + self.assertIsNone(exc.exc_value) # The following repeats the above tests with stdcall functions (where