Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,5 @@ CLAUDE.local.md
#### main branch only stuff below this line, things to backport go above. ####
# main branch only: ABI files are not checked/maintained.
Doc/data/python*.abi
# Ignore the build directory.
build*
1 change: 1 addition & 0 deletions Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
#include "fileutils.h"
#include "cpython/pyfpe.h"
#include "cpython/tracemalloc.h"
#include "immutability.h"

// Restore warning filter
#ifdef _MSC_VER
Expand Down
8 changes: 8 additions & 0 deletions Include/cpython/immutability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef Py_CPYTHON_IMMUTABLE_H
# error "this header file must not be included directly"
#endif

PyAPI_DATA(PyTypeObject) _PyNotFreezable_Type;

PyAPI_FUNC(int) _PyImmutability_Freeze(PyObject*);
PyAPI_FUNC(int) _PyImmutability_RegisterFreezable(PyTypeObject*);
1 change: 1 addition & 0 deletions Include/cpython/listobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ PyList_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) {
PyListObject *list = _PyList_CAST(op);
assert(0 <= index);
assert(index < list->allocated);
// TODO(Immutable): Add assert to check if the list is immutable
list->ob_item[index] = value;
}
#define PyList_SET_ITEM(op, index, value) \
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ typedef struct _heaptypeobject {
PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *);
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyType_LookupRef(PyTypeObject *, PyObject *);
PyAPI_FUNC(int) _PyType_HasExtensionSlots(PyTypeObject *);
PyAPI_FUNC(PyObject *) PyType_GetDict(PyTypeObject *);

PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
Expand Down
1 change: 1 addition & 0 deletions Include/descrobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ struct PyMemberDef {
#define Py_AUDIT_READ 2 // Added in 3.10, harmless no-op before that
#define _Py_WRITE_RESTRICTED 4 // Deprecated, no-op. Do not reuse the value.
#define Py_RELATIVE_OFFSET 8
// TODO(Immutable): Could use this to mark members as needing a lock.

PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *);
PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *);
Expand Down
3 changes: 2 additions & 1 deletion Include/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);
PyAPI_FUNC(PyObject *) PyDict_GetItemWithError(PyObject *mp, PyObject *key);
PyAPI_FUNC(int) PyDict_SetItem(PyObject *mp, PyObject *key, PyObject *item);
PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key);
PyAPI_FUNC(void) PyDict_Clear(PyObject *mp);
// Note(Immutable): If dictionary is immutable, then clear can fail. Had to change signature here!
PyAPI_FUNC(int) PyDict_Clear(PyObject *mp);
PyAPI_FUNC(int) PyDict_Next(
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value);
PyAPI_FUNC(PyObject *) PyDict_Keys(PyObject *mp);
Expand Down
19 changes: 19 additions & 0 deletions Include/immutability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef Py_IMMUTABILITY_H
#define Py_IMMUTABILITY_H

#ifdef __cplusplus
extern "C" {
#endif


#ifndef Py_LIMITED_API
# define Py_CPYTHON_IMMUTABLE_H
# include "cpython/immutability.h"
# undef Py_CPYTHON_IMMUTABLE_H
#endif


#ifdef __cplusplus
}
#endif
#endif /* !Py_IMMUTABILITY_H */
21 changes: 15 additions & 6 deletions Include/internal/pycore_cell.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,30 @@ extern "C" {
// Sets the cell contents to `value` and return previous contents. Steals a
// reference to `value`.
static inline PyObject *
PyCell_SwapTakeRef(PyCellObject *cell, PyObject *value)
PyCell_SwapTakeRef(PyCellObject *cell, PyObject *value, int* result)
{
PyObject *old_value;
PyObject *old_value = NULL;
*result = 0;
Py_BEGIN_CRITICAL_SECTION(cell);
old_value = cell->ob_ref;
FT_ATOMIC_STORE_PTR_RELEASE(cell->ob_ref, value);
if(Py_CHECKWRITE(cell)){
old_value = cell->ob_ref;
FT_ATOMIC_STORE_PTR_RELEASE(cell->ob_ref, value);
}
else {
*result = -1;
Py_XDECREF(value);
}
Py_END_CRITICAL_SECTION();
return old_value;
}

static inline void
static inline int
PyCell_SetTakeRef(PyCellObject *cell, PyObject *value)
{
PyObject *old_value = PyCell_SwapTakeRef(cell, value);
int result = 0;
PyObject *old_value = PyCell_SwapTakeRef(cell, value, &result);
Py_XDECREF(old_value);
return result;
}

// Gets the cell contents. Returns a new reference.
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ PyAPI_FUNC(int) _PyEval_ExceptionGroupMatch(_PyInterpreterFrame *, PyObject* exc
PyAPI_FUNC(void) _PyEval_FormatAwaitableError(PyThreadState *tstate, PyTypeObject *type, int oparg);
PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc, const char *format_str, PyObject *obj);
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
PyAPI_FUNC(void) _PyEval_FormatExcNotWriteable(PyThreadState *tstate, PyCodeObject *co, int oparg);
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *, _PyInterpreterFrame *, PyObject *, PyObject *, PyObject *);
Expand Down
21 changes: 21 additions & 0 deletions Include/internal/pycore_immutability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef Py_INTERNAL_IMMUTABILITY_H
#define Py_INTERNAL_IMMUTABILITY_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_BUILD_CORE
# error "Py_BUILD_CORE must be defined to include this header"
#endif

struct _Py_immutability_state {
PyObject *module_locks;
PyObject *blocking_on;
PyObject *freezable_types;
PyObject *destroy_cb;
};

