Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-84436: Implement Immortal Objects #19474

Merged
merged 201 commits into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from 142 commits
Commits
Show all changes
201 commits
Select commit Hold shift + click to select a range
0c930b7
Implement Immortal Instances
eduardo-elizondo Apr 11, 2020
7005944
Nits
eduardo-elizondo Apr 11, 2020
c6a1bfa
Bypass immortality in NewReference
eduardo-elizondo Apr 11, 2020
51e4879
Add News and Fix MSVC Build
eduardo-elizondo Apr 11, 2020
cc2ece3
Formatting Nits
eduardo-elizondo Apr 11, 2020
72d12fa
Typo
eduardo-elizondo Apr 11, 2020
f04776e
MSVC Test
eduardo-elizondo Apr 11, 2020
fa8d668
Skip test for MSVC
eduardo-elizondo Apr 11, 2020
f066633
Skip test for MSVC 32 & 64
eduardo-elizondo Apr 11, 2020
36e0a9a
Skip all tests for Windows
eduardo-elizondo Apr 11, 2020
2f9fa29
Immortalize known immortals
eduardo-elizondo Apr 15, 2020
a1bc981
static inits
eduardo-elizondo Apr 15, 2020
0b12a16
Immortalize more known immortals
eduardo-elizondo Apr 15, 2020
1332f03
Change static to define
eduardo-elizondo Apr 15, 2020
17883d7
Nits
eduardo-elizondo Apr 15, 2020
bc23d4d
Add parentheses for define
eduardo-elizondo Apr 15, 2020
825f8fa
Fix gc tests
eduardo-elizondo Apr 15, 2020
9a6f4f9
Remove module usage at runtime destruction
eduardo-elizondo Apr 15, 2020
6899f5c
Fix msvc
eduardo-elizondo Apr 15, 2020
d285010
Fix refcnt tests
eduardo-elizondo Apr 16, 2020
b7d2c21
Fix refcnt tests
eduardo-elizondo Apr 16, 2020
093edae
Merge branch 'immortal-references' of https://github.com/eduardo-eliz…
eduardo-elizondo Apr 16, 2020
54af788
Nits
eduardo-elizondo Apr 16, 2020
b29c8ff
Remove immortalize arenas
eduardo-elizondo Apr 16, 2020
701579a
Remove immortalize arenas
eduardo-elizondo Apr 16, 2020
a12da60
Merge branch 'immortal-references' of https://github.com/eduardo-eliz…
eduardo-elizondo Apr 16, 2020
a99a517
Fix ms
eduardo-elizondo Apr 16, 2020
15ad069
Fix msft
eduardo-elizondo Apr 16, 2020
874248a
Remove profile
eduardo-elizondo Apr 16, 2020
357a084
Make all tests pass
eduardo-elizondo Apr 16, 2020
937d4f6
Added skipUnless arg
eduardo-elizondo Apr 16, 2020
d3bcea6
Exclude refcnt tests
eduardo-elizondo Apr 16, 2020
b439b7b
Exclude leak tests
eduardo-elizondo Apr 16, 2020
ea23b5a
Exclude refleak tests
eduardo-elizondo Apr 16, 2020
07fa840
Pass. Tests.
eduardo-elizondo Apr 16, 2020
6ff9ab2
Merge branch 'master' into immortal-references
eduardo-elizondo Dec 12, 2021
3d6f709
Rebased to latest
eduardo-elizondo Dec 12, 2021
66dc3e4
Make tests pass
eduardo-elizondo Dec 12, 2021
385e075
Simplify Review
eduardo-elizondo Dec 13, 2021
b0bb995
Cleanup
eduardo-elizondo Dec 13, 2021
7e26465
Immortalize startup heap
eduardo-elizondo Dec 14, 2021
0c2bc92
Rebase
eduardo-elizondo Dec 16, 2021
ce1319a
Fix test
eduardo-elizondo Dec 16, 2021
fbb1b12
Branchless add
eduardo-elizondo Dec 16, 2021
61f128a
Branch if
eduardo-elizondo Dec 16, 2021
e6bf2e2
Remove branch in none,true,false return
eduardo-elizondo Dec 16, 2021
62ff2c7
Readuce feature set to singleton immortalization only
eduardo-elizondo Dec 18, 2021
0820520
More clenups
eduardo-elizondo Dec 18, 2021
3866ad7
Merge branch 'master' into immortal-references
eduardo-elizondo Dec 18, 2021
65c7e30
Remove exposed C-API
eduardo-elizondo Dec 18, 2021
e530f0b
Fixes for refcount tests
eduardo-elizondo Dec 18, 2021
3375cd6
Remove superfluous tests
eduardo-elizondo Dec 18, 2021
570aff4
Remove extra file
eduardo-elizondo Dec 18, 2021
e49b58a
Reduce test refcount check ammount
eduardo-elizondo Dec 18, 2021
ef478b1
Rerun tests
eduardo-elizondo Dec 18, 2021
7225f3e
Address comments
eduardo-elizondo Dec 29, 2021
0f6ad8e
Small fix
eduardo-elizondo Dec 29, 2021
54c8fe2
Increase initial immortal refcount
eduardo-elizondo Dec 30, 2021
e274104
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Feb 19, 2022
c829257
Cleanups
eduardo-elizondo Feb 19, 2022
5949df4
Move immortal bit to 2nd MSB
eduardo-elizondo Feb 27, 2022
2baee89
Improve comments
eduardo-elizondo Feb 28, 2022
f7da0f8
Remove extras refcounts inside interpreter loop
eduardo-elizondo Feb 28, 2022
4bea515
immortalize deepfreeze
eduardo-elizondo Feb 28, 2022
dfb5863
Remove unused files
eduardo-elizondo Feb 28, 2022
f7287b2
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Feb 28, 2022
5af0167
Remove unused refcounts in singletons within CPython/Objects
eduardo-elizondo Feb 28, 2022
02cf9af
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Mar 9, 2022
25fd52a
Revert "Remove unused refcounts in singletons within CPython/Objects"
eduardo-elizondo Mar 9, 2022
be86955
Include immortal interned strings
eduardo-elizondo Mar 9, 2022
38a14a9
Regen frozen main
eduardo-elizondo Mar 9, 2022
c828369
Properly clean up all immortal interned strings at runtime finalization
eduardo-elizondo Mar 9, 2022
ee41af6
Build and test fixes
eduardo-elizondo Mar 9, 2022
f835e6d
Temporarily disable single test_embed test
eduardo-elizondo Mar 9, 2022
8573af4
Fix structseq test
eduardo-elizondo Mar 9, 2022
ad19ff6
Move nonetype refcount to static refcnt
eduardo-elizondo Mar 18, 2022
66c625f
Remove unneeded reference counts in Cpython/Objects
eduardo-elizondo Mar 18, 2022
1379d50
Mark global instances as static globals
eduardo-elizondo Mar 18, 2022
1c9ee6d
Remove unneeded reference counts in Cpython/Python
eduardo-elizondo Mar 18, 2022
287b57c
Remove unneeded reference counts in gcmodule.c
eduardo-elizondo Mar 18, 2022
c736a7c
Cleanup bool and str usage
eduardo-elizondo Mar 18, 2022
60f0760
Merge branch 'main' into immortal-references
eduardo-elizondo Mar 18, 2022
1321ff6
Fix whitespaces
eduardo-elizondo Mar 18, 2022
a719b41
Remove static immortal bit in favor of unicode intern state
eduardo-elizondo Apr 17, 2022
9f3ed39
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 17, 2022
8f72afe
Regen frozen
eduardo-elizondo Apr 17, 2022
52d6d78
Fix regrtest
eduardo-elizondo Apr 17, 2022
9fd8a98
Change immortal refcount for PY_SSIZE_T_MAX
eduardo-elizondo Apr 17, 2022
3478467
Introduce saturated adds for increfs
eduardo-elizondo Apr 17, 2022
eb5da8c
Add default and msvc intrinsic saturated add
eduardo-elizondo Apr 18, 2022
def8da3
Fix msvc saturated add
eduardo-elizondo Apr 18, 2022
fe6727e
Fix docs
eduardo-elizondo Apr 18, 2022
38df3ce
Move unicode_is_singleton to Py_DEBUG
eduardo-elizondo Apr 18, 2022
73f6dcd
Skip immortal checks in frame clear
eduardo-elizondo Apr 18, 2022
d68efa1
Make code objects immortal
eduardo-elizondo Apr 19, 2022
18cff33
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 19, 2022
168a85c
Refcount fixes
eduardo-elizondo Apr 19, 2022
9ada9fd
Temporarily disable two code tests
eduardo-elizondo Apr 19, 2022
5d3beb9
Disable one more code test
eduardo-elizondo Apr 20, 2022
ea342e3
Cleanups
eduardo-elizondo Apr 21, 2022
d78a560
Simplify Implementation
eduardo-elizondo May 16, 2022
16d59e3
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo May 16, 2022
f49c13c
Cleanups
eduardo-elizondo May 16, 2022
8262e56
More Cleanups
eduardo-elizondo May 16, 2022
96c7caa
Regen Frozen
eduardo-elizondo May 16, 2022
3493c85
Fix regrtest
eduardo-elizondo May 16, 2022
0f38657
Only immortal changes
eduardo-elizondo May 16, 2022
401a3c3
Fix C++ compilation issue
eduardo-elizondo May 16, 2022
6bd2d94
Fix regen files
eduardo-elizondo May 16, 2022
9df1447
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo May 16, 2022
15f7365
Fix sat add
eduardo-elizondo May 16, 2022
ea9f01c
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo May 22, 2022
c39b617
32 bit fixes
eduardo-elizondo May 22, 2022
3ae8374
Fix msvc build
eduardo-elizondo May 22, 2022
ba7cfe1
Add 32 compat
eduardo-elizondo May 22, 2022
7a29123
More fixes
eduardo-elizondo May 22, 2022
88ede67
Fix inlined refcounts
eduardo-elizondo May 22, 2022
34bdf3c
Change refcount strategy for 32bit systems
eduardo-elizondo May 22, 2022
ab1f6e4
Add guard for saturated add function
eduardo-elizondo May 22, 2022
c2c228e
Cleanup unneeded port values
eduardo-elizondo May 22, 2022
219ebdc
branchless saturated add
eduardo-elizondo May 23, 2022
cd42e16
Use PY32 bit integers
eduardo-elizondo May 23, 2022
99e7549
Cleanups
eduardo-elizondo May 23, 2022
d7df473
Remove branchless add as it's slower
eduardo-elizondo May 23, 2022
00238eb
Immortalize Interned Strings
eduardo-elizondo May 23, 2022
9355ca2
Fix structseq test
eduardo-elizondo May 23, 2022
eedd412
Bring back interned stats
eduardo-elizondo May 23, 2022
ccf8b61
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo May 23, 2022
e57910d
Fix msvc ifdef
eduardo-elizondo May 23, 2022
6437df7
Only copy lower 32 bits to refcnt
eduardo-elizondo Oct 3, 2022
ba75726
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Nov 27, 2022
418b2ff
Merge cleanups
eduardo-elizondo Nov 27, 2022
1468f52
Fixing Test Failures
eduardo-elizondo Nov 28, 2022
e30fea4
Addressed static string issue
eduardo-elizondo Dec 17, 2022
5aa8c34
Addressed regrtest failures
eduardo-elizondo Dec 17, 2022
d74a4c5
Addressed CI failures
eduardo-elizondo Dec 17, 2022
9be58d4
Addressed CI failures second try
eduardo-elizondo Dec 19, 2022
f00f7f8
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Dec 20, 2022
747039d
Remove temporary fixes
eduardo-elizondo Dec 20, 2022
01017e1
Temporary windows fix
eduardo-elizondo Dec 20, 2022
6f0cf32
Remove duplicate immortal initialization
eduardo-elizondo Dec 20, 2022
7997d57
Windows fix
eduardo-elizondo Dec 21, 2022
749680e
Addressed CI failures third try
eduardo-elizondo Dec 21, 2022
c71c742
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Dec 21, 2022
bc28cb0
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Jan 9, 2023
c8b694f
Add tp_dealloc suggested changes by steering committee
eduardo-elizondo Jan 9, 2023
6abab4d
Fixed int leak
eduardo-elizondo Jan 9, 2023
1dfe27a
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Jan 9, 2023
7661541
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Jan 17, 2023
59513a7
Cleanup deallocation of immortal objects
eduardo-elizondo Jan 17, 2023
60329b5
Add DeepFreeze types for typle, long, and bytes
eduardo-elizondo Jan 17, 2023
a5e29d5
Fix regencode
eduardo-elizondo Jan 17, 2023
f88cbb6
Fix stable abi toml
eduardo-elizondo Jan 17, 2023
7efa760
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Jan 22, 2023
8ebb3db
Add DeepFreeze types for float, complex
eduardo-elizondo Jan 22, 2023
2c3d242
Add DeepFreeze types for code and update stable_abi
eduardo-elizondo Jan 22, 2023
5684be7
Remove PyDeepFreezeCode_Type from stable abi
eduardo-elizondo Jan 22, 2023
4529e23
Revert DeepFreeze changes
eduardo-elizondo Jan 29, 2023
cfb56b6
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Jan 29, 2023
a748e80
Replace incref memcpy with builtins
eduardo-elizondo Jan 29, 2023
033c86d
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 1, 2023
520fbc3
Rebase fixes
eduardo-elizondo Apr 1, 2023
90e0016
Debug build fixes
eduardo-elizondo Apr 1, 2023
bc726b0
Cleanups in prep for review
eduardo-elizondo Apr 3, 2023
5e0cd08
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 3, 2023
f7fbf01
Correct whatsnew
eduardo-elizondo Apr 3, 2023
92fbf96
More cleanups
eduardo-elizondo Apr 3, 2023
1c390cc
Delete _PyType_FixCacheRefcounts
eduardo-elizondo Apr 3, 2023
030016a
Addressed First Round of Comments
eduardo-elizondo Apr 6, 2023
093c405
Address comments
eduardo-elizondo Apr 7, 2023
6c0fdba
Return Py_DEBUG in unicode runtime shutdown
eduardo-elizondo Apr 7, 2023
74b6e7b
Nits
eduardo-elizondo Apr 7, 2023
433d1e3
Use Py_BUILD_CORE to set PyObject_HEAD_INIT as immortal
eduardo-elizondo Apr 8, 2023
069da16
Address Carl's comments
eduardo-elizondo Apr 8, 2023
d22a4bf
Use a union to refer to lower 32bits
eduardo-elizondo Apr 9, 2023
e04ef7e
Static declarations cleanups
eduardo-elizondo Apr 9, 2023
3b3b142
Only support split refcount in 64bit architectures
eduardo-elizondo Apr 9, 2023
ab3f951
Support incref in big-endian machines
eduardo-elizondo Apr 9, 2023
3e55a32
Cleanups and comments
eduardo-elizondo Apr 10, 2023
ff69be7
Fix bytes_method compiler error
eduardo-elizondo Apr 10, 2023
e6e459c
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 10, 2023
e19f50a
Add Py_ALWAYS_INLINE to Py_DECREF, Py_INCREF, and _Py_IsImmortal
eduardo-elizondo Apr 10, 2023
7c233b0
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 15, 2023
9b2b49c
Fix merge errors
eduardo-elizondo Apr 15, 2023
018be4c
Fix inlining warning
eduardo-elizondo Apr 15, 2023
f4aa5b4
Fix build errors with _testcppext.cpp
eduardo-elizondo Apr 19, 2023
1d2ee06
Also set initializers in PyModuleDef_HEAD_INIT
eduardo-elizondo Apr 19, 2023
c6a14f2
Revert "Also set initializers in PyModuleDef_HEAD_INIT"
eduardo-elizondo Apr 19, 2023
47819f6
Fix one bug that incorrectly tracks RefTotal
eduardo-elizondo Apr 19, 2023
9423c61
Fix another bug that incorrectly immortalizes non-small ints
eduardo-elizondo Apr 20, 2023
a4a9067
Introduce sys.getunicodeinternedsize to correctly track refleaks
eduardo-elizondo Apr 21, 2023
181aedd
Move _Py_SetImmortal to internal
eduardo-elizondo Apr 21, 2023
0a18468
Update whatsnew
eduardo-elizondo Apr 21, 2023
67b1c57
Fix build errors in PC/_wmimodule.cpp
eduardo-elizondo Apr 21, 2023
e82b165
Also include m_base in initalizers of cppext and _wmi
eduardo-elizondo Apr 21, 2023
d85d9d3
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 21, 2023
9053b22
Fix mixed designated initializer error
eduardo-elizondo Apr 21, 2023
a9caa2d
Small cleanups
eduardo-elizondo Apr 21, 2023
56f1d81
Add braces to indicate union initializer in ob_refcnt
eduardo-elizondo Apr 22, 2023
bae6195
Merge remote-tracking branch 'upstream/main' into immortal-references
eduardo-elizondo Apr 22, 2023
9cb2c21
Also remove designated initializers from _PyObject_EXTRA_INIT
eduardo-elizondo Apr 22, 2023
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
4 changes: 2 additions & 2 deletions Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@
# define _SGI_MP_SOURCE
#endif

