Revert "gh-128942: make array module thread safe (#128943)" (#130707)

The change regressed performance on scimark benchmarks from the
pyperformance benchmark suite.

This reverts commit 8ba0d7bbc2.
This commit is contained in:
Sam Gross 2025-02-28 16:57:48 -05:00 committed by GitHub
parent da4899b94a
commit 75f38af781
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 175 additions and 800 deletions

View file

@ -3,21 +3,16 @@
""" """
import collections.abc import collections.abc
import io
import unittest import unittest
from test import support from test import support
from test.support import import_helper from test.support import import_helper
from test.support import os_helper from test.support import os_helper
from test.support import threading_helper
from test.support import _2G from test.support import _2G
import weakref import weakref
import pickle import pickle
import operator import operator
import random
import struct import struct
import sys import sys
import sysconfig
import threading
import warnings import warnings
import array import array
@ -1678,266 +1673,5 @@ class LargeArrayTest(unittest.TestCase):
self.assertRaises(StopIteration, next, it) self.assertRaises(StopIteration, next, it)
class FreeThreadingTest(unittest.TestCase):
# Test pretty much everything that can break under free-threading.
# Non-deterministic, but at least one of these things will fail if
# array module is not free-thread safe.
@unittest.skipUnless(support.Py_GIL_DISABLED, 'this test can only possibly fail with GIL disabled')
@threading_helper.reap_threads
@threading_helper.requires_working_threading()
def test_free_threading(self):
def pop1(b, a): # MODIFIES!
b.wait()
try: a.pop()
except IndexError: pass
def append1(b, a): # MODIFIES!
b.wait()
a.append(2)
def insert1(b, a): # MODIFIES!
b.wait()
a.insert(0, 2)
def extend(b, a): # MODIFIES!
c = array.array('i', [2])
b.wait()
a.extend(c)
def extend2(b, a, c): # MODIFIES!
b.wait()
a.extend(c)
def inplace_concat(b, a): # MODIFIES!
c = array.array('i', [2])
b.wait()
a += c
def inplace_concat2(b, a, c): # MODIFIES!
b.wait()
a += c
def inplace_repeat2(b, a): # MODIFIES!
b.wait()
a *= 2
def clear(b, a, *args): # MODIFIES!
b.wait()
a.clear()
def clear2(b, a, c): # MODIFIES c!
b.wait()
try: c.clear()
except BufferError: pass
def remove1(b, a): # MODIFIES!
b.wait()
try: a.remove(1)
except ValueError: pass
def fromunicode(b, a): # MODIFIES!
b.wait()
a.fromunicode('test')
def frombytes(b, a): # MODIFIES!
b.wait()
a.frombytes(b'0000')
def frombytes2(b, a, c): # MODIFIES!
b.wait()
a.frombytes(c)
def fromlist(b, a): # MODIFIES!
n = random.randint(0, 100)
b.wait()
a.fromlist([2] * n)
def ass_subscr2(b, a, c): # MODIFIES!
b.wait()
a[:] = c
def ass0(b, a): # modifies inplace
b.wait()
try: a[0] = 0
except IndexError: pass
def byteswap(b, a): # modifies inplace
b.wait()
a.byteswap()
def tounicode(b, a):
b.wait()
a.tounicode()
def tobytes(b, a):
b.wait()
a.tobytes()
def tolist(b, a):
b.wait()
a.tolist()
def tofile(b, a):
f = io.BytesIO()
b.wait()
a.tofile(f)
def reduce_ex2(b, a):
b.wait()
a.__reduce_ex__(2)
def reduce_ex3(b, a):
b.wait()
c = a.__reduce_ex__(3)
assert not c[1] or 0xdd not in c[1][3]
def copy(b, a):
b.wait()
c = a.__copy__()
assert not c or 0xdd not in c
def repr1(b, a):
b.wait()
repr(a)
def repeat2(b, a):
b.wait()
a * 2
def count1(b, a):
b.wait()
a.count(1)
def index1(b, a):
b.wait()
try: a.index(1)
except ValueError: pass
def contains1(b, a):
b.wait()
try: 1 in a
except ValueError: pass
def subscr0(b, a):
b.wait()
try: a[0]
except IndexError: pass
def concat(b, a):
b.wait()
a + a
def concat2(b, a, c):
b.wait()
a + c
def richcmplhs(b, a):
c = a[:]
b.wait()
a == c
def richcmprhs(b, a):
c = a[:]
b.wait()
c == a
def new(b, a):
tc = a.typecode
b.wait()
array.array(tc, a)
def repr_(b, a):
b.wait()
repr(a)
def irepeat(b, a): # MODIFIES!
b.wait()
a *= 2
def newi(b, l):
b.wait()
array.array('i', l)
def fromlistl(b, a, l): # MODIFIES!
b.wait()
a.fromlist(l)
def fromlistlclear(b, a, l): # MODIFIES LIST!
b.wait()
l.clear()
def iter_next(b, a, it): # MODIFIES ITERATOR!
b.wait()
list(it)
def iter_reduce(b, a, it):
b.wait()
c = it.__reduce__()
assert not c[1] or 0xdd not in c[1][0]
def check(funcs, a=None, *args):
if a is None:
a = array.array('i', [1])
barrier = threading.Barrier(len(funcs))
threads = []
for func in funcs:
thread = threading.Thread(target=func, args=(barrier, a, *args))
threads.append(thread)
with threading_helper.start_threads(threads):
pass
check([pop1] * 10)
check([pop1] + [subscr0] * 10)
check([append1] * 10)
check([insert1] * 10)
check([pop1] + [index1] * 10)
check([pop1] + [contains1] * 10)
check([insert1] + [repeat2] * 10)
check([pop1] + [repr1] * 10)
check([inplace_repeat2] * 10)
check([byteswap] * 10)
check([insert1] + [clear] * 10)
check([pop1] + [count1] * 10)
check([remove1] * 10)
check([clear] + [copy] * 10, array.array('B', b'0' * 0x400000))
check([pop1] + [reduce_ex2] * 10)
check([clear] + [reduce_ex3] * 10, array.array('B', b'0' * 0x400000))
check([pop1] + [tobytes] * 10)
check([pop1] + [tolist] * 10)
check([clear, tounicode] * 10, array.array('w', 'a'*10000))
check([clear, tofile] * 10, array.array('w', 'a'*10000))
check([clear] + [extend] * 10)
check([clear] + [inplace_concat] * 10)
check([clear] + [concat] * 10, array.array('w', 'a'*10000))
check([fromunicode] * 10, array.array('w', 'a'))
check([frombytes] * 10)
check([fromlist] * 10)
check([clear] + [richcmplhs] * 10, array.array('i', [1]*10000))
check([clear] + [richcmprhs] * 10, array.array('i', [1]*10000))
check([clear, ass0] * 10, array.array('i', [1]*10000)) # to test array_ass_item must disable Py_mp_ass_subscript
check([clear] + [new] * 10, array.array('w', 'a'*10000))
check([clear] + [repr_] * 10, array.array('B', b'0' * 0x40000))
check([clear] + [repr_] * 10, array.array('B', b'0' * 0x40000))
check([clear] + [irepeat] * 10, array.array('B', b'0' * 0x40000))
check([clear] + [iter_reduce] * 10, a := array.array('B', b'0' * 0x400), iter(a))
# make sure we handle non-self objects correctly
check([clear] + [newi] * 10, [2] * random.randint(0, 100))
check([fromlistlclear] + [fromlistl] * 10, array.array('i', [1]), [2] * random.randint(0, 100))
check([clear2] + [concat2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
check([clear2] + [inplace_concat2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
check([clear2] + [extend2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
check([clear2] + [ass_subscr2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
check([clear2] + [frombytes2] * 10, array.array('w', 'a'*10000), array.array('B', b'a'*10000))
# iterator stuff
check([clear] + [iter_next] * 10, a := array.array('i', [1] * 10), iter(a))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -1 +0,0 @@
Make the :mod:`array` module safe under :term:`free threading`.

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,6 @@ preserve
# include "pycore_runtime.h" // _Py_SINGLETON() # include "pycore_runtime.h" // _Py_SINGLETON()
#endif #endif
#include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_abstract.h" // _PyNumber_Index()
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(array_array_clear__doc__, PyDoc_STRVAR(array_array_clear__doc__,
@ -24,13 +23,7 @@ array_array_clear_impl(arrayobject *self);
static PyObject * static PyObject *
array_array_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array_clear(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array_clear_impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_clear_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array___copy____doc__, PyDoc_STRVAR(array_array___copy____doc__,
@ -48,13 +41,7 @@ array_array___copy___impl(arrayobject *self);
static PyObject * static PyObject *
array_array___copy__(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array___copy__(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array___copy___impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array___copy___impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array___deepcopy____doc__, PyDoc_STRVAR(array_array___deepcopy____doc__,
@ -66,21 +53,6 @@ PyDoc_STRVAR(array_array___deepcopy____doc__,
#define ARRAY_ARRAY___DEEPCOPY___METHODDEF \ #define ARRAY_ARRAY___DEEPCOPY___METHODDEF \
{"__deepcopy__", (PyCFunction)array_array___deepcopy__, METH_O, array_array___deepcopy____doc__}, {"__deepcopy__", (PyCFunction)array_array___deepcopy__, METH_O, array_array___deepcopy____doc__},
static PyObject *
array_array___deepcopy___impl(arrayobject *self, PyObject *unused);
static PyObject *
array_array___deepcopy__(arrayobject *self, PyObject *unused)
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array___deepcopy___impl((arrayobject *)self, unused);
Py_END_CRITICAL_SECTION();
return return_value;
}
PyDoc_STRVAR(array_array_count__doc__, PyDoc_STRVAR(array_array_count__doc__,
"count($self, v, /)\n" "count($self, v, /)\n"
"--\n" "--\n"
@ -90,21 +62,6 @@ PyDoc_STRVAR(array_array_count__doc__,
#define ARRAY_ARRAY_COUNT_METHODDEF \ #define ARRAY_ARRAY_COUNT_METHODDEF \
{"count", (PyCFunction)array_array_count, METH_O, array_array_count__doc__}, {"count", (PyCFunction)array_array_count, METH_O, array_array_count__doc__},
static PyObject *
array_array_count_impl(arrayobject *self, PyObject *v);
static PyObject *
array_array_count(arrayobject *self, PyObject *v)
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_count_impl((arrayobject *)self, v);
Py_END_CRITICAL_SECTION();
return return_value;
}
PyDoc_STRVAR(array_array_index__doc__, PyDoc_STRVAR(array_array_index__doc__,
"index($self, v, start=0, stop=sys.maxsize, /)\n" "index($self, v, start=0, stop=sys.maxsize, /)\n"
"--\n" "--\n"
@ -145,9 +102,7 @@ array_array_index(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit; goto exit;
} }
skip_optional: skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_index_impl((arrayobject *)self, v, start, stop); return_value = array_array_index_impl((arrayobject *)self, v, start, stop);
Py_END_CRITICAL_SECTION();
exit: exit:
return return_value; return return_value;
@ -162,21 +117,6 @@ PyDoc_STRVAR(array_array_remove__doc__,
#define ARRAY_ARRAY_REMOVE_METHODDEF \ #define ARRAY_ARRAY_REMOVE_METHODDEF \
{"remove", (PyCFunction)array_array_remove, METH_O, array_array_remove__doc__}, {"remove", (PyCFunction)array_array_remove, METH_O, array_array_remove__doc__},
static PyObject *
array_array_remove_impl(arrayobject *self, PyObject *v);
static PyObject *
array_array_remove(arrayobject *self, PyObject *v)
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_remove_impl((arrayobject *)self, v);
Py_END_CRITICAL_SECTION();
return return_value;
}
PyDoc_STRVAR(array_array_pop__doc__, PyDoc_STRVAR(array_array_pop__doc__,
"pop($self, i=-1, /)\n" "pop($self, i=-1, /)\n"
"--\n" "--\n"
@ -216,9 +156,7 @@ array_array_pop(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
i = ival; i = ival;
} }
skip_optional: skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_pop_impl((arrayobject *)self, i); return_value = array_array_pop_impl((arrayobject *)self, i);
Py_END_CRITICAL_SECTION();
exit: exit:
return return_value; return return_value;
@ -303,9 +241,7 @@ array_array_insert(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
i = ival; i = ival;
} }
v = args[1]; v = args[1];
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_insert_impl((arrayobject *)self, i, v); return_value = array_array_insert_impl((arrayobject *)self, i, v);
Py_END_CRITICAL_SECTION();
exit: exit:
return return_value; return return_value;
@ -329,13 +265,7 @@ array_array_buffer_info_impl(arrayobject *self);
static PyObject * static PyObject *
array_array_buffer_info(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array_buffer_info(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array_buffer_info_impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_buffer_info_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array_append__doc__, PyDoc_STRVAR(array_array_append__doc__,
@ -347,21 +277,6 @@ PyDoc_STRVAR(array_array_append__doc__,
#define ARRAY_ARRAY_APPEND_METHODDEF \ #define ARRAY_ARRAY_APPEND_METHODDEF \
{"append", (PyCFunction)array_array_append, METH_O, array_array_append__doc__}, {"append", (PyCFunction)array_array_append, METH_O, array_array_append__doc__},
static PyObject *
array_array_append_impl(arrayobject *self, PyObject *v);
static PyObject *
array_array_append(arrayobject *self, PyObject *v)
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_append_impl((arrayobject *)self, v);
Py_END_CRITICAL_SECTION();
return return_value;
}
PyDoc_STRVAR(array_array_byteswap__doc__, PyDoc_STRVAR(array_array_byteswap__doc__,
"byteswap($self, /)\n" "byteswap($self, /)\n"
"--\n" "--\n"
@ -380,13 +295,7 @@ array_array_byteswap_impl(arrayobject *self);
static PyObject * static PyObject *
array_array_byteswap(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array_byteswap(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array_byteswap_impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_byteswap_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array_reverse__doc__, PyDoc_STRVAR(array_array_reverse__doc__,
@ -404,13 +313,7 @@ array_array_reverse_impl(arrayobject *self);
static PyObject * static PyObject *
array_array_reverse(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array_reverse(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array_reverse_impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_reverse_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array_fromfile__doc__, PyDoc_STRVAR(array_array_fromfile__doc__,
@ -509,9 +412,7 @@ array_array_tofile(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_
goto exit; goto exit;
} }
f = args[0]; f = args[0];
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tofile_impl((arrayobject *)self, cls, f); return_value = array_array_tofile_impl((arrayobject *)self, cls, f);
Py_END_CRITICAL_SECTION();
exit: exit:
return return_value; return return_value;
@ -526,21 +427,6 @@ PyDoc_STRVAR(array_array_fromlist__doc__,
#define ARRAY_ARRAY_FROMLIST_METHODDEF \ #define ARRAY_ARRAY_FROMLIST_METHODDEF \
{"fromlist", (PyCFunction)array_array_fromlist, METH_O, array_array_fromlist__doc__}, {"fromlist", (PyCFunction)array_array_fromlist, METH_O, array_array_fromlist__doc__},
static PyObject *
array_array_fromlist_impl(arrayobject *self, PyObject *list);
static PyObject *
array_array_fromlist(arrayobject *self, PyObject *list)
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION2(self, list);
return_value = array_array_fromlist_impl((arrayobject *)self, list);
Py_END_CRITICAL_SECTION2();
return return_value;
}
PyDoc_STRVAR(array_array_tolist__doc__, PyDoc_STRVAR(array_array_tolist__doc__,
"tolist($self, /)\n" "tolist($self, /)\n"
"--\n" "--\n"
@ -556,13 +442,7 @@ array_array_tolist_impl(arrayobject *self);
static PyObject * static PyObject *
array_array_tolist(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array_tolist(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array_tolist_impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tolist_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array_frombytes__doc__, PyDoc_STRVAR(array_array_frombytes__doc__,
@ -586,9 +466,7 @@ array_array_frombytes(PyObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &buffer, PyBUF_SIMPLE) != 0) { if (PyObject_GetBuffer(arg, &buffer, PyBUF_SIMPLE) != 0) {
goto exit; goto exit;
} }
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_frombytes_impl((arrayobject *)self, &buffer); return_value = array_array_frombytes_impl((arrayobject *)self, &buffer);
Py_END_CRITICAL_SECTION();
exit: exit:
/* Cleanup for buffer */ /* Cleanup for buffer */
@ -614,13 +492,7 @@ array_array_tobytes_impl(arrayobject *self);
static PyObject * static PyObject *
array_array_tobytes(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array_tobytes(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array_tobytes_impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tobytes_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array_fromunicode__doc__, PyDoc_STRVAR(array_array_fromunicode__doc__,
@ -650,9 +522,7 @@ array_array_fromunicode(PyObject *self, PyObject *arg)
goto exit; goto exit;
} }
ustr = arg; ustr = arg;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_fromunicode_impl((arrayobject *)self, ustr); return_value = array_array_fromunicode_impl((arrayobject *)self, ustr);
Py_END_CRITICAL_SECTION();
exit: exit:
return return_value; return return_value;
@ -677,13 +547,7 @@ array_array_tounicode_impl(arrayobject *self);
static PyObject * static PyObject *
array_array_tounicode(PyObject *self, PyObject *Py_UNUSED(ignored)) array_array_tounicode(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
PyObject *return_value = NULL; return array_array_tounicode_impl((arrayobject *)self);
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tounicode_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
} }
PyDoc_STRVAR(array_array___sizeof____doc__, PyDoc_STRVAR(array_array___sizeof____doc__,
@ -795,9 +659,7 @@ array_array___reduce_ex__(PyObject *self, PyTypeObject *cls, PyObject *const *ar
goto exit; goto exit;
} }
value = args[0]; value = args[0];
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array___reduce_ex___impl((arrayobject *)self, cls, value); return_value = array_array___reduce_ex___impl((arrayobject *)self, cls, value);
Py_END_CRITICAL_SECTION();
exit: exit:
return return_value; return return_value;
@ -833,4 +695,4 @@ PyDoc_STRVAR(array_arrayiterator___setstate____doc__,
#define ARRAY_ARRAYITERATOR___SETSTATE___METHODDEF \ #define ARRAY_ARRAYITERATOR___SETSTATE___METHODDEF \
{"__setstate__", (PyCFunction)array_arrayiterator___setstate__, METH_O, array_arrayiterator___setstate____doc__}, {"__setstate__", (PyCFunction)array_arrayiterator___setstate__, METH_O, array_arrayiterator___setstate____doc__},
/*[clinic end generated code: output=c9219e074c62e0c8 input=a9049054013a1b77]*/ /*[clinic end generated code: output=8120dc5c4fa414b9 input=a9049054013a1b77]*/