From bd588246659bffc3621c9b68752d28c6c5e094af Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 25 Mar 2026 17:36:31 +0530 Subject: [PATCH 1/4] add thread safety docs on bytes C-API --- Doc/c-api/bytes.rst | 14 ++++++++++++++ Doc/data/threadsafety.dat | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index b3cd26a8504715..04461dce425c42 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -127,6 +127,10 @@ called with a non-bytes parameter. Return the bytes representation of object *o* that implements the buffer protocol. + .. note:: + If the object implements the buffer protocol, then the buffer + should not be mutated while the bytes object is being created. + .. c:function:: Py_ssize_t PyBytes_Size(PyObject *o) @@ -185,6 +189,9 @@ called with a non-bytes parameter. created, the old reference to *bytes* will still be discarded and the value of *\*bytes* will be set to ``NULL``; the appropriate exception will be set. + .. note:: + If *newpart* implements the buffer protocol, then the buffer + should not be mutated while the new bytes object is being created. .. c:function:: void PyBytes_ConcatAndDel(PyObject **bytes, PyObject *newpart) @@ -192,6 +199,10 @@ called with a non-bytes parameter. appended to *bytes*. This version releases the :term:`strong reference` to *newpart* (i.e. decrements its reference count). + .. note:: + If *newpart* implements the buffer protocol, then the buffer + should not be mutated while the new bytes object is being created. + .. c:function:: PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) @@ -210,6 +221,9 @@ called with a non-bytes parameter. .. versionadded:: 3.14 + .. note:: + If *iterable* objects implement the buffer protocol, then the buffers + should not be mutated while the new bytes object is being created. .. c:function:: int _PyBytes_Resize(PyObject **bytes, Py_ssize_t newsize) diff --git a/Doc/data/threadsafety.dat b/Doc/data/threadsafety.dat index 103e8ef3e97ed1..e3651934d77fb7 100644 --- a/Doc/data/threadsafety.dat +++ b/Doc/data/threadsafety.dat @@ -66,10 +66,41 @@ PyList_Reverse:shared: # is a list PyList_SetSlice:shared: -# Sort - per-object lock held; comparison callbacks may execute -# arbitrary Python code +# Sort - per-object lock held; the list is emptied before sorting +# so other threads may observe an empty list, but they won't see the +# intermediate states of the sort PyList_Sort:shared: # Extend - lock target list; also lock source when it is a # list, set, or dict PyList_Extend:shared: + +# Creation - pure allocation, no shared state +PyBytes_FromString:atomic: +PyBytes_FromStringAndSize:atomic: +PyBytes_DecodeEscape:atomic: + +# Creation from object - may call arbitrary code +PyBytes_FromFormat:shared: +PyBytes_FromFormatV:shared: +PyBytes_FromObject:shared: + +# Size - uses atomic load on free-threaded builds +PyBytes_Size:atomic: +PyBytes_GET_SIZE:atomic: + +# Raw data - no locking; mutating it is unsafe if the bytes object is shared between threads +PyBytes_AsString:compatible: +PyBytes_AS_STRING:compatible: +PyBytes_AsStringAndSize:compatible: + +# Concatenation - uses buffer protocol; safe as long as buffer is not mutated by another thread during the operation +PyBytes_Concat:shared: +PyBytes_ConcatAndDel:shared: +PyBytes_Join:shared: + +# Resizing - safe if the object is unique +_PyBytes_Resize:distinct: + +# Repr - atomic as bytes are immutable +PyBytes_Repr:atomic: From 8c1f5ab9fdec7f5ad8611cb58e71db1d757197a0 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 26 Mar 2026 17:44:48 +0530 Subject: [PATCH 2/4] should -> must --- Doc/c-api/bytes.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 04461dce425c42..d1fde1baf71a45 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -129,7 +129,7 @@ called with a non-bytes parameter. .. note:: If the object implements the buffer protocol, then the buffer - should not be mutated while the bytes object is being created. + must not be mutated while the bytes object is being created. .. c:function:: Py_ssize_t PyBytes_Size(PyObject *o) @@ -191,7 +191,7 @@ called with a non-bytes parameter. .. note:: If *newpart* implements the buffer protocol, then the buffer - should not be mutated while the new bytes object is being created. + must not be mutated while the new bytes object is being created. .. c:function:: void PyBytes_ConcatAndDel(PyObject **bytes, PyObject *newpart) @@ -201,7 +201,7 @@ called with a non-bytes parameter. .. note:: If *newpart* implements the buffer protocol, then the buffer - should not be mutated while the new bytes object is being created. + must not be mutated while the new bytes object is being created. .. c:function:: PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) @@ -223,7 +223,7 @@ called with a non-bytes parameter. .. note:: If *iterable* objects implement the buffer protocol, then the buffers - should not be mutated while the new bytes object is being created. + must not be mutated while the new bytes object is being created. .. c:function:: int _PyBytes_Resize(PyObject **bytes, Py_ssize_t newsize) From 84f391d9c36d7e135c93fd68294c17b5cba0695a Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 27 Mar 2026 18:17:26 +0530 Subject: [PATCH 3/4] fix PyBytes_FromFormat comment --- Doc/data/threadsafety.dat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/data/threadsafety.dat b/Doc/data/threadsafety.dat index e3651934d77fb7..731106475ebe07 100644 --- a/Doc/data/threadsafety.dat +++ b/Doc/data/threadsafety.dat @@ -80,9 +80,9 @@ PyBytes_FromString:atomic: PyBytes_FromStringAndSize:atomic: PyBytes_DecodeEscape:atomic: -# Creation from object - may call arbitrary code -PyBytes_FromFormat:shared: -PyBytes_FromFormatV:shared: +# Creation from formatting C primitives - pure allocation, no shared state +PyBytes_FromFormat:atomic: +PyBytes_FromFormatV:atomic: PyBytes_FromObject:shared: # Size - uses atomic load on free-threaded builds From d8ed27b1dcad6b06286affed9fd831b0638b2b07 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 27 Mar 2026 18:23:12 +0530 Subject: [PATCH 4/4] split into two --- Doc/data/threadsafety.dat | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/data/threadsafety.dat b/Doc/data/threadsafety.dat index 731106475ebe07..1210ab17aaa35b 100644 --- a/Doc/data/threadsafety.dat +++ b/Doc/data/threadsafety.dat @@ -83,6 +83,9 @@ PyBytes_DecodeEscape:atomic: # Creation from formatting C primitives - pure allocation, no shared state PyBytes_FromFormat:atomic: PyBytes_FromFormatV:atomic: + +# Creation from object - uses buffer protocol so may call arbitrary code; +# safe as long as the buffer is not mutated by another thread during the operation PyBytes_FromObject:shared: # Size - uses atomic load on free-threaded builds