https://github.com/python/cpython/commit/64c417dee5594c882beac03e7d2942ca05b5c204
commit: 64c417dee5594c882beac03e7d2942ca05b5c204
branch: main
author: Pieter Eendebak <[email protected]>
committer: colesbury <[email protected]>
date: 2025-01-28T21:55:45Z
summary:
gh-112075: Remove critical section in dict.get (gh-129336)
The `dict.get` implementation uses `_Py_dict_lookup_threadsafe`, which is
thread-safe, so we remove the critical section from the argument clinic.
Add a test for concurrent dict get and set operations.
files:
M Lib/test/test_free_threading/test_dict.py
M Objects/clinic/dictobject.c.h
M Objects/dictobject.c
diff --git a/Lib/test/test_free_threading/test_dict.py
b/Lib/test/test_free_threading/test_dict.py
index 13717cb39fa35d..4f605e0c51f0d5 100644
--- a/Lib/test/test_free_threading/test_dict.py
+++ b/Lib/test/test_free_threading/test_dict.py
@@ -5,7 +5,7 @@
from ast import Or
from functools import partial
-from threading import Thread
+from threading import Barrier, Thread
from unittest import TestCase
try:
@@ -142,6 +142,27 @@ def writer_func(l):
for ref in thread_list:
self.assertIsNone(ref())
+ def test_racing_get_set_dict(self):
+ """Races getting and setting a dict should be thread safe"""
+ THREAD_COUNT = 10
+ barrier = Barrier(THREAD_COUNT)
+ def work(d):
+ barrier.wait()
+ for _ in range(1000):
+ d[10] = 0
+ d.get(10, None)
+ _ = d[10]
+
+ d = {}
+ worker_threads = []
+ for ii in range(THREAD_COUNT):
+ worker_threads.append(Thread(target=work, args=[d]))
+ for t in worker_threads:
+ t.start()
+ for t in worker_threads:
+ t.join()
+
+
def test_racing_set_object_dict(self):
"""Races assigning to __dict__ should be thread safe"""
class C: pass
diff --git a/Objects/clinic/dictobject.c.h b/Objects/clinic/dictobject.c.h
index cdf39ce147203b..c66916bb33aa37 100644
--- a/Objects/clinic/dictobject.c.h
+++ b/Objects/clinic/dictobject.c.h
@@ -94,9 +94,7 @@ dict_get(PyObject *self, PyObject *const *args, Py_ssize_t
nargs)
}
default_value = args[1];
skip_optional:
- Py_BEGIN_CRITICAL_SECTION(self);
return_value = dict_get_impl((PyDictObject *)self, key, default_value);
- Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -312,4 +310,4 @@ dict_values(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return dict_values_impl((PyDictObject *)self);
}
-/*[clinic end generated code: output=4956c5b276ea652f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0f04bf0e7e6b130f input=a9049054013a1b77]*/
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 8fe71123252a75..733a10a2e80b18 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -4248,7 +4248,6 @@ dict___contains__(PyDictObject *self, PyObject *key)
}
/*[clinic input]
-@critical_section
dict.get
key: object
@@ -4260,7 +4259,7 @@ Return the value for key if key is in the dictionary,
else default.
static PyObject *
dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
-/*[clinic end generated code: output=bba707729dee05bf input=a631d3f18f584c60]*/
+/*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/
{
PyObject *val = NULL;
Py_hash_t hash;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]