diff --git a/CHANGES/1327.bugfix.rst b/CHANGES/1327.bugfix.rst new file mode 100644 index 000000000..6fa502c68 --- /dev/null +++ b/CHANGES/1327.bugfix.rst @@ -0,0 +1,3 @@ +Fixed insertion race on free-threaded mode. + +-- by :user:`Vizonex` \ No newline at end of file diff --git a/multidict/_multilib/hashtable.h b/multidict/_multilib/hashtable.h index f2ba9868a..ff6a917f6 100644 --- a/multidict/_multilib/hashtable.h +++ b/multidict/_multilib/hashtable.h @@ -412,6 +412,7 @@ _md_add_with_hash_steal_refs(MultiDictObject *md, Py_hash_t hash, PyObject *identity, PyObject *key, PyObject *value) { + Py_BEGIN_CRITICAL_SECTION(md); htkeys_t *keys = md->keys; if (keys->usable <= 0 || keys == &empty_htkeys) { /* Need to resize. */ @@ -435,6 +436,7 @@ _md_add_with_hash_steal_refs(MultiDictObject *md, Py_hash_t hash, md->used += 1; keys->usable -= 1; keys->nentries += 1; + Py_END_CRITICAL_SECTION(); return 0; } diff --git a/tests/isolated/multidict_add_ft.py b/tests/isolated/multidict_add_ft.py new file mode 100644 index 000000000..f31ad1e35 --- /dev/null +++ b/tests/isolated/multidict_add_ft.py @@ -0,0 +1,30 @@ +import os +import subprocess +import sys +import sysconfig +import textwrap + +FREETHREADED = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) + +if __name__ == "__main__" and FREETHREADED: + child = textwrap.dedent(""" + import threading + from multidict import MultiDict + md: MultiDict[int] = MultiDict() + def worker(tid: int): + for i in range(800): + md.add(f"k{tid}_{i}", i) + ts = [threading.Thread(target=worker, args=(tid,), daemon=True) for tid in range(4)] + for t in ts: + t.start() + for t in ts: + t.join() + print(f"missing: {4*800 - len(md)}") + """) + subprocess.run( + [sys.executable, "-c", child], + env={**os.environ, "PYTHON_GIL": "0"}, + capture_output=True, + timeout=60, + check=True, + ) diff --git a/tests/test_leaks.py b/tests/test_leaks.py index c39abe245..f7d4e86af 100644 --- a/tests/test_leaks.py +++ b/tests/test_leaks.py @@ -11,6 +11,7 @@ @pytest.mark.parametrize( ("script"), ( + "multidict_add_ft.py", "multidict_extend_dict.py", "multidict_extend_multidict.py", "multidict_extend_tuple.py",