@@ -402,6 +402,7 @@ typedef struct {
402
402
PyTypeObject * recv_channel_type ;
403
403
404
404
/* heap types */
405
+ PyTypeObject * ChannelInfoType ;
405
406
PyTypeObject * ChannelIDType ;
406
407
PyTypeObject * XIBufferViewType ;
407
408
@@ -441,6 +442,7 @@ static int
441
442
traverse_module_state (module_state * state , visitproc visit , void * arg )
442
443
{
443
444
/* heap types */
445
+ Py_VISIT (state -> ChannelInfoType );
444
446
Py_VISIT (state -> ChannelIDType );
445
447
Py_VISIT (state -> XIBufferViewType );
446
448
@@ -457,10 +459,12 @@ traverse_module_state(module_state *state, visitproc visit, void *arg)
457
459
static int
458
460
clear_module_state (module_state * state )
459
461
{
462
+ /* external types */
460
463
Py_CLEAR (state -> send_channel_type );
461
464
Py_CLEAR (state -> recv_channel_type );
462
465
463
466
/* heap types */
467
+ Py_CLEAR (state -> ChannelInfoType );
464
468
if (state -> ChannelIDType != NULL ) {
465
469
(void )_PyCrossInterpreterData_UnregisterClass (state -> ChannelIDType );
466
470
}
@@ -2088,6 +2092,117 @@ channel_is_associated(_channels *channels, int64_t cid, int64_t interpid,
2088
2092
}
2089
2093
2090
2094
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
+
2091
2206
/* ChannelID class */
2092
2207
2093
2208
typedef struct channelid {
@@ -3079,6 +3194,33 @@ Close the channel for the current interpreter. 'send' and 'recv'\n\
3079
3194
(bool) may be used to indicate the ends to close. By default both\n\
3080
3195
ends are closed. Closing an already closed end is a noop." );
3081
3196
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
+
3082
3224
static PyObject *
3083
3225
channelsmod__channel_id (PyObject * self , PyObject * args , PyObject * kwds )
3084
3226
{
@@ -3143,6 +3285,8 @@ static PyMethodDef module_functions[] = {
3143
3285
METH_VARARGS | METH_KEYWORDS , channelsmod_close_doc },
3144
3286
{"release" , _PyCFunction_CAST (channelsmod_release ),
3145
3287
METH_VARARGS | METH_KEYWORDS , channelsmod_release_doc },
3288
+ {"get_info" , _PyCFunction_CAST (channelsmod_get_info ),
3289
+ METH_VARARGS | METH_KEYWORDS , channelsmod_get_info_doc },
3146
3290
{"_channel_id" , _PyCFunction_CAST (channelsmod__channel_id ),
3147
3291
METH_VARARGS | METH_KEYWORDS , NULL },
3148
3292
{"_register_end_types" , _PyCFunction_CAST (channelsmod__register_end_types ),
@@ -3179,19 +3323,30 @@ module_exec(PyObject *mod)
3179
3323
3180
3324
/* Add other types */
3181
3325
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
+
3182
3335
// ChannelID
3183
3336
state -> ChannelIDType = add_new_type (
3184
3337
mod , & channelid_typespec , _channelid_shared , xid_classes );
3185
3338
if (state -> ChannelIDType == NULL ) {
3186
3339
goto error ;
3187
3340
}
3188
3341
3342
+ // XIBufferView
3189
3343
state -> XIBufferViewType = add_new_type (mod , & XIBufferViewType_spec , NULL ,
3190
3344
xid_classes );
3191
3345
if (state -> XIBufferViewType == NULL ) {
3192
3346
goto error ;
3193
3347
}
3194
3348
3349
+ // Register external types.
3195
3350
if (register_builtin_xid_types (xid_classes ) < 0 ) {
3196
3351
goto error ;
3197
3352
}
0 commit comments