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 io
import unittest
from test import support
from test.support import import_helper
from test.support import os_helper
from test.support import threading_helper
from test.support import _2G
import weakref
import pickle
import operator
import random
import struct
import sys
import sysconfig
import threading
import warnings
import array
@ -1678,266 +1673,5 @@ class LargeArrayTest(unittest.TestCase):
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__":
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()
#endif
#include "pycore_abstract.h" // _PyNumber_Index()
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(array_array_clear__doc__,
@ -24,13 +23,7 @@ array_array_clear_impl(arrayobject *self);
static PyObject *
array_array_clear(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_clear_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array_clear_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array___copy____doc__,
@ -48,13 +41,7 @@ array_array___copy___impl(arrayobject *self);
static PyObject *
array_array___copy__(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array___copy___impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array___copy___impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array___deepcopy____doc__,
@ -66,21 +53,6 @@ PyDoc_STRVAR(array_array___deepcopy____doc__,
#define ARRAY_ARRAY___DEEPCOPY___METHODDEF \
{"__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__,
"count($self, v, /)\n"
"--\n"
@ -90,21 +62,6 @@ PyDoc_STRVAR(array_array_count__doc__,
#define ARRAY_ARRAY_COUNT_METHODDEF \
{"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__,
"index($self, v, start=0, stop=sys.maxsize, /)\n"
"--\n"
@ -145,9 +102,7 @@ array_array_index(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
goto exit;
}
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_index_impl((arrayobject *)self, v, start, stop);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -162,21 +117,6 @@ PyDoc_STRVAR(array_array_remove__doc__,
#define ARRAY_ARRAY_REMOVE_METHODDEF \
{"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__,
"pop($self, i=-1, /)\n"
"--\n"
@ -216,9 +156,7 @@ array_array_pop(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
i = ival;
}
skip_optional:
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_pop_impl((arrayobject *)self, i);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -303,9 +241,7 @@ array_array_insert(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
i = ival;
}
v = args[1];
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_insert_impl((arrayobject *)self, i, v);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -329,13 +265,7 @@ array_array_buffer_info_impl(arrayobject *self);
static PyObject *
array_array_buffer_info(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_buffer_info_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array_buffer_info_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_append__doc__,
@ -347,21 +277,6 @@ PyDoc_STRVAR(array_array_append__doc__,
#define ARRAY_ARRAY_APPEND_METHODDEF \
{"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__,
"byteswap($self, /)\n"
"--\n"
@ -380,13 +295,7 @@ array_array_byteswap_impl(arrayobject *self);
static PyObject *
array_array_byteswap(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_byteswap_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array_byteswap_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_reverse__doc__,
@ -404,13 +313,7 @@ array_array_reverse_impl(arrayobject *self);
static PyObject *
array_array_reverse(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_reverse_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array_reverse_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_fromfile__doc__,
@ -509,9 +412,7 @@ array_array_tofile(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_
goto exit;
}
f = args[0];
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tofile_impl((arrayobject *)self, cls, f);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -526,21 +427,6 @@ PyDoc_STRVAR(array_array_fromlist__doc__,
#define ARRAY_ARRAY_FROMLIST_METHODDEF \
{"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__,
"tolist($self, /)\n"
"--\n"
@ -556,13 +442,7 @@ array_array_tolist_impl(arrayobject *self);
static PyObject *
array_array_tolist(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tolist_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array_tolist_impl((arrayobject *)self);
}
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) {
goto exit;
}
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_frombytes_impl((arrayobject *)self, &buffer);
Py_END_CRITICAL_SECTION();
exit:
/* Cleanup for buffer */
@ -614,13 +492,7 @@ array_array_tobytes_impl(arrayobject *self);
static PyObject *
array_array_tobytes(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tobytes_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array_tobytes_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array_fromunicode__doc__,
@ -650,9 +522,7 @@ array_array_fromunicode(PyObject *self, PyObject *arg)
goto exit;
}
ustr = arg;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_fromunicode_impl((arrayobject *)self, ustr);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -677,13 +547,7 @@ array_array_tounicode_impl(arrayobject *self);
static PyObject *
array_array_tounicode(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *return_value = NULL;
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array_tounicode_impl((arrayobject *)self);
Py_END_CRITICAL_SECTION();
return return_value;
return array_array_tounicode_impl((arrayobject *)self);
}
PyDoc_STRVAR(array_array___sizeof____doc__,
@ -795,9 +659,7 @@ array_array___reduce_ex__(PyObject *self, PyTypeObject *cls, PyObject *const *ar
goto exit;
}
value = args[0];
Py_BEGIN_CRITICAL_SECTION(self);
return_value = array_array___reduce_ex___impl((arrayobject *)self, cls, value);
Py_END_CRITICAL_SECTION();
exit:
return return_value;
@ -833,4 +695,4 @@ PyDoc_STRVAR(array_arrayiterator___setstate____doc__,
#define ARRAY_ARRAYITERATOR___SETSTATE___METHODDEF \
{"__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]*/