Skip to content

Commit f897d61

Browse files
Add _channels.get_info().
1 parent 6292116 commit f897d61

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

Lib/test/support/interpreters.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ def __eq__(self, other):
161161
def id(self):
162162
return self._id
163163

164+
@property
165+
def _info(self):
166+
return _channels.get_info(self._id)
167+
164168

165169
_NOT_SET = object()
166170

Modules/_xxinterpchannelsmodule.c

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ typedef struct {
402402
PyTypeObject *recv_channel_type;
403403

404404
/* heap types */
405+
PyTypeObject *ChannelInfoType;
405406
PyTypeObject *ChannelIDType;
406407
PyTypeObject *XIBufferViewType;
407408

@@ -441,6 +442,7 @@ static int
441442
traverse_module_state(module_state *state, visitproc visit, void *arg)
442443
{
443444
/* heap types */
445+
Py_VISIT(state->ChannelInfoType);
444446
Py_VISIT(state->ChannelIDType);
445447
Py_VISIT(state->XIBufferViewType);
446448

@@ -457,10 +459,12 @@ traverse_module_state(module_state *state, visitproc visit, void *arg)
457459
static int
458460
clear_module_state(module_state *state)
459461
{
462+
/* external types */
460463
Py_CLEAR(state->send_channel_type);
461464
Py_CLEAR(state->recv_channel_type);
462465

463466
/* heap types */
467+
Py_CLEAR(state->ChannelInfoType);
464468
if (state->ChannelIDType != NULL) {
465469
(void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType);
466470
}
@@ -2088,6 +2092,117 @@ channel_is_associated(_channels *channels, int64_t cid, int64_t interpid,
20882092
}
20892093

20902094

2095+
/* channel info */
2096+
2097+
struct channel_info {
2098+
struct {
2099+
// 1: closed; -1: closing
2100+
int closed;
2101+
} status;
2102+
Py_ssize_t count;
2103+
};
2104+
2105+
static int
2106+
_channel_get_info(_channels *channels, int64_t cid, struct channel_info *info)
2107+
{
2108+
int err = 0;
2109+
*info = (struct channel_info){0};
2110+
2111+
// Hold the global lock until we're done.
2112+
PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
2113+
2114+
// Find the channel.
2115+
_channelref *ref = _channelref_find(channels->head, cid, NULL);
2116+
if (ref == NULL) {
2117+
err = ERR_CHANNEL_NOT_FOUND;
2118+
goto finally;
2119+
}
2120+
_channel_state *chan = ref->chan;
2121+
2122+
// Check if open.
2123+
if (chan == NULL) {
2124+
info->status.closed = 1;
2125+
goto finally;
2126+
}
2127+
if (!chan->open) {
2128+
assert(chan->queue->count == 0);
2129+
info->status.closed = 1;
2130+
goto finally;
2131+
}
2132+
if (chan->closing != NULL) {
2133+
assert(chan->queue->count > 0);
2134+
info->status.closed = -1;
2135+
}
2136+
else {
2137+
info->status.closed = 0;
2138+
}
2139+
2140+
// Get the number of queued objects.
2141+
info->count = chan->queue->count;
2142+
2143+
finally:
2144+
PyThread_release_lock(channels->mutex);
2145+
return err;
2146+
}
2147+
2148+
PyDoc_STRVAR(channel_info_doc,
2149+
"ChannelInfo\n\
2150+
\n\
2151+
A named tuple of a channel's state.");
2152+
2153+
static PyStructSequence_Field channel_info_fields[] = {
2154+
{"open", "both ends are open"},
2155+
{"closing", "send is closed, recv is non-empty"},
2156+
{"closed", "both ends are closed"},
2157+
{"count", "queued objects"},
2158+
{0}
2159+
};
2160+
2161+
static PyStructSequence_Desc channel_info_desc = {
2162+
.name = "ChannelInfo",
2163+
.doc = channel_info_doc,
2164+
.fields = channel_info_fields,
2165+
.n_in_sequence = 4,
2166+
};
2167+
2168+
static PyObject *
2169+
new_channel_info(PyObject *mod, struct channel_info *info)
2170+
{
2171+
module_state *state = get_module_state(mod);
2172+
if (state == NULL) {
2173+
return NULL;
2174+
}
2175+
2176+
assert(state->ChannelInfoType != NULL);
2177+
PyObject *self = PyStructSequence_New(state->ChannelInfoType);
2178+
if (self == NULL) {
2179+
return NULL;
2180+
}
2181+
2182+
int pos = 0;
2183+
#define SET_BOOL(val) \
2184+
PyStructSequence_SET_ITEM(self, pos++, \
2185+
Py_NewRef(val ? Py_True : Py_False))
2186+
#define SET_COUNT(val) \
2187+
do { \
2188+
PyObject *obj = PyLong_FromLongLong(val); \
2189+
if (obj == NULL) { \
2190+
Py_CLEAR(info); \
2191+
return NULL; \
2192+
} \
2193+
PyStructSequence_SET_ITEM(self, pos++, obj); \
2194+
} while(0)
2195+
SET_BOOL(info->status.closed == 0);
2196+
SET_BOOL(info->status.closed == -1);
2197+
SET_BOOL(info->status.closed == 1);
2198+
SET_COUNT(info->count);
2199+
#undef SET_COUNT
2200+
#undef SET_BOOL
2201+
assert(!PyErr_Occurred());
2202+
return self;
2203+
}
2204+
2205+
20912206
/* ChannelID class */
20922207

20932208
typedef struct channelid {
@@ -3079,6 +3194,33 @@ Close the channel for the current interpreter. 'send' and 'recv'\n\
30793194
(bool) may be used to indicate the ends to close. By default both\n\
30803195
ends are closed. Closing an already closed end is a noop.");
30813196

3197+
static PyObject *
3198+
channelsmod_get_info(PyObject *self, PyObject *args, PyObject *kwds)
3199+
{
3200+
static char *kwlist[] = {"cid", NULL};
3201+
struct channel_id_converter_data cid_data = {
3202+
.module = self,
3203+
};
3204+
if (!PyArg_ParseTupleAndKeywords(args, kwds,
3205+
"O&:_get_info", kwlist,
3206+
channel_id_converter, &cid_data)) {
3207+
return NULL;
3208+
}
3209+
int64_t cid = cid_data.cid;
3210+
3211+
struct channel_info info;
3212+
int err = _channel_get_info(&_globals.channels, cid, &info);
3213+
if (handle_channel_error(err, self, cid)) {
3214+
return NULL;
3215+
}
3216+
return new_channel_info(self, &info);
3217+
}
3218+
3219+
PyDoc_STRVAR(channelsmod_get_info_doc,
3220+
"get_info(cid)\n\
3221+
\n\
3222+
Return details about the channel.");
3223+
30823224
static PyObject *
30833225
channelsmod__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
30843226
{
@@ -3143,6 +3285,8 @@ static PyMethodDef module_functions[] = {
31433285
METH_VARARGS | METH_KEYWORDS, channelsmod_close_doc},
31443286
{"release", _PyCFunction_CAST(channelsmod_release),
31453287
METH_VARARGS | METH_KEYWORDS, channelsmod_release_doc},
3288+
{"get_info", _PyCFunction_CAST(channelsmod_get_info),
3289+
METH_VARARGS | METH_KEYWORDS, channelsmod_get_info_doc},
31463290
{"_channel_id", _PyCFunction_CAST(channelsmod__channel_id),
31473291
METH_VARARGS | METH_KEYWORDS, NULL},
31483292
{"_register_end_types", _PyCFunction_CAST(channelsmod__register_end_types),
@@ -3179,19 +3323,30 @@ module_exec(PyObject *mod)
31793323

31803324
/* Add other types */
31813325

3326+
// ChannelInfo
3327+
state->ChannelInfoType = PyStructSequence_NewType(&channel_info_desc);
3328+
if (state->ChannelInfoType == NULL) {
3329+
goto error;
3330+
}
3331+
if (PyModule_AddType(mod, state->ChannelInfoType) < 0) {
3332+
goto error;
3333+
}
3334+
31823335
// ChannelID
31833336
state->ChannelIDType = add_new_type(
31843337
mod, &channelid_typespec, _channelid_shared, xid_classes);
31853338
if (state->ChannelIDType == NULL) {
31863339
goto error;
31873340
}
31883341

3342+
// XIBufferView
31893343
state->XIBufferViewType = add_new_type(mod, &XIBufferViewType_spec, NULL,
31903344
xid_classes);
31913345
if (state->XIBufferViewType == NULL) {
31923346
goto error;
31933347
}
31943348

3349+
// Register external types.
31953350
if (register_builtin_xid_types(xid_classes) < 0) {
31963351
goto error;
31973352
}

0 commit comments

Comments
 (0)