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; } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy