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.
This commit is contained in:
Pieter Eendebak 2025-01-28 22:55:45 +01:00 committed by GitHub
parent a4459c34ea
commit 64c417dee5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 6 deletions

View file

@ -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

View file

@ -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]*/

View file

@ -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;