@@ -260,3 +260,113 @@ thread, iterate over a copy:
260260
261261 Consider external synchronization when sharing :class: `dict ` instances
262262across threads.
263+
264+
265+ .. _thread-safety-set :
266+
267+ Thread safety for set objects
268+ ==============================
269+
270+ The :func: `len ` function is lock-free and :term: `atomic <atomic operation> `.
271+
272+ The following read operation is lock-free. It does not block concurrent
273+ modifications and may observe intermediate states from operations that
274+ hold the per-object lock:
275+
276+ .. code-block ::
277+ :class: good
278+
279+ elem in s # set.__contains__
280+
281+ This operation may compare elements using :meth: `~object.__eq__ `, which can
282+ execute arbitrary Python code. During such comparisons, the set may be
283+ modified by another thread. For built-in types like :class: `str `,
284+ :class: `int `, and :class: `float `, :meth: `!__eq__ ` does not release the
285+ underlying lock during comparisons and this is not a concern.
286+
287+ All other operations from here on hold the per-object lock.
288+
289+ Adding or removing a single element is safe to call from multiple threads
290+ and will not corrupt the set:
291+
292+ .. code-block ::
293+ :class: good
294+
295+ s.add(elem) # add element
296+ s.remove(elem) # remove element, raise if missing
297+ s.discard(elem) # remove element if present
298+ s.pop() # remove and return arbitrary element
299+
300+ These operations also compare elements, so the same :meth: `~object.__eq__ `
301+ considerations as above apply.
302+
303+ The following operations return new objects and hold the per-object lock
304+ for the duration:
305+
306+ .. code-block ::
307+ :class: good
308+
309+ s.copy() # returns a shallow copy
310+
311+ The :meth: `~set.clear ` method holds the lock for its duration. Other
312+ threads cannot observe elements being removed.
313+
314+ The following operations only accept :class: `set ` or :class: `frozenset `
315+ as operands and always lock both objects:
316+
317+ .. code-block ::
318+ :class: good
319+
320+ s |= other # other must be set/frozenset
321+ s &= other # other must be set/frozenset
322+ s -= other # other must be set/frozenset
323+ s ^= other # other must be set/frozenset
324+ s & other # other must be set/frozenset
325+ s | other # other must be set/frozenset
326+ s - other # other must be set/frozenset
327+ s ^ other # other must be set/frozenset
328+
329+ :meth: `set.update `, :meth: `set.union `, :meth: `set.intersection ` and
330+ :meth: `set.difference ` can take multiple iterables as arguments. They all
331+ iterate through all the passed iterables and do the following:
332+
333+ * :meth: `set.update ` and :meth: `set.union ` lock both objects only when
334+ the other operand is a :class: `set `, :class: `frozenset `, or :class: `dict `.
335+ * :meth: `set.intersection ` and :meth: `set.difference ` always try to lock
336+ all objects.
337+
338+ :meth: `set.symmetric_difference ` tries to lock both objects.
339+
340+ The update variants of the above methods also have some differences between
341+ them:
342+
343+ * :meth: `set.difference_update ` and :meth: `set.intersection_update ` try
344+ to lock all objects.
345+ * :meth: `set.symmetric_difference_update ` only lock the argument if it is
346+ of type :class: `set `, :class: `frozenset `, or :class: `dict `.
347+
348+ The following methods always try to lock both objects:
349+
350+ .. code-block ::
351+ :class: good
352+
353+ s.isdisjoint(other) # both locked
354+ s.issubset(other) # both locked
355+ s.issuperset(other) # both locked
356+
357+ Operations that involve multiple accesses, as well as iteration, are never
358+ atomic:
359+
360+ .. code-block ::
361+ :class: bad
362+
363+ # NOT atomic: check-then-act
364+ if elem in s:
365+ s.remove(elem)
366+
367+ # NOT thread-safe: iteration while modifying
368+ for elem in s:
369+ process(elem) # another thread may modify s
370+
371+ Consider external synchronization when sharing :class: `set ` instances
372+ across threads. See :ref: `freethreading-python-howto ` for more information.
0 commit comments