]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-127065: Make `methodcaller` thread-safe in free threading build (#127109)
authorSam Gross <colesbury@gmail.com>
Fri, 22 Nov 2024 14:21:59 +0000 (14:21 +0000)
committerGitHub <noreply@github.com>
Fri, 22 Nov 2024 14:21:59 +0000 (09:21 -0500)
The `methodcaller` C vectorcall implementation uses an arguments array
that is shared across calls. The first argument is modified on every
invocation. This isn't thread-safe in the free threading build. I think
it's also not safe in general, but for now just disable it in the free
threading build.

Misc/NEWS.d/next/Library/2024-11-21-16-23-16.gh-issue-127065.cfL1zd.rst [new file with mode: 0644]
Modules/_operator.c

diff --git a/Misc/NEWS.d/next/Library/2024-11-21-16-23-16.gh-issue-127065.cfL1zd.rst b/Misc/NEWS.d/next/Library/2024-11-21-16-23-16.gh-issue-127065.cfL1zd.rst
new file mode 100644 (file)
index 0000000..83457da
--- /dev/null
@@ -0,0 +1,2 @@
+Fix crash when calling a :func:`operator.methodcaller` instance from
+multiple threads in the free threading build.
index 7e0d1f3df87e4d1e7ded93fa22639234fb92b8c9..6c1945174ab7cd02ca76cd47c8329c81c483aac2 100644 (file)
@@ -1602,6 +1602,7 @@ typedef struct {
     vectorcallfunc vectorcall;
 } methodcallerobject;
 
+#ifndef Py_GIL_DISABLED
 static int _methodcaller_initialize_vectorcall(methodcallerobject* mc)
 {
     PyObject* args = mc->xargs;
@@ -1664,6 +1665,7 @@ methodcaller_vectorcall(
             (PyTuple_GET_SIZE(mc->xargs)) | PY_VECTORCALL_ARGUMENTS_OFFSET,
             mc->vectorcall_kwnames);
 }
+#endif
 
 
 /* AC 3.5: variable number of arguments, not currently support by AC */
@@ -1703,7 +1705,14 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     mc->vectorcall_args = 0;
 
 
+#ifdef Py_GIL_DISABLED
+    // gh-127065: The current implementation of methodcaller_vectorcall
+    // is not thread-safe because it modifies the `vectorcall_args` array,
+    // which is shared across calls.
+    mc->vectorcall = NULL;
+#else
     mc->vectorcall = (vectorcallfunc)methodcaller_vectorcall;
+#endif
 
     PyObject_GC_Track(mc);
     return (PyObject *)mc;