Skip to content

Commit dffc9cd

Browse files
committed
add test
1 parent 9aa4540 commit dffc9cd

File tree

2 files changed

+272
-2
lines changed

2 files changed

+272
-2
lines changed

Lib/test/test_array.py

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@
33
"""
44

55
import collections.abc
6+
import io
67
import unittest
78
from test import support
89
from test.support import import_helper
910
from test.support import os_helper
11+
from test.support import threading_helper
1012
from test.support import _2G
1113
import weakref
1214
import pickle
1315
import operator
16+
import random
1417
import struct
1518
import sys
19+
import sysconfig
20+
import threading
1621
import warnings
1722

1823
import array
@@ -1673,5 +1678,270 @@ def test_gh_128961(self):
16731678
self.assertRaises(StopIteration, next, it)
16741679

16751680

1681+
class FreeThreadingTest(unittest.TestCase):
1682+
# Test pretty much everything that can break under free-threading.
1683+
# Non-deterministic, but at least one of these things will fail if
1684+
# array module is not free-thread safe.
1685+
1686+
@unittest.skipUnless(sysconfig.get_config_var('Py_GIL_DISABLED'),
1687+
'this test can only fail with GIL disabled')
1688+
@threading_helper.reap_threads
1689+
@threading_helper.requires_working_threading()
1690+
def test_free_threading(self):
1691+
def pop1(b, a): # MODIFIES!
1692+
b.wait()
1693+
try: a.pop()
1694+
except IndexError: pass
1695+
1696+
def append1(b, a): # MODIFIES!
1697+
b.wait()
1698+
a.append(2)
1699+
1700+
def insert1(b, a): # MODIFIES!
1701+
b.wait()
1702+
a.insert(0, 2)
1703+
1704+
def extend(b, a): # MODIFIES!
1705+
c = array.array('i', [2])
1706+
b.wait()
1707+
a.extend(c)
1708+
1709+
def extend2(b, a, c): # MODIFIES!
1710+
b.wait()
1711+
a.extend(c)
1712+
1713+
def inplace_concat(b, a): # MODIFIES!
1714+
c = array.array('i', [2])
1715+
b.wait()
1716+
a += c
1717+
1718+
def inplace_concat2(b, a, c): # MODIFIES!
1719+
b.wait()
1720+
a += c
1721+
1722+
def inplace_repeat2(b, a): # MODIFIES!
1723+
b.wait()
1724+
a *= 2
1725+
1726+
def clear(b, a, *args): # MODIFIES!
1727+
b.wait()
1728+
a.clear()
1729+
1730+
def clear2(b, a, c): # MODIFIES c!
1731+
b.wait()
1732+
try: c.clear()
1733+
except BufferError: pass
1734+
1735+
def remove1(b, a): # MODIFIES!
1736+
b.wait()
1737+
try: a.remove(1)
1738+
except ValueError: pass
1739+
1740+
def fromunicode(b, a): # MODIFIES!
1741+
b.wait()
1742+
a.fromunicode('test')
1743+
1744+
def frombytes(b, a): # MODIFIES!
1745+
b.wait()
1746+
a.frombytes(b'0000')
1747+
1748+
def frombytes2(b, a, c): # MODIFIES!
1749+
b.wait()
1750+
a.frombytes(c)
1751+
1752+
def fromlist(b, a): # MODIFIES!
1753+
n = random.randint(0, 100)
1754+
b.wait()
1755+
a.fromlist([2] * n)
1756+
1757+
def ass_subscr2(b, a, c): # MODIFIES!
1758+
b.wait()
1759+
a[:] = c
1760+
1761+
def ass0(b, a): # modifies inplace
1762+
b.wait()
1763+
try: a[0] = 0
1764+
except IndexError: pass
1765+
1766+
def byteswap(b, a): # modifies inplace
1767+
b.wait()
1768+
a.byteswap()
1769+
1770+
def tounicode(b, a):
1771+
b.wait()
1772+
a.tounicode()
1773+
1774+
def tobytes(b, a):
1775+
b.wait()
1776+
a.tobytes()
1777+
1778+
def tolist(b, a):
1779+
b.wait()
1780+
a.tolist()
1781+
1782+
def tofile(b, a):
1783+
f = io.BytesIO()
1784+
b.wait()
1785+
a.tofile(f)
1786+
1787+
def reduce_ex2(b, a):
1788+
b.wait()
1789+
a.__reduce_ex__(2)
1790+
1791+
def reduce_ex3(b, a):
1792+
b.wait()
1793+
c = a.__reduce_ex__(3)
1794+
assert not c[1] or b'\xdd' not in c[1][3]
1795+
1796+
def copy(b, a):
1797+
b.wait()
1798+
c = a.__copy__()
1799+
assert not c or 0xdd not in c
1800+
1801+
def repr1(b, a):
1802+
b.wait()
1803+
repr(a)
1804+
1805+
def repeat2(b, a):
1806+
b.wait()
1807+
a * 2
1808+
1809+
def count1(b, a):
1810+
b.wait()
1811+
a.count(1)
1812+
1813+
def index1(b, a):
1814+
b.wait()
1815+
try: a.index(1)
1816+
except ValueError: pass
1817+
1818+
def contains1(b, a):
1819+
b.wait()
1820+
try: 1 in a
1821+
except ValueError: pass
1822+
1823+
def subscr0(b, a):
1824+
b.wait()
1825+
try: a[0]
1826+
except IndexError: pass
1827+
1828+
def concat(b, a):
1829+
b.wait()
1830+
a + a
1831+
1832+
def concat2(b, a, c):
1833+
b.wait()
1834+
a + c
1835+
1836+
def richcmplhs(b, a):
1837+
c = a[:]
1838+
b.wait()
1839+
a == c
1840+
1841+
def richcmprhs(b, a):
1842+
c = a[:]
1843+
b.wait()
1844+
c == a
1845+
1846+
def new(b, a):
1847+
tc = a.typecode
1848+
b.wait()
1849+
array.array(tc, a)
1850+
1851+
def repr_(b, a):
1852+
b.wait()
1853+
repr(a)
1854+
1855+
def irepeat(b, a): # MODIFIES!
1856+
b.wait()
1857+
a *= 2
1858+
1859+
def newi(b, l):
1860+
b.wait()
1861+
array.array('i', l)
1862+
1863+
def fromlistl(b, a, l): # MODIFIES!
1864+
b.wait()
1865+
a.fromlist(l)
1866+
1867+
def fromlistlclear(b, a, l): # MODIFIES LIST!
1868+
b.wait()
1869+
l.clear()
1870+
1871+
def iter_next(b, a, it): # MODIFIES ITERATOR!
1872+
b.wait()
1873+
list(it)
1874+
1875+
def iter_reduce(b, a, it):
1876+
b.wait()
1877+
c = it.__reduce__()
1878+
assert not c[1] or b'\xdd' not in c[1][0]
1879+
1880+
def check(funcs, a=None, *args):
1881+
if a is None:
1882+
a = array.array('i', [1])
1883+
1884+
barrier = threading.Barrier(len(funcs))
1885+
threads = []
1886+
1887+
for func in funcs:
1888+
thread = threading.Thread(target=func, args=(barrier, a, *args))
1889+
1890+
threads.append(thread)
1891+
1892+
with threading_helper.start_threads(threads):
1893+
pass
1894+
1895+
for thread in threads:
1896+
threading_helper.join_thread(thread)
1897+
1898+
check([pop1] * 10)
1899+
check([pop1] + [subscr0] * 10)
1900+
check([append1] * 10)
1901+
check([insert1] * 10)
1902+
check([pop1] + [index1] * 10)
1903+
check([pop1] + [contains1] * 10)
1904+
check([insert1] + [repeat2] * 10)
1905+
check([pop1] + [repr1] * 10)
1906+
check([inplace_repeat2] * 10)
1907+
check([byteswap] * 10)
1908+
check([insert1] + [clear] * 10)
1909+
check([pop1] + [count1] * 10)
1910+
check([remove1] * 10)
1911+
check([clear] + [copy] * 10, array.array('B', b'0' * 0x400000))
1912+
check([pop1] + [reduce_ex2] * 10)
1913+
check([clear] + [reduce_ex3] * 10, array.array('B', b'0' * 0x400000))
1914+
check([pop1] + [tobytes] * 10)
1915+
check([pop1] + [tolist] * 10)
1916+
check([clear, tounicode] * 10, array.array('w', 'a'*10000))
1917+
check([clear, tofile] * 10, array.array('w', 'a'*10000))
1918+
check([clear] + [extend] * 10)
1919+
check([clear] + [inplace_concat] * 10)
1920+
check([clear] + [concat] * 10, array.array('w', 'a'*10000))
1921+
check([fromunicode] * 10, array.array('w', 'a'))
1922+
check([frombytes] * 10)
1923+
check([fromlist] * 10)
1924+
check([clear] + [richcmplhs] * 10, array.array('i', [1]*10000))
1925+
check([clear] + [richcmprhs] * 10, array.array('i', [1]*10000))
1926+
check([clear, ass0] * 10, array.array('i', [1]*10000)) # to test array_ass_item must disable Py_mp_ass_subscript
1927+
check([clear] + [new] * 10, array.array('w', 'a'*10000))
1928+
check([clear] + [repr_] * 10, array.array('B', b'0' * 0x40000))
1929+
check([clear] + [repr_] * 10, array.array('B', b'0' * 0x40000))
1930+
check([clear] + [irepeat] * 10, array.array('B', b'0' * 0x40000))
1931+
check([clear] + [iter_reduce] * 10, a := array.array('B', b'0' * 0x400), iter(a))
1932+
1933+
# make sure we handle non-self objects correctly
1934+
check([clear] + [newi] * 10, [2] * random.randint(0, 100))
1935+
check([fromlistlclear] + [fromlistl] * 10, array.array('i', [1]), [2] * random.randint(0, 100))
1936+
check([clear2] + [concat2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
1937+
check([clear2] + [inplace_concat2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
1938+
check([clear2] + [extend2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
1939+
check([clear2] + [ass_subscr2] * 10, array.array('w', 'a'*10000), array.array('w', 'a'*10000))
1940+
check([clear2] + [frombytes2] * 10, array.array('w', 'a'*10000), array.array('B', b'a'*10000))
1941+
1942+
# iterator stuff
1943+
check([clear] + [iter_next] * 10, a := array.array('i', [1] * 10), iter(a))
1944+
1945+
16761946
if __name__ == "__main__":
16771947
unittest.main()

Modules/arraymodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3248,13 +3248,13 @@ arrayiter_next(PyObject *op)
32483248
return NULL;
32493249
}
32503250
PyObject *ret;
3251+
arrayobject *ao = it->ao;
32513252
#ifndef NDEBUG
32523253
array_state *state = find_array_state_by_type(Py_TYPE(it));
32533254
assert(PyObject_TypeCheck(it, state->ArrayIterType));
3254-
assert(array_Check(it->ao, state));
3255+
assert(array_Check(ao, state));
32553256
#endif
32563257

3257-
arrayobject *ao = it->ao;
32583258
Py_BEGIN_CRITICAL_SECTION(ao);
32593259
if (index < Py_SIZE(ao)) {
32603260
ret = (*it->getitem)(ao, index);

0 commit comments

Comments
 (0)