Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES/1327.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed insertion race on free-threaded mode.
-- by :user:`Vizonex`
Comment thread
Vizonex marked this conversation as resolved.
2 changes: 2 additions & 0 deletions multidict/_multilib/hashtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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;
}

Expand Down
30 changes: 30 additions & 0 deletions tests/isolated/multidict_add_ft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import os
import subprocess
import sys
import sysconfig
import textwrap

FREETHREADED = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced we should gate on this here.

cc @Dreamsorcerer


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,
)
1 change: 1 addition & 0 deletions tests/test_leaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
@pytest.mark.parametrize(
("script"),
(
"multidict_add_ft.py",
"multidict_extend_dict.py",
"multidict_extend_multidict.py",
"multidict_extend_tuple.py",
Expand Down
Loading