Skip to content

Commit 2bc154f

Browse files
ericsnowcurrentlyGlyphack
authored andcommitted
pythongh-76785: Module-level Fixes for test.support.interpreters (pythongh-110236)
* add RecvChannel.close() and SendChannel.close() * make RecvChannel and SendChannel shareable * expose ChannelEmptyError and ChannelNotEmptyError
1 parent 6097054 commit 2bc154f

File tree

3 files changed

+206
-25
lines changed

3 files changed

+206
-25
lines changed

Lib/test/support/interpreters.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
# aliases:
88
from _xxsubinterpreters import is_shareable
99
from _xxinterpchannels import (
10-
ChannelError, ChannelNotFoundError, ChannelEmptyError,
10+
ChannelError, ChannelNotFoundError, ChannelClosedError,
11+
ChannelEmptyError, ChannelNotEmptyError,
1112
)
1213

1314

@@ -117,10 +118,16 @@ def list_all_channels():
117118
class _ChannelEnd:
118119
"""The base class for RecvChannel and SendChannel."""
119120

120-
def __init__(self, id):
121-
if not isinstance(id, (int, _channels.ChannelID)):
122-
raise TypeError(f'id must be an int, got {id!r}')
123-
self._id = id
121+
_end = None
122+
123+
def __init__(self, cid):
124+
if self._end == 'send':
125+
cid = _channels._channel_id(cid, send=True, force=True)
126+
elif self._end == 'recv':
127+
cid = _channels._channel_id(cid, recv=True, force=True)
128+
else:
129+
raise NotImplementedError(self._end)
130+
self._id = cid
124131

125132
def __repr__(self):
126133
return f'{type(self).__name__}(id={int(self._id)})'
@@ -147,6 +154,8 @@ def id(self):
147154
class RecvChannel(_ChannelEnd):
148155
"""The receiving end of a cross-interpreter channel."""
149156

157+
_end = 'recv'
158+
150159
def recv(self, *, _sentinel=object(), _delay=10 / 1000): # 10 milliseconds
151160
"""Return the next object from the channel.
152161
@@ -171,10 +180,15 @@ def recv_nowait(self, default=_NOT_SET):
171180
else:
172181
return _channels.recv(self._id, default)
173182

183+
def close(self):
184+
_channels.close(self._id, recv=True)
185+
174186

175187
class SendChannel(_ChannelEnd):
176188
"""The sending end of a cross-interpreter channel."""
177189

190+
_end = 'send'
191+
178192
def send(self, obj):
179193
"""Send the object (i.e. its data) to the channel's receiving end.
180194
@@ -196,3 +210,9 @@ def send_nowait(self, obj):
196210
# None. This should be fixed when channel_send_wait() is added.
197211
# See bpo-32604 and gh-19829.
198212
return _channels.send(self._id, obj)
213+
214+
def close(self):
215+
_channels.close(self._id, send=True)
216+
217+
218+
_channels._register_end_types(SendChannel, RecvChannel)

Lib/test/test_interpreters.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,22 @@ def test_list_all(self):
822822
after = set(interpreters.list_all_channels())
823823
self.assertEqual(after, created)
824824

825+
def test_shareable(self):
826+
rch, sch = interpreters.create_channel()
827+
828+
self.assertTrue(
829+
interpreters.is_shareable(rch))
830+
self.assertTrue(
831+
interpreters.is_shareable(sch))
832+
833+
sch.send_nowait(rch)
834+
sch.send_nowait(sch)
835+
rch2 = rch.recv()
836+
sch2 = rch.recv()
837+
838+
self.assertEqual(rch2, rch)
839+
self.assertEqual(sch2, sch)
840+
825841

826842
class TestRecvChannelAttrs(TestBase):
827843

Modules/_xxinterpchannelsmodule.c

Lines changed: 165 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ _release_xid_data(_PyCrossInterpreterData *data, int flags)
198198
/* module state *************************************************************/
199199

200200
typedef struct {
201+
PyTypeObject *send_channel_type;
202+
PyTypeObject *recv_channel_type;
203+
201204
/* heap types */
202205
PyTypeObject *ChannelIDType;
203206

@@ -218,6 +221,21 @@ get_module_state(PyObject *mod)
218221
return state;
219222
}
220223

