8000 gh-128942: make arraymodule.c free-thread safe (lock-free) by tom-pytel · Pull Request #130771 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-128942: make arraymodule.c free-thread safe (lock-free) #130771

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

Open
wants to merge 65 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f54c5fc
slow arraymodule
tom-pytel Mar 1, 2025
4c71f00
lockfree read and write single element
tom-pytel Mar 2, 2025
512c4c7
📜🤖 Added by blurb_it.
blurb-it[bot] Mar 2, 2025
060100f
ensure_shared_on_resize() in one more place
tom-pytel Mar 2, 2025
d0b17c6
fix stupid direct return out of critical section
tom-pytel Mar 3, 2025
c17b787
requested changes
tom-pytel Mar 3, 2025
4fd8383
downgrade to _Py_atomic_load_ptr_relaxed in missed place
tom-pytel Mar 3, 2025
d00f2b7
arraymodule linked statically
tom-pytel Mar 4, 2025
a3e6004
cleanups
tom-pytel Mar 5, 2025
fb6212a
test and tsan stuff
tom-pytel Mar 5, 2025
04a8f9d
getters and setters using atomics
tom-pytel Mar 5, 2025
01423fd
fix 2 byte wchar_t
tom-pytel Mar 6, 2025
99331dd
remove debug printf
tom-pytel Mar 6, 2025
07c98cc
just a hunch...
tom-pytel Mar 6, 2025
cf3bbbb
atomic "memcpy"
tom-pytel Mar 6, 2025
51135a4
misc
tom-pytel Mar 7, 2025
fff827e
use proper type FT_ macros
tom-pytel Mar 8, 2025
12f0ff6
atomic aggregate _Py_atomic_source_memcpy_relaxed()
tom-pytel Mar 10, 2025
1a6b8df
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Mar 11, 2025
94ef417
remove atomic memcpy
tom-pytel Mar 11, 2025
0056412
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Mar 11, 2025
1431784
regen clinic
tom-pytel Mar 11, 2025
460d3d7
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Mar 11, 2025
409239c
requested changes
tom-pytel Mar 15, 2025
a6f17c9
more requested changes
tom-pytel Mar 17, 2025
b5d219e
__declspec(align(8)) for Windows
tom-pytel Mar 17, 2025
2c82071
shut up check-c-globals
tom-pytel Mar 17, 2025
1ba50e9
alignment changes
tom-pytel Mar 20, 2025
576aebf
MS_WINDOWS -> _MSC_VER
tom-pytel Mar 20, 2025
4dd0954
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Mar 20, 2025
d4e5313
#include "pycore_gc.h", something moved
tom-pytel Mar 20, 2025
48eabe3
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Mar 25, 2025
c056b13
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Mar 25, 2025
affae8e
remove NULL check in arraydata_free()
tom-pytel Mar 25, 2025
689c7a3
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Mar 31, 2025
3e2f8cd
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Apr 4, 2025
7286fed
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Apr 14, 2025
6550906
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Apr 23, 2025
5b35203
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel May 2, 2025
15d92ca
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel May 5, 2025
8000
2e7132e
change to use new _Py_ALIGN_AS() macro
tom-pytel May 5, 2025
06f86cf
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel May 8, 2025
d29c208
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel May 23, 2025
8db665a
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Jun 10, 2025
cad8ca2
Merge in the main branch, switch to _Py_ALIGNED_DEF
encukou Jun 11, 2025
1a50981
requested changes
tom-pytel Jun 11, 2025
f023e77
shut up UBSan
tom-pytel Jun 13, 2025
b53ba2a
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Jun 21, 2025
eb60ffa
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Jul 6, 2025
6d50e9a
add size to _PyMem_FreeDelayed()
tom-pytel Jul 6, 2025
711d86e
Merge branch 'main' into fix-issue-128942-lockfree
tom-pytel Jul 21, 2025
a813353
fix for recent changes to Setup.stdlib.in
tom-pytel Jul 21, 2025
684b953
fix broken fix of Setup.stdlib.in
tom-pytel Jul 21, 2025
0cec45d
with open(), remove test_array from TSAN tests
tom-pytel Jul 21, 2025
fd97dc3
Update Include/internal/pycore_pymem.h
tom-pytel Jul 21, 2025
229383b
Update Include/internal/pycore_pymem.h
tom-pytel Jul 21, 2025
8198469
remove array module from Setup.stdlib.in
tom-pytel Jul 21, 2025
8af6be7
requested changes
tom-pytel Jul 22, 2025
201e7ad
rest of requested changes
tom-pytel Jul 22, 2025
0bd7ec1
remove import os from test_array
tom-pytel Jul 22, 2025
b8bdb13
add assert size * itemisze < size_t in buffer alloc func
tom-pytel Jul 23, 2025
05b1a91
fix formatting
tom-pytel Jul 23, 2025
60f6e5b
fix and clean up array size checks everywhere
tom-pytel Jul 23, 2025
30cfdbf
fix sizeof signed compare
tom-pytel Jul 23, 2025
07c9c15
remove misc comment
tom-pytel Jul 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
requested changes
  • Loading branch information
