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
4 changes: 4 additions & 0 deletions mypyc/irbuild/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,9 @@ def set_immortal_if_free_threaded(self, v: Value, line: int) -> None:
"""Make an object immortal on free-threaded builds (to avoid contention)."""
self.builder.set_immortal_if_free_threaded(v, line)

def set_immortal_if_py312plus(self, v: Value, line: int) -> None:
self.builder.set_immortal_if_py312plus(v, line)

# Helpers for IR building

def add_to_non_ext_dict(
Expand Down Expand Up @@ -572,6 +575,7 @@ def init_final_static(
coerced = self.coerce(rvalue_reg, type_override or self.node_type(lvalue), lvalue.line)
self.final_names.append((name, coerced.type))
self.add(InitStatic(coerced, name, self.module_name))
self.set_immortal_if_py312plus(coerced, lvalue.line)

def load_final_static(
self, fullname: str, typ: RType, line: int, error_name: str | None = None
Expand Down
3 changes: 1 addition & 2 deletions mypyc/irbuild/classdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,7 @@ def finalize(self, ir: ClassIR) -> None:
non_ext_class = load_non_ext_class(self.builder, ir, self.non_ext, self.cdef.line)
non_ext_class = load_decorated_class(self.builder, self.cdef, non_ext_class)

# Try to avoid contention when using free threading.
self.builder.set_immortal_if_free_threaded(non_ext_class, self.cdef.line)
self.builder.set_immortal_if_py312plus(non_ext_class, self.cdef.line)

# Save the decorated class
self.builder.add(
Expand Down
5 changes: 5 additions & 0 deletions mypyc/irbuild/ll_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2492,6 +2492,11 @@ def set_immortal_if_free_threaded(self, v: Value, line: int) -> None:
if IS_FREE_THREADED and sys.version_info >= (3, 14):
self.primitive_op(set_immortal_op, [v], line)

def set_immortal_if_py312plus(self, v: Value, line: int) -> None:
"""Emit IR to mark an object as immortal in Python 3.12+ (calls CPy_SetImmortal)."""
if sys.version_info >= (3, 12):
self.primitive_op(set_immortal_op, [v], line)

# Internal helpers

def decompose_union_helper(
Expand Down
15 changes: 12 additions & 3 deletions mypyc/lib-rt/misc_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1129,12 +1129,21 @@ void CPy_SetTypeAliasTypeComputeFunction(PyObject *alias, PyObject *compute_valu

#endif

#if CPY_3_14_FEATURES

#include "internal/pycore_object.h"
#if CPY_3_12_FEATURES
#if CPY_3_14_FEATURES
#include "internal/pycore_object.h"
#else
// The immortal refcount value is -1 (see PEP 683 and CPython source)
// This is safe for statically allocated objects and matches CPython's logic.
#define IMMORTAL_REFCNT ((Py_ssize_t)-1)
#endif

void CPy_SetImmortal(PyObject *obj) {
#if CPY_3_14_FEATURES
_Py_SetImmortal(obj);
#elif CPY_3_12_FEATURES
Py_SET_REFCNT(obj, IMMORTAL_REFCNT);
#endif
}

#endif
4 changes: 2 additions & 2 deletions mypyc/primitives/misc_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,9 @@
# threading, since this eliminates contention from concurrent reference count
# updates.
#
# Needs at least Python 3.14.
# Available for Python 3.12+ (uses public API in 3.14+, internal logic in 3.12/3.13).
set_immortal_op = custom_primitive_op(
name="set_immmortal",
name="set_immortal",
c_function_name="CPy_SetImmortal",
arg_types=[object_rprimitive],
return_type=void_rtype,
Expand Down
Loading