224+
static module_state *
225+
_get_current_module_state(void)
226+
{
227+
PyObject *mod = _get_current_module();
228+
if (mod == NULL) {
229+
// XXX import it?
230+
PyErr_SetString(PyExc_RuntimeError,
231+
MODULE_NAME " module not imported yet");
232+
return NULL;
233+
}
234+
module_state *state = get_module_state(mod);
235+
Py_DECREF(mod);
236+
return state;
237+
}
238+
221239
static int
222240
traverse_module_state(module_state *state, visitproc visit, void *arg)
223241
{
@@ -237,6 +255,9 @@ traverse_module_state(module_state *state, visitproc visit, void *arg)
237255
static int
238256
clear_module_state(module_state *state)
239257
{
258+
Py_CLEAR(state->send_channel_type);
259+
Py_CLEAR(state->recv_channel_type);
260+
240261
/* heap types */
241262
if (state->ChannelIDType != NULL) {
242263
(void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType);
@@ -1529,17 +1550,20 @@ typedef struct channelid {
15291550
struct channel_id_converter_data {
15301551
PyObject *module;
15311552
int64_t cid;
1553+
int end;
15321554
};
15331555

15341556
static int
15351557
channel_id_converter(PyObject *arg, void *ptr)
15361558
{
15371559
int64_t cid;
1560+
int end = 0;
15381561
struct channel_id_converter_data *data = ptr;
15391562
module_state *state = get_module_state(data->module);
15401563
assert(state != NULL);
15411564
if (PyObject_TypeCheck(arg, state->ChannelIDType)) {
15421565
cid = ((channelid *)arg)->id;
1566+
end = ((channelid *)arg)->end;
15431567
}
15441568
else if (PyIndex_Check(arg)) {
15451569
cid = PyLong_AsLongLong(arg);
@@ -1559,6 +1583,7 @@ channel_id_converter(PyObject *arg, void *ptr)
15591583
return 0;
15601584
}
15611585
data->cid = cid;
1586+
data->end = end;
15621587
return 1;
15631588
}
15641589

@@ -1600,6 +1625,7 @@ _channelid_new(PyObject *mod, PyTypeObject *cls,
16001625
{
16011626
static char *kwlist[] = {"id", "send", "recv", "force", "_resolve", NULL};
16021627
int64_t cid;
1628+
int end;
16031629
struct channel_id_converter_data cid_data = {
16041630
.module = mod,
16051631
};
@@ -1614,21 +1640,25 @@ _channelid_new(PyObject *mod, PyTypeObject *cls,
16141640
return NULL;
16151641
}
16161642
cid = cid_data.cid;
1643+
end = cid_data.end;
16171644

16181645
// Handle "send" and "recv".
16191646
if (send == 0 && recv == 0) {
16201647
PyErr_SetString(PyExc_ValueError,
16211648
"'send' and 'recv' cannot both be False");
16221649
return NULL;
16231650
}
1624-
1625-
int end = 0;
1626-
if (send == 1) {
1651+
else if (send == 1) {
16271652
if (recv == 0 || recv == -1) {
16281653
end = CHANNEL_SEND;
16291654
}
1655+
else {
1656+
assert(recv == 1);
1657+
end = 0;
1658+
}
16301659
}
16311660
else if (recv == 1) {
1661+
assert(send == 0 || send == -1);
16321662
end = CHANNEL_RECV;
16331663
}
16341664

@@ -1773,21 +1803,12 @@ channelid_richcompare(PyObject *self, PyObject *other, int op)
17731803
return res;
17741804
}
17751805

1806+
static PyTypeObject * _get_current_channel_end_type(int end);
1807+
17761808
static PyObject *
17771809
_channel_from_cid(PyObject *cid, int end)
17781810
{
1779-
PyObject *highlevel = PyImport_ImportModule("interpreters");
1780-
if (highlevel == NULL) {
1781-
PyErr_Clear();
1782-
highlevel = PyImport_ImportModule("test.support.interpreters");
1783-
if (highlevel == NULL) {
1784-
return NULL;
1785-
}
1786-
}
1787-
const char *clsname = (end == CHANNEL_RECV) ? "RecvChannel" :
1788-
"SendChannel";
1789-
PyObject *cls = PyObject_GetAttrString(highlevel, clsname);
1790-
Py_DECREF(highlevel);
1811+
PyObject *cls = (PyObject *)_get_current_channel_end_type(end);
17911812
if (cls == NULL) {
17921813
return NULL;
17931814
}
@@ -1943,6 +1964,103 @@ static PyType_Spec ChannelIDType_spec = {
19431964
};
19441965

19451966

1967+
/* SendChannel and RecvChannel classes */
1968+
1969+
// XXX Use a new __xid__ protocol instead?
1970+
1971+
static PyTypeObject *
1972+
_get_current_channel_end_type(int end)
1973+
{
1974+
module_state *state = _get_current_module_state();
1975+
if (state == NULL) {
1976+
return NULL;
1977+
}
1978+
PyTypeObject *cls;
1979+
if (end == CHANNEL_SEND) {
1980+
cls = state->send_channel_type;
1981+
}
1982+
else {
1983+
assert(end == CHANNEL_RECV);
1984+
cls = state->recv_channel_type;
1985+
}
1986+
if (cls == NULL) {
1987+
PyObject *highlevel = PyImport_ImportModule("interpreters");
1988+
if (highlevel == NULL) {
1989+
PyErr_Clear();
1990+
highlevel = PyImport_ImportModule("test.support.interpreters");
1991+
if (highlevel == NULL) {
1992+
return NULL;
1993+
}
1994+
}
1995+
if (end == CHANNEL_SEND) {
1996+
cls = state->send_channel_type;
1997+
}
1998+
else {
1999+
cls = state->recv_channel_type;
2000+
}
2001+
assert(cls != NULL);
2002+
}
2003+
return cls;
2004+
}
2005+
2006+
static PyObject *
2007+
_channel_end_from_xid(_PyCrossInterpreterData *data)
2008+
{
2009+
channelid *cid = (channelid *)_channelid_from_xid(data);
2010+
if (cid == NULL) {
2011+
return NULL;
2012+
}
2013+
PyTypeObject *cls = _get_current_channel_end_type(cid->end);
2014+
if (cls == NULL) {
2015+
return NULL;
2016+
}
2017+
PyObject *obj = PyObject_CallOneArg((PyObject *)cls, (PyObject *)cid);
2018+
Py_DECREF(cid);
2019+
return obj;
2020+
}
2021+
2022+
static int
2023+
_channel_end_shared(PyThreadState *tstate, PyObject *obj,
2024+
_PyCrossInterpreterData *data)
2025+
{
2026+
PyObject *cidobj = PyObject_GetAttrString(obj, "_id");
2027+
if (cidobj == NULL) {
2028+
return -1;
2029+
}
2030+
if (_channelid_shared(tstate, cidobj, data) < 0) {
2031+
return -1;
2032+
}
2033+
data->new_object = _channel_end_from_xid;
2034+
return 0;
2035+
}
2036+
2037+
static int
2038+
set_channel_end_types(PyObject *mod, PyTypeObject *send, PyTypeObject *recv)
2039+
{
2040+
module_state *state = get_module_state(mod);
2041+
if (state == NULL) {
2042+
return -1;
2043+
}
2044+
2045+
if (state->send_channel_type != NULL
2046+
|| state->recv_channel_type != NULL)
2047+
{
2048+
PyErr_SetString(PyExc_TypeError, "already registered");
2049+
return -1;
2050+
}
2051+
state->send_channel_type = (PyTypeObject *)Py_NewRef(send);
2052+
state->recv_channel_type = (PyTypeObject *)Py_NewRef(recv);
2053+
2054+
if (_PyCrossInterpreterData_RegisterClass(send, _channel_end_shared)) {
2055+
return -1;
2056+
}
2057+
if (_PyCrossInterpreterData_RegisterClass(recv, _channel_end_shared)) {
2058+
return -1;
2059+
}
2060+
2061+
return 0;
2062+
}
2063+
19462064
/* module level code ********************************************************/
19472065

19482066
/* globals is the process-global state for the module. It holds all
@@ -2346,13 +2464,38 @@ channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
23462464
return NULL;
23472465
}
23482466
PyTypeObject *cls = state->ChannelIDType;
2349-
PyObject *mod = get_module_from_owned_type(cls);
2350-
if (mod == NULL) {
2467+
assert(get_module_from_owned_type(cls) == self);
2468+
2469+
return _channelid_new(self, cls, args, kwds);
2470+
}
2471+
2472+
static PyObject *
2473+
channel__register_end_types(PyObject *self, PyObject *args, PyObject *kwds)
2474+
{
2475+
static char *kwlist[] = {"send", "recv", NULL};
2476+
PyObject *send;
2477+
PyObject *recv;
2478+
if (!PyArg_ParseTupleAndKeywords(args, kwds,
2479+
"OO:_register_end_types", kwlist,
2480+
&send, &recv)) {
23512481
return NULL;
23522482
}
2353-
PyObject *cid = _channelid_new(mod, cls, args, kwds);
2354-
Py_DECREF(mod);
2355-
return cid;
2483+
if (!PyType_Check(send)) {
2484+
PyErr_SetString(PyExc_TypeError, "expected a type for 'send'");
2485+
return NULL;
2486+
}
2487+
if (!PyType_Check(recv)) {
2488+
PyErr_SetString(PyExc_TypeError, "expected a type for 'recv'");
2489+
return NULL;
2490+
}
2491+
PyTypeObject *cls_send = (PyTypeObject *)send;
2492+
PyTypeObject *cls_recv = (PyTypeObject *)recv;
2493+
2494+
if (set_channel_end_types(self, cls_send, cls_recv) < 0) {
2495+
return NULL;
2496+
}
2497+
2498+
Py_RETURN_NONE;
23562499
}
23572500

23582501
static PyMethodDef module_functions[] = {
@@ -2374,6 +2517,8 @@ static PyMethodDef module_functions[] = {
23742517
METH_VARARGS | METH_KEYWORDS, channel_release_doc},
23752518
{"_channel_id", _PyCFunction_CAST(channel__channel_id),
23762519
METH_VARARGS | METH_KEYWORDS, NULL},
2520+
{"_register_end_types", _PyCFunction_CAST(channel__register_end_types),
2521+
METH_VARARGS | METH_KEYWORDS, NULL},
23772522

23782523
{NULL, NULL} /* sentinel */
23792524
};

0 commit comments

Comments
 (0)