@@ -223,8 +223,8 @@ static PyTypeObject *
223223add_new_type (PyObject * mod , PyType_Spec * spec , crossinterpdatafunc shared ,
224224 struct xid_class_registry * classes )
225225{
226- PyTypeObject * cls = (PyTypeObject * )PyType_FromMetaclass (
227- NULL , mod , spec , NULL );
226+ PyTypeObject * cls = (PyTypeObject * )PyType_FromModuleAndSpec (
227+ mod , spec , NULL );
228228 if (cls == NULL ) {
229229 return NULL ;
230230 }
@@ -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
441442traverse_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)
457459static int
458460clear_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,236 @@ 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+ 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+
20912325/* ChannelID class */
20922326
20932327typedef struct channelid {
@@ -3079,6 +3313,33 @@ Close the channel for the current interpreter. 'send' and 'recv'\n\
30793313(bool) may be used to indicate the ends to close. By default both\n\
30803314ends are closed. Closing an already closed end is a noop." );
30813315
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+
30823343static PyObject *
30833344channelsmod__channel_id (PyObject * self , PyObject * args , PyObject * kwds )
30843345{
@@ -3143,6 +3404,8 @@ static PyMethodDef module_functions[] = {
31433404 METH_VARARGS | METH_KEYWORDS , channelsmod_close_doc },
31443405 {"release" , _PyCFunction_CAST (channelsmod_release ),
31453406 METH_VARARGS | METH_KEYWORDS , channelsmod_release_doc },
3407+ {"get_info" , _PyCFunction_CAST (channelsmod_get_info ),
3408+ METH_VARARGS | METH_KEYWORDS , channelsmod_get_info_doc },
31463409 {"_channel_id" , _PyCFunction_CAST (channelsmod__channel_id ),
31473410 METH_VARARGS | METH_KEYWORDS , NULL },
31483411 {"_register_end_types" , _PyCFunction_CAST (channelsmod__register_end_types ),
@@ -3179,19 +3442,30 @@ module_exec(PyObject *mod)
31793442
31803443 /* Add other types */
31813444
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+
31823454 // ChannelID
31833455 state -> ChannelIDType = add_new_type (
31843456 mod , & channelid_typespec , _channelid_shared , xid_classes );
31853457 if (state -> ChannelIDType == NULL ) {
31863458 goto error ;
31873459 }
31883460
3461+ // XIBufferView
31893462 state -> XIBufferViewType = add_new_type (mod , & XIBufferViewType_spec , NULL ,
31903463 xid_classes );
31913464 if (state -> XIBufferViewType == NULL ) {
31923465 goto error ;
31933466 }
31943467
3468+ // Register external types.
31953469 if (register_builtin_xid_types (xid_classes ) < 0 ) {
31963470 goto error ;
31973471 }
0 commit comments