@@ -223,8 +223,8 @@ static PyTypeObject *
223
223
add_new_type (PyObject * mod , PyType_Spec * spec , crossinterpdatafunc shared ,
224
224
struct xid_class_registry * classes )
225
225
{
226
- PyTypeObject * cls = (PyTypeObject * )PyType_FromMetaclass (
227
- NULL , mod , spec , NULL );
226
+ PyTypeObject * cls = (PyTypeObject * )PyType_FromModuleAndSpec (
227
+ mod , spec , NULL );
228
228
if (cls == NULL ) {
229
229
return NULL ;
230
230
}
@@ -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,236 @@ 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
+ struct {
2102
+ Py_ssize_t nsend_only ; // not released
2103
+ Py_ssize_t nsend_only_released ;
2104
+ Py_ssize_t nrecv_only ; // not released
2105
+ Py_ssize_t nrecv_only_released ;
2106
+ Py_ssize_t nboth ; // not released
2107
+ Py_ssize_t nboth_released ;
2108
+ Py_ssize_t nboth_send_released ;
2109
+ Py_ssize_t nboth_recv_released ;
2110
+ } all ;
2111
+ struct {
2112
+ // 1: associated; -1: released
2113
+ int send ;
2114
+ int recv ;
2115
+ } cur ;
2116
+ } status ;
2117
+ Py_ssize_t count ;
2118
+ };
2119
+
2120
+ static int
2121
+ _channel_get_info (_channels * channels , int64_t cid , struct channel_info * info )
2122
+ {
2123
+ int err = 0 ;
2124
+ * info = (struct channel_info ){0 };
2125
+
2126
+ // Get the current interpreter.
2127
+ PyInterpreterState * interp = _get_current_interp ();
2128
+ if (interp == NULL ) {
2129
+ return -1 ;
2130
+ }
2131
+ Py_ssize_t interpid = PyInterpreterState_GetID (interp );
2132
+
2133
+ // Hold the global lock until we're done.
2134
+ PyThread_acquire_lock (channels -> mutex , WAIT_LOCK );
2135
+
2136
+ // Find the channel.
2137
+ _channelref * ref = _channelref_find (channels -> head , cid , NULL );
2138
+ if (ref == NULL ) {
2139
+ err = ERR_CHANNEL_NOT_FOUND ;
2140
+ goto finally ;
2141
+ }
2142
+ _channel_state * chan = ref -> chan ;
2143
+
2144
+ // Check if open.
2145
+ if (chan == NULL ) {
2146
+ info -> status .closed = 1 ;
2147
+ goto finally ;
2148
+ }
2149
+ if (!chan -> open ) {
2150
+ assert (chan -> queue -> count == 0 );
2151
+ info -> status .closed = 1 ;
2152
+ goto finally ;
2153
+ }
2154
+ if (chan -> closing != NULL ) {
2155
+ assert (chan -> queue -> count > 0 );
2156
+ info -> status .closed = -1 ;
2157
+ }
2158
+ else {
2159
+ info -> status .closed = 0 ;
2160
+ }
2161
+
2162
+ // Get the number of queued objects.
2163
+ info -> count = chan -> queue -> count ;
2164
+
2165
+ // Get the ends statuses.
2166
+ assert (info -> status .cur .send == 0 );
2167
+ assert (info -> status .cur .recv == 0 );
2168
+ _channelend * send = chan -> ends -> send ;
2169
+ while (send != NULL ) {
2170
+ if (send -> interpid == interpid ) {
2171
+ info -> status .cur .send = send -> open ? 1 : -1 ;
2172
+ }
2173
+
2174
+ if (send -> open ) {
2175
+ info -> status .all .nsend_only += 1 ;
2176
+ }
2177
+ else {
2178
+ info -> status .all .nsend_only_released += 1 ;
2179
+ }
2180
+ send = send -> next ;
2181
+ }
2182
+ _channelend * recv = chan -> ends -> recv ;
2183
+ while (recv != NULL ) {
2184
+ if (recv -> interpid == interpid ) {
2185
+ info -> status .cur .recv = recv -> open ? 1 : -1 ;
2186
+ }
2187
+
2188
+ // XXX This is O(n*n). Why do we have 2 linked lists?
2189
+ _channelend * send = chan -> ends -> send ;
2190
+ while (send != NULL ) {
2191
+ if (send -> interpid == recv -> interpid ) {
2192
+ break ;
2193
+ }
2194
+ send = send -> next ;
2195
+ }
2196
+ if (send == NULL ) {
2197
+ if (recv -> open ) {
2198
+ info -> status .all .nrecv_only += 1 ;
2199
+ }
2200
+ else {
2201
+ info -> status .all .nrecv_only_released += 1 ;
2202
+ }
2203
+ }
2204
+ else {
2205
+ if (recv -> open ) {
2206
+ if (send -> open ) {
2207
+ info -> status .all .nboth += 1 ;
2208
+ info -> status .all .nsend_only -= 1 ;
2209
+ }
2210
+ else {
2211
+ info -> status .all .nboth_recv_released += 1 ;
2212
+ info -> status .all .nsend_only_released -= 1 ;
2213
+ }
2214
+ }
2215
+ else {
2216
+ if (send -> open ) {
2217
+ info -> status .all .nboth_send_released += 1 ;
2218
+ info -> status .all .nsend_only -= 1 ;
2219
+ }
2220
+ else {
2221
+ info -> status .all .nboth_released += 1 ;
2222
+ info -> status .all .nsend_only_released -= 1 ;
2223
+ }
2224
+ }
2225
+ }
2226
+ recv = recv -> next ;
2227
+ }
2228
+
2229
+ finally :
2230
+ PyThread_release_lock (channels -> mutex );
2231
+ return err ;
2232
+ }
2233
+
2234
+ PyDoc_STRVAR (channel_info_doc ,
2235
+ "ChannelInfo\n\
2236
+ \n\
2237
+ A named tuple of a channel's state." );
2238
+
2239
+ static PyStructSequence_Field channel_info_fields [] = {
2240
+ {"open" , "both ends are open" },
2241
+ {"closing" , "send is closed, recv is non-empty" },
2242
+ {"closed" , "both ends are closed" },
2243
+ {"count" , "queued objects" },
2244
+
2245
+ {"num_interp_send" , "interpreters bound to the send end" },
2246
+ {"num_interp_send_released" ,
2247
+ "interpreters bound to the send end and released" },
2248
+
2249
+ {"num_interp_recv" , "interpreters bound to the send end" },
2250
+ {"num_interp_recv_released" ,
2251
+ "interpreters bound to the send end and released" },
2252
+
2253
+ {"num_interp_both" , "interpreters bound to both ends" },
2254
+ {"num_interp_both_released" ,
2255
+ "interpreters bound to both ends and released_from_both" },
2256
+ {"num_interp_both_send_released" ,
2257
+ "interpreters bound to both ends and released_from_the send end" },
2258
+ {"num_interp_both_recv_released" ,
2259
+ "interpreters bound to both ends and released_from_the recv end" },
2260
+
2261
+ {"send_associated" , "current interpreter is bound to the send end" },
2262
+ {"send_released" , "current interpreter *was* bound to the send end" },
2263
+ {"recv_associated" , "current interpreter is bound to the recv end" },
2264
+ {"recv_released" , "current interpreter *was* bound to the recv end" },
2265
+ {0 }
2266
+ };
2267
+
2268
+ static PyStructSequence_Desc channel_info_desc = {
2269
+ .name = MODULE_NAME ".ChannelInfo" ,
2270
+ .doc = channel_info_doc ,
2271
+ .fields = channel_info_fields ,
2272
+ .n_in_sequence = 8 ,
2273
+ };
2274
+
2275
+ static PyObject *
2276
+ new_channel_info (PyObject * mod , struct channel_info * info )
2277
+ {
2278
+ module_state * state = get_module_state (mod );
2279
+ if (state == NULL ) {
2280
+ return NULL ;
2281
+ }
2282
+
2283
+ assert (state -> ChannelInfoType != NULL );
2284
+ PyObject * self = PyStructSequence_New (state -> ChannelInfoType );
2285
+ if (self == NULL ) {
2286
+ return NULL ;
2287
+ }
2288
+
2289
+ int pos = 0 ;
2290
+ #define SET_BOOL (val ) \
2291
+ PyStructSequence_SET_ITEM(self, pos++, \
2292
+ Py_NewRef(val ? Py_True : Py_False))
2293
+ #define SET_COUNT (val ) \
2294
+ do { \
2295
+ PyObject *obj = PyLong_FromLongLong(val); \
2296
+ if (obj == NULL) { \
2297
+ Py_CLEAR(info); \
2298
+ return NULL; \
2299
+ } \
2300
+ PyStructSequence_SET_ITEM(self, pos++, obj); \
2301
+ } while(0)
2302
+ SET_BOOL (info -> status .closed == 0 );
2303
+ SET_BOOL (info -> status .closed == -1 );
2304
+ SET_BOOL (info -> status .closed == 1 );
2305
+ SET_COUNT (info -> count );
2306
+ SET_COUNT (info -> status .all .nsend_only );
2307
+ SET_COUNT (info -> status .all .nsend_only_released );
2308
+ SET_COUNT (info -> status .all .nrecv_only );
2309
+ SET_COUNT (info -> status .all .nrecv_only_released );
2310
+ SET_COUNT (info -> status .all .nboth );
2311
+ SET_COUNT (info -> status .all .nboth_released );
2312
+ SET_COUNT (info -> status .all .nboth_send_released );
2313
+ SET_COUNT (info -> status .all .nboth_recv_released );
2314
+ SET_BOOL (info -> status .cur .send == 1 );
2315
+ SET_BOOL (info -> status .cur .send == -1 );
2316
+ SET_BOOL (info -> status .cur .recv == 1 );
2317
+ SET_BOOL (info -> status .cur .recv == -1 );
2318
+ #undef SET_COUNT
2319
+ #undef SET_BOOL
2320
+ assert (!PyErr_Occurred ());
2321
+ return self ;
2322
+ }
2323
+
2324
+
2091
2325
/* ChannelID class */
2092
2326
2093
2327
typedef struct channelid {
@@ -3079,6 +3313,33 @@ Close the channel for the current interpreter. 'send' and 'recv'\n\
3079
3313
(bool) may be used to indicate the ends to close. By default both\n\
3080
3314
ends are closed. Closing an already closed end is a noop." );
3081
3315
3316
+ static PyObject *
3317
+ channelsmod_get_info (PyObject * self , PyObject * args , PyObject * kwds )
3318
+ {
3319
+ static char * kwlist [] = {"cid" , NULL };
3320
+ struct channel_id_converter_data cid_data = {
3321
+ .module = self ,
3322
+ };
3323
+ if (!PyArg_ParseTupleAndKeywords (args , kwds ,
3324
+ "O&:_get_info" , kwlist ,
3325
+ channel_id_converter , & cid_data )) {
3326
+ return NULL ;
3327
+ }
3328
+ int64_t cid = cid_data .cid ;
3329
+
3330
+ struct channel_info info ;
3331
+ int err = _channel_get_info (& _globals .channels , cid , & info );
3332
+ if (handle_channel_error (err , self , cid )) {
3333
+ return NULL ;
3334
+ }
3335
+ return new_channel_info (self , & info );
3336
+ }
3337
+
3338
+ PyDoc_STRVAR (channelsmod_get_info_doc ,
3339
+ "get_info(cid)\n\
3340
+ \n\
3341
+ Return details about the channel." );
3342
+
3082
3343
static PyObject *
3083
3344
channelsmod__channel_id (PyObject * self , PyObject * args , PyObject * kwds )
3084
3345
{
@@ -3143,6 +3404,8 @@ static PyMethodDef module_functions[] = {
3143
3404
METH_VARARGS | METH_KEYWORDS , channelsmod_close_doc },
3144
3405
{"release" , _PyCFunction_CAST (channelsmod_release ),
3145
3406
METH_VARARGS | METH_KEYWORDS , channelsmod_release_doc },
3407
+ {"get_info" , _PyCFunction_CAST (channelsmod_get_info ),
3408
+ METH_VARARGS | METH_KEYWORDS , channelsmod_get_info_doc },
3146
3409
{"_channel_id" , _PyCFunction_CAST (channelsmod__channel_id ),
3147
3410
METH_VARARGS | METH_KEYWORDS , NULL },
3148
3411
{"_register_end_types" , _PyCFunction_CAST (channelsmod__register_end_types ),
@@ -3179,19 +3442,30 @@ module_exec(PyObject *mod)
3179
3442
3180
3443
/* Add other types */
3181
3444
3445
+ // ChannelInfo
3446
+ state -> ChannelInfoType = PyStructSequence_NewType (& channel_info_desc );
3447
+ if (state -> ChannelInfoType == NULL ) {
3448
+ goto error ;
3449
+ }
3450
+ if (PyModule_AddType (mod , state -> ChannelInfoType ) < 0 ) {
3451
+ goto error ;
3452
+ }
3453
+
3182
3454
// ChannelID
3183
3455
state -> ChannelIDType = add_new_type (
3184
3456
mod , & channelid_typespec , _channelid_shared , xid_classes );
3185
3457
if (state -> ChannelIDType == NULL ) {
3186
3458
goto error ;
3187
3459
}
3188
3460
3461
+ // XIBufferView
3189
3462
state -> XIBufferViewType = add_new_type (mod , & XIBufferViewType_spec , NULL ,
3190
3463
xid_classes );
3191
3464
if (state -> XIBufferViewType == NULL ) {
3192
3465
goto error ;
3193
3466
}
3194
3467
3468
+ // Register external types.
3195
3469
if (register_builtin_xid_types (xid_classes ) < 0 ) {
3196
3470
goto error ;
3197
3471
}
0 commit comments