// stdlib.h, stdio.h, errno.h and string.h headers are not used by Python
// stdlib.h, stdio.h, and errno.h headers are not used by Python
// headers, but kept for backward compatibility. They are excluded from the
// limited C API of Python 3.11.
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# include <stdlib.h>
# include <stdio.h> // FILE*
# include <errno.h> // errno
# include <string.h> // memcpy()
#endif
#ifndef MS_WINDOWS
# include <unistd.h>
Expand All @@ -34,6 +33,7 @@

#include <assert.h> // assert()
#include <wchar.h> // wchar_t
#include <string.h> // memcpy()

#include "pyport.h"
#include "pymacro.h"
Expand Down
7 changes: 3 additions & 4 deletions Include/boolobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ PyAPI_DATA(PyTypeObject) PyBool_Type;

#define PyBool_Check(x) Py_IS_TYPE((x), &PyBool_Type)

/* Py_False and Py_True are the only two bools in existence.
Don't forget to apply Py_INCREF() when returning either!!! */
/* Py_False and Py_True are the only two bools in existence. */

/* Don't use these directly */
PyAPI_DATA(PyLongObject) _Py_FalseStruct;
Expand All @@ -31,8 +30,8 @@ PyAPI_FUNC(int) Py_IsFalse(PyObject *x);
#define Py_IsFalse(x) Py_Is((x), Py_False)

