diff --git a/Include/refcount.h b/Include/refcount.h index 4279a8f690b317..204e06cfacd7b0 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -142,6 +142,7 @@ static inline Py_ALWAYS_INLINE int _Py_IsImmutable(PyObject *op) } #define _Py_IsImmutable(op) _Py_IsImmutable(_PyObject_CAST(op)) +// Artifact[Implementation]: The definition of the `Py_CHECKWRITE` macro // Check whether an object is writeable. // This check will always succeed during runtime finalization. #define Py_CHECKWRITE(op) ((op) && (!_Py_IsImmutable(op) || _PyImmModule_Check(op) || Py_IsFinalizing())) @@ -388,6 +389,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op) return; } #ifndef Py_LIMITED_API + // Artifact[Implementation]: The atomic RC branch for immutable objects in Py_INCREF if (_Py_IsImmutable(op)) { // Object is immutable. // Slight chance of overflow, and an issue here, so check, and @@ -559,6 +561,7 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op) return; } #ifndef Py_LIMITED_API + // Artifact[Implementation]: The atomic RC branch for immutable objects in Py_DECREF if (_Py_IsImmutable(op)) { if (_Py_DecRef_Immutable(op)) { diff --git a/Lib/immutable.py b/Lib/immutable.py index fc87f6310b9c68..e1c00152f94bbd 100644 --- a/Lib/immutable.py +++ b/Lib/immutable.py @@ -27,6 +27,8 @@ # aliases it to `is_frozen` isfrozen = is_frozen + +# Artifact[Benchmarking]: The implementation of immutability related decorators def freezable(cls): """Class decorator: mark a class as always freezable.""" set_freezable(cls, FREEZABLE_YES) diff --git a/Lib/test/test_freeze/README.md b/Lib/test/test_freeze/README.md new file mode 100644 index 00000000000000..cd4fbc4c07ba7b --- /dev/null +++ b/Lib/test/test_freeze/README.md @@ -0,0 +1,8 @@ +# Tests for Immutability + + + +This folder contains tests for individual parts of the immutability +implementation. diff --git a/Modules/_immutablemodule.c b/Modules/_immutablemodule.c index 553ece6d75f852..94ea460c340526 100644 --- a/Modules/_immutablemodule.c +++ b/Modules/_immutablemodule.c @@ -189,6 +189,7 @@ _immutable_unset_freezable(PyObject *module, PyObject *obj) Py_RETURN_NONE; } +// Artifact[Implementation]: The implementation of the `InterpreterLocal` type /* * InterpreterLocal type * @@ -360,6 +361,7 @@ static PyType_Spec interpreterlocal_spec = { .slots = interpreterlocal_slots, }; +// Artifact[Implementation]: The implementation of the `SharedField` type /* * SharedField type @@ -373,7 +375,6 @@ static PyType_Spec interpreterlocal_spec = { * can be accessed concurrently from different sub-interpreters (each * with its own GIL). */ - typedef struct { PyObject_HEAD PyObject *value; // Always frozen; guarded by lock diff --git a/Objects/funcobject.c b/Objects/funcobject.c index e92aee26078221..6106a3dced0209 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1205,6 +1205,7 @@ func_descr_get(PyObject *func, PyObject *obj, PyObject *type) return PyMethod_New(func, obj); } +// Artifact[Implementation]: The pre-freeze hook of function objects /** * Special function for replacing globals and builtins with a copy of just what they use. * @@ -1737,6 +1738,7 @@ PyTypeObject PyClassMethod_Type = { PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ PyObject_GC_Del, /* tp_free */ + .tp_reachable = _PyObject_ReachableVisitType, }; PyObject * diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index c8e1b6394baf5d..fb94c6a648f48b 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -1554,6 +1554,7 @@ module_make_immutable_proxy(PyObject *self) { return 0; } +// Artifact[Implementation]: The pre-freeze hook of module objects static int module_prefreeze(PyObject *self) { // TODO(immutable): Check if the module defines a custom pre-freeze hook: diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 66792e1c71743c..0abf281112e766 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -33,6 +33,7 @@ */ #else +// Artifact[Implementation]: Explanation how weak references work for immutable objects /* * Thread-safety for immutable objects * =================================== diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 2b27e0b8424157..ac3e7f538abeb1 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -488,6 +488,7 @@ _get_xidata(PyThreadState *tstate, return -1; } + // Artifact[Implementation]: The branch that allows direct sharing for immutable object across sub-interpreters if (_Py_IsImmutable(obj)) { _Py_IncRef(obj); xidata->obj = obj; diff --git a/Python/immutability.c b/Python/immutability.c index ee22fa84e4fe29..0c4a83716b3d3f 100644 --- a/Python/immutability.c +++ b/Python/immutability.c @@ -245,6 +245,7 @@ static PyObject* pop(PyObject* s){ +// Artifact[Implementation]: Explanation how a stack is used to implement the DFS based SCC algorithm /** * The DFS walk for SCC calculations needs to perform actions on both * the pre-order and post-order visits to an object. To achieve this @@ -290,6 +291,7 @@ static bool is_c_wrapper(PyObject* obj){ return PyCFunction_Check(obj) || Py_IS_TYPE(obj, &_PyMethodWrapper_Type) || Py_IS_TYPE(obj, &PyWrapperDescr_Type); } +// Artifact[Implementation]: The state used to track a single freeze call and construct SCCs /** * Used to track the state of an in progress freeze operation. * @@ -1468,8 +1470,8 @@ static int check_freezable(struct _Py_immutability_state *state, PyObject* obj, } goto error; case _Py_FREEZABLE_PROXY: - // Reserved for future use — fall through to existing checks. - break; + assert(PyModule_Check(obj) || obj == _PyObject_CAST(&PyModule_Type)); + return 0; } } @@ -2015,6 +2017,7 @@ static void make_weakrefs_safe(struct FreezeState* freeze_state) /* This undoes a freeze belonging to the given state */ static void undo_freeze(struct FreezeState* state) { + // Artifact[Implementation]: The function that rolls back immutability on failure debug("Unfreezing all frozen objects belonging to %p\n", state); // Clear dfs stack @@ -2237,6 +2240,15 @@ late_init(struct _Py_immutability_state *state) static int freeze_impl(PyObject *const *objs, Py_ssize_t nobjs) { + // Artifact[Implementation]: The entry point to the `freeze()` function in C + // + // This is the central function to the freeze algorithm that handles + // DFS traversal and calls into other functions to: + // - Checking freezability + // - Checking and calling the pre-freeze hook + // - Construct SCCs + // - Handle failures + // - Remove objects from the GC list struct _Py_immutability_state* imm_state = NULL; int result = 0; TRACE_MERMAID_START();