From e12d6415c01d24f852ea1b58508c0e6893a82429 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 30 Jan 2018 04:14:12 +0000 Subject: [PATCH 01/19] DECREF cls in _PyCrossInterpreterData_Lookup(). --- Python/pystate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/pystate.c b/Python/pystate.c index a474549a8c730d..8dbda73de7015d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1242,6 +1242,7 @@ _PyCrossInterpreterData_Lookup(PyObject *obj) break; } } + Py_DECREF(cls); PyThread_release_lock(_PyRuntime.xidregistry.mutex); return getdata; } From ce72eef3f8bfc2e62da50a3baa7f3055daa93253 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 30 Jan 2018 04:30:13 +0000 Subject: [PATCH 02/19] DECREF the id in interp_list_all(). --- Modules/_xxsubinterpretersmodule.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index d2b5f26fae1d09..9d74b8a8b90278 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1612,7 +1612,9 @@ interp_list_all(PyObject *self) return NULL; } // insert at front of list - if (PyList_Insert(ids, 0, id) < 0) { + int res = PyList_Insert(ids, 0, id); + Py_DECREF(id); + if (res < 0) { Py_DECREF(ids); return NULL; } From 27bbab7f66354dd341c2a6b525d56058d6366b5f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 31 Jan 2018 01:33:57 +0000 Subject: [PATCH 03/19] Avoid raising RunFailedError. --- Lib/test/test__xxsubinterpreters.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 2b170443a3b638..8d8581cc139395 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -362,13 +362,15 @@ def test_bad_id(self): def test_from_current(self): main, = interpreters.list_all() id = interpreters.create() - script = dedent(""" + script = dedent(f""" import _xxsubinterpreters as _interpreters - _interpreters.destroy({}) - """).format(id) + try: + _interpreters.destroy({id}) + except RuntimeError: + pass + """) - with self.assertRaises(RuntimeError): - interpreters.run_string(id, script) + interpreters.run_string(id, script) self.assertEqual(set(interpreters.list_all()), {main, id}) def test_from_sibling(self): From c744873cee13a39f929b2dc24dde80195c96a868 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 31 Jan 2018 01:35:32 +0000 Subject: [PATCH 04/19] DECREF id in _coerce_id(). --- Modules/_xxsubinterpretersmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 9d74b8a8b90278..33b036e29dd54d 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -32,6 +32,7 @@ _coerce_id(PyObject *id) return -1; } long long cid = PyLong_AsLongLong(id); + Py_DECREF(id); if (cid == -1 && PyErr_Occurred() != NULL) { PyErr_SetString(PyExc_ValueError, "'id' must be a non-negative int"); From e982d873129877788b755765b81542b0155e0ab8 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 31 Jan 2018 01:38:59 +0000 Subject: [PATCH 05/19] DECREF the original error message. --- Modules/_xxsubinterpretersmodule.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 33b036e29dd54d..cd79edefca49f2 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -140,6 +140,12 @@ typedef struct _sharedexception { char *msg; } _sharedexception; +static void +_sharedexception_free(_sharedexception *exc) { + PyMem_Free(exc->msg); + PyMem_Free(exc); +} + static _sharedexception * _get_shared_exception(void) { @@ -162,7 +168,18 @@ _get_shared_exception(void) err->msg = "unable to format exception"; return err; } - err->msg = (char *)PyUnicode_AsUTF8(msg); + const char *errmsg = PyUnicode_AsUTF8(msg); + if (errmsg == NULL) { + err->msg = "unable to encode exception"; + } + err->msg = PyMem_Malloc(strlen(errmsg)+1); + if (err->msg == NULL) { + Py_DECREF(msg); + err->msg = "MemoryError: out of memory copying error message"; + return err; + } + strcpy(err->msg, errmsg); + Py_DECREF(msg); if (err->msg == NULL) { err->msg = "unable to encode exception"; } @@ -1079,6 +1096,7 @@ channelid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds) "'send' and 'recv' cannot both be False"); return NULL; } + int end = 0; if (send == 1) { if (recv == 0 || recv == -1) { @@ -1471,7 +1489,7 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, // Propagate any exception out to the caller. if (exc != NULL) { _apply_shared_exception(exc); - PyMem_Free(exc); + _sharedexception_free(exc); } else if (result != 0) { // We were unable to allocate a shared exception. From aac9485031de7b9c2ee78d13d4a3d9d1fa8e6ec6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 31 Jan 2018 02:00:38 +0000 Subject: [PATCH 06/19] Do not explicitly check against INT64_MAX. --- Lib/test/test__xxsubinterpreters.py | 6 +++--- Modules/_xxsubinterpretersmodule.c | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 8d8581cc139395..8d72ca20021486 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -763,12 +763,12 @@ def __int__(self): self.assertEqual(int(cid), 10) def test_bad_id(self): - ids = [-1, 2**64, "spam"] - for cid in ids: + for cid in [-1, 'spam']: with self.subTest(cid): with self.assertRaises(ValueError): interpreters._channel_id(cid) - + with self.assertRaises(OverflowError): + interpreters._channel_id(2**64) with self.assertRaises(TypeError): interpreters._channel_id(object()) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index cd79edefca49f2..ca850c4ccc4635 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -31,11 +31,13 @@ _coerce_id(PyObject *id) } return -1; } - long long cid = PyLong_AsLongLong(id); + int64_t cid = PyLong_AsLongLong(id); Py_DECREF(id); if (cid == -1 && PyErr_Occurred() != NULL) { - PyErr_SetString(PyExc_ValueError, - "'id' must be a non-negative int"); + if (!PyErr_ExceptionMatches(PyExc_OverflowError)) { + PyErr_SetString(PyExc_ValueError, + "'id' must be a non-negative int"); + } return -1; } if (cid < 0) { @@ -43,11 +45,6 @@ _coerce_id(PyObject *id) "'id' must be a non-negative int"); return -1; } - if (cid > INT64_MAX) { - PyErr_SetString(PyExc_ValueError, - "'id' too large (must be 64-bit int)"); - return -1; - } return cid; } @@ -1231,7 +1228,7 @@ channelid_richcompare(PyObject *self, PyObject *other, int op) if (othercid == -1 && PyErr_Occurred() != NULL) { return NULL; } - if (othercid < 0 || othercid > INT64_MAX) { + if (othercid < 0) { equal = 0; } else { From 35f60460d0106cab5da691276168a544bb3bb30d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 31 Jan 2018 05:06:30 +0000 Subject: [PATCH 07/19] Free cids when done. --- Modules/_xxsubinterpretersmodule.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index ca850c4ccc4635..f90a87de9a1902 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1840,11 +1840,11 @@ channel_list_all(PyObject *self) } PyObject *ids = PyList_New((Py_ssize_t)count); if (ids == NULL) { - // XXX free cids - return NULL; + goto finally; } - for (int64_t i=0; i < count; cids++, i++) { - PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cids, 0, + int64_t *cur = cids; + for (int64_t i=0; i < count; cur++, i++) { + PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cur, 0, &_globals.channels, 0); if (id == NULL) { Py_DECREF(ids); @@ -1853,7 +1853,9 @@ channel_list_all(PyObject *self) } PyList_SET_ITEM(ids, i, id); } - // XXX free cids + +finally: + PyMem_Free(cids); return ids; } From 38d924ece6db72c89b96bbb30e79a4a8bda316b6 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 31 Jan 2018 05:16:09 +0000 Subject: [PATCH 08/19] DECREF other in channelid_richcompare(). --- Modules/_xxsubinterpretersmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index f90a87de9a1902..70193f8d1af5a4 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1224,7 +1224,7 @@ channelid_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_NOTIMPLEMENTED; } int64_t othercid = PyLong_AsLongLong(other); - // XXX decref other here? + Py_DECREF(other); if (othercid == -1 && PyErr_Occurred() != NULL) { return NULL; } From 4321ecf1edd15d441a2cc682412fb5b91fd2e2c3 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 2 Feb 2018 19:47:16 +0000 Subject: [PATCH 09/19] Add items to non-empty channels. --- Modules/_xxsubinterpretersmodule.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 70193f8d1af5a4..c69bd24b409a2d 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -538,6 +538,9 @@ _channel_add(_PyChannelState *chan, int64_t interp, if (chan->first == NULL) { chan->first = item; } + else { + chan->last->next = item; + } chan->last = item; res = 0; @@ -551,6 +554,7 @@ _channel_next(_PyChannelState *chan, int64_t interp) { _PyCrossInterpreterData *data = NULL; PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + if (_channel_associate_end(chan, interp, 0) == NULL) { goto done; } From e5a3515af46b3c1fe94624374b68e5418a838465 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 2 Feb 2018 21:43:33 +0000 Subject: [PATCH 10/19] Add struct _sharedns and isolate related memory ops. --- Modules/_xxsubinterpretersmodule.c | 189 ++++++++++++++++++----------- 1 file changed, 119 insertions(+), 70 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index c69bd24b409a2d..64e2c2b16d81f0 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -7,6 +7,22 @@ #include "internal/pystate.h" +static char * +_copy_raw_string(PyObject *strobj) +{ + const char *str = PyUnicode_AsUTF8(strobj); + if (str == NULL) { + return NULL; + } + char *copied = PyMem_Malloc(strlen(str)+1); + if (str == NULL) { + PyErr_NoMemory(); + return NULL; + } + strcpy(copied, str); + return copied; +} + static PyInterpreterState * _get_current(void) { @@ -50,82 +66,125 @@ _coerce_id(PyObject *id) /* data-sharing-specific code ***********************************************/ -typedef struct _shareditem { - Py_UNICODE *name; - Py_ssize_t namelen; +struct _sharednsitem { + char *name; _PyCrossInterpreterData data; -} _shareditem; +}; + +static int +_sharednsitem_init(struct _sharednsitem *item, PyObject *key, PyObject *value) +{ + item->name = _copy_raw_string(key); + if (item->name == NULL) { + return -1; + } + if (_PyObject_GetCrossInterpreterData(value, &item->data) != 0) { + return -1; + } + return 0; +} + +static void +_sharednsitem_clear(struct _sharednsitem *item) +{ + if (item->name != NULL) { + PyMem_Free(item->name); + } + _PyCrossInterpreterData_Release(&item->data); +} -void -_sharedns_clear(_shareditem *shared) +static int +_sharednsitem_apply(struct _sharednsitem *item, PyObject *ns) { - for (_shareditem *item=shared; item->name != NULL; item += 1) { - _PyCrossInterpreterData_Release(&item->data); + PyObject *name = PyUnicode_FromString(item->name); + if (name == NULL) { + return -1; } + PyObject *value = _PyCrossInterpreterData_NewObject(&item->data); + if (value == NULL) { + Py_DECREF(name); + return -1; + } + int res = PyDict_SetItem(ns, name, value); + Py_DECREF(name); + Py_DECREF(value); + return res; } -static _shareditem * -_get_shared_ns(PyObject *shareable, Py_ssize_t *lenp) +typedef struct _sharedns { + Py_ssize_t len; + struct _sharednsitem* items; +} _sharedns; + +static _sharedns * +_sharedns_new(Py_ssize_t len) +{ + _sharedns *shared = PyMem_NEW(_sharedns, 1); + if (shared == NULL) { + PyErr_NoMemory(); + return NULL; + } + shared->len = len; + shared->items = PyMem_NEW(struct _sharednsitem, len); + if (shared->items == NULL) { + PyErr_NoMemory(); + PyMem_Free(shared); + return NULL; + } + return shared; +} + +static void +_sharedns_free(_sharedns *shared) +{ + for (Py_ssize_t i=0; i < shared->len; i++) { + _sharednsitem_clear(&shared->items[i]); + } + PyMem_Free(shared->items); + PyMem_Free(shared); +} + +static _sharedns * +_get_shared_ns(PyObject *shareable) { if (shareable == NULL || shareable == Py_None) { - *lenp = 0; return NULL; } Py_ssize_t len = PyDict_Size(shareable); - *lenp = len; if (len == 0) { return NULL; } - _shareditem *shared = PyMem_NEW(_shareditem, len+1); + _sharedns *shared = _sharedns_new(len); if (shared == NULL) { return NULL; } - for (Py_ssize_t i=0; i < len; i++) { - *(shared + i) = (_shareditem){0}; - } Py_ssize_t pos = 0; for (Py_ssize_t i=0; i < len; i++) { PyObject *key, *value; if (PyDict_Next(shareable, &pos, &key, &value) == 0) { break; } - _shareditem *item = shared + i; - - if (_PyObject_GetCrossInterpreterData(value, &item->data) != 0) { - break; - } - item->name = PyUnicode_AsUnicodeAndSize(key, &item->namelen); - if (item->name == NULL) { - _PyCrossInterpreterData_Release(&item->data); + if (_sharednsitem_init(&shared->items[i], key, value) != 0) { break; } - (item + 1)->name = NULL; // Mark the next one as the last. } if (PyErr_Occurred()) { - _sharedns_clear(shared); - PyMem_Free(shared); + _sharedns_free(shared); return NULL; } return shared; } static int -_shareditem_apply(_shareditem *item, PyObject *ns) +_sharedns_apply(_sharedns *shared, PyObject *ns) { - PyObject *name = PyUnicode_FromUnicode(item->name, item->namelen); - if (name == NULL) { - return 1; - } - PyObject *value = _PyCrossInterpreterData_NewObject(&item->data); - if (value == NULL) { - Py_DECREF(name); - return 1; + for (Py_ssize_t i=0; i < shared->len; i++) { + if (_sharednsitem_apply(&shared->items[i], ns) != 0) { + return -1; + } } - int res = PyDict_SetItem(ns, name, value); - Py_DECREF(name); - Py_DECREF(value); - return res; + return 0; } // Ultimately we'd like to preserve enough information about the @@ -138,8 +197,11 @@ typedef struct _sharedexception { } _sharedexception; static void -_sharedexception_free(_sharedexception *exc) { - PyMem_Free(exc->msg); +_sharedexception_free(_sharedexception *exc) +{ + if (exc->msg != NULL) { + PyMem_Free(exc->msg); + } PyMem_Free(exc); } @@ -165,20 +227,14 @@ _get_shared_exception(void) err->msg = "unable to format exception"; return err; } - const char *errmsg = PyUnicode_AsUTF8(msg); - if (errmsg == NULL) { - err->msg = "unable to encode exception"; - } - err->msg = PyMem_Malloc(strlen(errmsg)+1); - if (err->msg == NULL) { - Py_DECREF(msg); - err->msg = "MemoryError: out of memory copying error message"; - return err; - } - strcpy(err->msg, errmsg); + + err->msg = _copy_raw_string(msg); Py_DECREF(msg); if (err->msg == NULL) { - err->msg = "unable to encode exception"; + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + err->msg = "MemoryError: out of memory copying error message"; + } + err->msg = "unable to encode and copy exception message"; } return err; } @@ -289,7 +345,8 @@ _channelend_new(int64_t interp) } static void -_channelend_free_all(_channelend *end) { +_channelend_free_all(_channelend *end) +{ while (end != NULL) { _channelend *last = end; end = end->next; @@ -1416,10 +1473,8 @@ _ensure_not_running(PyInterpreterState *interp) static int _run_script(PyInterpreterState *interp, const char *codestr, - _shareditem *shared, Py_ssize_t num_shared, - _sharedexception **exc) + _sharedns *shared, _sharedexception **exc) { - assert(num_shared >= 0); PyObject *main_mod = PyMapping_GetItemString(interp->modules, "__main__"); if (main_mod == NULL) { goto error; @@ -1433,12 +1488,9 @@ _run_script(PyInterpreterState *interp, const char *codestr, // Apply the cross-interpreter data. if (shared != NULL) { - for (Py_ssize_t i=0; i < num_shared; i++) { - _shareditem *item = &shared[i]; - if (_shareditem_apply(item, ns) != 0) { - Py_DECREF(ns); - goto error; - } + if (_sharedns_apply(shared, ns) != 0) { + Py_DECREF(ns); + goto error; } } @@ -1453,7 +1505,6 @@ _run_script(PyInterpreterState *interp, const char *codestr, } return 0; - error: *exc = _get_shared_exception(); PyErr_Clear(); @@ -1468,8 +1519,7 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, return -1; } - Py_ssize_t num_shared = -1; - _shareditem *shared = _get_shared_ns(shareables, &num_shared); + _sharedns *shared = _get_shared_ns(shareables); if (shared == NULL && PyErr_Occurred()) { return -1; } @@ -1480,7 +1530,7 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, // Run the script. _sharedexception *exc = NULL; - int result = _run_script(interp, codestr, shared, num_shared, &exc); + int result = _run_script(interp, codestr, shared, &exc); // Switch back. if (save_tstate != NULL) { @@ -1498,8 +1548,7 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, } if (shared != NULL) { - _sharedns_clear(shared); - PyMem_Free(shared); + _sharedns_free(shared); } return result; From 0c0fa3df7cc1f003fefabd4f620857f4b93c986b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 2 Feb 2018 23:00:20 +0000 Subject: [PATCH 11/19] Clean up memory ops for _sharedexception. --- Modules/_xxsubinterpretersmodule.c | 129 +++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 64e2c2b16d81f0..3e1abf635576bd 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -193,48 +193,91 @@ _sharedns_apply(_sharedns *shared, PyObject *ns) // of the exception in the calling interpreter. typedef struct _sharedexception { + char *name; char *msg; } _sharedexception; +static _sharedexception * +_sharedexception_new(void) +{ + _sharedexception *err = PyMem_NEW(_sharedexception, 1); + if (err == NULL) { + PyErr_NoMemory(); + return NULL; + } + err->name = NULL; + err->msg = NULL; + return err; +} + static void -_sharedexception_free(_sharedexception *exc) +_sharedexception_clear(_sharedexception *exc) { + if (exc->name != NULL) { + PyMem_Free(exc->name); + } if (exc->msg != NULL) { PyMem_Free(exc->msg); } +} + +static void +_sharedexception_free(_sharedexception *exc) +{ + _sharedexception_clear(exc); PyMem_Free(exc); } static _sharedexception * -_get_shared_exception(void) +_sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb) { - _sharedexception *err = PyMem_NEW(_sharedexception, 1); + assert(exctype != NULL); + char *failure = NULL; + + _sharedexception *err = _sharedexception_new(); if (err == NULL) { - return NULL; + goto finally; } - PyObject *exc; - PyObject *value; - PyObject *tb; - PyErr_Fetch(&exc, &value, &tb); - PyObject *msg; - if (value == NULL) { - msg = PyUnicode_FromFormat("%S", exc); + + PyObject *name = PyUnicode_FromFormat("%S", exctype); + if (name == NULL) { + failure = "unable to format exception type name"; + goto finally; } - else { - msg = PyUnicode_FromFormat("%S: %S", exc, value); + err->name = _copy_raw_string(name); + if (err->name == NULL) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + failure = "out of memory copying exception type name"; + } + failure = "unable to encode and copy exception type name"; + goto finally; } - if (msg == NULL) { - err->msg = "unable to format exception"; - return err; + + if (exc != NULL) { + PyObject *msg = PyUnicode_FromFormat("%S", exc); + if (msg == NULL) { + failure = "unable to format exception message"; + goto finally; + } + err->msg = _copy_raw_string(msg); + Py_DECREF(msg); + if (err->msg == NULL) { + if (PyErr_ExceptionMatches(PyExc_MemoryError)) { + failure = "out of memory copying exception message"; + } + failure = "unable to encode and copy exception message"; + goto finally; + } } - err->msg = _copy_raw_string(msg); - Py_DECREF(msg); - if (err->msg == NULL) { - if (PyErr_ExceptionMatches(PyExc_MemoryError)) { - err->msg = "MemoryError: out of memory copying error message"; +finally: + if (failure != NULL) { + PyErr_Clear(); + if (err->name != NULL) { + PyMem_Free(err->name); + err->name = NULL; } - err->msg = "unable to encode and copy exception message"; + err->msg = failure; } return err; } @@ -260,9 +303,22 @@ interp_exceptions_init(PyObject *ns) } static void -_apply_shared_exception(_sharedexception *exc) +_sharedexception_apply(_sharedexception *exc) { - PyErr_SetString(RunFailedError, exc->msg); + if (exc->name != NULL) { + if (exc->msg != NULL) { + PyErr_Format(RunFailedError, "%s: %s", exc->name, exc->msg); + } + else { + PyErr_SetString(RunFailedError, exc->name); + } + } + else if (exc->msg != NULL) { + PyErr_SetString(RunFailedError, exc->msg); + } + else { + PyErr_SetNone(RunFailedError); + } } /* channel-specific code */ @@ -1475,6 +1531,10 @@ static int _run_script(PyInterpreterState *interp, const char *codestr, _sharedns *shared, _sharedexception **exc) { + PyObject *exctype = NULL; + PyObject *excval = NULL; + PyObject *tb = NULL; + PyObject *main_mod = PyMapping_GetItemString(interp->modules, "__main__"); if (main_mod == NULL) { goto error; @@ -1504,10 +1564,27 @@ _run_script(PyInterpreterState *interp, const char *codestr, Py_DECREF(result); // We throw away the result. } + *exc = NULL; return 0; error: - *exc = _get_shared_exception(); + PyErr_Fetch(&exctype, &excval, &tb); + Py_INCREF(exctype); + Py_XINCREF(excval); + Py_XINCREF(tb); PyErr_Clear(); + + _sharedexception *sharedexc = _sharedexception_bind(exctype, excval, tb); + Py_DECREF(exctype); + Py_XDECREF(excval); + Py_XDECREF(tb); + if (sharedexc == NULL) { + fprintf(stderr, "RunFailedError: script raised an uncaught exception"); + PyErr_Clear(); + } + else { + assert(!PyErr_Occurred()); + *exc = sharedexc; + } return -1; } @@ -1539,7 +1616,7 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, // Propagate any exception out to the caller. if (exc != NULL) { - _apply_shared_exception(exc); + _sharedexception_apply(exc); _sharedexception_free(exc); } else if (result != 0) { From f0d44300e2cbc89338aa8292b53dd4be6fdb7706 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 01:04:09 +0000 Subject: [PATCH 12/19] Clean up _PyChannelState memory ops. --- Modules/_xxsubinterpretersmodule.c | 469 +++++++++++++++++++---------- 1 file changed, 311 insertions(+), 158 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 3e1abf635576bd..f39068b2131b98 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -321,7 +321,7 @@ _sharedexception_apply(_sharedexception *exc) } } -/* channel-specific code */ +/* channel-specific code ****************************************************/ static PyObject *ChannelError; static PyObject *ChannelNotFoundError; @@ -376,6 +376,139 @@ channel_exceptions_init(PyObject *ns) return 0; } +/* the channel queue */ + +struct _channelitem; + +typedef struct _channelitem { + _PyCrossInterpreterData *data; + struct _channelitem *next; +} _channelitem; + +static _channelitem * +_channelitem_new(void) +{ + _channelitem *item = PyMem_NEW(_channelitem, 1); + if (item == NULL) { + PyErr_NoMemory(); + return NULL; + } + item->data = NULL; + item->next = NULL; + return item; +} + +static void +_channelitem_clear(_channelitem *item) +{ + if (item->data != NULL) { + _PyCrossInterpreterData_Release(item->data); + PyMem_Free(item->data); + item->data = NULL; + } + item->next = NULL; +} + +static void +_channelitem_free(_channelitem *item) +{ + _channelitem_clear(item); + PyMem_Free(item); +} + +static void +_channelitem_free_all(_channelitem *item) +{ + while (item != NULL) { + _channelitem *last = item; + item = item->next; + _channelitem_free(last); + } +} + +static _PyCrossInterpreterData * +_channelitem_popped(_channelitem *item) +{ + _PyCrossInterpreterData *data = item->data; + item->data = NULL; + _channelitem_free(item); + return data; +} + +typedef struct _channelqueue { + int64_t count; + _channelitem *first; + _channelitem *last; +} _channelqueue; + +static _channelqueue * +_channelqueue_new(void) +{ + _channelqueue *queue = PyMem_NEW(_channelqueue, 1); + if (queue == NULL) { + PyErr_NoMemory(); + return NULL; + } + queue->count = 0; + queue->first = NULL; + queue->last = NULL; + return queue; +} + +static void +_channelqueue_clear(_channelqueue *queue) +{ + _channelitem_free_all(queue->first); + queue->count = 0; + queue->first = NULL; + queue->last = NULL; +} + +static void +_channelqueue_free(_channelqueue *queue) +{ + _channelqueue_clear(queue); + PyMem_Free(queue); +} + +static int +_channelqueue_put(_channelqueue *queue, _PyCrossInterpreterData *data) +{ + _channelitem *item = _channelitem_new(); + if (item == NULL) { + return -1; + } + item->data = data; + + queue->count += 1; + if (queue->first == NULL) { + queue->first = item; + } + else { + queue->last->next = item; + } + queue->last = item; + return 0; +} + +static _PyCrossInterpreterData * +_channelqueue_get(_channelqueue *queue) +{ + _channelitem *item = queue->first; + if (item == NULL) { + return NULL; + } + queue->first = item->next; + if (queue->last == item) { + queue->last = NULL; + } + queue->count -= 1; + + return _channelitem_popped(item); +} + +/* channel-interpreter associations */ + struct _channelend; typedef struct _channelend { @@ -389,24 +522,28 @@ _channelend_new(int64_t interp) { _channelend *end = PyMem_NEW(_channelend, 1); if (end == NULL) { + PyErr_NoMemory(); return NULL; } - end->next = NULL; end->interp = interp; - end->open = 1; - return end; } +static void +_channelend_free(_channelend *end) +{ + PyMem_Free(end); +} + static void _channelend_free_all(_channelend *end) { while (end != NULL) { _channelend *last = end; end = end->next; - PyMem_Free(last); + _channelend_free(last); } } @@ -428,24 +565,7 @@ _channelend_find(_channelend *first, int64_t interp, _channelend **pprev) return end; } -struct _channelitem; - -typedef struct _channelitem { - _PyCrossInterpreterData *data; - struct _channelitem *next; -} _channelitem; - -struct _channel; - -typedef struct _channel { - PyThread_type_lock mutex; - - int open; - - int64_t count; - _channelitem *first; - _channelitem *last; - +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 // practice. Also, a channel isn't automatically closed when an @@ -454,39 +574,43 @@ typedef struct _channel { int64_t numrecvopen; _channelend *send; _channelend *recv; -} _PyChannelState; +} _channelends; -static _PyChannelState * -_channel_new(void) +static _channelends * +_channelends_new(void) { - _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1); - if (chan == NULL) { - return NULL; - } - chan->mutex = PyThread_allocate_lock(); - if (chan->mutex == NULL) { - PyMem_Free(chan); - PyErr_SetString(ChannelError, - "can't initialize mutex for new channel"); + _channelends *ends = PyMem_NEW(_channelends, 1); + if (ends== NULL) { return NULL; } + ends->numsendopen = 0; + ends->numrecvopen = 0; + ends->send = NULL; + ends->recv = NULL; + return ends; +} - chan->open = 1; - - chan->count = 0; - chan->first = NULL; - chan->last = NULL; +static void +_channelends_clear(_channelends *ends) +{ + _channelend_free_all(ends->send); + ends->send = NULL; + ends->numsendopen = 0; - chan->numsendopen = 0; - chan->numrecvopen = 0; - chan->send = NULL; - chan->recv = NULL; + _channelend_free_all(ends->recv); + ends->recv = NULL; + ends->numrecvopen = 0; +} - return chan; +static void +_channelends_free(_channelends *ends) +{ + _channelends_clear(ends); + PyMem_Free(ends); } static _channelend * -_channel_add_end(_PyChannelState *chan, _channelend *prev, int64_t interp, +_channelends_add(_channelends *ends, _channelend *prev, int64_t interp, int send) { _channelend *end = _channelend_new(interp); @@ -496,137 +620,163 @@ _channel_add_end(_PyChannelState *chan, _channelend *prev, int64_t interp, if (prev == NULL) { if (send) { - chan->send = end; + ends->send = end; } else { - chan->recv = end; + ends->recv = end; } } else { prev->next = end; } if (send) { - chan->numsendopen += 1; + ends->numsendopen += 1; } else { - chan->numrecvopen += 1; + ends->numrecvopen += 1; } return end; } -static _channelend * -_channel_associate_end(_PyChannelState *chan, int64_t interp, int send) +static int +_channelends_associate(_channelends *ends, int64_t interp, int send) { - if (!chan->open) { - PyErr_SetString(ChannelClosedError, "channel closed"); - return NULL; - } - _channelend *prev; - _channelend *end = _channelend_find(send ? chan->send : chan->recv, + _channelend *end = _channelend_find(send ? ends->send : ends->recv, interp, &prev); if (end != NULL) { if (!end->open) { PyErr_SetString(ChannelClosedError, "channel already closed"); - return NULL; + return -1; } // already associated - return end; + return 0; + } + if (_channelends_add(ends, prev, interp, send) == NULL) { + return -1; + } + return 0; +} + +static int +_channelends_is_open(_channelends *ends) +{ + if (ends->numsendopen != 0 || ends->numrecvopen != 0) { + return 1; + } + if (ends->send == NULL && ends->recv == NULL) { + return 1; } - return _channel_add_end(chan, prev, interp, send); + return 0; } static void -_channel_close_channelend(_PyChannelState *chan, _channelend *end, int send) +_channelends_close_end(_channelends *ends, _channelend *end, int send) { end->open = 0; if (send) { - chan->numsendopen -= 1; + ends->numsendopen -= 1; } else { - chan->numrecvopen -= 1; + ends->numrecvopen -= 1; } } static int -_channel_close_interpreter(_PyChannelState *chan, int64_t interp, int which) +_channelends_close_interpreter(_channelends *ends, int64_t interp, int which) { - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - - int res = -1; - if (!chan->open) { - PyErr_SetString(ChannelClosedError, "channel already closed"); - goto done; - } - _channelend *prev; _channelend *end; if (which >= 0) { // send/both - end = _channelend_find(chan->send, interp, &prev); + end = _channelend_find(ends->send, interp, &prev); if (end == NULL) { // never associated so add it - end = _channel_add_end(chan, prev, interp, 1); + end = _channelends_add(ends, prev, interp, 1); if (end == NULL) { - goto done; + return -1; } } - _channel_close_channelend(chan, end, 1); + _channelends_close_end(ends, end, 1); } if (which <= 0) { // recv/both - end = _channelend_find(chan->recv, interp, &prev); + end = _channelend_find(ends->recv, interp, &prev); if (end == NULL) { // never associated so add it - end = _channel_add_end(chan, prev, interp, 0); + end = _channelends_add(ends, prev, interp, 0); if (end == NULL) { - goto done; + return -1; } } - _channel_close_channelend(chan, end, 0); + _channelends_close_end(ends, end, 0); } - - if (chan->numsendopen == 0 && chan->numrecvopen == 0) { - if (chan->send != NULL || chan->recv != NULL) { - chan->open = 0; - } - } - - res = 0; -done: - PyThread_release_lock(chan->mutex); - return res; + return 0; } -static int -_channel_close_all(_PyChannelState *chan) +static void +_channelends_close_all(_channelends *ends) { - int res = -1; - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + // Ensure all the "send"-associated interpreters are closed. + _channelend *end; + for (end = ends->send; end != NULL; end = end->next) { + _channelends_close_end(ends, end, 1); + } - if (!chan->open) { - PyErr_SetString(ChannelClosedError, "channel already closed"); - goto done; + // Ensure all the "recv"-associated interpreters are closed. + for (end = ends->recv; end != NULL; end = end->next) { + _channelends_close_end(ends, end, 0); } +} - chan->open = 0; +/* channels */ - // We *could* also just leave these in place, since we've marked - // the channel as closed already. +struct _channel; - // Ensure all the "send"-associated interpreters are closed. - _channelend *end; - for (end = chan->send; end != NULL; end = end->next) { - _channel_close_channelend(chan, end, 1); - } +typedef struct _channel { + PyThread_type_lock mutex; + _channelqueue *queue; + _channelends *ends; + int open; +} _PyChannelState; - // Ensure all the "recv"-associated interpreters are closed. - for (end = chan->recv; end != NULL; end = end->next) { - _channel_close_channelend(chan, end, 0); +static _PyChannelState * +_channel_new(void) +{ + _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1); + if (chan == NULL) { + return NULL; } + chan->mutex = PyThread_allocate_lock(); + if (chan->mutex == NULL) { + PyMem_Free(chan); + PyErr_SetString(ChannelError, + "can't initialize mutex for new channel"); + return NULL; + } + chan->queue = _channelqueue_new(); + if (chan->queue == NULL) { + PyMem_Free(chan); + return NULL; + } + chan->ends = _channelends_new(); + if (chan->ends == NULL) { + _channelqueue_free(chan->queue); + PyMem_Free(chan); + return NULL; + } + chan->open = 1; + return chan; +} - res = 0; -done: +static void +_channel_free(_PyChannelState *chan) +{ + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + _channelqueue_free(chan->queue); + _channelends_free(chan->ends); PyThread_release_lock(chan->mutex); - return res; + + PyThread_free_lock(chan->mutex); + PyMem_Free(chan); } static int @@ -634,27 +784,19 @@ _channel_add(_PyChannelState *chan, int64_t interp, _PyCrossInterpreterData *data) { int res = -1; - PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - if (_channel_associate_end(chan, interp, 1) == NULL) { + + if (!chan->open) { + PyErr_SetString(ChannelClosedError, "channel closed"); goto done; } - - _channelitem *item = PyMem_NEW(_channelitem, 1); - if (item == NULL) { + if (_channelends_associate(chan->ends, interp, 1) != 0) { goto done; } - item->data = data; - item->next = NULL; - chan->count += 1; - if (chan->first == NULL) { - chan->first = item; - } - else { - chan->last->next = item; + if (_channelqueue_put(chan->queue, data) != 0) { + goto done; } - chan->last = item; res = 0; done: @@ -668,56 +810,67 @@ _channel_next(_PyChannelState *chan, int64_t interp) _PyCrossInterpreterData *data = NULL; PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - if (_channel_associate_end(chan, interp, 0) == NULL) { + if (!chan->open) { + PyErr_SetString(ChannelClosedError, "channel closed"); goto done; } - - _channelitem *item = chan->first; - if (item == NULL) { + if (_channelends_associate(chan->ends, interp, 0) != 0) { goto done; } - chan->first = item->next; - if (chan->last == item) { - chan->last = NULL; - } - chan->count -= 1; - - data = item->data; - PyMem_Free(item); + data = _channelqueue_get(chan->queue); done: PyThread_release_lock(chan->mutex); return data; } -static void -_channel_clear(_PyChannelState *chan) +static int +_channel_close_interpreter(_PyChannelState *chan, int64_t interp, int which) { - _channelitem *item = chan->first; - while (item != NULL) { - _PyCrossInterpreterData_Release(item->data); - PyMem_Free(item->data); - _channelitem *last = item; - item = item->next; - PyMem_Free(last); + PyThread_acquire_lock(chan->mutex, WAIT_LOCK); + + int res = -1; + if (!chan->open) { + PyErr_SetString(ChannelClosedError, "channel already closed"); + goto done; } - chan->first = NULL; - chan->last = NULL; + + if (_channelends_close_interpreter(chan->ends, interp, which) != 0) { + goto done; + } + chan->open = _channelends_is_open(chan->ends); + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; } -static void -_channel_free(_PyChannelState *chan) +static int +_channel_close_all(_PyChannelState *chan) { + int res = -1; PyThread_acquire_lock(chan->mutex, WAIT_LOCK); - _channel_clear(chan); - _channelend_free_all(chan->send); - _channelend_free_all(chan->recv); - PyThread_release_lock(chan->mutex); - PyThread_free_lock(chan->mutex); - PyMem_Free(chan); + if (!chan->open) { + PyErr_SetString(ChannelClosedError, "channel already closed"); + goto done; + } + + chan->open = 0; + + // We *could* also just leave these in place, since we've marked + // the channel as closed already. + _channelends_close_all(chan->ends); + + res = 0; +done: + PyThread_release_lock(chan->mutex); + return res; } +/* the set of channels */ + struct _channelref; typedef struct _channelref { From 4802046131d18a4a8294fcde4349f25dccf09085 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 01:23:36 +0000 Subject: [PATCH 13/19] Add _channelref_free(). --- Modules/_xxsubinterpretersmodule.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index f39068b2131b98..0cd595835832f5 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -894,6 +894,22 @@ _channelref_new(int64_t id, _PyChannelState *chan) return ref; } +//static void +//_channelref_clear(_channelref *ref) +//{ +// ref->id = -1; +// ref->chan = NULL; +// ref->next = NULL; +// ref->objcount = 0; +//} + +static void +_channelref_free(_channelref *ref) +{ + //_channelref_clear(ref); + PyMem_Free(ref); +} + static _channelref * _channelref_find(_channelref *first, int64_t id, _channelref **pprev) { @@ -925,7 +941,6 @@ _channels_init(_channels *channels) if (channels->mutex == NULL) { channels->mutex = PyThread_allocate_lock(); if (channels->mutex == NULL) { - PyMem_Free(channels); PyErr_SetString(ChannelError, "can't initialize mutex for channel management"); return -1; @@ -1061,7 +1076,7 @@ _channels_remove_ref(_channels *channels, _channelref *ref, _channelref *prev, if (pchan != NULL) { *pchan = ref->chan; } - PyMem_Free(ref); + _channelref_free(ref); } static int From ac7bad55356103302af46255bc00bdba497a4860 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 01:50:05 +0000 Subject: [PATCH 14/19] Move the RunFailedError definition down. --- Modules/_xxsubinterpretersmodule.c | 62 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 0cd595835832f5..e35dfe0b0c1830 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -64,6 +64,7 @@ _coerce_id(PyObject *id) return cid; } + /* data-sharing-specific code ***********************************************/ struct _sharednsitem { @@ -282,45 +283,26 @@ _sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb) return err; } -static PyObject * RunFailedError; - -static int -interp_exceptions_init(PyObject *ns) -{ - // XXX Move the exceptions into per-module memory? - - // An uncaught exception came out of interp_run_string(). - RunFailedError = PyErr_NewException("_xxsubinterpreters.RunFailedError", - PyExc_RuntimeError, NULL); - if (RunFailedError == NULL) { - return -1; - } - if (PyDict_SetItemString(ns, "RunFailedError", RunFailedError) != 0) { - return -1; - } - - return 0; -} - static void -_sharedexception_apply(_sharedexception *exc) +_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass) { if (exc->name != NULL) { if (exc->msg != NULL) { - PyErr_Format(RunFailedError, "%s: %s", exc->name, exc->msg); + PyErr_Format(wrapperclass, "%s: %s", exc->name, exc->msg); } else { - PyErr_SetString(RunFailedError, exc->name); + PyErr_SetString(wrapperclass, exc->name); } } else if (exc->msg != NULL) { - PyErr_SetString(RunFailedError, exc->msg); + PyErr_SetString(wrapperclass, exc->msg); } else { - PyErr_SetNone(RunFailedError); + PyErr_SetNone(wrapperclass); } } + /* channel-specific code ****************************************************/ static PyObject *ChannelError; @@ -1639,7 +1621,30 @@ static PyTypeObject ChannelIDtype = { NULL, /* tp_new */ }; -/* interpreter-specific functions *******************************************/ + +/* interpreter-specific code ************************************************/ + +static PyObject * RunFailedError = NULL; + +static int +interp_exceptions_init(PyObject *ns) +{ + // XXX Move the exceptions into per-module memory? + + if (RunFailedError == NULL) { + // An uncaught exception came out of interp_run_string(). + RunFailedError = PyErr_NewException("_xxsubinterpreters.RunFailedError", + PyExc_RuntimeError, NULL); + if (RunFailedError == NULL) { + return -1; + } + if (PyDict_SetItemString(ns, "RunFailedError", RunFailedError) != 0) { + return -1; + } + } + + return 0; +} static PyInterpreterState * _look_up(PyObject *requested_id) @@ -1748,11 +1753,12 @@ _run_script(PyInterpreterState *interp, const char *codestr, if (sharedexc == NULL) { fprintf(stderr, "RunFailedError: script raised an uncaught exception"); PyErr_Clear(); + sharedexc = NULL; } else { assert(!PyErr_Occurred()); - *exc = sharedexc; } + *exc = sharedexc; return -1; } @@ -1784,7 +1790,7 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, // Propagate any exception out to the caller. if (exc != NULL) { - _sharedexception_apply(exc); + _sharedexception_apply(exc, RunFailedError); _sharedexception_free(exc); } else if (result != 0) { From 0689a9ceebde5645f41fe887efd8a48028ad40f7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 03:36:06 +0000 Subject: [PATCH 15/19] Do not INCREF after PyErr_Fetch(). --- Modules/_xxsubinterpretersmodule.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index e35dfe0b0c1830..c0251cbd695356 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1739,15 +1739,12 @@ _run_script(PyInterpreterState *interp, const char *codestr, *exc = NULL; return 0; + error: PyErr_Fetch(&exctype, &excval, &tb); - Py_INCREF(exctype); - Py_XINCREF(excval); - Py_XINCREF(tb); - PyErr_Clear(); _sharedexception *sharedexc = _sharedexception_bind(exctype, excval, tb); - Py_DECREF(exctype); + Py_XDECREF(exctype); Py_XDECREF(excval); Py_XDECREF(tb); if (sharedexc == NULL) { From 9a91a287dbfb78520b0f3adfeefb0112d522fb93 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 03:36:30 +0000 Subject: [PATCH 16/19] DECREF the exception name. --- Modules/_xxsubinterpretersmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index c0251cbd695356..7949b1cf9f4c5c 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -246,6 +246,7 @@ _sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb) goto finally; } err->name = _copy_raw_string(name); + Py_DECREF(name); if (err->name == NULL) { if (PyErr_ExceptionMatches(PyExc_MemoryError)) { failure = "out of memory copying exception type name"; From 654e59adb98d96da4d6282617d7bd99d3d6e5e47 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 04:00:51 +0000 Subject: [PATCH 17/19] Free the channel when we close it. --- Modules/_xxsubinterpretersmodule.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 7949b1cf9f4c5c..c8026934f39e33 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1035,6 +1035,9 @@ _channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan) if (pchan != NULL) { *pchan = ref->chan; } + else { + _channel_free(ref->chan); + } ref->chan = NULL; } From 8ca3c9691db0a4412c9480cc72722958d3d2af66 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 04:14:29 +0000 Subject: [PATCH 18/19] Free the data when we are done with it. --- Modules/_xxsubinterpretersmodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index c8026934f39e33..ca208b626a181c 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1260,6 +1260,7 @@ _channel_recv(_channels *channels, int64_t id) return NULL; } _PyCrossInterpreterData_Release(data); + PyMem_Free(data); return obj; } @@ -1281,7 +1282,7 @@ _channel_drop(_channels *channels, int64_t id, int send, int recv) // Past this point we are responsible for releasing the mutex. // Close one or both of the two ends. - int res =_channel_close_interpreter(chan, interp->id, send-recv); + int res = _channel_close_interpreter(chan, interp->id, send-recv); PyThread_release_lock(mutex); return res; } From 702876b9fee86bd328921a2b0ebae0c69ed60002 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Sat, 3 Feb 2018 04:22:38 +0000 Subject: [PATCH 19/19] DECREF the ID in channelid_hash(). --- Modules/_xxsubinterpretersmodule.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index ca208b626a181c..7829b4cd951156 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1464,7 +1464,9 @@ channelid_hash(PyObject *self) if (id == NULL) { return -1; } - return PyObject_Hash(id); + Py_hash_t hash = PyObject_Hash(id); + Py_DECREF(id); + return hash; } static PyObject *