tom-pytel committed Jun 11, 2025
commit 1a509813d640d9c610facb0172479899c931bc54
81 changes: 30 additions & 51 deletions Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,14 @@ enum machine_format_code {
static arraydata *
arraydata_alloc(Py_ssize_t size, int itemsize)
{
arraydata *data = (arraydata *)PyMem_Malloc(sizeof(arraydata) + size * itemsize);
size_t bufsize = sizeof(arraydata) + size * itemsize;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Py_ssize_t for sizes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case the size_t was in the original code and the signature is PyMem_Malloc(size_t size) so it needs to be cast anyway... Your call, confirm yes or no.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my knowledge, we typically use Py_ssize_t in most places for PyMem_Malloc, and I'd say that it fits nicely here since the size parameter is already a Py_ssize_t. Let's change it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw guards like if (size > PY_SSIZE_T_MAX / descr->itemsize). Maybe it is worth to add an assert here to be sure that size * itemsize doesn't overflow size_t?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there are checks everywhere, but also yes an assert here is not a bad idea.

arraydata *data = (arraydata *)PyMem_Malloc(bufsize);
if (data == NULL) {
return NULL;
}
#ifdef Py_DEBUG
memset(data, 0, bufsize);
#endif
data->allocated = size;
return data;
}
Expand Down Expand Up @@ -191,7 +195,7 @@ arraydata_realloc(arraydata *data, Py_ssize_t size, int itemsize)

#endif

static char *
static inline char *
array_items_ptr(arrayobject *self)
{
return self->data == NULL ? NULL : self->data->items;
Expand Down Expand Up @@ -316,9 +320,8 @@ b_setitem(char *items, Py_ssize_t i, PyObject *v)
/* PyArg_Parse's 'b' formatter is for an unsigned char, therefore
must use the next size up that is signed ('h') and manually do
the overflow checking */
if (!PyArg_Parse(v, "h;array item must be integer", &x)) {
if (!PyArg_Parse(v, "h;array item must be integer", &x))
return -1;
}
else if (x < -128) {
PyErr_SetString(PyExc_OverflowError,
"signed char is less than minimum");
Expand All @@ -329,9 +332,8 @@ b_setitem(char *items, Py_ssize_t i, PyObject *v)
"signed char is greater than maximum");
return -1;
}
if (i >= 0) {
if (i >= 0)
((char *)items)[i] = (char)x;
}
return 0;
}

Expand All @@ -347,12 +349,10 @@ BB_setitem(char *items, Py_ssize_t i, PyObject *v)
{
unsigned char x;
/* 'B' == unsigned char, maps to PyArg_Parse's 'b' formatter */
if (!PyArg_Parse(v, "b;array item must be integer", &x)) {
if (!PyArg_Parse(v, "b;array item must be integer", &x))
return -1;
}
if (i >= 0) {
if (i >= 0)
((unsigned char *)items)[i] = x;
}
return 0;
}

Expand Down Expand Up @@ -441,12 +441,10 @@ h_setitem(char *items, Py_ssize_t i, PyObject *v)
{
short x;
/* 'h' == signed short, maps to PyArg_Parse's 'h' formatter */
if (!PyArg_Parse(v, "h;array item must be integer", &x)) {
if (!PyArg_Parse(v, "h;array item must be integer", &x))
return -1;
}
if (i >= 0) {
if (i >= 0)
((short *)items)[i] = x;
}
return 0;
}

Expand All @@ -462,9 +460,8 @@ HH_setitem(char *items, Py_ssize_t i, PyObject *v)
int x;
/* PyArg_Parse's 'h' formatter is for a signed short, therefore
must use the next size up and manually do the overflow checking */
if (!PyArg_Parse(v, "i;array item must be integer", &x)) {
if (!PyArg_Parse(v, "i;array item must be integer", &x))
return -1;
}
else if (x < 0) {
PyErr_SetString(PyExc_OverflowError,
"unsigned short is less than minimum");
Expand All @@ -475,9 +472,8 @@ HH_setitem(char *items, Py_ssize_t i, PyObject *v)
"unsigned short is greater than maximum");
return -1;
}
if (i >= 0) {
if (i >= 0)
((short *)items)[i] = (short)x;
}
return 0;
}

