Skip to content

Commit 688b25a

Browse files
committed
Better explain lock-free and atomicity
1 parent 2b9e711 commit 688b25a

File tree

1 file changed

+64
-35
lines changed

1 file changed

+64
-35
lines changed

Doc/library/stdtypes.rst

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,59 +1438,88 @@ application).
14381438

14391439
.. admonition:: Thread safety
14401440

1441-
Most individual operations on :class:`list` instances are atomic:
1441+
Reading a single element from a :class:`list` is
1442+
:term:`atomic <atomic operation>`:
14421443

14431444
.. code-block::
1444-
:class: good
1445+
:class: green
1446+
1447+
lst[i] # list.__getitem__
14451448
1446-
# The following operations are atomic
1447-
lst1 + lst2 # atomic concatenation of two lists
1448-
x * lst # atomic repeat of lst x times
1449-
item = lst[i] # atomically retrieves item at index i
1450-
lst[i] = value # atomically replaces item at index i
1451-
lst *= x # atomically extend the list x times
1452-
1453-
# Calls to the following list methods are atomic
1454-
lst.clear()
1455-
lst.copy()
1456-
lst.append(item)
1457-
lst.insert(idx, item)
1458-
lst.pop(idx)
1459-
lst.remove(item)
1460-
lst.reverse()
1461-
lst.sort()
1462-
1463-
The following operations/methods are not fully atomic:
1449+
The following methods traverse the list and use :term:`atomic <atomic operation>`
1450+
reads of each item to perform their function. That means that they may
1451+
return results affected by concurrent modifications:
14641452

14651453
.. code-block::
14661454
:class: maybe
14671455
1456+
item in lst
14681457
lst.index(item)
14691458
lst.count(item)
1470-
item in lst
14711459
1472-
lst.extend(iterable)
1473-
lst += iterable
1460+
All of the above methods/operations are also lock-free. They do not block
1461+
concurrent modifications. Other operations that hold a lock will not block
1462+
these from observing intermediate states.
1463+
1464+
All other operations from here on block using the per-object lock.
1465+
1466+
Writing a single item via ``lst[i] = x`` is safe to call from multiple
1467+
threads and will not corrupt the list.
1468+
1469+
The following operations return new objects and appear
1470+
:term:`atomic <atomic operations>` to other threads:
1471+
1472+
.. code-block::
1473+
:class: good
1474+
1475+
lst1 + lst2 # concatenates two lists into a new list
1476+
x * lst # repeats lst x times into a new list
1477+
lst.copy() # returns a shallow copy of the list
1478+
1479+
Methods that only operate on a single elements with no shifting required are
1480+
:term:`atomic <atomic operations>`:
1481+
1482+
.. code-block::
1483+
:class: good
1484+
1485+
lst.append(x) # append to the end of the list, no shifting required
1486+
lst.pop() # pop element from the end of the list, no shifting required
1487+
1488+
The :meth:`~list.clear` method is also :term:`atomic <atomic operations>`.
1489+
Other threads cannot observe elements being removed.
1490+
1491+
The :meth:`~list.sort` method is not :term:`atomic <atomic operation>`.
1492+
Other threads cannot observe intermediate states during sorting, but the
1493+
list appears empty for the duration of the sort.
1494+
1495+
The following operations may allow lock-free operations to observe
1496+
intermediate states since they modify multiple elements in place:
1497+
1498+
.. code-block::
1499+
:class: maybe
14741500
1475-
lst[i:j] = iterable
1501+
lst.insert(idx, item) # shifts elements
1502+
lst.pop(idx) # idx not at the end of the list, shifts elements
1503+
lst *= x # copies elements in place
14761504
1477-
The :meth:`~list.index` and :meth:`~list.count` methods, and the ``in``
1478-
operator, iterate the list without holding a lock. They are safe to call
1479-
concurrently but may return results affected by concurrent modifications.
1505+
The :meth:`~list.remove` method may allow concurrent modifications since
1506+
element comparison may execute arbitrary Python code (via
1507+
:meth:`~object.__eq__`).
14801508

1481-
:meth:`~list.extend` is safe to call from multiple threads. However, the
1482-
operation is fully atomic only when the iterable that's passed to ``extend``
1483-
is a :class:`list`, a :class:`tuple`, a :class:`set`, a :class:`frozenset`,
1484-
a :class:`dict` or a :ref:`dictionary view object <dict-views>` (but not
1485-
their subclasses). Otherwise, an iterator is created which can be
1486-
concurrently modified by another thread. The same applies to inplace
1487-
concatenation of list with other iterables when using ``lst += iterable``.
1509+
:meth:`~list.extend` is safe to call from multiple threads. However, its
1510+
guarantees depend on the iterable passed to it. If it is a :class:`list`, a
1511+
:class:`tuple`, a :class:`set`, a :class:`frozenset`, a :class:`dict` or a
1512+
:ref:`dictionary view object <dict-views>` (but not their subclasses), the
1513+
``extend`` operation is safe from concurrent modifications to the iterable.
1514+
Otherwise, an iterator is created which can be concurrently modified by
1515+
another thread. The same applies to inplace concatenation of a list with
1516+
other iterables when using ``lst += iterable``.
14881517

14891518
Similarly, assigning to a list slice with ``lst[i:j] = iterable`` is safe
14901519
to call from multiple threads, but ``iterable`` is only locked when it is
14911520
also a :class:`list` (but not its subclasses).
14921521

1493-
Operations that involve multiple accesses, as well as iteration, are not
1522+
Operations that involve multiple accesses, as well as iteration, are never
14941523
atomic. For example:
14951524

14961525
.. code-block::

0 commit comments

Comments
 (0)