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 58 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/boolobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,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
2 changes: 1 addition & 1 deletion Include/internal/pycore_global_objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extern "C" {

#define _PyObject_IMMORTAL_INIT(type) \
{ \
.ob_refcnt = 999999999, \
.ob_refcnt = _Py_IMMORTAL_REFCNT, \
.ob_type = type, \
}
#define _PyVarObject_IMMORTAL_INIT(type, size) \
Expand Down
12 changes: 7 additions & 5 deletions Include/moduleobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ typedef struct PyModuleDef_Base {
PyObject* m_copy;
} PyModuleDef_Base;

#define PyModuleDef_HEAD_INIT { \
PyObject_HEAD_INIT(NULL) \
NULL, /* m_init */ \
0, /* m_index */ \
NULL, /* m_copy */ \
// TODO(eduardo-elizondo): This is only used to simplify the review of GH-19474
// Rather than changing this API, we'll introduce PyModuleDef_HEAD_IMMORTAL_INIT
#define PyModuleDef_HEAD_INIT { \
methane marked this conversation as resolved.
Show resolved Hide resolved
PyObject_HEAD_IMMORTAL_INIT(NULL) \
NULL, /* m_init */ \
0, /* m_index */ \
NULL, /* m_copy */ \
}

struct PyModuleDef_Slot;
Expand Down
47 changes: 45 additions & 2 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,34 @@ typedef struct _typeobject PyTypeObject;
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD PyObject ob_base;

/*
Immortalization:

This marks the reference count bit that will be used to define immortality.
The GC bit-shifts refcounts left by two, and after that shift it still needs
to be larger than zero, so it's placed after the first three high bits.

For backwards compatibility the actual reference count of an immortal instance
is set to higher than just the immortal bit. This will ensure that the immortal
bit will remain active, even with extensions compiled without the updated checks
in Py_INCREF and Py_DECREF. This can be safely changed to a smaller value if
additional bits are needed in the reference count field.
*/
#define _Py_IMMORTAL_BIT_OFFSET (8 * sizeof(Py_ssize_t) - 4)
#define _Py_IMMORTAL_BIT (1LL << _Py_IMMORTAL_BIT_OFFSET)
ericsnowcurrently marked this conversation as resolved.
Show resolved Hide resolved
#define _Py_IMMORTAL_REFCNT (_Py_IMMORTAL_BIT + (_Py_IMMORTAL_BIT / 2))

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

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

// TODO(eduardo-elizondo): This is only used to simplify the review of GH-19474
// Rather than changing this API, we'll introduce PyVarObject_HEAD_IMMORTAL_INIT
#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 @@ -146,6 +168,18 @@ static inline Py_ssize_t _Py_SIZE(const PyVarObject *ob) {
#define Py_SIZE(ob) _Py_SIZE(_PyVarObject_CAST_CONST(ob))


static inline int _Py_IsImmortal(PyObject *op)
{
return (op->ob_refcnt & _Py_IMMORTAL_BIT) != 0;
}

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