Expand All @@ -492,12 +488,10 @@ i_setitem(char *items, Py_ssize_t i, PyObject *v)
{
int x;
/* 'i' == signed int, maps to PyArg_Parse's 'i' formatter */
if (!PyArg_Parse(v, "i;array item must be integer", &x)) {
if (!PyArg_Parse(v, "i;array item must be integer", &x))
return -1;
}
if (i >= 0) {
if (i >= 0)
((int *)items)[i] = x;
}
return 0;
}

Expand Down Expand Up @@ -536,9 +530,8 @@ II_setitem(char *items, Py_ssize_t i, PyObject *v)
}
return -1;
}
if (i >= 0) {
if (i >= 0)
((unsigned int *)items)[i] = (unsigned int)x;
}

if (do_decref) {
Py_DECREF(v);
Expand All @@ -556,12 +549,10 @@ static int
l_setitem(char *items, Py_ssize_t i, PyObject *v)
{
long x;
if (!PyArg_Parse(v, "l;array item must be integer", &x)) {
if (!PyArg_Parse(v, "l;array item must be integer", &x))
return -1;
}
if (i >= 0) {
if (i >= 0)
((long *)items)[i] = x;
}
return 0;
}

Expand Down Expand Up @@ -591,9 +582,8 @@ LL_setitem(char *items, Py_ssize_t i, PyObject *v)
}
return -1;
}
if (i >= 0) {
if (i >= 0)
((unsigned long *)items)[i] = x;
}

if (do_decref) {
Py_DECREF(v);
Expand All @@ -611,12 +601,10 @@ static int
q_setitem(char *items, Py_ssize_t i, PyObject *v)
{
long long x;
if (!PyArg_Parse(v, "L;array item must be integer", &x)) {
if (!PyArg_Parse(v, "L;array item must be integer", &x))
return -1;
}
if (i >= 0) {
if (i >= 0)
((long long *)items)[i] = x;
}
return 0;
}

Expand Down Expand Up @@ -647,9 +635,8 @@ QQ_setitem(char *items, Py_ssize_t i, PyObject *v)
}
return -1;
}
if (i >= 0) {
if (i >= 0)
((unsigned long long *)items)[i] = x;
}

if (do_decref) {
Py_DECREF(v);
Expand All @@ -667,12 +654,10 @@ static int
f_setitem(char *items, Py_ssize_t i, PyObject *v)
{
float x;
if (!PyArg_Parse(v, "f;array item must be float", &x)) {
if (!PyArg_Parse(v, "f;array item must be float", &x))
return -1;
}
if (i >= 0) {
if (i >= 0)
((float *)items)[i] = x;
}
return 0;
}

Expand All @@ -686,12 +671,10 @@ static int
d_setitem(char *items, Py_ssize_t i, PyObject *v)
{
double x;
if (!PyArg_Parse(v, "d;array item must be float", &x)) {
if (!PyArg_Parse(v, "d;array item must be float", &x))
return -1;
}
if (i >= 0) {
if (i >= 0)
((double *)items)[i] = x;
}
return 0;
}

Expand All @@ -700,11 +683,9 @@ d_setitem(char *items, Py_ssize_t i, PyObject *v)
code##_compareitems(const void *lhs, const void *rhs, Py_ssize_t length) \
{ \
const type *a = lhs, *b = rhs; \
for (Py_ssize_t i = 0; i < length; ++i) { \
if (a[i] != b[i]) { \
for (Py_ssize_t i = 0; i < length; ++i) \
if (a[i] != b[i]) \
return a[i] < b[i] ? -1 : 1; \
} \
} \
return 0; \
}

Expand Down Expand Up @@ -984,9 +965,7 @@ array_dealloc(PyObject *op)

arrayobject *self = arrayobject_CAST(op);
if (self->ob_exports > 0) {
PyErr_SetString(PyExc_SystemError,
"deallocated array object has exported buffers");
PyErr_WriteUnraisable(NULL);
PyErr_FormatUnraisable("deallocated array object has exported buffers");
}
if (self->weakreflist != NULL) {
PyObject_ClearWeakRefs(op);
Expand Down
Loading
0