From 269005e78429d6ee518643daa633e9aa7e8b9c33 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 3 Oct 2023 18:36:50 -0600 Subject: [PATCH] gh-110300: Fix Refleaks in test_interpreters and test__xxinterpchannels (gh-110318) --- Lib/test/support/interpreters.py | 3 ++- Lib/test/test_interpreters.py | 26 +++++++++++++++++++------- Modules/_xxinterpchannelsmodule.c | 11 +++++++++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Lib/test/support/interpreters.py b/Lib/test/support/interpreters.py index d2beba31e80283..3b501614bc4b4d 100644 --- a/Lib/test/support/interpreters.py +++ b/Lib/test/support/interpreters.py @@ -215,4 +215,5 @@ def close(self): _channels.close(self._id, send=True) -_channels._register_end_types(SendChannel, RecvChannel) +# XXX This is causing leaks (gh-110318): +#_channels._register_end_types(SendChannel, RecvChannel) diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index ffdd8a12769397..f2ef172d26dfc8 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -68,6 +68,17 @@ def run(): class TestBase(unittest.TestCase): + def pipe(self): + def ensure_closed(fd): + try: + os.close(fd) + except OSError: + pass + r, w = os.pipe() + self.addCleanup(lambda: ensure_closed(r)) + self.addCleanup(lambda: ensure_closed(w)) + return r, w + def tearDown(self): clean_up_interpreters() @@ -262,7 +273,7 @@ def test_subinterpreter(self): self.assertFalse(interp.is_running()) def test_finished(self): - r, w = os.pipe() + r, w = self.pipe() interp = interpreters.create() interp.run(f"""if True: import os @@ -299,8 +310,8 @@ def test_bad_id(self): interp.is_running() def test_with_only_background_threads(self): - r_interp, w_interp = os.pipe() - r_thread, w_thread = os.pipe() + r_interp, w_interp = self.pipe() + r_thread, w_thread = self.pipe() DONE = b'D' FINISHED = b'F' @@ -425,8 +436,8 @@ def test_still_running(self): self.assertTrue(interp.is_running()) def test_subthreads_still_running(self): - r_interp, w_interp = os.pipe() - r_thread, w_thread = os.pipe() + r_interp, w_interp = self.pipe() + r_thread, w_thread = self.pipe() FINISHED = b'F' @@ -532,8 +543,8 @@ def test_bytes_for_script(self): interp.run(b'print("spam")') def test_with_background_threads_still_running(self): - r_interp, w_interp = os.pipe() - r_thread, w_thread = os.pipe() + r_interp, w_interp = self.pipe() + r_thread, w_thread = self.pipe() RAN = b'R' DONE = b'D' @@ -822,6 +833,7 @@ def test_list_all(self): after = set(interpreters.list_all_channels()) self.assertEqual(after, created) + @unittest.expectedFailure # See gh-110318: def test_shareable(self): rch, sch = interpreters.create_channel() diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_xxinterpchannelsmodule.c index d5be76f1f0e38e..d762f449c407a3 100644 --- a/Modules/_xxinterpchannelsmodule.c +++ b/Modules/_xxinterpchannelsmodule.c @@ -1992,6 +1992,7 @@ _get_current_channel_end_type(int end) return NULL; } } + Py_DECREF(highlevel); if (end == CHANNEL_SEND) { cls = state->send_channel_type; } @@ -2012,6 +2013,7 @@ _channel_end_from_xid(_PyCrossInterpreterData *data) } PyTypeObject *cls = _get_current_channel_end_type(cid->end); if (cls == NULL) { + Py_DECREF(cid); return NULL; } PyObject *obj = PyObject_CallOneArg((PyObject *)cls, (PyObject *)cid); @@ -2027,7 +2029,9 @@ _channel_end_shared(PyThreadState *tstate, PyObject *obj, if (cidobj == NULL) { return -1; } - if (_channelid_shared(tstate, cidobj, data) < 0) { + int res = _channelid_shared(tstate, cidobj, data); + Py_DECREF(cidobj); + if (res < 0) { return -1; } data->new_object = _channel_end_from_xid; @@ -2464,7 +2468,10 @@ channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } PyTypeObject *cls = state->ChannelIDType; - assert(get_module_from_owned_type(cls) == self); + + PyObject *mod = get_module_from_owned_type(cls); + assert(mod == self); + Py_DECREF(mod); return _channelid_new(self, cls, args, kwds); }