/* Macros for returning Py_True or Py_False, respectively */
#define Py_RETURN_TRUE return Py_NewRef(Py_True)
#define Py_RETURN_FALSE return Py_NewRef(Py_False)
#define Py_RETURN_TRUE return Py_True
#define Py_RETURN_FALSE return Py_False

/* Function to return a bool from a C long */
PyAPI_FUNC(PyObject *) PyBool_FromLong(long);
Expand Down
6 changes: 4 additions & 2 deletions Include/cpython/unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ typedef struct {
struct {
/* If interned is set, the two references from the
dictionary to this object are *not* counted in ob_refcnt. */
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
unsigned int interned:1;
unsigned int interned:2;
/* Character size:

- PyUnicode_1BYTE_KIND (1):
Expand Down Expand Up @@ -135,7 +135,7 @@ typedef struct {
unsigned int ascii:1;
/* Padding to ensure that PyUnicode_DATA() is always aligned to
4 bytes (see issue #19537 on m68k). */
unsigned int :26;
unsigned int :25;
} state;
} PyASCIIObject;

Expand Down Expand Up @@ -183,6 +183,8 @@ PyAPI_FUNC(int) _PyUnicode_CheckConsistency(
/* Interning state. */
#define SSTATE_NOT_INTERNED 0
#define SSTATE_INTERNED_MORTAL 1
#define SSTATE_INTERNED_IMMORTAL 2
#define SSTATE_INTERNED_IMMORTAL_STATIC 3
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved

/* Use only if you know it's a string */
static inline unsigned int PyUnicode_CHECK_INTERNED(PyObject *op) {
Expand Down
6 changes: 2 additions & 4 deletions Include/internal/pycore_global_objects_fini_generated.h

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

18 changes: 6 additions & 12 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ extern "C" {
/* This value provides *effective* immortality, meaning the object should never
be deallocated (until runtime finalization). See PEP 683 for more details about
immortality, as well as a proposed mechanism for proper immortality. */
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
#define _PyObject_IMMORTAL_REFCNT 999999999

#define _PyObject_IMMORTAL_INIT(type) \
{ \
.ob_refcnt = _PyObject_IMMORTAL_REFCNT, \
.ob_type = (type), \
}
#define _PyVarObject_IMMORTAL_INIT(type, size) \
{ \
.ob_base = _PyObject_IMMORTAL_INIT(type), \
.ob_size = size, \
}

PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
const char *func,
Expand All @@ -50,6 +38,9 @@ static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
static inline void
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
{
if (_Py_IsImmortal(op)) {
return;
}
_Py_DECREF_STAT_INC();
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
Expand All @@ -68,6 +59,9 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
static inline void
_Py_DECREF_NO_DEALLOC(PyObject *op)
{
if (_Py_IsImmortal(op)) {
return;
}
_Py_DECREF_STAT_INC();
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
Expand Down
16 changes: 8 additions & 8 deletions Include/internal/pycore_runtime_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ extern "C" {
.latin1 = _Py_str_latin1_INIT, \
}, \
.tuple_empty = { \
.ob_base = _PyVarObject_IMMORTAL_INIT(&PyTuple_Type, 0) \
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, 0) \
}, \
.hamt_bitmap_node_empty = { \
.ob_base = _PyVarObject_IMMORTAL_INIT(&_PyHamt_BitmapNode_Type, 0) \
.ob_base = PyVarObject_HEAD_INIT(&_PyHamt_BitmapNode_Type, 0) \
}, \
.context_token_missing = { \
.ob_base = _PyObject_IMMORTAL_INIT(&_PyContextTokenMissing_Type), \
.ob_base = PyObject_HEAD_IMMORTAL_INIT(&_PyContextTokenMissing_Type) \
}, \
}, \
}, \
Expand Down Expand Up @@ -130,7 +130,7 @@ extern "C" {
.singletons = { \
._not_used = 1, \
.hamt_empty = { \
.ob_base = _PyObject_IMMORTAL_INIT(&_PyHamt_Type), \
.ob_base = PyObject_HEAD_IMMORTAL_INIT(&_PyHamt_Type) \
.h_root = (PyHamtNode*)&_Py_SINGLETON(hamt_bitmap_node_empty), \
}, \
}, \
Expand All @@ -149,14 +149,14 @@ extern "C" {

#define _PyLong_DIGIT_INIT(val) \
{ \
_PyVarObject_IMMORTAL_INIT(&PyLong_Type, \
((val) == 0 ? 0 : ((val) > 0 ? 1 : -1))), \
PyVarObject_HEAD_INIT(&PyLong_Type, \
((val) == 0 ? 0 : ((val) > 0 ? 1 : -1))) \
.ob_digit = { ((val) >= 0 ? (val) : -(val)) }, \
}

#define _PyBytes_SIMPLE_INIT(CH, LEN) \
{ \
_PyVarObject_IMMORTAL_INIT(&PyBytes_Type, (LEN)), \
PyVarObject_HEAD_INIT(&PyBytes_Type, (LEN)) \
.ob_shash = -1, \
.ob_sval = { (CH) }, \
}
Expand All @@ -167,7 +167,7 @@ extern "C" {

#define _PyUnicode_ASCII_BASE_INIT(LITERAL, ASCII) \
{ \
.ob_base = _PyObject_IMMORTAL_INIT(&PyUnicode_Type), \
.ob_base = PyObject_HEAD_IMMORTAL_INIT(&PyUnicode_Type) \
.length = sizeof(LITERAL) - 1, \
.hash = -1, \
.state = { \
Expand Down
10 changes: 5 additions & 5 deletions Include/moduleobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ typedef struct PyModuleDef_Base {
PyObject* m_copy;
} PyModuleDef_Base;

#define PyModuleDef_HEAD_INIT { \
PyObject_HEAD_INIT(_Py_NULL) \
_Py_NULL, /* m_init */ \
0, /* m_index */ \
_Py_NULL, /* m_copy */ \
#define PyModuleDef_HEAD_INIT { \
PyObject_HEAD_IMMORTAL_INIT(_Py_NULL) \
_Py_NULL, /* m_init */ \
0, /* m_index */ \
_Py_NULL, /* m_copy */ \
}

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000
Expand Down
109 changes: 104 additions & 5 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,69 @@ whose size is determined when the object is allocated.
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD PyObject ob_base;

/*
Immortalization:

The following indicates the immortalization strategy depending on the amount
of available bits in the reference count field. All strategies are backwards
compatible but the specific reference count value or immortalization check
might change depending on the specializations for the underlying system.

Proper deallocation of immortal instances requires distinguishing between
statically allocated immortal instances vs those promoted by the runtime to be
immortal. The latter which should be the only instances that require proper
cleanup during runtime finalization.
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
*/

#if SIZEOF_VOID_P > 4
/*
In 64+ bit systems, an object will be marked as immortal by setting all of the
lower 32 bits of the reference count field.

i.e in a 64 bit system the reference count will be set to:
00000000 00000000 00000000 00000000
11111111 11111111 11111111 11111111
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved

Using the lower 32 bits makes the value backwards compatible by allowing
C-Extensions without the updated checks in Py_INCREF and Py_DECREF to safely
increase and decrease the objects reference count. The object would lose its
immortality, but the execution would still be correct.

Reference count increases will use saturated arithmetic, taking advantage of
having all the lower 32 bits set, which will avoid the reference count to go
beyond the refcount limit. Immortality checks for reference count decreases will
be done by checking the bit sign flag in the lower 32 bits.
*/
#define _Py_IMMORTAL_REFCNT UINT_MAX

#else
/*
In 32 bit systems, an object will be marked as immortal by setting all of the
lower 30 bits of the reference count field.

i.e The reference count will be set to:
00111111 11111111 11111111 11111111

Using the lower 30 bits makes the value backwards compatible by allowing
C-Extensions without the updated checks in Py_INCREF and Py_DECREF to safely
increase and decrease the objects reference count. The object would lose its
immortality, but the execution would still be correct.

Reference count increases and decreases will first go through an immortality
check by comparing the reference count field to the immortality reference count.
*/
#define _Py_IMMORTAL_REFCNT (UINT_MAX >> 2)
#endif

#define PyObject_HEAD_INIT(type) \
{ _PyObject_EXTRA_INIT \
1, (type) },

#define PyObject_HEAD_IMMORTAL_INIT(type) \
{ _PyObject_EXTRA_INIT _Py_IMMORTAL_REFCNT, type },

#define PyVarObject_HEAD_INIT(type, size) \
{ PyObject_HEAD_INIT(type) (size) },
{ PyObject_HEAD_IMMORTAL_INIT(type) size },

/* PyObject_VAR_HEAD defines the initial segment of all variable-size
* container objects. These end with a declaration of an array with 1
Expand Down Expand Up @@ -147,6 +204,23 @@ static inline Py_ssize_t Py_SIZE(PyObject *ob) {
# define Py_SIZE(ob) Py_SIZE(_PyObject_CAST(ob))
#endif

static inline int _Py_IsImmortal(PyObject *op)
{
#if SIZEOF_VOID_P > 4
return _Py_CAST(PY_INT32_T, op->ob_refcnt) < 0;
#else
return op->ob_refcnt == _Py_IMMORTAL_REFCNT;
#endif
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
}
#define _Py_IsImmortal(op) _Py_IsImmortal(_PyObject_CAST(op))

static inline void _Py_SetImmortal(PyObject *op)
{
if (op) {
op->ob_refcnt = _Py_IMMORTAL_REFCNT;
}
}
#define _Py_SetImmortal(op) _Py_SetImmortal(_PyObject_CAST(op))

static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
return Py_TYPE(ob) == type;
Expand All @@ -157,6 +231,9 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {


static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
if (_Py_IsImmortal(ob)) {
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
return;
}
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
ob->ob_refcnt = refcnt;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
Expand Down Expand Up @@ -518,10 +595,26 @@ static inline void Py_INCREF(PyObject *op)
_Py_INCREF_STAT_INC();
// Non-limited C API and limited C API for Python 3.9 and older access
// directly PyObject.ob_refcnt.
#if SIZEOF_VOID_P > 4
// Portable saturated add, branching on the carry flag and set low bits
PY_UINT32_T new_refcnt;
PY_UINT32_T cur_refcnt = _Py_CAST(PY_UINT32_T, op->ob_refcnt);
new_refcnt = cur_refcnt + 1;
if (new_refcnt < cur_refcnt) {
return;
}
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
memcpy(&op->ob_refcnt, &new_refcnt, sizeof(new_refcnt));
#else
// Explicitly check immortality against the immortal value
if (_Py_IsImmortal(op)) {
return;
}
op->ob_refcnt++;
#endif
_Py_INCREF_STAT_INC();
#ifdef Py_REF_DEBUG
_Py_RefTotal++;
#endif
op->ob_refcnt++;
#endif
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
Expand All @@ -538,6 +631,9 @@ static inline void Py_DECREF(PyObject *op) {
#elif defined(Py_REF_DEBUG)
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
{
if (_Py_IsImmortal(op)) {
return;
}
_Py_DECREF_STAT_INC();
_Py_RefTotal--;
if (--op->ob_refcnt != 0) {
Expand All @@ -554,9 +650,12 @@ static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
#else
static inline void Py_DECREF(PyObject *op)
{
_Py_DECREF_STAT_INC();
// Non-limited C API and limited C API for Python 3.9 and older access
// directly PyObject.ob_refcnt.
if (_Py_IsImmortal(op)) {
return;
}
_Py_DECREF_STAT_INC();
if (--op->ob_refcnt == 0) {
_Py_Dealloc(op);
}
Expand Down Expand Up @@ -703,7 +802,7 @@ PyAPI_FUNC(int) Py_IsNone(PyObject *x);
#define Py_IsNone(x) Py_Is((x), Py_None)

/* Macro for returning Py_None from a function */
#define Py_RETURN_NONE return Py_NewRef(Py_None)
#define Py_RETURN_NONE return Py_None

/*
Py_NotImplemented is a singleton used to signal that an operation is
Expand All @@ -713,7 +812,7 @@ PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */
#define Py_NotImplemented (&_Py_NotImplementedStruct)

/* Macro for returning Py_NotImplemented from a function */
#define Py_RETURN_NOTIMPLEMENTED return Py_NewRef(Py_NotImplemented)
#define Py_RETURN_NOTIMPLEMENTED return Py_NotImplemented

/* Rich comparison opcodes */
#define Py_LT 0
Expand Down
Loading