blob: 456771726f7a8f4ce648949d4ede5f619d5bc205 [file]
import unittest
from test import support
from test.support import import_helper, threading_helper
from test.support.threading_helper import run_concurrently
sqlite3 = import_helper.import_module("sqlite3")
NAME = "FREE_THREADING_RACE"
NCOLUMNS = 64
NITER = 3000
QUERY = "select " + ", ".join(
f"'value' as 'c{i} [{NAME}]'" for i in range(NCOLUMNS)
)
@threading_helper.requires_working_threading()
class TestSQLite3(unittest.TestCase):
def test_concurrent_converter_replacement(self):
# gh-155781: Converter lookups must retain a strong reference while
# another thread updates the public converter registry.
class Converter:
__slots__ = ("value",)
def __init__(self, value):
self.value = value
def __call__(self, value):
return self.value
def reader():
con = sqlite3.connect(":memory:",
detect_types=sqlite3.PARSE_COLNAMES)
try:
for _ in range(NITER):
row = con.execute(QUERY).fetchone()
self.assertEqual(len(row), NCOLUMNS)
finally:
con.close()
def mutator():
for i in range(NITER * NCOLUMNS):
sqlite3.register_converter(NAME, Converter(i))
if i % 3 == 0:
sqlite3.converters.pop(NAME, None)
with support.swap_item(sqlite3.converters, NAME, Converter(0)):
run_concurrently([reader] * 8 + [mutator] * 2)
if __name__ == "__main__":
unittest.main()