mirror of
https://github.com/python/cpython.git
synced 2026-04-21 03:10:52 +00:00
284 lines
8.2 KiB
Text
284 lines
8.2 KiB
Text
# Thread safety annotations for C API functions.
|
|
#
|
|
# Each line has the form:
|
|
# function_name : level
|
|
#
|
|
# Where level is one of:
|
|
# incompatible -- not safe even with external locking
|
|
# compatible -- safe if the caller serializes all access with external locks
|
|
# distinct -- safe on distinct objects without external synchronization
|
|
# shared -- safe for concurrent use on the same object
|
|
# atomic -- atomic
|
|
#
|
|
# Lines beginning with '#' are ignored.
|
|
# The function name must match the C domain identifier used in the documentation.
|
|
|
|
# Synchronization primitives (Doc/c-api/synchronization.rst)
|
|
PyMutex_Lock:atomic:
|
|
PyMutex_Unlock:atomic:
|
|
PyMutex_IsLocked:atomic:
|
|
|
|
|
|
# Dictionary objects (Doc/c-api/dict.rst)
|
|
|
|
# Type checks - read ob_type pointer, always safe
|
|
PyDict_Check:atomic:
|
|
PyDict_CheckExact:atomic:
|
|
|
|
# Creation - pure allocation, no shared state
|
|
PyDict_New:atomic:
|
|
|
|
# Lock-free lookups - use _Py_dict_lookup_threadsafe(), no locking.
|
|
# Atomic with simple types.
|
|
PyDict_Contains:shared:
|
|
PyDict_ContainsString:atomic:
|
|
PyDict_GetItemRef:shared:
|
|
PyDict_GetItemStringRef:atomic:
|
|
PyDict_Size:atomic:
|
|
PyDict_GET_SIZE:atomic:
|
|
|
|
# Borrowed-reference lookups - lock-free dict access but returned
|
|
# borrowed reference is unsafe in free-threaded builds without
|
|
# external synchronization
|
|
PyDict_GetItem:compatible:
|
|
PyDict_GetItemWithError:compatible:
|
|
PyDict_GetItemString:compatible:
|
|
PyDict_SetDefault:compatible:
|
|
|
|
# Iteration - no locking; returns borrowed refs
|
|
PyDict_Next:compatible:
|
|
|
|
# Single-item mutations - protected by per-object critical section
|
|
PyDict_SetItem:shared:
|
|
PyDict_SetItemString:atomic:
|
|
PyDict_DelItem:shared:
|
|
PyDict_DelItemString:atomic:
|
|
PyDict_SetDefaultRef:shared:
|
|
PyDict_Pop:shared:
|
|
PyDict_PopString:atomic:
|
|
|
|
# Bulk reads - hold per-object lock for duration
|
|
PyDict_Clear:atomic:
|
|
PyDict_Copy:atomic:
|
|
PyDict_Keys:atomic:
|
|
PyDict_Values:atomic:
|
|
PyDict_Items:atomic:
|
|
|
|
# Merge/update - lock target dict; also lock source when it is a dict
|
|
PyDict_Update:shared:
|
|
PyDict_Merge:shared:
|
|
PyDict_MergeFromSeq2:shared:
|
|
|
|
# Watcher registration - no synchronization on interpreter state
|
|
PyDict_AddWatcher:compatible:
|
|
PyDict_ClearWatcher:compatible:
|
|
|
|
# Per-dict watcher tags - non-atomic RMW on _ma_watcher_tag;
|
|
# safe on distinct dicts only
|
|
PyDict_Watch:distinct:
|
|
PyDict_Unwatch:distinct:
|
|
|
|
|
|
# List objects (Doc/c-api/list.rst)
|
|
|
|
# Type checks - read ob_type pointer, always safe
|
|
PyList_Check:atomic:
|
|
PyList_CheckExact:atomic:
|
|
|
|
# Creation - pure allocation, no shared state
|
|
PyList_New:atomic:
|
|
|
|
# Size - uses atomic load on free-threaded builds
|
|
PyList_Size:atomic:
|
|
PyList_GET_SIZE:atomic:
|
|
|
|
# Strong-reference lookup - lock-free with atomic ops
|
|
PyList_GetItemRef:atomic:
|
|
|
|
# Borrowed-reference lookups - no locking; returned borrowed
|
|
# reference is unsafe in free-threaded builds without
|
|
# external synchronization
|
|
PyList_GetItem:compatible:
|
|
PyList_GET_ITEM:compatible:
|
|
|
|
# Single-item mutations - hold per-object lock for duration;
|
|
# appear atomic to lock-free readers
|
|
PyList_SetItem:atomic:
|
|
PyList_Append:atomic:
|
|
|
|
# Insert - protected by per-object critical section; shifts
|
|
# elements so lock-free readers may observe intermediate states
|
|
PyList_Insert:shared:
|
|
|
|
# Initialization macro - no synchronization; normally only used
|
|
# to fill in new lists where there is no previous content
|
|
PyList_SET_ITEM:compatible:
|
|
|
|
# Bulk operations - hold per-object lock for duration
|
|
PyList_GetSlice:atomic:
|
|
PyList_AsTuple:atomic:
|
|
PyList_Clear:atomic:
|
|
|
|
# Reverse - protected by per-object critical section; swaps
|
|
# elements so lock-free readers may observe intermediate states
|
|
PyList_Reverse:shared:
|
|
|
|
# Slice assignment - lock target list; also lock source when it
|
|
# is a list
|
|
PyList_SetSlice:shared:
|
|
|
|
# Sort - per-object lock held; the list is emptied before sorting
|
|
# so other threads may observe an empty list, but they won't see the
|
|
# intermediate states of the sort
|
|
PyList_Sort:shared:
|
|
|
|
# Extend - lock target list; also lock source when it is a
|
|
# list, set, or dict
|
|
PyList_Extend:shared:
|
|
|
|
# Creation - pure allocation, no shared state
|
|
PyBytes_FromString:atomic:
|
|
PyBytes_FromStringAndSize:atomic:
|
|
PyBytes_DecodeEscape:atomic:
|
|
|
|
# Creation from formatting C primitives - pure allocation, no shared state
|
|
PyBytes_FromFormat:atomic:
|
|
PyBytes_FromFormatV:atomic:
|
|
|
|
# Creation from object - uses buffer protocol so may call arbitrary code;
|
|
# safe as long as the buffer is not mutated by another thread during the operation
|
|
PyBytes_FromObject:shared:
|
|
|
|
# Size - uses atomic load on free-threaded builds
|
|
PyBytes_Size:atomic:
|
|
PyBytes_GET_SIZE:atomic:
|
|
|
|
# Raw data - no locking; mutating it is unsafe if the bytes object is shared between threads
|
|
PyBytes_AsString:compatible:
|
|
PyBytes_AS_STRING:compatible:
|
|
PyBytes_AsStringAndSize:compatible:
|
|
|
|
# Concatenation - uses buffer protocol; safe as long as buffer is not mutated by another thread during the operation
|
|
PyBytes_Concat:shared:
|
|
PyBytes_ConcatAndDel:shared:
|
|
PyBytes_Join:shared:
|
|
|
|
# Resizing - safe if the object is unique
|
|
_PyBytes_Resize:distinct:
|
|
|
|
# Repr - atomic as bytes are immutable
|
|
PyBytes_Repr:atomic:
|
|
|
|
# Creation from object - may call arbitrary code
|
|
PyByteArray_FromObject:shared:
|
|
|
|
# Creation - pure allocation, no shared state
|
|
PyByteArray_FromStringAndSize:atomic:
|
|
|
|
# Concatenation - uses buffer protocol; safe as long as buffer is not mutated by another thread during the operation
|
|
PyByteArray_Concat:shared:
|
|
|
|
# Size - uses atomic load on free-threaded builds
|
|
PyByteArray_Size:atomic:
|
|
PyByteArray_GET_SIZE:atomic:
|
|
|
|
# Raw data - no locking; mutating it is unsafe if the bytearray object is shared between threads
|
|
PyByteArray_AsString:compatible:
|
|
PyByteArray_AS_STRING:compatible:
|
|
|
|
# Creation - may iterate the iterable argument, calling arbitrary code.
|
|
# Atomic for sets, frozensets, dicts, and frozendicts.
|
|
PySet_New:shared:
|
|
PyFrozenSet_New:shared:
|
|
|
|
# Size - uses atomic load on free-threaded builds
|
|
PySet_Size:atomic:
|
|
PySet_GET_SIZE:atomic:
|
|
|
|
# Contains - lock-free, atomic with simple types
|
|
PySet_Contains:shared:
|
|
|
|
# Mutations - hold per-object lock for duration
|
|
# atomic with simple types
|
|
PySet_Add:shared:
|
|
PySet_Discard:shared:
|
|
|
|
# Pop - hold per-object lock for duration
|
|
PySet_Pop:atomic:
|
|
|
|
# Clear - empties the set before clearing
|
|
PySet_Clear:atomic:
|
|
|
|
# Capsule objects (Doc/c-api/capsule.rst)
|
|
|
|
# Type check - read ob_type pointer, always safe
|
|
PyCapsule_CheckExact:atomic:
|
|
|
|
# Creation - pure allocation, no shared state
|
|
PyCapsule_New:atomic:
|
|
|
|
# Validation - reads pointer and name fields; safe on distinct objects
|
|
PyCapsule_IsValid:distinct:
|
|
|
|
# Getters - read struct fields; safe on distinct objects but
|
|
# concurrent access to the same capsule requires external synchronization
|
|
PyCapsule_GetPointer:distinct:
|
|
PyCapsule_GetName:distinct:
|
|
PyCapsule_GetDestructor:distinct:
|
|
PyCapsule_GetContext:distinct:
|
|
|
|
# Setters - write struct fields; safe on distinct objects but
|
|
# concurrent access to the same capsule requires external synchronization
|
|
PyCapsule_SetPointer:distinct:
|
|
PyCapsule_SetName:distinct:
|
|
PyCapsule_SetDestructor:distinct:
|
|
PyCapsule_SetContext:distinct:
|
|
|
|
# Import - looks up a capsule from a module attribute and
|
|
# calls PyCapsule_GetPointer; may call arbitrary code
|
|
PyCapsule_Import:compatible:
|
|
|
|
# Tuple objects
|
|
|
|
# Creation - pure allocation, no shared state
|
|
PyTuple_New:atomic:
|
|
PyTuple_FromArray:atomic:
|
|
PyTuple_Pack:atomic:
|
|
|
|
# Size - tuples are immutable so size never changes
|
|
PyTuple_Size:atomic:
|
|
PyTuple_GET_SIZE:atomic:
|
|
|
|
# Borrowed-reference lookups - tuples are immutable so items
|
|
# never change, however the tuple must be kept alive while using the borrowed reference
|
|
PyTuple_GetItem:compatible:
|
|
PyTuple_GET_ITEM:compatible:
|
|
|
|
# Slice - creates a new tuple from an existing tuple
|
|
PyTuple_GetSlice:atomic:
|
|
|
|
# SetItem - only usable on tuples with refcount 1
|
|
PyTuple_SetItem:compatible:
|
|
PyTuple_SET_ITEM:compatible:
|
|
|
|
# Resize - only usable on tuples with refcount 1
|
|
_PyTuple_Resize:compatible:
|
|
|
|
# Struct Sequence objects
|
|
|
|
# Creation
|
|
PyStructSequence_NewType:atomic:
|
|
PyStructSequence_New:atomic:
|
|
|
|
# Initialization - modifies the type object in place
|
|
PyStructSequence_InitType:distinct:
|
|
PyStructSequence_InitType2:distinct:
|
|
|
|
# Borrowed-reference lookups - same as tuple items
|
|
PyStructSequence_GetItem:compatible:
|
|
PyStructSequence_GET_ITEM:compatible:
|
|
|
|
# SetItem - only for filling in brand new instances
|
|
PyStructSequence_SetItem:compatible:
|
|
PyStructSequence_SET_ITEM:compatible:
|
|
|