Skip to content

Commit d5b515f

Browse files
authored
Merge pull request #91 from fxpl/immutable-artifact-markers
Immutable artifact markers
2 parents d4f8054 + 2c90688 commit d5b515f

9 files changed

Lines changed: 34 additions & 3 deletions

File tree

Include/refcount.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ static inline Py_ALWAYS_INLINE int _Py_IsImmutable(PyObject *op)
142142
}
143143
#define _Py_IsImmutable(op) _Py_IsImmutable(_PyObject_CAST(op))
144144

145+
// Artifact[Implementation]: The definition of the `Py_CHECKWRITE` macro
145146
// Check whether an object is writeable.
146147
// This check will always succeed during runtime finalization.
147148
#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)
388389
return;
389390
}
390391
#ifndef Py_LIMITED_API
392+
// Artifact[Implementation]: The atomic RC branch for immutable objects in Py_INCREF
391393
if (_Py_IsImmutable(op)) {
392394
// Object is immutable.
393395
// 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)
559561
return;
560562
}
561563
#ifndef Py_LIMITED_API
564+
// Artifact[Implementation]: The atomic RC branch for immutable objects in Py_DECREF
562565
if (_Py_IsImmutable(op))
563566
{
564567
if (_Py_DecRef_Immutable(op)) {

Lib/immutable.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
# aliases it to `is_frozen`
2828
isfrozen = is_frozen
2929

30+
31+
# Artifact[Benchmarking]: The implementation of immutability related decorators
3032
def freezable(cls):
3133
"""Class decorator: mark a class as always freezable."""
3234
set_freezable(cls, FREEZABLE_YES)

Lib/test/test_freeze/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Tests for Immutability
2+
3+
<!--
4+
// Artifact[Tests]: The collection of tests for immutability
5+
-->
6+
7+
This folder contains tests for individual parts of the immutability
8+
implementation.

Modules/_immutablemodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ _immutable_unset_freezable(PyObject *module, PyObject *obj)
189189
Py_RETURN_NONE;
190190
}
191191

192+
// Artifact[Implementation]: The implementation of the `InterpreterLocal` type
192193
/*
193194
* InterpreterLocal type
194195
*
@@ -360,6 +361,7 @@ static PyType_Spec interpreterlocal_spec = {
360361
.slots = interpreterlocal_slots,
361362
};
362363

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

364366
/*
365367
* SharedField type
@@ -373,7 +375,6 @@ static PyType_Spec interpreterlocal_spec = {
373375
* can be accessed concurrently from different sub-interpreters (each
374376
* with its own GIL).
375377
*/
376-
377378
typedef struct {
378379
PyObject_HEAD
379380
PyObject *value; // Always frozen; guarded by lock

Objects/funcobject.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,7 @@ func_descr_get(PyObject *func, PyObject *obj, PyObject *type)
12051205
return PyMethod_New(func, obj);
12061206
}
12071207

1208+
// Artifact[Implementation]: The pre-freeze hook of function objects
12081209
/**
12091210
* Special function for replacing globals and builtins with a copy of just what they use.
12101211
*
@@ -1737,6 +1738,7 @@ PyTypeObject PyClassMethod_Type = {
17371738
PyType_GenericAlloc, /* tp_alloc */
17381739
PyType_GenericNew, /* tp_new */
17391740
PyObject_GC_Del, /* tp_free */
1741+
.tp_reachable = _PyObject_ReachableVisitType,
17401742
};
17411743

17421744
PyObject *

Objects/moduleobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,7 @@ module_make_immutable_proxy(PyObject *self) {
15541554
return 0;
15551555
}
15561556

1557+
// Artifact[Implementation]: The pre-freeze hook of module objects
15571558
static int
15581559
module_prefreeze(PyObject *self) {
15591560
// TODO(immutable): Check if the module defines a custom pre-freeze hook:

Objects/weakrefobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
*/
3434

3535
#else
36+
// Artifact[Implementation]: Explanation how weak references work for immutable objects
3637
/*
3738
* Thread-safety for immutable objects
3839
* ===================================

Python/crossinterp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ _get_xidata(PyThreadState *tstate,
488488
return -1;
489489
}
490490

491+
// Artifact[Implementation]: The branch that allows direct sharing for immutable object across sub-interpreters
491492
if (_Py_IsImmutable(obj)) {
492493
_Py_IncRef(obj);
493494
xidata->obj = obj;

Python/immutability.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ static PyObject* pop(PyObject* s){
245245

246246

247247

248+
// Artifact[Implementation]: Explanation how a stack is used to implement the DFS based SCC algorithm
248249
/**
249250
* The DFS walk for SCC calculations needs to perform actions on both
250251
* the pre-order and post-order visits to an object. To achieve this
@@ -290,6 +291,7 @@ static bool is_c_wrapper(PyObject* obj){
290291
return PyCFunction_Check(obj) || Py_IS_TYPE(obj, &_PyMethodWrapper_Type) || Py_IS_TYPE(obj, &PyWrapperDescr_Type);
291292
}
292293

294+
// Artifact[Implementation]: The state used to track a single freeze call and construct SCCs
293295
/**
294296
* Used to track the state of an in progress freeze operation.
295297
*
@@ -1468,8 +1470,8 @@ static int check_freezable(struct _Py_immutability_state *state, PyObject* obj,
14681470
}
14691471
goto error;
14701472
case _Py_FREEZABLE_PROXY:
1471-
// Reserved for future use — fall through to existing checks.
1472-
break;
1473+
assert(PyModule_Check(obj) || obj == _PyObject_CAST(&PyModule_Type));
1474+
return 0;
14731475
}
14741476
}
14751477

@@ -2015,6 +2017,7 @@ static void make_weakrefs_safe(struct FreezeState* freeze_state)
20152017

20162018
/* This undoes a freeze belonging to the given state */
20172019
static void undo_freeze(struct FreezeState* state) {
2020+
// Artifact[Implementation]: The function that rolls back immutability on failure
20182021
debug("Unfreezing all frozen objects belonging to %p\n", state);
20192022

20202023
// Clear dfs stack
@@ -2237,6 +2240,15 @@ late_init(struct _Py_immutability_state *state)
22372240
static int
22382241
freeze_impl(PyObject *const *objs, Py_ssize_t nobjs)
22392242
{
2243+
// Artifact[Implementation]: The entry point to the `freeze()` function in C
2244+
//
2245+
// This is the central function to the freeze algorithm that handles
2246+
// DFS traversal and calls into other functions to:
2247+
// - Checking freezability
2248+
// - Checking and calling the pre-freeze hook
2249+
// - Construct SCCs
2250+
// - Handle failures
2251+
// - Remove objects from the GC list
22402252
struct _Py_immutability_state* imm_state = NULL;
22412253
int result = 0;
22422254
TRACE_MERMAID_START();

0 commit comments

Comments
 (0)