Skip to content
Merged
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
21 changes: 20 additions & 1 deletion Lib/test/test_type_aliases.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pickle
import types
import unittest
from test.support import check_syntax_error, run_code
import weakref
from test.support import check_syntax_error, gc_collect, run_code
from test.typinganndata import mod_generics_cache

from typing import (
Expand Down Expand Up @@ -131,6 +132,24 @@ def more_generic[T, *Ts, **P]():
T, Ts, P = more_generic.__type_params__
self.assertEqual(alias.__value__, tuple[Callable[P, tuple[T, *Ts]], Callable[P2, tuple[T2, *Ts2]]])

def test_reentrant_value_does_not_leak(self):
class Value:
pass

refs = []

def compute():
value = Value()
refs.append(weakref.ref(value))
if len(refs) == 1:
Alias.__value__
return value

type Alias = compute()
self.assertIs(Alias.__value__, refs[0]())
gc_collect()
self.assertIsNone(refs[1]())

def test_subscripting(self):
type NonGeneric = int
type Generic[A] = dict[A, A]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a reference leak when the value of a type alias is evaluated
recursively or concurrently.
15 changes: 12 additions & 3 deletions Objects/typevarobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1899,14 +1899,23 @@ typealias_dealloc(PyObject *self)
static PyObject *
typealias_get_value(typealiasobject *ta)
{
if (ta->value != NULL) {
return Py_NewRef(ta->value);
PyObject *result;
Py_BEGIN_CRITICAL_SECTION(ta);
result = Py_XNewRef(ta->value);
Py_END_CRITICAL_SECTION();
if (result != NULL) {
return result;
}
PyObject *result = PyObject_CallNoArgs(ta->compute_value);
result = PyObject_CallNoArgs(ta->compute_value);
if (result == NULL) {
return NULL;
}
PyObject *old_value;
Py_BEGIN_CRITICAL_SECTION(ta);
old_value = ta->value;
ta->value = Py_NewRef(result);
Py_END_CRITICAL_SECTION();
Py_XDECREF(old_value);
return result;
}

Expand Down
Loading