#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_IMMUTABILITY_H */
2 changes: 2 additions & 0 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extern "C" {
#endif

#include "pycore_ast_state.h" // struct ast_state
#include "pycore_immutability.h" // struct _immutability_runtime_state
#include "pycore_llist.h" // struct llist_node
#include "pycore_opcode_utils.h" // NUM_COMMON_CONSTANTS
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
Expand Down Expand Up @@ -934,6 +935,7 @@ struct _is {

struct _Py_dict_state dict_state;
struct _Py_exc_state exc_state;
struct _Py_immutability_state immutability;
struct _Py_mem_interp_free_queue mem_free_queue;

struct ast_state ast;
Expand Down
43 changes: 38 additions & 5 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,17 @@ static inline void _Py_SetMortal(PyObject *op, short refcnt)
if (op) {
assert(_Py_IsImmortal(op));
#ifdef Py_GIL_DISABLED
// TODO(Immutable): Do we need to do something here?
op->ob_tid = _Py_UNOWNED_TID;
op->ob_ref_local = 0;
op->ob_ref_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED);
#else
// TODO(Immutable): Need to clear flag in other cases?
// note this also clears the _Py_IMMUTABLE_FLAG, if set in 32bit
op->ob_refcnt = refcnt;
#if SIZEOF_VOID_P > 4
op->ob_flags &= ~_Py_IMMORTAL_FLAGS;
#endif
#endif
}
}
Expand All @@ -229,18 +235,38 @@ static inline void _Py_ClearImmortal(PyObject *op)
} while (0)

#if !defined(Py_GIL_DISABLED)
static inline void _Py_SetImmutable(PyObject *op)
{
if(op) {
#if SIZEOF_VOID_P > 4
op->ob_flags |= _Py_IMMUTABLE_FLAG;
#else
op->ob_refcnt |= _Py_IMMUTABLE_FLAG;
#endif
}
}
#define _Py_SetImmutable(op) _Py_SetImmutable(_PyObject_CAST(op))

static inline void
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
{
if (_Py_IsImmortal(op)) {
_Py_DECREF_IMMORTAL_STAT_INC();
if (_Py_IsImmortalOrImmutable(op)) {
if (_Py_IsImmortal(op)) {
_Py_DECREF_IMMORTAL_STAT_INC();
return;
}
assert(_Py_IsImmutable(op));
if (_Py_DecRef_Immutable(op)) {
destruct(op);
}
return;
}
_Py_DECREF_STAT_INC();
#ifdef Py_REF_DEBUG
_Py_DEC_REFTOTAL(PyInterpreterState_Get());
#endif
if (--op->ob_refcnt != 0) {
op->ob_refcnt -= 1;
if (_Py_IMMUTABLE_FLAG_CLEAR(op->ob_refcnt) != 0) {
assert(op->ob_refcnt > 0);
}
else {
Expand All @@ -255,8 +281,12 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
static inline void
_Py_DECREF_NO_DEALLOC(PyObject *op)
{
if (_Py_IsImmortal(op)) {
_Py_DECREF_IMMORTAL_STAT_INC();
if (_Py_IsImmortalOrImmutable(op)) {
if (_Py_IsImmortal(op)) {
_Py_DECREF_IMMORTAL_STAT_INC();
return;
}
_Py_DecRef_Immutable(op);
return;
}
_Py_DECREF_STAT_INC();
Expand All @@ -272,6 +302,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
}

#else
// TODO(Immutable): We need to do stuff in the NoGIL build
// TODO: implement Py_DECREF specializations for Py_GIL_DISABLED build
static inline void
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
Expand Down Expand Up @@ -476,6 +507,7 @@ static inline void _Py_DECREF_MORTAL_SPECIALIZED(const char *filename, int linen

static inline void Py_DECREF_MORTAL(PyObject *op)
{
// TODO(Immutable): Need to catch immutable things here
assert(!_Py_IsStaticImmortal(op));
_Py_DECREF_STAT_INC();
if (--op->ob_refcnt == 0) {
Expand All @@ -486,6 +518,7 @@ static inline void Py_DECREF_MORTAL(PyObject *op)

static inline void Py_DECREF_MORTAL_SPECIALIZED(PyObject *op, destructor destruct)
{
// TODO(Immutable): Need to catch immutable things here
assert(!_Py_IsStaticImmortal(op));
_Py_DECREF_STAT_INC();
if (--op->ob_refcnt == 0) {
Expand Down
11 changes: 5 additions & 6 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Include/internal/pycore_uop_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,6 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
# define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST(ob), (type))
#endif


static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
ob->ob_type = type;
}
Expand Down
2 changes: 2 additions & 0 deletions Include/pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ PyAPI_DATA(PyObject *) PyExc_ResourceWarning;
PyAPI_FUNC(int) PyErr_BadArgument(void);
PyAPI_FUNC(PyObject *) PyErr_NoMemory(void);
PyAPI_FUNC(PyObject *) PyErr_SetFromErrno(PyObject *);
PyAPI_FUNC(PyObject *) _PyErr_WriteToImmutable(PyObject *);
#define PyErr_WriteToImmutable(op) _PyErr_WriteToImmutable(_PyObject_CAST(op))
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObject(
PyObject *, PyObject *);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000
Expand Down
1 change: 1 addition & 0 deletions Include/pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ PyAPI_FUNC(void) Py_Finalize(void);
PyAPI_FUNC(int) Py_FinalizeEx(void);
#endif
PyAPI_FUNC(int) Py_IsInitialized(void);
PyAPI_FUNC(int) Py_IsFinalizing(void);

/* Subinterpreter support */
PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void);
Expand Down
Loading