static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
// bpo-44378: Don't use Py_TYPE() since Py_TYPE() requires a non-const
// object.
Expand All @@ -155,6 +189,9 @@ static inline int _Py_IS_TYPE(const PyObject *ob, const 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;
}
#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt)
Expand Down Expand Up @@ -483,6 +520,9 @@ static inline void _Py_INCREF(PyObject *op)
#else
// Non-limited C API and limited C API for Python 3.9 and older access
// directly PyObject.ob_refcnt.
if (_Py_IsImmortal(op)) {
return;
}
#ifdef Py_REF_DEBUG
_Py_RefTotal++;
#endif
Expand All @@ -503,6 +543,9 @@ static inline void _Py_DECREF(
#else
// Non-limited C API and limited C API for Python 3.9 and older access
// directly PyObject.ob_refcnt.
if (_Py_IsImmortal(op)) {
return;
}
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
#endif
Expand Down Expand Up @@ -627,7 +670,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 Down
3 changes: 2 additions & 1 deletion Lib/ctypes/test/test_python_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def test_PyLong_Long(self):
pythonapi.PyLong_AsLong.restype = c_long

res = pythonapi.PyLong_AsLong(42)
self.assertEqual(grc(res), ref42 + 1)
# Small int refcnts don't change
self.assertEqual(grc(res), ref42)
del res
self.assertEqual(grc(42), ref42)

Expand Down
25 changes: 24 additions & 1 deletion Lib/test/test_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from types import AsyncGeneratorType, FunctionType
from operator import neg
from test import support
from test.support import (swap_attr, maybe_get_event_loop_policy)
from test.support import (cpython_only, swap_attr, maybe_get_event_loop_policy)
from test.support.os_helper import (EnvironmentVarGuard, TESTFN, unlink)
from test.support.script_helper import assert_python_ok
from test.support.warnings_helper import check_warnings
Expand Down Expand Up @@ -2190,6 +2190,29 @@ def __del__(self):
self.assertEqual(["before", "after"], out.decode().splitlines())


@cpython_only
class ImmortalTests(unittest.TestCase):
eduardo-elizondo marked this conversation as resolved.
Show resolved Hide resolved
def test_immortal(self):
none_refcount = sys.getrefcount(None)
true_refcount = sys.getrefcount(True)
false_refcount = sys.getrefcount(False)
smallint_refcount = sys.getrefcount(100)

# Assert that all of these immortal instances have large ref counts
self.assertGreater(none_refcount, 1e8)
self.assertGreater(true_refcount, 1e8)
self.assertGreater(false_refcount, 1e8)
self.assertGreater(smallint_refcount, 1e8)

# Confirm that the refcount doesn't change even with a new ref to them
l = [None, True, False, 100]
self.assertEqual(sys.getrefcount(None), none_refcount)
self.assertEqual(sys.getrefcount(True), true_refcount)
self.assertEqual(sys.getrefcount(False), false_refcount)
self.assertEqual(sys.getrefcount(100), smallint_refcount)



class TestType(unittest.TestCase):
def test_new_type(self):
A = type('A', (), {})
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_regrtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ class RefLeakTest(unittest.TestCase):
def test_leak(self):
GLOBAL_LIST.append(object())
""")
self.check_leak(code, 'references')
self.check_leak(code, 'memory blocks')

@unittest.skipUnless(Py_DEBUG, 'need a debug build')
def test_huntrleaks_fd_leak(self):
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,8 @@ def test_refcount(self):
self.assertRaises(TypeError, sys.getrefcount)
c = sys.getrefcount(None)
n = None
self.assertEqual(sys.getrefcount(None), c+1)
# Singleton refcnts don't change
self.assertEqual(sys.getrefcount(None), c)
del n
self.assertEqual(sys.getrefcount(None), c)
if hasattr(sys, "gettotalrefcount"):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This introduces Immortal Instances which allows objects to bypass reference
counting and remain alive throughout the execution of the runtime
4 changes: 1 addition & 3 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ static PyObject *
get_small_int(sdigit ival)
{
assert(IS_SMALL_INT(ival));
PyObject *v = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + ival];
Py_INCREF(v);
return v;
return (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + ival];
}

static PyLongObject *
Expand Down
7 changes: 5 additions & 2 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1718,7 +1718,8 @@ PyTypeObject _PyNone_Type = {

PyObject _Py_NoneStruct = {
_PyObject_EXTRA_INIT
1, &_PyNone_Type
_Py_IMMORTAL_REFCNT,
&_PyNone_Type
};

/* NotImplemented is an object that can be used to signal that an
Expand Down Expand Up @@ -1952,7 +1953,9 @@ _Py_NewReference(PyObject *op)
#ifdef Py_REF_DEBUG
_Py_RefTotal++;
#endif
Py_SET_REFCNT(op, 1);
/* Do not use Py_SET_REFCNT to skip the Immortal Instance check. This
* API guarantees that an instance will always be set to a refcnt of 1 */
op->ob_refcnt = 1;
#ifdef Py_TRACE_REFS
_Py_AddToAllObjects(op, 1);
#endif
Expand Down