From 152fed396015d55c242682b50bd1260af5e3a2d3 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 27 Jan 2025 09:24:35 +0100 Subject: [PATCH 1/2] Remove redundant critical section in dict.get --- Objects/clinic/dictobject.c.h | 4 +--- Objects/dictobject.c | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) 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; From 772e7e03cc7c1e51f5d85893fb6fc0af9869bef4 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 27 Jan 2025 09:24:51 +0100 Subject: [PATCH 2/2] add free-threading test --- Lib/test/test_free_threading/test_dict.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) 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