mirror of
https://github.com/python/cpython.git
synced 2026-06-06 01:41:04 +00:00
Fixes a sequential consistency bug whereby two threads that are deleting a struct member may observe both their deletions to be successful.
306 lines
8.4 KiB
Python
306 lines
8.4 KiB
Python
import _testcapi
|
|
import threading
|
|
from test.support import threading_helper
|
|
from unittest import TestCase
|
|
|
|
|
|
def run_in_threads(targets):
|
|
"""Run `targets` in separate threads"""
|
|
threads = [
|
|
threading.Thread(target=target)
|
|
for target in targets
|
|
]
|
|
for thread in threads:
|
|
thread.start()
|
|
for thread in threads:
|
|
thread.join()
|
|
|
|
|
|
class Spam:
|
|
__slots__ = [
|
|
"eggs",
|
|
]
|
|
|
|
def __init__(self, initial_value):
|
|
self.eggs = initial_value
|
|
|
|
|
|
@threading_helper.requires_working_threading()
|
|
class TestSlots(TestCase):
|
|
|
|
def test_object(self):
|
|
spam = Spam(0)
|
|
iters = 20_000
|
|
|
|
def writer():
|
|
for _ in range(iters):
|
|
spam.eggs += 1
|
|
|
|
def reader():
|
|
for _ in range(iters):
|
|
eggs = spam.eggs
|
|
assert type(eggs) is int
|
|
assert 0 <= eggs <= iters
|
|
|
|
run_in_threads([writer, reader, reader, reader])
|
|
|
|
def test_del_object_is_atomic(self):
|
|
# Testing whether the implementation of `del slots_object.attribute`
|
|
# removes the attribute atomically, thus avoiding non-sequentially-
|
|
# consistent behaviors.
|
|
# https://github.com/python/cpython/issues/146270
|
|
def deleter(spam, successes):
|
|
try:
|
|
del spam.eggs
|
|
successes.append(True)
|
|
except AttributeError:
|
|
successes.append(False)
|
|
|
|
for _ in range(10):
|
|
spam = Spam(0)
|
|
successes = []
|
|
threading_helper.run_concurrently(deleter, nthreads=4, args=(spam, successes))
|
|
self.assertEqual(sum(successes), 1)
|
|
|
|
def test_T_BOOL(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
# different code paths for True and False
|
|
spam_old.T_BOOL = True
|
|
spam_new.T_BOOL = True
|
|
spam_old.T_BOOL = False
|
|
spam_new.T_BOOL = False
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_BOOL
|
|
spam_new.T_BOOL
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_BYTE(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_BYTE = 0
|
|
spam_new.T_BYTE = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_BYTE
|
|
spam_new.T_BYTE
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_UBYTE(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_UBYTE = 0
|
|
spam_new.T_UBYTE = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_UBYTE
|
|
spam_new.T_UBYTE
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_SHORT(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_SHORT = 0
|
|
spam_new.T_SHORT = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_SHORT
|
|
spam_new.T_SHORT
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_USHORT(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_USHORT = 0
|
|
spam_new.T_USHORT = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_USHORT
|
|
spam_new.T_USHORT
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_INT(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_INT = 0
|
|
spam_new.T_INT = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_INT
|
|
spam_new.T_INT
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_UINT(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_UINT = 0
|
|
spam_new.T_UINT = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_UINT
|
|
spam_new.T_UINT
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_LONG(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_LONG = 0
|
|
spam_new.T_LONG = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_LONG
|
|
spam_new.T_LONG
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_ULONG(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_ULONG = 0
|
|
spam_new.T_ULONG = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_ULONG
|
|
spam_new.T_ULONG
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_PYSSIZET(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_PYSSIZET = 0
|
|
spam_new.T_PYSSIZET = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_PYSSIZET
|
|
spam_new.T_PYSSIZET
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_FLOAT(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_FLOAT = 0.0
|
|
spam_new.T_FLOAT = 0.0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_FLOAT
|
|
spam_new.T_FLOAT
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_DOUBLE(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_DOUBLE = 0.0
|
|
spam_new.T_DOUBLE = 0.0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_DOUBLE
|
|
spam_new.T_DOUBLE
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_LONGLONG(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_LONGLONG = 0
|
|
spam_new.T_LONGLONG = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_LONGLONG
|
|
spam_new.T_LONGLONG
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_ULONGLONG(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_ULONGLONG = 0
|
|
spam_new.T_ULONGLONG = 0
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_ULONGLONG
|
|
spam_new.T_ULONGLONG
|
|
|
|
run_in_threads([writer, reader])
|
|
|
|
def test_T_CHAR(self):
|
|
spam_old = _testcapi._test_structmembersType_OldAPI()
|
|
spam_new = _testcapi._test_structmembersType_NewAPI()
|
|
|
|
def writer():
|
|
for _ in range(1_000):
|
|
spam_old.T_CHAR = "c"
|
|
spam_new.T_CHAR = "c"
|
|
|
|
def reader():
|
|
for _ in range(1_000):
|
|
spam_old.T_CHAR
|
|
spam_new.T_CHAR
|
|
|
|
run_in_threads([writer, reader])
|