From ec5aa60f9b025915510871c7bbbeb4ca56b885ae Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Tue, 12 Nov 2019 11:24:05 +0000 Subject: [PATCH 01/33] Attempt at implementing channel_list_interpreters() --- Modules/_xxsubinterpretersmodule.c | 87 ++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 4a6ffdd3266783..19aca1ad70f7df 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -627,6 +627,30 @@ _channelends_associate(_channelends *ends, int64_t interp, int send) return 0; } +static int64_t * +_channelends_list_interpreters(_channelends *ends, int64_t *count, int send) +{ + int64_t *ids = NULL; + int64_t numopen = send ? ends->numsendopen : ends->numrecvopen; + if (numopen >= PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_RuntimeError, + "too many interpreters using the channel"); + goto done; + } + ids = PyMem_NEW(int64_t, (Py_ssize_t)numopen); + if (ids == NULL) { + goto done; + } + _channelend *ref = send ? ends->send : ends->recv; + for (int64_t i=0; ref != NULL; ref = ref->next, i++) { + ids[i] = ref->interp; + } + *count = numopen; + +done: + return ids; +} + static int _channelends_is_open(_channelends *ends) { @@ -2323,6 +2347,67 @@ PyDoc_STRVAR(channel_list_all_doc, \n\ Return the list of all IDs for active channels."); + +static PyObject * +channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"cid", NULL}; + int64_t cid; + _PyChannelState *chan; + int send = 1; /* Send or receive ends? */ + int64_t count; /* Number of interpreters to return */ + int64_t *ids; /* Array of interpreter IDs to return */ + PyObject *id_obj; + PyObject *ret; /* Python list of interpreter IDs */ + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_list_interps", + kwlist, channel_id_converter, &cid)) { + return NULL; + } + + chan = _channels_lookup(&_globals.channels, cid, NULL); + ids = _channelends_list_interpreters(chan->ends, &count, send); + + ret = PyList_New((Py_ssize_t)count); + if (ret == NULL) { + goto finally; + } + + for (int64_t i=0; i < count; i++) { + PyInterpreterState *interp = PyInterpreterState_Head(); + while (interp != NULL) { + id_obj = _PyInterpreterState_GetIDObject(interp); + if (id_obj == NULL) { + goto except; + } + if (ids[i] == PyInterpreterState_GetID(interp)) { + int rc = PyList_SetItem(ret, i, id_obj); + Py_DECREF(id_obj); + if (rc < 0) { + goto except; + } + break; + } + interp = PyInterpreterState_Next(interp); + } + } + +except: + Py_XDECREF(ret); + ret = NULL; + +finally: + PyMem_Free(ids); + return ret; +} + +PyDoc_STRVAR(channel_list_interpreters_doc, +"channel_list_interpreters(cid) -> [id]\n\ +\n\ +Return the list of all interpreter IDs associated with the channel\n\ +XXX in the send direction."); + + static PyObject * channel_send(PyObject *self, PyObject *args, PyObject *kwds) { @@ -2476,6 +2561,8 @@ static PyMethodDef module_functions[] = { METH_VARARGS | METH_KEYWORDS, channel_destroy_doc}, {"channel_list_all", channel_list_all, METH_NOARGS, channel_list_all_doc}, + {"channel_list_interpreters", (PyCFunction)(void(*)(void))channel_list_interpreters, + METH_VARARGS | METH_KEYWORDS, channel_list_interpreters_doc}, {"channel_send", (PyCFunction)(void(*)(void))channel_send, METH_VARARGS | METH_KEYWORDS, channel_send_doc}, {"channel_recv", (PyCFunction)(void(*)(void))channel_recv, From ee781339735d021ae30feb8995523b5eb5bc8439 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Tue, 12 Nov 2019 16:56:12 +0000 Subject: [PATCH 02/33] Fix some error cases --- Modules/_xxsubinterpretersmodule.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 19aca1ad70f7df..b265c7d87d2eaa 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2356,21 +2356,25 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) _PyChannelState *chan; int send = 1; /* Send or receive ends? */ int64_t count; /* Number of interpreters to return */ - int64_t *ids; /* Array of interpreter IDs to return */ - PyObject *id_obj; - PyObject *ret; /* Python list of interpreter IDs */ + int64_t *ids = NULL; /* Array of interpreter IDs to return */ + PyObject *id_obj = NULL; + PyObject *ret = NULL; /* Python list of interpreter IDs */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_list_interps", + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_list_interpreters", kwlist, channel_id_converter, &cid)) { return NULL; } chan = _channels_lookup(&_globals.channels, cid, NULL); + if (chan == NULL) { + goto except; + } + ids = _channelends_list_interpreters(chan->ends, &count, send); ret = PyList_New((Py_ssize_t)count); if (ret == NULL) { - goto finally; + goto except; } for (int64_t i=0; i < count; i++) { @@ -2392,6 +2396,8 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) } } + goto finally; + except: Py_XDECREF(ret); ret = NULL; From 433a5b0aef9fdbf873d350a741a21fbf60f4f1ad Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Tue, 12 Nov 2019 17:46:43 +0000 Subject: [PATCH 03/33] Fix issue with decreasing list items' references --- Modules/_xxsubinterpretersmodule.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index b265c7d87d2eaa..7eb67cec93baeb 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2382,14 +2382,11 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) while (interp != NULL) { id_obj = _PyInterpreterState_GetIDObject(interp); if (id_obj == NULL) { + Py_DECREF(id_obj); goto except; } if (ids[i] == PyInterpreterState_GetID(interp)) { - int rc = PyList_SetItem(ret, i, id_obj); - Py_DECREF(id_obj); - if (rc < 0) { - goto except; - } + PyList_SET_ITEM(ret, i, id_obj); break; } interp = PyInterpreterState_Next(interp); From 64c1661854ea7488abaa14adfd2125ccb71fff18 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Wed, 20 Nov 2019 11:32:45 +0000 Subject: [PATCH 04/33] Fix issue with reference to interpreter --- Modules/_xxsubinterpretersmodule.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 7eb67cec93baeb..eb65e10479c368 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2388,6 +2388,8 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) if (ids[i] == PyInterpreterState_GetID(interp)) { PyList_SET_ITEM(ret, i, id_obj); break; + } else { + Py_DECREF(id_obj); } interp = PyInterpreterState_Next(interp); } From 2c5c6df01629e304cef490814a05938c57c73b83 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Wed, 20 Nov 2019 11:34:07 +0000 Subject: [PATCH 05/33] Add ability to list interpreters on send or receive channels --- Modules/_xxsubinterpretersmodule.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index eb65e10479c368..c1d6f3f7d17394 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2354,17 +2354,23 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) static char *kwlist[] = {"cid", NULL}; int64_t cid; _PyChannelState *chan; - int send = 1; /* Send or receive ends? */ - int64_t count; /* Number of interpreters to return */ - int64_t *ids = NULL; /* Array of interpreter IDs to return */ - PyObject *id_obj = NULL; + int send = 0; /* Send end? */ + int recv = 0; /* Receive end? */ + int64_t *ids = NULL; /* Array of interpreter IDs */ + int64_t count = 0; /* Number of interpreters to return */ PyObject *ret = NULL; /* Python list of interpreter IDs */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_list_interpreters", - kwlist, channel_id_converter, &cid)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|$pp:channel_list_interpreters", + kwlist, channel_id_converter, &cid, &send, &recv)) { return NULL; } + if ((send && recv) || (!send && !recv)) { + PyErr_SetString(PyExc_RuntimeError, + "Can only list interpreters for a single end of a channel"); + goto except; + } + chan = _channels_lookup(&_globals.channels, cid, NULL); if (chan == NULL) { goto except; @@ -2380,7 +2386,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) for (int64_t i=0; i < count; i++) { PyInterpreterState *interp = PyInterpreterState_Head(); while (interp != NULL) { - id_obj = _PyInterpreterState_GetIDObject(interp); + PyObject *id_obj = _PyInterpreterState_GetIDObject(interp); if (id_obj == NULL) { Py_DECREF(id_obj); goto except; @@ -2407,7 +2413,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) } PyDoc_STRVAR(channel_list_interpreters_doc, -"channel_list_interpreters(cid) -> [id]\n\ +"channel_list_interpreters(cid, *, send=False, recv=False) -> [id]\n\ \n\ Return the list of all interpreter IDs associated with the channel\n\ XXX in the send direction."); From 7f439fa8743d580f722103a34096ddd3c4df353f Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Wed, 20 Nov 2019 11:45:41 +0000 Subject: [PATCH 06/33] Fix arg parsing in C code --- Modules/_xxsubinterpretersmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index c1d6f3f7d17394..b5c9af0bf93f92 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2351,7 +2351,7 @@ Return the list of all IDs for active channels."); static PyObject * channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"cid", NULL}; + static char *kwlist[] = {"cid", "send", "recv", NULL}; int64_t cid; _PyChannelState *chan; int send = 0; /* Send end? */ From d86ae971377dcae1e0bfffabd165c54e8a620f43 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Wed, 20 Nov 2019 11:58:49 +0000 Subject: [PATCH 07/33] Minor changes --- Modules/_xxsubinterpretersmodule.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index b5c9af0bf93f92..ca227b5760880e 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2360,14 +2360,15 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) int64_t count = 0; /* Number of interpreters to return */ PyObject *ret = NULL; /* Python list of interpreter IDs */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|$pp:channel_list_interpreters", - kwlist, channel_id_converter, &cid, &send, &recv)) { + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "O&|$pp:channel_list_interpreters", + kwlist, channel_id_converter, &cid, &send, &recv)) { return NULL; } if ((send && recv) || (!send && !recv)) { - PyErr_SetString(PyExc_RuntimeError, - "Can only list interpreters for a single end of a channel"); + PyErr_SetString(PyExc_ValueError, + "Specify exactly one of send or recv"); goto except; } From c529c8814085200494224fd4c0a9039e84d07cef Mon Sep 17 00:00:00 2001 From: Benjamin Edwards Date: Thu, 21 Nov 2019 02:58:04 -0800 Subject: [PATCH 08/33] Add channel_list_interpreters() tests --- Lib/test/test__xxsubinterpreters.py | 75 ++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 207b5db5d8fb9b..e5494212d4eada 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1202,6 +1202,58 @@ def test_ids_global(self): cid2 = int(out.strip()) self.assertEqual(cid2, int(cid1) + 1) + + def _assert_interpreters_returned_are(self, cid, send, recv): + ilist = set(interpreters.channel_list_interpreters(cid, send=True)) + self.assertEqual(ilist, set(send)) + ilist = set(interpreters.channel_list_interpreters(cid, recv=True)) + self.assertEqual(ilist, set(recv)) + + def test_channel_list_interpreters_empty(self): + # Test for channel with no associated interpreters. + cid = interpreters.channel_create() + self._assert_interpreters_returned_are(cid, [], []) + + def test_channel_list_interpreters_mainline(self): + interp0 = interpreters.get_main() + cid = interpreters.channel_create() + interpreters.channel_send(cid, "send") + # Test for a channel that has one end associated to an interpreter. + self._assert_interpreters_returned_are( + cid, [interp0], []) + + interp1 = interpreters.create() + _run_output(interp1, dedent(f""" + import _interpreters + obj = _interpreters.channel_recv({cid}) + """)) + # Test for channel that has boths ends associated to an interpreter. + self._assert_interpreters_returned_are( + cid, [interp0], [interp1]) + + def test_channel_list_interpreters_multiple(self): + # Test for channel with both ends associated to many interpreters. + interp0 = interpreters.get_main() + interp1 = interpreters.create() + interp2 = interpreters.create() + interp3 = interpreters.create() + cid = interpreters.channel_create() + + interpreters.channel_send(cid, "send") + _run_output(interp1, dedent(f""" + import _interpreters + obj = _interpreters.channel_send({cid}, "send") + """)) + _run_output(interp2, dedent(f""" + import _interpreters + obj = _interpreters.channel_recv({cid}) + """)) + _run_output(interp3, dedent(f""" + import _interpreters + obj = _interpreters.channel_recv({cid}) + """)) + self._assert_interpreters_returned_are( + cid, [interp0, interp1], [interp3, interp2]) #################### @@ -1514,7 +1566,28 @@ def test_close_used_multiple_times_by_single_user(self): interpreters.channel_send(cid, b'eggs') with self.assertRaises(interpreters.ChannelClosedError): interpreters.channel_recv(cid) - + + def test_channel_list_interpreters_invalid_channel(self): + cid = interpreters.channel_create() + # Test for invalid channel ID. + with self.assertRaises(interpreters.ChannelNotFoundError): + interpreters.channel_list_interpreters(1000, send=True) + + interpreters.channel_close(cid) + # Test for a channel that has been closed. + with self.assertRaises(interpreters.ChannelClosedError): + interpreters.channel_list_interpreters(cid, send=True) + + def test_channel_list_interpreters_invalid_args(self): + # Tests for invalid arguments passed to the API. + cid = interpreters.channel_create() + + with self.assertRaises(ValueError): + interpreters.channel_list_interpreters(cid) + + with self.assertRaises(ValueError): + interpreters.channel_list_interpreters(cid, send=True, recv=True) + class ChannelReleaseTests(TestBase): From 168ebf36028ade4e7db6588982453905abb6c0d0 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 11:58:49 +0000 Subject: [PATCH 09/33] Fix docstring --- Modules/_xxsubinterpretersmodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index ca227b5760880e..0c28946c57ff98 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2417,7 +2417,8 @@ PyDoc_STRVAR(channel_list_interpreters_doc, "channel_list_interpreters(cid, *, send=False, recv=False) -> [id]\n\ \n\ Return the list of all interpreter IDs associated with the channel\n\ -XXX in the send direction."); +Exactly one of 'send' or 'recv' should be True, corresponding to the end of\n\ +the channel to list interpreters for."); static PyObject * From 2a7b508e5ba107dd0c77e55587e137bf0f9a1e22 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 12:12:43 +0000 Subject: [PATCH 10/33] Improve naming in tests --- Lib/test/test__xxsubinterpreters.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index e5494212d4eada..d346f21aae976a 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1203,23 +1203,23 @@ def test_ids_global(self): self.assertEqual(cid2, int(cid1) + 1) - def _assert_interpreters_returned_are(self, cid, send, recv): - ilist = set(interpreters.channel_list_interpreters(cid, send=True)) - self.assertEqual(ilist, set(send)) - ilist = set(interpreters.channel_list_interpreters(cid, recv=True)) - self.assertEqual(ilist, set(recv)) + def _assert_interpreters_returned(self, cid, send, recv): + actual = set(interpreters.channel_list_interpreters(cid, send=True)) + self.assertEqual(actual, set(send)) + actual = set(interpreters.channel_list_interpreters(cid, recv=True)) + self.assertEqual(actual, set(recv)) def test_channel_list_interpreters_empty(self): # Test for channel with no associated interpreters. cid = interpreters.channel_create() - self._assert_interpreters_returned_are(cid, [], []) + self._assert_interpreters_returned(cid, [], []) def test_channel_list_interpreters_mainline(self): interp0 = interpreters.get_main() cid = interpreters.channel_create() interpreters.channel_send(cid, "send") # Test for a channel that has one end associated to an interpreter. - self._assert_interpreters_returned_are( + self._assert_interpreters_returned( cid, [interp0], []) interp1 = interpreters.create() @@ -1228,7 +1228,7 @@ def test_channel_list_interpreters_mainline(self): obj = _interpreters.channel_recv({cid}) """)) # Test for channel that has boths ends associated to an interpreter. - self._assert_interpreters_returned_are( + self._assert_interpreters_returned( cid, [interp0], [interp1]) def test_channel_list_interpreters_multiple(self): @@ -1252,7 +1252,7 @@ def test_channel_list_interpreters_multiple(self): import _interpreters obj = _interpreters.channel_recv({cid}) """)) - self._assert_interpreters_returned_are( + self._assert_interpreters_returned( cid, [interp0, interp1], [interp3, interp2]) #################### From 4d6ac6ed5362d0fbb61d355e5e2aefd67e5e6048 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 13:36:30 +0000 Subject: [PATCH 11/33] Add macros for int64_t max etc --- Include/pyport.h | 24 ++++++++++++++++++++++++ Modules/_xxsubinterpretersmodule.c | 11 ++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Include/pyport.h b/Include/pyport.h index 64c73f012e7a71..14b3a2cd335615 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -72,10 +72,34 @@ Used in: Py_SAFE_DOWNCAST #define PY_UINT32_T uint32_t #define PY_UINT64_T uint64_t +#ifdef HAVE_STDINT_H +#include +#define PY_UINT32_T_MIN UINT32_MIN +#define PY_UINT32_T_MAX UINT32_MAX +#define PY_UINT64_T_MIN UINT64_MIN +#define PY_UINT64_T_MAX UINT64_MAX +#else +#define PY_UINT32_T_MIN 0 +#define PY_UINT32_T_MAX ((uint32_t)-1) +#define PY_UINT64_T_MIN 0 +#define PY_UINT64_T_MAX ((uint64_t)-1) +#endif /* Signed variants of the above */ #define PY_INT32_T int32_t #define PY_INT64_T int64_t +#ifdef HAVE_STDINT_H +#include +#define PY_INT32_T_MIN INT32_MIN +#define PY_INT32_T_MAX INT32_MAX +#define PY_INT64_T_MIN INT64_MIN +#define PY_INT64_T_MAX INT64_MAX +#else +#define PY_INT32_T_MAX ((int32_t)(((uint32_t)-1)>>1)) +#define PY_INT32_T_MIN (-PY_INT32_T_MAX-1) +#define PY_INT64_T_MAX ((int64_t)(((uint64_t)-1)>>1)) +#define PY_INT64_T_MIN (-PY_INT64_T_MAX-1) +#endif /* If PYLONG_BITS_IN_DIGIT is not defined then we'll use 30-bit digits if all the necessary integer types are available, and we're on a 64-bit platform diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 0c28946c57ff98..0333f41d2f8ed7 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -631,8 +631,13 @@ static int64_t * _channelends_list_interpreters(_channelends *ends, int64_t *count, int send) { int64_t *ids = NULL; - int64_t numopen = send ? ends->numsendopen : ends->numrecvopen; - if (numopen >= PY_SSIZE_T_MAX) { + int64_t numopen; + + assert(ends != NULL); + assert(count != NULL); + numopen = send ? ends->numsendopen : ends->numrecvopen; + + if (numopen >= PY_INT64_T_MAX) { PyErr_SetString(PyExc_RuntimeError, "too many interpreters using the channel"); goto done; @@ -1202,7 +1207,7 @@ _channels_list_all(_channels *channels, int64_t *count) int64_t *cids = NULL; PyThread_acquire_lock(channels->mutex, WAIT_LOCK); int64_t numopen = channels->numopen; - if (numopen >= PY_SSIZE_T_MAX) { + if (numopen >= PY_INT64_T_MAX) { PyErr_SetString(PyExc_RuntimeError, "too many channels open"); goto done; } From a44c2d271d4f526da0b4fd8c2e9611b5a9bc3218 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 14:03:56 +0000 Subject: [PATCH 12/33] Fix error handling --- Modules/_xxsubinterpretersmodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 0333f41d2f8ed7..320f9149d380a4 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -644,6 +644,7 @@ _channelends_list_interpreters(_channelends *ends, int64_t *count, int send) } ids = PyMem_NEW(int64_t, (Py_ssize_t)numopen); if (ids == NULL) { + PyErr_SetNone(PyExc_MemoryError); goto done; } _channelend *ref = send ? ends->send : ends->recv; @@ -2383,6 +2384,9 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) } ids = _channelends_list_interpreters(chan->ends, &count, send); + if (ids == NULL) { + goto except; + } ret = PyList_New((Py_ssize_t)count); if (ret == NULL) { From 215300b3161f9dcfaf331dfc5e49b0201770f35b Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 14:37:16 +0000 Subject: [PATCH 13/33] Expose _PyInterpreterState_LookUpID --- Include/cpython/pystate.h | 2 ++ Modules/_xxsubinterpretersmodule.c | 25 +++++++++++-------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 6c8d2ae041ea54..9d647e957b5616 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -147,6 +147,8 @@ struct _ts { The caller must hold the GIL.*/ PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_Get(void); +PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_LookUpID(PY_INT64_T requested_id); + PyAPI_FUNC(int) _PyState_AddModule(PyObject*, struct PyModuleDef*); PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *); diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 320f9149d380a4..21005448a6fb23 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2394,20 +2394,17 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) } for (int64_t i=0; i < count; i++) { - PyInterpreterState *interp = PyInterpreterState_Head(); - while (interp != NULL) { - PyObject *id_obj = _PyInterpreterState_GetIDObject(interp); - if (id_obj == NULL) { - Py_DECREF(id_obj); - goto except; - } - if (ids[i] == PyInterpreterState_GetID(interp)) { - PyList_SET_ITEM(ret, i, id_obj); - break; - } else { - Py_DECREF(id_obj); - } - interp = PyInterpreterState_Next(interp); + PyInterpreterState *interp = _PyInterpreterState_LookUpID(ids[i]); + PyObject *id_obj = _PyInterpreterState_GetIDObject(interp); + if (id_obj == NULL) { + Py_DECREF(id_obj); + goto except; + } + if (ids[i] == PyInterpreterState_GetID(interp)) { + PyList_SET_ITEM(ret, i, id_obj); + break; + } else { + Py_DECREF(id_obj); } } From 9b124db988f86ed8c34777bec7d7179ca76a51f6 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 15:29:01 +0000 Subject: [PATCH 14/33] Fix bad git rebase (use _xxsubinterpreters module name) --- Lib/test/test__xxsubinterpreters.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index d346f21aae976a..f57f565d60f7b1 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1224,7 +1224,7 @@ def test_channel_list_interpreters_mainline(self): interp1 = interpreters.create() _run_output(interp1, dedent(f""" - import _interpreters + import _xxsubinterpreters as _interpreters obj = _interpreters.channel_recv({cid}) """)) # Test for channel that has boths ends associated to an interpreter. @@ -1241,15 +1241,15 @@ def test_channel_list_interpreters_multiple(self): interpreters.channel_send(cid, "send") _run_output(interp1, dedent(f""" - import _interpreters + import _xxsubinterpreters as _interpreters obj = _interpreters.channel_send({cid}, "send") """)) _run_output(interp2, dedent(f""" - import _interpreters + import _xxsubinterpreters as _interpreters obj = _interpreters.channel_recv({cid}) """)) _run_output(interp3, dedent(f""" - import _interpreters + import _xxsubinterpreters as _interpreters obj = _interpreters.channel_recv({cid}) """)) self._assert_interpreters_returned( From 76621c714256bf2bfd4728a27a9b3335d95ee563 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 15:36:05 +0000 Subject: [PATCH 15/33] Remove decref of NULL --- Modules/_xxsubinterpretersmodule.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 21005448a6fb23..dc58621cca2460 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2397,7 +2397,6 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) PyInterpreterState *interp = _PyInterpreterState_LookUpID(ids[i]); PyObject *id_obj = _PyInterpreterState_GetIDObject(interp); if (id_obj == NULL) { - Py_DECREF(id_obj); goto except; } if (ids[i] == PyInterpreterState_GetID(interp)) { From d4c51bfbd3b8599e36cce32c4abe7ba48ea2873f Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 15:44:41 +0000 Subject: [PATCH 16/33] Fix leftover broken code from nested loop --- Modules/_xxsubinterpretersmodule.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index dc58621cca2460..949b1ceb29f969 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2399,12 +2399,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) if (id_obj == NULL) { goto except; } - if (ids[i] == PyInterpreterState_GetID(interp)) { - PyList_SET_ITEM(ret, i, id_obj); - break; - } else { - Py_DECREF(id_obj); - } + PyList_SET_ITEM(ret, i, id_obj); } goto finally; From f3c2b34bc718f642c86a6391f1d465031ccc601f Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 18:15:39 +0000 Subject: [PATCH 17/33] Remove whitespace from tests --- Lib/test/test__xxsubinterpreters.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index f57f565d60f7b1..de69d19970f1af 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1202,18 +1202,18 @@ def test_ids_global(self): cid2 = int(out.strip()) self.assertEqual(cid2, int(cid1) + 1) - + def _assert_interpreters_returned(self, cid, send, recv): actual = set(interpreters.channel_list_interpreters(cid, send=True)) self.assertEqual(actual, set(send)) actual = set(interpreters.channel_list_interpreters(cid, recv=True)) self.assertEqual(actual, set(recv)) - + def test_channel_list_interpreters_empty(self): # Test for channel with no associated interpreters. cid = interpreters.channel_create() self._assert_interpreters_returned(cid, [], []) - + def test_channel_list_interpreters_mainline(self): interp0 = interpreters.get_main() cid = interpreters.channel_create() @@ -1221,7 +1221,7 @@ def test_channel_list_interpreters_mainline(self): # Test for a channel that has one end associated to an interpreter. self._assert_interpreters_returned( cid, [interp0], []) - + interp1 = interpreters.create() _run_output(interp1, dedent(f""" import _xxsubinterpreters as _interpreters @@ -1230,7 +1230,7 @@ def test_channel_list_interpreters_mainline(self): # Test for channel that has boths ends associated to an interpreter. self._assert_interpreters_returned( cid, [interp0], [interp1]) - + def test_channel_list_interpreters_multiple(self): # Test for channel with both ends associated to many interpreters. interp0 = interpreters.get_main() @@ -1238,7 +1238,7 @@ def test_channel_list_interpreters_multiple(self): interp2 = interpreters.create() interp3 = interpreters.create() cid = interpreters.channel_create() - + interpreters.channel_send(cid, "send") _run_output(interp1, dedent(f""" import _xxsubinterpreters as _interpreters @@ -1566,28 +1566,28 @@ def test_close_used_multiple_times_by_single_user(self): interpreters.channel_send(cid, b'eggs') with self.assertRaises(interpreters.ChannelClosedError): interpreters.channel_recv(cid) - + def test_channel_list_interpreters_invalid_channel(self): - cid = interpreters.channel_create() - # Test for invalid channel ID. + cid = interpreters.channel_create() + # Test for invalid channel ID. with self.assertRaises(interpreters.ChannelNotFoundError): interpreters.channel_list_interpreters(1000, send=True) - + interpreters.channel_close(cid) # Test for a channel that has been closed. with self.assertRaises(interpreters.ChannelClosedError): interpreters.channel_list_interpreters(cid, send=True) - + def test_channel_list_interpreters_invalid_args(self): # Tests for invalid arguments passed to the API. cid = interpreters.channel_create() - + with self.assertRaises(ValueError): interpreters.channel_list_interpreters(cid) - + with self.assertRaises(ValueError): interpreters.channel_list_interpreters(cid, send=True, recv=True) - + class ChannelReleaseTests(TestBase): From af10d1f3bf9c6317d8eb0546f230867ad8495e24 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Thu, 21 Nov 2019 18:19:00 +0000 Subject: [PATCH 18/33] Add to Misc/ACKS --- Misc/ACKS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Misc/ACKS b/Misc/ACKS index 13c6676bace1fb..399a6724cd4990 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -444,6 +444,7 @@ Rodolpho Eckhardt Ulrich Eckhardt David Edelsohn John Edmonds +Benjamin Edwards Grant Edwards Zvi Effron John Ehresman @@ -557,6 +558,7 @@ Lars Marius Garshol Jake Garver Dan Gass Andrew Gaul +Lewis Gaul Matthieu Gautier Stephen M. Gava Xavier de Gaye From ac09b6cdeb0969e231f6e481bd7751894a1b28fc Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Fri, 22 Nov 2019 14:09:16 +0000 Subject: [PATCH 19/33] Remove PY_INT64_T_MAX etc. and use stdint.h --- Include/pyport.h | 24 ------------------------ Modules/_xxsubinterpretersmodule.c | 4 ++-- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/Include/pyport.h b/Include/pyport.h index 14b3a2cd335615..64c73f012e7a71 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -72,34 +72,10 @@ Used in: Py_SAFE_DOWNCAST #define PY_UINT32_T uint32_t #define PY_UINT64_T uint64_t -#ifdef HAVE_STDINT_H -#include -#define PY_UINT32_T_MIN UINT32_MIN -#define PY_UINT32_T_MAX UINT32_MAX -#define PY_UINT64_T_MIN UINT64_MIN -#define PY_UINT64_T_MAX UINT64_MAX -#else -#define PY_UINT32_T_MIN 0 -#define PY_UINT32_T_MAX ((uint32_t)-1) -#define PY_UINT64_T_MIN 0 -#define PY_UINT64_T_MAX ((uint64_t)-1) -#endif /* Signed variants of the above */ #define PY_INT32_T int32_t #define PY_INT64_T int64_t -#ifdef HAVE_STDINT_H -#include -#define PY_INT32_T_MIN INT32_MIN -#define PY_INT32_T_MAX INT32_MAX -#define PY_INT64_T_MIN INT64_MIN -#define PY_INT64_T_MAX INT64_MAX -#else -#define PY_INT32_T_MAX ((int32_t)(((uint32_t)-1)>>1)) -#define PY_INT32_T_MIN (-PY_INT32_T_MAX-1) -#define PY_INT64_T_MAX ((int64_t)(((uint64_t)-1)>>1)) -#define PY_INT64_T_MIN (-PY_INT64_T_MAX-1) -#endif /* If PYLONG_BITS_IN_DIGIT is not defined then we'll use 30-bit digits if all the necessary integer types are available, and we're on a 64-bit platform diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 949b1ceb29f969..9713d39742457b 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -637,7 +637,7 @@ _channelends_list_interpreters(_channelends *ends, int64_t *count, int send) assert(count != NULL); numopen = send ? ends->numsendopen : ends->numrecvopen; - if (numopen >= PY_INT64_T_MAX) { + if (numopen >= INT64_MAX) { PyErr_SetString(PyExc_RuntimeError, "too many interpreters using the channel"); goto done; @@ -1208,7 +1208,7 @@ _channels_list_all(_channels *channels, int64_t *count) int64_t *cids = NULL; PyThread_acquire_lock(channels->mutex, WAIT_LOCK); int64_t numopen = channels->numopen; - if (numopen >= PY_INT64_T_MAX) { + if (numopen >= INT64_MAX) { PyErr_SetString(PyExc_RuntimeError, "too many channels open"); goto done; } From 8e698649558a862bbe423ca44db7983a32325ff4 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Fri, 22 Nov 2019 14:12:15 +0000 Subject: [PATCH 20/33] Remove whitespace --- Modules/_xxsubinterpretersmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 9713d39742457b..f2991c494123f8 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2387,7 +2387,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) if (ids == NULL) { goto except; } - + ret = PyList_New((Py_ssize_t)count); if (ret == NULL) { goto except; From 0546702c41a292565c3cca261945e8f11931ed5d Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2019 14:34:48 +0000 Subject: [PATCH 21/33] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst b/Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst new file mode 100644 index 00000000000000..6b3a356c8cfcf5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst @@ -0,0 +1 @@ +Added the ability to list interpreters associated with channel ends, as required for PEP 554. \ No newline at end of file From ad511ef6cf1f0882448407f8639cc05796fe43b3 Mon Sep 17 00:00:00 2001 From: Lewis G Date: Sat, 23 Nov 2019 22:18:53 +0000 Subject: [PATCH 22/33] Update news entry to remove mention of PEP 554 --- .../Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst b/Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst index 6b3a356c8cfcf5..07a7f5ec22aa10 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2019-11-22-14-34-47.bpo-38880.evcCPa.rst @@ -1 +1 @@ -Added the ability to list interpreters associated with channel ends, as required for PEP 554. \ No newline at end of file +Added the ability to list interpreters associated with channel ends in the internal subinterpreters module. From a1b7c3a681146af342595d1ebfb81a69cebefbe0 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Sat, 23 Nov 2019 22:58:29 +0000 Subject: [PATCH 23/33] Remove unnecessary asserts --- Modules/_xxsubinterpretersmodule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index f2991c494123f8..eab5e53a52420f 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -633,8 +633,6 @@ _channelends_list_interpreters(_channelends *ends, int64_t *count, int send) int64_t *ids = NULL; int64_t numopen; - assert(ends != NULL); - assert(count != NULL); numopen = send ? ends->numsendopen : ends->numrecvopen; if (numopen >= INT64_MAX) { From d9b3e277d406509527e31f308dc04692ec675584 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Sat, 23 Nov 2019 23:01:09 +0000 Subject: [PATCH 24/33] Use single 'send' argument in channel_list_interpreters() API --- Lib/test/test__xxsubinterpreters.py | 8 ++------ Modules/_xxsubinterpretersmodule.c | 24 +++++++++--------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index de69d19970f1af..b4b4f7bc141155 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1206,7 +1206,7 @@ def test_ids_global(self): def _assert_interpreters_returned(self, cid, send, recv): actual = set(interpreters.channel_list_interpreters(cid, send=True)) self.assertEqual(actual, set(send)) - actual = set(interpreters.channel_list_interpreters(cid, recv=True)) + actual = set(interpreters.channel_list_interpreters(cid, send=False)) self.assertEqual(actual, set(recv)) def test_channel_list_interpreters_empty(self): @@ -1581,13 +1581,9 @@ def test_channel_list_interpreters_invalid_channel(self): def test_channel_list_interpreters_invalid_args(self): # Tests for invalid arguments passed to the API. cid = interpreters.channel_create() - - with self.assertRaises(ValueError): + with self.assertRaises(TypeError): interpreters.channel_list_interpreters(cid) - with self.assertRaises(ValueError): - interpreters.channel_list_interpreters(cid, send=True, recv=True) - class ChannelReleaseTests(TestBase): diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index eab5e53a52420f..7b89113bda997e 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2355,27 +2355,20 @@ Return the list of all IDs for active channels."); static PyObject * channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) { - static char *kwlist[] = {"cid", "send", "recv", NULL}; + static char *kwlist[] = {"cid", "send", NULL}; int64_t cid; _PyChannelState *chan; - int send = 0; /* Send end? */ - int recv = 0; /* Receive end? */ + int send = 0; /* Send or receive end? */ int64_t *ids = NULL; /* Array of interpreter IDs */ int64_t count = 0; /* Number of interpreters to return */ PyObject *ret = NULL; /* Python list of interpreter IDs */ if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O&|$pp:channel_list_interpreters", - kwlist, channel_id_converter, &cid, &send, &recv)) { + args, kwds, "O&$p:channel_list_interpreters", + kwlist, channel_id_converter, &cid, &send)) { return NULL; } - if ((send && recv) || (!send && !recv)) { - PyErr_SetString(PyExc_ValueError, - "Specify exactly one of send or recv"); - goto except; - } - chan = _channels_lookup(&_globals.channels, cid, NULL); if (chan == NULL) { goto except; @@ -2412,11 +2405,12 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) } PyDoc_STRVAR(channel_list_interpreters_doc, -"channel_list_interpreters(cid, *, send=False, recv=False) -> [id]\n\ +"channel_list_interpreters(cid, *, send) -> [id]\n\ +\n\ +Return the list of all interpreter IDs associated with an end of the channel.\n\ \n\ -Return the list of all interpreter IDs associated with the channel\n\ -Exactly one of 'send' or 'recv' should be True, corresponding to the end of\n\ -the channel to list interpreters for."); +The 'send' argument should be a boolean indicating whether to use the send or\n\ +receive end."); static PyObject * From f4990b01a541a222a7d8d5dc32cd34240d16e9af Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Sat, 23 Nov 2019 23:07:21 +0000 Subject: [PATCH 25/33] Tidy up _channelends_list_interpreters() function --- Modules/_xxsubinterpretersmodule.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 7b89113bda997e..8d6a45fe5d13f3 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -630,28 +630,21 @@ _channelends_associate(_channelends *ends, int64_t interp, int send) static int64_t * _channelends_list_interpreters(_channelends *ends, int64_t *count, int send) { - int64_t *ids = NULL; - int64_t numopen; - - numopen = send ? ends->numsendopen : ends->numrecvopen; + int64_t numopen = send ? ends->numsendopen : ends->numrecvopen; - if (numopen >= INT64_MAX) { - PyErr_SetString(PyExc_RuntimeError, - "too many interpreters using the channel"); - goto done; - } - ids = PyMem_NEW(int64_t, (Py_ssize_t)numopen); + int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)numopen); if (ids == NULL) { - PyErr_SetNone(PyExc_MemoryError); - goto done; + PyErr_NoMemory(); + return NULL; } + _channelend *ref = send ? ends->send : ends->recv; for (int64_t i=0; ref != NULL; ref = ref->next, i++) { ids[i] = ref->interp; } + *count = numopen; -done: return ids; } From c7dbb04af0fafe3044039bfb73ea47d03ae84e6c Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Sat, 23 Nov 2019 23:30:05 +0000 Subject: [PATCH 26/33] Move variable declarations inline --- Modules/_xxsubinterpretersmodule.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 8d6a45fe5d13f3..739fd49eb99c7e 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2349,12 +2349,9 @@ static PyObject * channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"cid", "send", NULL}; - int64_t cid; - _PyChannelState *chan; + int64_t cid; /* Channel ID */ int send = 0; /* Send or receive end? */ - int64_t *ids = NULL; /* Array of interpreter IDs */ - int64_t count = 0; /* Number of interpreters to return */ - PyObject *ret = NULL; /* Python list of interpreter IDs */ + PyObject *ret = NULL; if (!PyArg_ParseTupleAndKeywords( args, kwds, "O&$p:channel_list_interpreters", @@ -2362,12 +2359,13 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - chan = _channels_lookup(&_globals.channels, cid, NULL); + _PyChannelState *chan = _channels_lookup(&_globals.channels, cid, NULL); if (chan == NULL) { - goto except; + return NULL; } - ids = _channelends_list_interpreters(chan->ends, &count, send); + int64_t count = 0; /* Number of interpreters */ + int64_t *ids = _channelends_list_interpreters(chan->ends, &count, send); if (ids == NULL) { goto except; } From 6202ecd358124f46ed6fad68c778b1cf6dfd1b49 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Sat, 23 Nov 2019 23:40:24 +0000 Subject: [PATCH 27/33] Use _PyInterpreterID_New() instead of getting existing objects --- Include/cpython/pystate.h | 2 -- Modules/_xxsubinterpretersmodule.c | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 9d647e957b5616..6c8d2ae041ea54 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -147,8 +147,6 @@ struct _ts { The caller must hold the GIL.*/ PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_Get(void); -PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_LookUpID(PY_INT64_T requested_id); - PyAPI_FUNC(int) _PyState_AddModule(PyObject*, struct PyModuleDef*); PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *); diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 739fd49eb99c7e..4457f07c64b1ae 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2376,8 +2376,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) } for (int64_t i=0; i < count; i++) { - PyInterpreterState *interp = _PyInterpreterState_LookUpID(ids[i]); - PyObject *id_obj = _PyInterpreterState_GetIDObject(interp); + PyObject *id_obj = _PyInterpreterID_New(ids[i]); if (id_obj == NULL) { goto except; } From 357101fd1551eb367d7df201d0c1ad6e3a217269 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Sat, 14 Dec 2019 17:54:53 +0000 Subject: [PATCH 28/33] Markups - remove check for number of open channels and improve test structure --- Lib/test/test__xxsubinterpreters.py | 37 ++++++++++++++++------------- Modules/_xxsubinterpretersmodule.c | 5 ---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index b4b4f7bc141155..2b27aafb3a81bf 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1203,24 +1203,25 @@ def test_ids_global(self): self.assertEqual(cid2, int(cid1) + 1) - def _assert_interpreters_returned(self, cid, send, recv): - actual = set(interpreters.channel_list_interpreters(cid, send=True)) - self.assertEqual(actual, set(send)) - actual = set(interpreters.channel_list_interpreters(cid, send=False)) - self.assertEqual(actual, set(recv)) - - def test_channel_list_interpreters_empty(self): + def test_channel_list_interpreters_none(self): + """Test listing interpreters for a channel with no associations.""" # Test for channel with no associated interpreters. cid = interpreters.channel_create() - self._assert_interpreters_returned(cid, [], []) + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(send_interps, []) + self.assertEqual(recv_interps, []) - def test_channel_list_interpreters_mainline(self): + def test_channel_list_interpreters_basic(self): + """Test basic listing channel interpreters.""" interp0 = interpreters.get_main() cid = interpreters.channel_create() interpreters.channel_send(cid, "send") # Test for a channel that has one end associated to an interpreter. - self._assert_interpreters_returned( - cid, [interp0], []) + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, []) interp1 = interpreters.create() _run_output(interp1, dedent(f""" @@ -1228,11 +1229,13 @@ def test_channel_list_interpreters_mainline(self): obj = _interpreters.channel_recv({cid}) """)) # Test for channel that has boths ends associated to an interpreter. - self._assert_interpreters_returned( - cid, [interp0], [interp1]) + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, [interp1]) def test_channel_list_interpreters_multiple(self): - # Test for channel with both ends associated to many interpreters. + """Test listing interpreters for a channel with many associations.""" interp0 = interpreters.get_main() interp1 = interpreters.create() interp2 = interpreters.create() @@ -1252,8 +1255,10 @@ def test_channel_list_interpreters_multiple(self): import _xxsubinterpreters as _interpreters obj = _interpreters.channel_recv({cid}) """)) - self._assert_interpreters_returned( - cid, [interp0, interp1], [interp3, interp2]) + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(set(send_interps), {interp0, interp1}) + self.assertEqual(set(recv_interps), {interp2, interp3}) #################### diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 4457f07c64b1ae..95602d8669f076 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1198,11 +1198,6 @@ _channels_list_all(_channels *channels, int64_t *count) { int64_t *cids = NULL; PyThread_acquire_lock(channels->mutex, WAIT_LOCK); - int64_t numopen = channels->numopen; - if (numopen >= INT64_MAX) { - PyErr_SetString(PyExc_RuntimeError, "too many channels open"); - goto done; - } int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen)); if (ids == NULL) { goto done; From faca1dfba7b1a596a052796fbfc79c6406f3d5f7 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Tue, 21 Jan 2020 22:26:23 +0000 Subject: [PATCH 29/33] Add more listing subinterpreter tests --- Lib/test/test__xxsubinterpreters.py | 97 +++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 2b27aafb3a81bf..3d7e97b0173551 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1260,6 +1260,103 @@ def test_channel_list_interpreters_multiple(self): self.assertEqual(set(send_interps), {interp0, interp1}) self.assertEqual(set(recv_interps), {interp2, interp3}) + @unittest.skip("Failing due to handling of destroyed interpreters") + def test_channel_list_interpreters_destroyed(self): + """Test listing channel interpreters with a destroyed interpreter.""" + interp0 = interpreters.get_main() + interp1 = interpreters.create() + cid = interpreters.channel_create() + interpreters.channel_send(cid, "send") + _run_output(interp1, dedent(f""" + import _xxsubinterpreters as _interpreters + obj = _interpreters.channel_recv({cid}) + """)) + # Should be one interpreter associated with each end. + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, [interp1]) + + interpreters.destroy(interp1) + # Destroyed interpreter should not be listed. + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(send_interps, [interp0]) + self.assertEqual(recv_interps, []) + + def test_channel_list_interpreters_released(self): + """Test listing channel interpreters with a released channel.""" + # Set up one channel with main interpreter on the send end and two + # subinterpreters on the receive end. + interp0 = interpreters.get_main() + interp1 = interpreters.create() + interp2 = interpreters.create() + cid = interpreters.channel_create() + interpreters.channel_send(cid, "data") + _run_output(interp1, dedent(f""" + import _xxsubinterpreters as _interpreters + obj = _interpreters.channel_recv({cid}) + """)) + interpreters.channel_send(cid, "data") + _run_output(interp2, dedent(f""" + import _xxsubinterpreters as _interpreters + obj = _interpreters.channel_recv({cid}) + """)) + # Check the setup. + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 2) + + # Release the main interpreter from the send end. + interpreters.channel_release(cid, send=True) + # Send end should raise an error for the main interpreter. + with self.assertRaises(interpreters.ChannelClosedError): + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(len(recv_interps), 2) + + # Release one of the subinterpreters from the receive end. + _run_output(interp2, dedent(f""" + import _xxsubinterpreters as _interpreters + obj = _interpreters.channel_release({cid}) + """)) + # Receive end should have the released interpreter removed. + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(recv_interps, [interp1]) + + def test_channel_list_interpreters_closed(self): + """Test listing channel interpreters with a closed channel.""" + interp0 = interpreters.get_main() + interp1 = interpreters.create() + cid = interpreters.channel_create() + # Should be no interpreters associated with either end. + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 0) + self.assertEqual(len(recv_interps), 0) + + # Close the send end of the channel. + interpreters.channel_close(cid, send=True) + # Send end should raise an error. + with self.assertRaises(interpreters.ChannelClosedError): + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(len(recv_interps), 0) + + # Close the receive end of the channel from a subinterpreter. + _run_output(interp3, dedent(f""" + import _xxsubinterpreters as _interpreters + obj = _interpreters.channel_close({cid}) + """)) + # Both ends should raise an error. + with self.assertRaises(interpreters.ChannelClosedError): + send_interps = interpreters.channel_list_interpreters(cid, send=True) + with self.assertRaises(interpreters.ChannelClosedError): + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + #################### def test_send_recv_main(self): From 75e791f66e3c83f53084d0a0d0463b68d9d929f8 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Sat, 18 Apr 2020 21:37:47 +0100 Subject: [PATCH 30/33] Implementation rewrite upon Eric's suggestion. Just one testcase now failing --- Lib/test/test__xxsubinterpreters.py | 9 ++-- Modules/_xxsubinterpretersmodule.c | 84 ++++++++++++++--------------- 2 files changed, 44 insertions(+), 49 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 3d7e97b0173551..77e9f05296601c 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1260,7 +1260,6 @@ def test_channel_list_interpreters_multiple(self): self.assertEqual(set(send_interps), {interp0, interp1}) self.assertEqual(set(recv_interps), {interp2, interp3}) - @unittest.skip("Failing due to handling of destroyed interpreters") def test_channel_list_interpreters_destroyed(self): """Test listing channel interpreters with a destroyed interpreter.""" interp0 = interpreters.get_main() @@ -1310,10 +1309,10 @@ def test_channel_list_interpreters_released(self): # Release the main interpreter from the send end. interpreters.channel_release(cid, send=True) - # Send end should raise an error for the main interpreter. - with self.assertRaises(interpreters.ChannelClosedError): - send_interps = interpreters.channel_list_interpreters(cid, send=True) + # Send end should have no associated interpreters. + send_interps = interpreters.channel_list_interpreters(cid, send=True) recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 0) self.assertEqual(len(recv_interps), 2) # Release one of the subinterpreters from the receive end. @@ -1324,7 +1323,7 @@ def test_channel_list_interpreters_released(self): # Receive end should have the released interpreter removed. send_interps = interpreters.channel_list_interpreters(cid, send=True) recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(send_interps), 1) + self.assertEqual(len(send_interps), 0) self.assertEqual(recv_interps, [interp1]) def test_channel_list_interpreters_closed(self): diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 95602d8669f076..6e8d8d2b502dc4 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -536,7 +536,7 @@ _channelend_find(_channelend *first, int64_t interp, _channelend **pprev) typedef struct _channelassociations { // Note that the list entries are never removed for interpreter - // for which the channel is closed. This should be a problem in + // for which the channel is closed. This should not be a problem in // practice. Also, a channel isn't automatically closed when an // interpreter is destroyed. int64_t numsendopen; @@ -627,27 +627,6 @@ _channelends_associate(_channelends *ends, int64_t interp, int send) return 0; } -static int64_t * -_channelends_list_interpreters(_channelends *ends, int64_t *count, int send) -{ - int64_t numopen = send ? ends->numsendopen : ends->numrecvopen; - - int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)numopen); - if (ids == NULL) { - PyErr_NoMemory(); - return NULL; - } - - _channelend *ref = send ? ends->send : ends->recv; - for (int64_t i=0; ref != NULL; ref = ref->next, i++) { - ids[i] = ref->interp; - } - - *count = numopen; - - return ids; -} - static int _channelends_is_open(_channelends *ends) { @@ -1409,6 +1388,21 @@ _channel_close(_channels *channels, int64_t id, int end, int force) return _channels_close(channels, id, NULL, end, force); } +static int +_channel_is_associated(_channels *channels, int64_t cid, int64_t interp, + int send) +{ + _PyChannelState *chan = _channels_lookup(channels, cid, NULL); + if (chan == NULL) { + return -1; + } + + _channelend *end = _channelend_find(send ? chan->ends->send : chan->ends->recv, + interp, NULL); + + return (end != NULL && end->open); +} + /* ChannelID class */ static PyTypeObject ChannelIDtype; @@ -2339,14 +2333,15 @@ PyDoc_STRVAR(channel_list_all_doc, \n\ Return the list of all IDs for active channels."); - static PyObject * channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"cid", "send", NULL}; int64_t cid; /* Channel ID */ int send = 0; /* Send or receive end? */ - PyObject *ret = NULL; + int64_t id; + PyObject *ids, *id_obj; + PyInterpreterState *interp; if (!PyArg_ParseTupleAndKeywords( args, kwds, "O&$p:channel_list_interpreters", @@ -2354,39 +2349,40 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - _PyChannelState *chan = _channels_lookup(&_globals.channels, cid, NULL); - if (chan == NULL) { - return NULL; - } - - int64_t count = 0; /* Number of interpreters */ - int64_t *ids = _channelends_list_interpreters(chan->ends, &count, send); + ids = PyList_New(0); if (ids == NULL) { goto except; } - ret = PyList_New((Py_ssize_t)count); - if (ret == NULL) { - goto except; - } - - for (int64_t i=0; i < count; i++) { - PyObject *id_obj = _PyInterpreterID_New(ids[i]); - if (id_obj == NULL) { + interp = PyInterpreterState_Head(); + while (interp != NULL) { + id = PyInterpreterState_GetID(interp); + assert(id >= 0); + int res = _channel_is_associated(&_globals.channels, cid, id, send); + if (res < 0) { goto except; } - PyList_SET_ITEM(ret, i, id_obj); + if (res) { + id_obj = _PyInterpreterState_GetIDObject(interp); + if (id_obj == NULL) { + goto except; + } + res = PyList_Insert(ids, 0, id_obj); + if (res < 0) { + goto except; + } + } + interp = PyInterpreterState_Next(interp); } goto finally; except: - Py_XDECREF(ret); - ret = NULL; + Py_XDECREF(ids); + ids = NULL; finally: - PyMem_Free(ids); - return ret; + return ids; } PyDoc_STRVAR(channel_list_interpreters_doc, From 79f5d35a8fee381527fe44c790eb79673da40666 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Tue, 28 Apr 2020 22:24:14 +0100 Subject: [PATCH 31/33] Fix issue with ChannelClosedError not being raised when 'send' end of channel is closed --- Lib/test/test__xxsubinterpreters.py | 22 +++++++++++++--------- Modules/_xxsubinterpretersmodule.c | 3 +++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 77e9f05296601c..633cff6e7e2ab2 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1245,7 +1245,7 @@ def test_channel_list_interpreters_multiple(self): interpreters.channel_send(cid, "send") _run_output(interp1, dedent(f""" import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_send({cid}, "send") + _interpreters.channel_send({cid}, "send") """)) _run_output(interp2, dedent(f""" import _xxsubinterpreters as _interpreters @@ -1318,7 +1318,7 @@ def test_channel_list_interpreters_released(self): # Release one of the subinterpreters from the receive end. _run_output(interp2, dedent(f""" import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_release({cid}) + _interpreters.channel_release({cid}) """)) # Receive end should have the released interpreter removed. send_interps = interpreters.channel_list_interpreters(cid, send=True) @@ -1331,30 +1331,34 @@ def test_channel_list_interpreters_closed(self): interp0 = interpreters.get_main() interp1 = interpreters.create() cid = interpreters.channel_create() - # Should be no interpreters associated with either end. + # Put something in the channel so that it's not empty. + interpreters.channel_send(cid, "send") + + # Check initial state. send_interps = interpreters.channel_list_interpreters(cid, send=True) recv_interps = interpreters.channel_list_interpreters(cid, send=False) - self.assertEqual(len(send_interps), 0) + self.assertEqual(len(send_interps), 1) self.assertEqual(len(recv_interps), 0) # Close the send end of the channel. interpreters.channel_close(cid, send=True) # Send end should raise an error. with self.assertRaises(interpreters.ChannelClosedError): - send_interps = interpreters.channel_list_interpreters(cid, send=True) + interpreters.channel_list_interpreters(cid, send=True) + # Receive end should not be closed (since channel is not empty). recv_interps = interpreters.channel_list_interpreters(cid, send=False) self.assertEqual(len(recv_interps), 0) # Close the receive end of the channel from a subinterpreter. - _run_output(interp3, dedent(f""" + _run_output(interp1, dedent(f""" import _xxsubinterpreters as _interpreters - obj = _interpreters.channel_close({cid}) + _interpreters.channel_close({cid}, force=True) """)) # Both ends should raise an error. with self.assertRaises(interpreters.ChannelClosedError): - send_interps = interpreters.channel_list_interpreters(cid, send=True) + interpreters.channel_list_interpreters(cid, send=True) with self.assertRaises(interpreters.ChannelClosedError): - recv_interps = interpreters.channel_list_interpreters(cid, send=False) + interpreters.channel_list_interpreters(cid, send=False) #################### diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 6e8d8d2b502dc4..d460eeca1b8dc5 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1395,6 +1395,9 @@ _channel_is_associated(_channels *channels, int64_t cid, int64_t interp, _PyChannelState *chan = _channels_lookup(channels, cid, NULL); if (chan == NULL) { return -1; + } else if (send && chan->closing != NULL) { + PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", cid); + return -1; } _channelend *end = _channelend_find(send ? chan->ends->send : chan->ends->recv, From 5481e82e070d2472affa0313e0aa87e4936178d1 Mon Sep 17 00:00:00 2001 From: Lewis Gaul Date: Tue, 28 Apr 2020 22:28:47 +0100 Subject: [PATCH 32/33] Add testcase for listing associated interpreters with basic closed channel --- Lib/test/test__xxsubinterpreters.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 633cff6e7e2ab2..a98a8496ab8626 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1340,6 +1340,28 @@ def test_channel_list_interpreters_closed(self): self.assertEqual(len(send_interps), 1) self.assertEqual(len(recv_interps), 0) + # Force close the channel. + interpreters.channel_close(cid, force=True) + # Both ends should raise an error. + with self.assertRaises(interpreters.ChannelClosedError): + interpreters.channel_list_interpreters(cid, send=True) + with self.assertRaises(interpreters.ChannelClosedError): + interpreters.channel_list_interpreters(cid, send=False) + + def test_channel_list_interpreters_closed_send_end(self): + """Test listing channel interpreters with a channel's send end closed.""" + interp0 = interpreters.get_main() + interp1 = interpreters.create() + cid = interpreters.channel_create() + # Put something in the channel so that it's not empty. + interpreters.channel_send(cid, "send") + + # Check initial state. + send_interps = interpreters.channel_list_interpreters(cid, send=True) + recv_interps = interpreters.channel_list_interpreters(cid, send=False) + self.assertEqual(len(send_interps), 1) + self.assertEqual(len(recv_interps), 0) + # Close the send end of the channel. interpreters.channel_close(cid, send=True) # Send end should raise an error. From 79be8a0d8bdd1b2f99d3495e7fcbd078dc1bcb47 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 28 Apr 2020 17:15:59 -0600 Subject: [PATCH 33/33] Fix a refleak. --- Modules/_xxsubinterpretersmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index d460eeca1b8dc5..e9ed08206aa3e0 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2371,6 +2371,7 @@ channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds) goto except; } res = PyList_Insert(ids, 0, id_obj); + Py_DECREF(id_obj); if (res < 0) { goto except; }