Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Include/refcount.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)) {
Expand Down
2 changes: 2 additions & 0 deletions Lib/immutable.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_freeze/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Tests for Immutability

<!--
// Artifact[Tests]: The collection of tests for immutability
-->

This folder contains tests for individual parts of the immutability
implementation.
3 changes: 2 additions & 1 deletion Modules/_immutablemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ _immutable_unset_freezable(PyObject *module, PyObject *obj)
Py_RETURN_NONE;
}

// Artifact[Implementation]: The implementation of the `InterpreterLocal` type
/*
* InterpreterLocal type
*
Expand Down Expand Up @@ -360,6 +361,7 @@ static PyType_Spec interpreterlocal_spec = {
.slots = interpreterlocal_slots,
};

// Artifact[Implementation]: The implementation of the `SharedField` type

/*
* SharedField type
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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 *
Expand Down
1 change: 1 addition & 0 deletions Objects/moduleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions Objects/weakrefobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
*/

#else
// Artifact[Implementation]: Explanation how weak references work for immutable objects
/*
* Thread-safety for immutable objects
* ===================================
Expand Down
1 change: 1 addition & 0 deletions Python/crossinterp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 14 additions & 2 deletions Python/immutability.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@



// 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
Expand Down Expand Up @@ -290,6 +291,7 @@
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.
*
Expand Down Expand Up @@ -669,7 +671,7 @@
return true;
}

static PyObject* get_next(PyObject* obj, struct FreezeState *freeze_state)

Check warning on line 674 in Python/immutability.c

View workflow job for this annotation

GitHub Actions / Cross build Linux

‘get_next’ defined but not used [-Wunused-function]

Check warning on line 674 in Python/immutability.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

‘get_next’ defined but not used [-Wunused-function]
{
(void)freeze_state;
PyObject* next = scc_next(obj);
Expand Down Expand Up @@ -1468,8 +1470,8 @@
}
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;
}
}

Expand Down Expand Up @@ -2015,6 +2017,7 @@

/* 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
Expand Down Expand Up @@ -2121,7 +2124,7 @@
return 0;
}
#else
#error "Immutability currently only works on 64bit platforms"

Check failure on line 2127 in Python/immutability.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

#error: "Immutability currently only works on 64bit platforms" [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check failure on line 2127 in Python/immutability.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

#error: "Immutability currently only works on 64bit platforms" [C:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]
#endif

// Mark pre-freeze hook as completed. This has to be set before calling
Expand Down Expand Up @@ -2237,6 +2240,15 @@
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();
Expand Down
Loading