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]

Reply via email to