Skip to content

Commit f5f03ec

Browse files
committed
gh-116738: Test mdb.gnu module on FT Python build
1 parent 2985c63 commit f5f03ec

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import unittest
2+
3+
from test.support import import_helper, threading_helper
4+
from test.support.threading_helper import run_concurrently
5+
6+
import tempfile
7+
import threading
8+
9+
gdbm = import_helper.import_module("dbm.gnu")
10+
11+
NTHREADS = 10
12+
KEY_PER_THREAD = 1000
13+
14+
gdbm_filename = "test_gdbm_file"
15+
16+
17+
@threading_helper.requires_working_threading()
18+
class TestGdbm(unittest.TestCase):
19+
def test_racing_dbm_gnu(self):
20+
def gdbm_multi_op_worker(db):
21+
# Each thread sets, gets, and iterates
22+
tid = threading.get_ident()
23+
24+
# Insert keys
25+
for i in range(KEY_PER_THREAD):
26+
db[f"key_{tid}_{i}"] = f"value_{tid}_{i}"
27+
28+
for i in range(KEY_PER_THREAD):
29+
# Keys and values are stored as bytes; encode values for
30+
# comparison
31+
key = f"key_{tid}_{i}"
32+
value = f"value_{tid}_{i}".encode()
33+
self.assertIn(key, db)
34+
self.assertEqual(db[key], value)
35+
self.assertEqual(db.get(key), value)
36+
self.assertIsNone(db.get("not_exist"))
37+
with self.assertRaises(KeyError):
38+
db["not_exist"]
39+
40+
# Iterate over the database keys and verify only those belonging
41+
# to this thread. Other threads may concurrently delete their keys.
42+
key_prefix = f"key_{tid}".encode()
43+
key = db.firstkey()
44+
key_count = 0
45+
while key:
46+
if key.startswith(key_prefix):
47+
self.assertIn(key, db)
48+
key_count += 1
49+
key = db.nextkey(key)
50+
51+
# Can't assert key_count == KEY_PER_THREAD because concurrent
52+
# threads may insert or delete keys during iteration. This can
53+
# cause keys to be skipped or counted multiple times, making the
54+
# count unreliable.
55+
# See: https://www.gnu.org.ua/software/gdbm/manual/Sequential.html
56+
# self.assertEqual(key_count, KEY_PER_THREAD)
57+
58+
# Delete this thread's keys
59+
for i in range(KEY_PER_THREAD):
60+
key = f"key_{tid}_{i}"
61+
del db[key]
62+
self.assertNotIn(key, db)
63+
with self.assertRaises(KeyError):
64+
del db["not_exist"]
65+
66+
# Re-insert keys
67+
for i in range(KEY_PER_THREAD):
68+
db[f"key_{tid}_{i}"] = f"value_{tid}_{i}"
69+
70+
with tempfile.TemporaryDirectory() as tmpdirname:
71+
db = gdbm.open(f"{tmpdirname}/{gdbm_filename}", "c")
72+
run_concurrently(
73+
worker_func=gdbm_multi_op_worker, nthreads=NTHREADS, args=(db,)
74+
)
75+
self.assertEqual(len(db), NTHREADS * KEY_PER_THREAD)
76+
db.close()
77+
78+
79+
if __name__ == "__main__":
80+
unittest.main()

0 commit comments

Comments
 (0)