mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	gh-120858: PyDict_Next should not lock the dict (#120859)
PyDict_Next no longer locks the dictionary in the free-threaded build. Locking around individual PyDict_Next calls is not sufficient because the function returns borrowed references and because it allows concurrent modifications during the iteraiton loop. The internal locking also interferes with correct external synchronization because it may suspend outer critical sections created by the caller.
This commit is contained in:
		
							parent
							
								
									dee63cb359
								
							
						
					
					
						commit
						375b723d58
					
				
					 4 changed files with 34 additions and 8 deletions
				
			
		| 
						 | 
				
			
			@ -290,6 +290,17 @@ Dictionary Objects
 | 
			
		|||
          Py_DECREF(o);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
   The function is not thread-safe in the :term:`free-threaded <free threading>`
 | 
			
		||||
   build without external synchronization.  You can use
 | 
			
		||||
   :c:macro:`Py_BEGIN_CRITICAL_SECTION` to lock the dictionary while iterating
 | 
			
		||||
   over it::
 | 
			
		||||
 | 
			
		||||
      Py_BEGIN_CRITICAL_SECTION(self->dict);
 | 
			
		||||
      while (PyDict_Next(self->dict, &pos, &key, &value)) {
 | 
			
		||||
          ...
 | 
			
		||||
      }
 | 
			
		||||
      Py_END_CRITICAL_SECTION();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. c:function:: int PyDict_Merge(PyObject *a, PyObject *b, int override)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,6 +114,24 @@ Containers like :c:struct:`PyListObject`,
 | 
			
		|||
in the free-threaded build.  For example, the :c:func:`PyList_Append` will
 | 
			
		||||
lock the list before appending an item.
 | 
			
		||||
 | 
			
		||||
.. _PyDict_Next:
 | 
			
		||||
 | 
			
		||||
``PyDict_Next``
 | 
			
		||||
'''''''''''''''
 | 
			
		||||
 | 
			
		||||
A notable exception is :c:func:`PyDict_Next`, which does not lock the
 | 
			
		||||
dictionary.  You should use :c:macro:`Py_BEGIN_CRITICAL_SECTION` to protect
 | 
			
		||||
the dictionary while iterating over it if the dictionary may be concurrently
 | 
			
		||||
modified::
 | 
			
		||||
 | 
			
		||||
    Py_BEGIN_CRITICAL_SECTION(dict);
 | 
			
		||||
    PyObject *key, *value;
 | 
			
		||||
    Py_ssize_t pos = 0;
 | 
			
		||||
    while (PyDict_Next(dict, &pos, &key, &value)) {
 | 
			
		||||
        ...
 | 
			
		||||
    }
 | 
			
		||||
    Py_END_CRITICAL_SECTION();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Borrowed References
 | 
			
		||||
===================
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +159,7 @@ that return :term:`strong references <strong reference>`.
 | 
			
		|||
+-----------------------------------+-----------------------------------+
 | 
			
		||||
| :c:func:`PyDict_SetDefault`       | :c:func:`PyDict_SetDefaultRef`    |
 | 
			
		||||
+-----------------------------------+-----------------------------------+
 | 
			
		||||
| :c:func:`PyDict_Next`             | no direct replacement             |
 | 
			
		||||
| :c:func:`PyDict_Next`             | none (see :ref:`PyDict_Next`)     |
 | 
			
		||||
+-----------------------------------+-----------------------------------+
 | 
			
		||||
| :c:func:`PyWeakref_GetObject`     | :c:func:`PyWeakref_GetRef`        |
 | 
			
		||||
+-----------------------------------+-----------------------------------+
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue