LCOV - code coverage report
Current view: top level - Modules - _xxinterpchannelsmodule.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit 5e6661bce9] Lines: 88 1065 8.3 %
Date: 2023-03-20 08:15:36 Functions: 15 95 15.8 %
Branches: 46 532 8.6 %

           Branch data     Line data    Source code
       1                 :            : 
       2                 :            : /* interpreters module */
       3                 :            : /* low-level access to interpreter primitives */
       4                 :            : #ifndef Py_BUILD_CORE_BUILTIN
       5                 :            : #  define Py_BUILD_CORE_MODULE 1
       6                 :            : #endif
       7                 :            : 
       8                 :            : #include "Python.h"
       9                 :            : #include "pycore_pystate.h"       // _PyThreadState_GET()
      10                 :            : #include "pycore_interpreteridobject.h"
      11                 :            : 
      12                 :            : 
      13                 :            : #define MODULE_NAME "_xxinterpchannels"
      14                 :            : 
      15                 :            : 
      16                 :            : static PyInterpreterState *
      17                 :          0 : _get_current_interp(void)
      18                 :            : {
      19                 :            :     // PyInterpreterState_Get() aborts if lookup fails, so don't need
      20                 :            :     // to check the result for NULL.
      21                 :          0 :     return PyInterpreterState_Get();
      22                 :            : }
      23                 :            : 
      24                 :            : static PyObject *
      25                 :          0 : _get_current_module(void)
      26                 :            : {
      27                 :          0 :     PyObject *name = PyUnicode_FromString(MODULE_NAME);
      28         [ #  # ]:          0 :     if (name == NULL) {
      29                 :          0 :         return NULL;
      30                 :            :     }
      31                 :          0 :     PyObject *mod = PyImport_GetModule(name);
      32                 :          0 :     Py_DECREF(name);
      33         [ #  # ]:          0 :     if (mod == NULL) {
      34                 :          0 :         return NULL;
      35                 :            :     }
      36                 :            :     assert(mod != Py_None);
      37                 :          0 :     return mod;
      38                 :            : }
      39                 :            : 
      40                 :            : static PyObject *
      41                 :          0 : get_module_from_owned_type(PyTypeObject *cls)
      42                 :            : {
      43                 :            :     assert(cls != NULL);
      44                 :          0 :     return _get_current_module();
      45                 :            :     // XXX Use the more efficient API now that we use heap types:
      46                 :            :     //return PyType_GetModule(cls);
      47                 :            : }
      48                 :            : 
      49                 :            : static struct PyModuleDef moduledef;
      50                 :            : 
      51                 :            : static PyObject *
      52                 :          0 : get_module_from_type(PyTypeObject *cls)
      53                 :            : {
      54                 :            :     assert(cls != NULL);
      55                 :          0 :     return _get_current_module();
      56                 :            :     // XXX Use the more efficient API now that we use heap types:
      57                 :            :     //return PyType_GetModuleByDef(cls, &moduledef);
      58                 :            : }
      59                 :            : 
      60                 :            : static PyObject *
      61                 :          5 : add_new_exception(PyObject *mod, const char *name, PyObject *base)
      62                 :            : {
      63                 :            :     assert(!PyObject_HasAttrString(mod, name));
      64                 :          5 :     PyObject *exctype = PyErr_NewException(name, base, NULL);
      65         [ -  + ]:          5 :     if (exctype == NULL) {
      66                 :          0 :         return NULL;
      67                 :            :     }
      68                 :          5 :     int res = PyModule_AddType(mod, (PyTypeObject *)exctype);
      69         [ -  + ]:          5 :     if (res < 0) {
      70                 :          0 :         Py_DECREF(exctype);
      71                 :          0 :         return NULL;
      72                 :            :     }
      73                 :          5 :     return exctype;
      74                 :            : }
      75                 :            : 
      76                 :            : #define ADD_NEW_EXCEPTION(MOD, NAME, BASE) \
      77                 :            :     add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE)
      78                 :            : 
      79                 :            : static PyTypeObject *
      80                 :          1 : add_new_type(PyObject *mod, PyType_Spec *spec, crossinterpdatafunc shared)
      81                 :            : {
      82                 :          1 :     PyTypeObject *cls = (PyTypeObject *)PyType_FromMetaclass(
      83                 :            :                 NULL, mod, spec, NULL);
      84         [ -  + ]:          1 :     if (cls == NULL) {
      85                 :          0 :         return NULL;
      86                 :            :     }
      87         [ -  + ]:          1 :     if (PyModule_AddType(mod, cls) < 0) {
      88                 :          0 :         Py_DECREF(cls);
      89                 :          0 :         return NULL;
      90                 :            :     }
      91         [ +  - ]:          1 :     if (shared != NULL) {
      92         [ -  + ]:          1 :         if (_PyCrossInterpreterData_RegisterClass(cls, shared)) {
      93                 :          0 :             Py_DECREF(cls);
      94                 :          0 :             return NULL;
      95                 :            :         }
      96                 :            :     }
      97                 :          1 :     return cls;
      98                 :            : }
      99                 :            : 
     100                 :            : static int
     101                 :          0 : _release_xid_data(_PyCrossInterpreterData *data, int ignoreexc)
     102                 :            : {
     103                 :            :     PyObject *exc;
     104         [ #  # ]:          0 :     if (ignoreexc) {
     105                 :          0 :         exc = PyErr_GetRaisedException();
     106                 :            :     }
     107                 :          0 :     int res = _PyCrossInterpreterData_Release(data);
     108         [ #  # ]:          0 :     if (res < 0) {
     109                 :            :         // XXX Fix this!
     110                 :            :         /* The owning interpreter is already destroyed.
     111                 :            :          * Ideally, this shouldn't ever happen.  When an interpreter is
     112                 :            :          * about to be destroyed, we should clear out all of its objects
     113                 :            :          * from every channel associated with that interpreter.
     114                 :            :          * For now we hack around that to resolve refleaks, by decref'ing
     115                 :            :          * the released object here, even if its the wrong interpreter.
     116                 :            :          * The owning interpreter has already been destroyed
     117                 :            :          * so we should be okay, especially since the currently
     118                 :            :          * shareable types are all very basic, with no GC.
     119                 :            :          * That said, it becomes much messier once interpreters
     120                 :            :          * no longer share a GIL, so this needs to be fixed before then. */
     121                 :          0 :         _PyCrossInterpreterData_Clear(NULL, data);
     122         [ #  # ]:          0 :         if (ignoreexc) {
     123                 :            :             // XXX Emit a warning?
     124                 :          0 :             PyErr_Clear();
     125                 :            :         }
     126                 :            :     }
     127         [ #  # ]:          0 :     if (ignoreexc) {
     128                 :          0 :         PyErr_SetRaisedException(exc);
     129                 :            :     }
     130                 :          0 :     return res;
     131                 :            : }
     132                 :            : 
     133                 :            : 
     134                 :            : /* module state *************************************************************/
     135                 :            : 
     136                 :            : typedef struct {
     137                 :            :     /* heap types */
     138                 :            :     PyTypeObject *ChannelIDType;
     139                 :            : 
     140                 :            :     /* exceptions */
     141                 :            :     PyObject *ChannelError;
     142                 :            :     PyObject *ChannelNotFoundError;
     143                 :            :     PyObject *ChannelClosedError;
     144                 :            :     PyObject *ChannelEmptyError;
     145                 :            :     PyObject *ChannelNotEmptyError;
     146                 :            : } module_state;
     147                 :            : 
     148                 :            : static inline module_state *
     149                 :         10 : get_module_state(PyObject *mod)
     150                 :            : {
     151                 :            :     assert(mod != NULL);
     152                 :         10 :     module_state *state = PyModule_GetState(mod);
     153                 :            :     assert(state != NULL);
     154                 :         10 :     return state;
     155                 :            : }
     156                 :            : 
     157                 :            : static int
     158                 :          6 : traverse_module_state(module_state *state, visitproc visit, void *arg)
     159                 :            : {
     160                 :            :     /* heap types */
     161   [ +  -  -  + ]:          6 :     Py_VISIT(state->ChannelIDType);
     162                 :            : 
     163                 :            :     /* exceptions */
     164   [ +  -  -  + ]:          6 :     Py_VISIT(state->ChannelError);
     165   [ +  -  -  + ]:          6 :     Py_VISIT(state->ChannelNotFoundError);
     166   [ +  -  -  + ]:          6 :     Py_VISIT(state->ChannelClosedError);
     167   [ +  -  -  + ]:          6 :     Py_VISIT(state->ChannelEmptyError);
     168   [ +  -  -  + ]:          6 :     Py_VISIT(state->ChannelNotEmptyError);
     169                 :            : 
     170                 :          6 :     return 0;
     171                 :            : }
     172                 :            : 
     173                 :            : static int
     174                 :          2 : clear_module_state(module_state *state)
     175                 :            : {
     176                 :            :     /* heap types */
     177         [ +  + ]:          2 :     if (state->ChannelIDType != NULL) {
     178                 :          1 :         (void)_PyCrossInterpreterData_UnregisterClass(state->ChannelIDType);
     179                 :            :     }
     180         [ +  + ]:          2 :     Py_CLEAR(state->ChannelIDType);
     181                 :            : 
     182                 :            :     /* exceptions */
     183         [ +  + ]:          2 :     Py_CLEAR(state->ChannelError);
     184         [ +  + ]:          2 :     Py_CLEAR(state->ChannelNotFoundError);
     185         [ +  + ]:          2 :     Py_CLEAR(state->ChannelClosedError);
     186         [ +  + ]:          2 :     Py_CLEAR(state->ChannelEmptyError);
     187         [ +  + ]:          2 :     Py_CLEAR(state->ChannelNotEmptyError);
     188                 :            : 
     189                 :          2 :     return 0;
     190                 :            : }
     191                 :            : 
     192                 :            : 
     193                 :            : /* channel-specific code ****************************************************/
     194                 :            : 
     195                 :            : #define CHANNEL_SEND 1
     196                 :            : #define CHANNEL_BOTH 0
     197                 :            : #define CHANNEL_RECV -1
     198                 :            : 
     199                 :            : /* channel errors */
     200                 :            : 
     201                 :            : #define ERR_CHANNEL_NOT_FOUND -2
     202                 :            : #define ERR_CHANNEL_CLOSED -3
     203                 :            : #define ERR_CHANNEL_INTERP_CLOSED -4
     204                 :            : #define ERR_CHANNEL_EMPTY -5
     205                 :            : #define ERR_CHANNEL_NOT_EMPTY -6
     206                 :            : #define ERR_CHANNEL_MUTEX_INIT -7
     207                 :            : #define ERR_CHANNELS_MUTEX_INIT -8
     208                 :            : #define ERR_NO_NEXT_CHANNEL_ID -9
     209                 :            : 
     210                 :            : static int
     211                 :          1 : exceptions_init(PyObject *mod)
     212                 :            : {
     213                 :          1 :     module_state *state = get_module_state(mod);
     214         [ -  + ]:          1 :     if (state == NULL) {
     215                 :          0 :         return -1;
     216                 :            :     }
     217                 :            : 
     218                 :            : #define ADD(NAME, BASE) \
     219                 :            :     do { \
     220                 :            :         assert(state->NAME == NULL); \
     221                 :            :         state->NAME = ADD_NEW_EXCEPTION(mod, NAME, BASE); \
     222                 :            :         if (state->NAME == NULL) { \
     223                 :            :             return -1; \
     224                 :            :         } \
     225                 :            :     } while (0)
     226                 :            : 
     227                 :            :     // A channel-related operation failed.
     228         [ -  + ]:          1 :     ADD(ChannelError, PyExc_RuntimeError);
     229                 :            :     // An operation tried to use a channel that doesn't exist.
     230         [ -  + ]:          1 :     ADD(ChannelNotFoundError, state->ChannelError);
     231                 :            :     // An operation tried to use a closed channel.
     232         [ -  + ]:          1 :     ADD(ChannelClosedError, state->ChannelError);
     233                 :            :     // An operation tried to pop from an empty channel.
     234         [ -  + ]:          1 :     ADD(ChannelEmptyError, state->ChannelError);
     235                 :            :     // An operation tried to close a non-empty channel.
     236         [ -  + ]:          1 :     ADD(ChannelNotEmptyError, state->ChannelError);
     237                 :            : #undef ADD
     238                 :            : 
     239                 :          1 :     return 0;
     240                 :            : }
     241                 :            : 
     242                 :            : static int
     243                 :          0 : handle_channel_error(int err, PyObject *mod, int64_t cid)
     244                 :            : {
     245         [ #  # ]:          0 :     if (err == 0) {
     246                 :            :         assert(!PyErr_Occurred());
     247                 :          0 :         return 0;
     248                 :            :     }
     249                 :            :     assert(err < 0);
     250                 :          0 :     module_state *state = get_module_state(mod);
     251                 :            :     assert(state != NULL);
     252         [ #  # ]:          0 :     if (err == ERR_CHANNEL_NOT_FOUND) {
     253                 :          0 :         PyErr_Format(state->ChannelNotFoundError,
     254                 :            :                      "channel %" PRId64 " not found", cid);
     255                 :            :     }
     256         [ #  # ]:          0 :     else if (err == ERR_CHANNEL_CLOSED) {
     257                 :          0 :         PyErr_Format(state->ChannelClosedError,
     258                 :            :                      "channel %" PRId64 " is closed", cid);
     259                 :            :     }
     260         [ #  # ]:          0 :     else if (err == ERR_CHANNEL_INTERP_CLOSED) {
     261                 :          0 :         PyErr_Format(state->ChannelClosedError,
     262                 :            :                      "channel %" PRId64 " is already closed", cid);
     263                 :            :     }
     264         [ #  # ]:          0 :     else if (err == ERR_CHANNEL_EMPTY) {
     265                 :          0 :         PyErr_Format(state->ChannelEmptyError,
     266                 :            :                      "channel %" PRId64 " is empty", cid);
     267                 :            :     }
     268         [ #  # ]:          0 :     else if (err == ERR_CHANNEL_NOT_EMPTY) {
     269                 :          0 :         PyErr_Format(state->ChannelNotEmptyError,
     270                 :            :                      "channel %" PRId64 " may not be closed "
     271                 :            :                      "if not empty (try force=True)",
     272                 :            :                      cid);
     273                 :            :     }
     274         [ #  # ]:          0 :     else if (err == ERR_CHANNEL_MUTEX_INIT) {
     275                 :          0 :         PyErr_SetString(state->ChannelError,
     276                 :            :                         "can't initialize mutex for new channel");
     277                 :            :     }
     278         [ #  # ]:          0 :     else if (err == ERR_CHANNELS_MUTEX_INIT) {
     279                 :          0 :         PyErr_SetString(state->ChannelError,
     280                 :            :                         "can't initialize mutex for channel management");
     281                 :            :     }
     282         [ #  # ]:          0 :     else if (err == ERR_NO_NEXT_CHANNEL_ID) {
     283                 :          0 :         PyErr_SetString(state->ChannelError,
     284                 :            :                         "failed to get a channel ID");
     285                 :            :     }
     286                 :            :     else {
     287                 :            :         assert(PyErr_Occurred());
     288                 :            :     }
     289                 :          0 :     return 1;
     290                 :            : }
     291                 :            : 
     292                 :            : /* the channel queue */
     293                 :            : 
     294                 :            : struct _channelitem;
     295                 :            : 
     296                 :            : typedef struct _channelitem {
     297                 :            :     _PyCrossInterpreterData *data;
     298                 :            :     struct _channelitem *next;
     299                 :            : } _channelitem;
     300                 :            : 
     301                 :            : static _channelitem *
     302                 :          0 : _channelitem_new(void)
     303                 :            : {
     304                 :          0 :     _channelitem *item = PyMem_NEW(_channelitem, 1);
     305         [ #  # ]:          0 :     if (item == NULL) {
     306                 :          0 :         PyErr_NoMemory();
     307                 :          0 :         return NULL;
     308                 :            :     }
     309                 :          0 :     item->data = NULL;
     310                 :          0 :     item->next = NULL;
     311                 :          0 :     return item;
     312                 :            : }
     313                 :            : 
     314                 :            : static void
     315                 :          0 : _channelitem_clear(_channelitem *item)
     316                 :            : {
     317         [ #  # ]:          0 :     if (item->data != NULL) {
     318                 :          0 :         (void)_release_xid_data(item->data, 1);
     319                 :          0 :         PyMem_Free(item->data);
     320                 :          0 :         item->data = NULL;
     321                 :            :     }
     322                 :          0 :     item->next = NULL;
     323                 :          0 : }
     324                 :            : 
     325                 :            : static void
     326                 :          0 : _channelitem_free(_channelitem *item)
     327                 :            : {
     328                 :          0 :     _channelitem_clear(item);
     329                 :          0 :     PyMem_Free(item);
     330                 :          0 : }
     331                 :            : 
     332                 :            : static void
     333                 :          0 : _channelitem_free_all(_channelitem *item)
     334                 :            : {
     335         [ #  # ]:          0 :     while (item != NULL) {
     336                 :          0 :         _channelitem *last = item;
     337                 :          0 :         item = item->next;
     338                 :          0 :         _channelitem_free(last);
     339                 :            :     }
     340                 :          0 : }
     341                 :            : 
     342                 :            : static _PyCrossInterpreterData *
     343                 :          0 : _channelitem_popped(_channelitem *item)
     344                 :            : {
     345                 :          0 :     _PyCrossInterpreterData *data = item->data;
     346                 :          0 :     item->data = NULL;
     347                 :          0 :     _channelitem_free(item);
     348                 :          0 :     return data;
     349                 :            : }
     350                 :            : 
     351                 :            : typedef struct _channelqueue {
     352                 :            :     int64_t count;
     353                 :            :     _channelitem *first;
     354                 :            :     _channelitem *last;
     355                 :            : } _channelqueue;
     356                 :            : 
     357                 :            : static _channelqueue *
     358                 :          0 : _channelqueue_new(void)
     359                 :            : {
     360                 :          0 :     _channelqueue *queue = PyMem_NEW(_channelqueue, 1);
     361         [ #  # ]:          0 :     if (queue == NULL) {
     362                 :          0 :         PyErr_NoMemory();
     363                 :          0 :         return NULL;
     364                 :            :     }
     365                 :          0 :     queue->count = 0;
     366                 :          0 :     queue->first = NULL;
     367                 :          0 :     queue->last = NULL;
     368                 :          0 :     return queue;
     369                 :            : }
     370                 :            : 
     371                 :            : static void
     372                 :          0 : _channelqueue_clear(_channelqueue *queue)
     373                 :            : {
     374                 :          0 :     _channelitem_free_all(queue->first);
     375                 :          0 :     queue->count = 0;
     376                 :          0 :     queue->first = NULL;
     377                 :          0 :     queue->last = NULL;
     378                 :          0 : }
     379                 :            : 
     380                 :            : static void
     381                 :          0 : _channelqueue_free(_channelqueue *queue)
     382                 :            : {
     383                 :          0 :     _channelqueue_clear(queue);
     384                 :          0 :     PyMem_Free(queue);
     385                 :          0 : }
     386                 :            : 
     387                 :            : static int
     388                 :          0 : _channelqueue_put(_channelqueue *queue, _PyCrossInterpreterData *data)
     389                 :            : {
     390                 :          0 :     _channelitem *item = _channelitem_new();
     391         [ #  # ]:          0 :     if (item == NULL) {
     392                 :          0 :         return -1;
     393                 :            :     }
     394                 :          0 :     item->data = data;
     395                 :            : 
     396                 :          0 :     queue->count += 1;
     397         [ #  # ]:          0 :     if (queue->first == NULL) {
     398                 :          0 :         queue->first = item;
     399                 :            :     }
     400                 :            :     else {
     401                 :          0 :         queue->last->next = item;
     402                 :            :     }
     403                 :          0 :     queue->last = item;
     404                 :          0 :     return 0;
     405                 :            : }
     406                 :            : 
     407                 :            : static _PyCrossInterpreterData *
     408                 :          0 : _channelqueue_get(_channelqueue *queue)
     409                 :            : {
     410                 :          0 :     _channelitem *item = queue->first;
     411         [ #  # ]:          0 :     if (item == NULL) {
     412                 :          0 :         return NULL;
     413                 :            :     }
     414                 :          0 :     queue->first = item->next;
     415         [ #  # ]:          0 :     if (queue->last == item) {
     416                 :          0 :         queue->last = NULL;
     417                 :            :     }
     418                 :          0 :     queue->count -= 1;
     419                 :            : 
     420                 :          0 :     return _channelitem_popped(item);
     421                 :            : }
     422                 :            : 
     423                 :            : /* channel-interpreter associations */
     424                 :            : 
     425                 :            : struct _channelend;
     426                 :            : 
     427                 :            : typedef struct _channelend {
     428                 :            :     struct _channelend *next;
     429                 :            :     int64_t interp;
     430                 :            :     int open;
     431                 :            : } _channelend;
     432                 :            : 
     433                 :            : static _channelend *
     434                 :          0 : _channelend_new(int64_t interp)
     435                 :            : {
     436                 :          0 :     _channelend *end = PyMem_NEW(_channelend, 1);
     437         [ #  # ]:          0 :     if (end == NULL) {
     438                 :          0 :         PyErr_NoMemory();
     439                 :          0 :         return NULL;
     440                 :            :     }
     441                 :          0 :     end->next = NULL;
     442                 :          0 :     end->interp = interp;
     443                 :          0 :     end->open = 1;
     444                 :          0 :     return end;
     445                 :            : }
     446                 :            : 
     447                 :            : static void
     448                 :          0 : _channelend_free(_channelend *end)
     449                 :            : {
     450                 :          0 :     PyMem_Free(end);
     451                 :          0 : }
     452                 :            : 
     453                 :            : static void
     454                 :          0 : _channelend_free_all(_channelend *end)
     455                 :            : {
     456         [ #  # ]:          0 :     while (end != NULL) {
     457                 :          0 :         _channelend *last = end;
     458                 :          0 :         end = end->next;
     459                 :          0 :         _channelend_free(last);
     460                 :            :     }
     461                 :          0 : }
     462                 :            : 
     463                 :            : static _channelend *
     464                 :          0 : _channelend_find(_channelend *first, int64_t interp, _channelend **pprev)
     465                 :            : {
     466                 :          0 :     _channelend *prev = NULL;
     467                 :          0 :     _channelend *end = first;
     468         [ #  # ]:          0 :     while (end != NULL) {
     469         [ #  # ]:          0 :         if (end->interp == interp) {
     470                 :          0 :             break;
     471                 :            :         }
     472                 :          0 :         prev = end;
     473                 :          0 :         end = end->next;
     474                 :            :     }
     475         [ #  # ]:          0 :     if (pprev != NULL) {
     476                 :          0 :         *pprev = prev;
     477                 :            :     }
     478                 :          0 :     return end;
     479                 :            : }
     480                 :            : 
     481                 :            : typedef struct _channelassociations {
     482                 :            :     // Note that the list entries are never removed for interpreter
     483                 :            :     // for which the channel is closed.  This should not be a problem in
     484                 :            :     // practice.  Also, a channel isn't automatically closed when an
     485                 :            :     // interpreter is destroyed.
     486                 :            :     int64_t numsendopen;
     487                 :            :     int64_t numrecvopen;
     488                 :            :     _channelend *send;
     489                 :            :     _channelend *recv;
     490                 :            : } _channelends;
     491                 :            : 
     492                 :            : static _channelends *
     493                 :          0 : _channelends_new(void)
     494                 :            : {
     495                 :          0 :     _channelends *ends = PyMem_NEW(_channelends, 1);
     496         [ #  # ]:          0 :     if (ends== NULL) {
     497                 :          0 :         return NULL;
     498                 :            :     }
     499                 :          0 :     ends->numsendopen = 0;
     500                 :          0 :     ends->numrecvopen = 0;
     501                 :          0 :     ends->send = NULL;
     502                 :          0 :     ends->recv = NULL;
     503                 :          0 :     return ends;
     504                 :            : }
     505                 :            : 
     506                 :            : static void
     507                 :          0 : _channelends_clear(_channelends *ends)
     508                 :            : {
     509                 :          0 :     _channelend_free_all(ends->send);
     510                 :          0 :     ends->send = NULL;
     511                 :          0 :     ends->numsendopen = 0;
     512                 :            : 
     513                 :          0 :     _channelend_free_all(ends->recv);
     514                 :          0 :     ends->recv = NULL;
     515                 :          0 :     ends->numrecvopen = 0;
     516                 :          0 : }
     517                 :            : 
     518                 :            : static void
     519                 :          0 : _channelends_free(_channelends *ends)
     520                 :            : {
     521                 :          0 :     _channelends_clear(ends);
     522                 :          0 :     PyMem_Free(ends);
     523                 :          0 : }
     524                 :            : 
     525                 :            : static _channelend *
     526                 :          0 : _channelends_add(_channelends *ends, _channelend *prev, int64_t interp,
     527                 :            :                  int send)
     528                 :            : {
     529                 :          0 :     _channelend *end = _channelend_new(interp);
     530         [ #  # ]:          0 :     if (end == NULL) {
     531                 :          0 :         return NULL;
     532                 :            :     }
     533                 :            : 
     534         [ #  # ]:          0 :     if (prev == NULL) {
     535         [ #  # ]:          0 :         if (send) {
     536                 :          0 :             ends->send = end;
     537                 :            :         }
     538                 :            :         else {
     539                 :          0 :             ends->recv = end;
     540                 :            :         }
     541                 :            :     }
     542                 :            :     else {
     543                 :          0 :         prev->next = end;
     544                 :            :     }
     545         [ #  # ]:          0 :     if (send) {
     546                 :          0 :         ends->numsendopen += 1;
     547                 :            :     }
     548                 :            :     else {
     549                 :          0 :         ends->numrecvopen += 1;
     550                 :            :     }
     551                 :          0 :     return end;
     552                 :            : }
     553                 :            : 
     554                 :            : static int
     555                 :          0 : _channelends_associate(_channelends *ends, int64_t interp, int send)
     556                 :            : {
     557                 :            :     _channelend *prev;
     558         [ #  # ]:          0 :     _channelend *end = _channelend_find(send ? ends->send : ends->recv,
     559                 :            :                                         interp, &prev);
     560         [ #  # ]:          0 :     if (end != NULL) {
     561         [ #  # ]:          0 :         if (!end->open) {
     562                 :          0 :             return ERR_CHANNEL_CLOSED;
     563                 :            :         }
     564                 :            :         // already associated
     565                 :          0 :         return 0;
     566                 :            :     }
     567         [ #  # ]:          0 :     if (_channelends_add(ends, prev, interp, send) == NULL) {
     568                 :          0 :         return -1;
     569                 :            :     }
     570                 :          0 :     return 0;
     571                 :            : }
     572                 :            : 
     573                 :            : static int
     574                 :          0 : _channelends_is_open(_channelends *ends)
     575                 :            : {
     576   [ #  #  #  # ]:          0 :     if (ends->numsendopen != 0 || ends->numrecvopen != 0) {
     577                 :          0 :         return 1;
     578                 :            :     }
     579   [ #  #  #  # ]:          0 :     if (ends->send == NULL && ends->recv == NULL) {
     580                 :          0 :         return 1;
     581                 :            :     }
     582                 :          0 :     return 0;
     583                 :            : }
     584                 :            : 
     585                 :            : static void
     586                 :          0 : _channelends_close_end(_channelends *ends, _channelend *end, int send)
     587                 :            : {
     588                 :          0 :     end->open = 0;
     589         [ #  # ]:          0 :     if (send) {
     590                 :          0 :         ends->numsendopen -= 1;
     591                 :            :     }
     592                 :            :     else {
     593                 :          0 :         ends->numrecvopen -= 1;
     594                 :            :     }
     595                 :          0 : }
     596                 :            : 
     597                 :            : static int
     598                 :          0 : _channelends_close_interpreter(_channelends *ends, int64_t interp, int which)
     599                 :            : {
     600                 :            :     _channelend *prev;
     601                 :            :     _channelend *end;
     602         [ #  # ]:          0 :     if (which >= 0) {  // send/both
     603                 :          0 :         end = _channelend_find(ends->send, interp, &prev);
     604         [ #  # ]:          0 :         if (end == NULL) {
     605                 :            :             // never associated so add it
     606                 :          0 :             end = _channelends_add(ends, prev, interp, 1);
     607         [ #  # ]:          0 :             if (end == NULL) {
     608                 :          0 :                 return -1;
     609                 :            :             }
     610                 :            :         }
     611                 :          0 :         _channelends_close_end(ends, end, 1);
     612                 :            :     }
     613         [ #  # ]:          0 :     if (which <= 0) {  // recv/both
     614                 :          0 :         end = _channelend_find(ends->recv, interp, &prev);
     615         [ #  # ]:          0 :         if (end == NULL) {
     616                 :            :             // never associated so add it
     617                 :          0 :             end = _channelends_add(ends, prev, interp, 0);
     618         [ #  # ]:          0 :             if (end == NULL) {
     619                 :          0 :                 return -1;
     620                 :            :             }
     621                 :            :         }
     622                 :          0 :         _channelends_close_end(ends, end, 0);
     623                 :            :     }
     624                 :          0 :     return 0;
     625                 :            : }
     626                 :            : 
     627                 :            : static void
     628                 :          0 : _channelends_close_all(_channelends *ends, int which, int force)
     629                 :            : {
     630                 :            :     // XXX Handle the ends.
     631                 :            :     // XXX Handle force is True.
     632                 :            : 
     633                 :            :     // Ensure all the "send"-associated interpreters are closed.
     634                 :            :     _channelend *end;
     635         [ #  # ]:          0 :     for (end = ends->send; end != NULL; end = end->next) {
     636                 :          0 :         _channelends_close_end(ends, end, 1);
     637                 :            :     }
     638                 :            : 
     639                 :            :     // Ensure all the "recv"-associated interpreters are closed.
     640         [ #  # ]:          0 :     for (end = ends->recv; end != NULL; end = end->next) {
     641                 :          0 :         _channelends_close_end(ends, end, 0);
     642                 :            :     }
     643                 :          0 : }
     644                 :            : 
     645                 :            : /* channels */
     646                 :            : 
     647                 :            : struct _channel;
     648                 :            : struct _channel_closing;
     649                 :            : static void _channel_clear_closing(struct _channel *);
     650                 :            : static void _channel_finish_closing(struct _channel *);
     651                 :            : 
     652                 :            : typedef struct _channel {
     653                 :            :     PyThread_type_lock mutex;
     654                 :            :     _channelqueue *queue;
     655                 :            :     _channelends *ends;
     656                 :            :     int open;
     657                 :            :     struct _channel_closing *closing;
     658                 :            : } _PyChannelState;
     659                 :            : 
     660                 :            : static _PyChannelState *
     661                 :          0 : _channel_new(PyThread_type_lock mutex)
     662                 :            : {
     663                 :          0 :     _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1);
     664         [ #  # ]:          0 :     if (chan == NULL) {
     665                 :          0 :         return NULL;
     666                 :            :     }
     667                 :          0 :     chan->mutex = mutex;
     668                 :          0 :     chan->queue = _channelqueue_new();
     669         [ #  # ]:          0 :     if (chan->queue == NULL) {
     670                 :          0 :         PyMem_Free(chan);
     671                 :          0 :         return NULL;
     672                 :            :     }
     673                 :          0 :     chan->ends = _channelends_new();
     674         [ #  # ]:          0 :     if (chan->ends == NULL) {
     675                 :          0 :         _channelqueue_free(chan->queue);
     676                 :          0 :         PyMem_Free(chan);
     677                 :          0 :         return NULL;
     678                 :            :     }
     679                 :          0 :     chan->open = 1;
     680                 :          0 :     chan->closing = NULL;
     681                 :          0 :     return chan;
     682                 :            : }
     683                 :            : 
     684                 :            : static void
     685                 :          0 : _channel_free(_PyChannelState *chan)
     686                 :            : {
     687                 :          0 :     _channel_clear_closing(chan);
     688                 :          0 :     PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
     689                 :          0 :     _channelqueue_free(chan->queue);
     690                 :          0 :     _channelends_free(chan->ends);
     691                 :          0 :     PyThread_release_lock(chan->mutex);
     692                 :            : 
     693                 :          0 :     PyThread_free_lock(chan->mutex);
     694                 :          0 :     PyMem_Free(chan);
     695                 :          0 : }
     696                 :            : 
     697                 :            : static int
     698                 :          0 : _channel_add(_PyChannelState *chan, int64_t interp,
     699                 :            :              _PyCrossInterpreterData *data)
     700                 :            : {
     701                 :          0 :     int res = -1;
     702                 :          0 :     PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
     703                 :            : 
     704         [ #  # ]:          0 :     if (!chan->open) {
     705                 :          0 :         res = ERR_CHANNEL_CLOSED;
     706                 :          0 :         goto done;
     707                 :            :     }
     708         [ #  # ]:          0 :     if (_channelends_associate(chan->ends, interp, 1) != 0) {
     709                 :          0 :         res = ERR_CHANNEL_INTERP_CLOSED;
     710                 :          0 :         goto done;
     711                 :            :     }
     712                 :            : 
     713         [ #  # ]:          0 :     if (_channelqueue_put(chan->queue, data) != 0) {
     714                 :          0 :         goto done;
     715                 :            :     }
     716                 :            : 
     717                 :          0 :     res = 0;
     718                 :          0 : done:
     719                 :          0 :     PyThread_release_lock(chan->mutex);
     720                 :          0 :     return res;
     721                 :            : }
     722                 :            : 
     723                 :            : static int
     724                 :          0 : _channel_next(_PyChannelState *chan, int64_t interp,
     725                 :            :               _PyCrossInterpreterData **res)
     726                 :            : {
     727                 :          0 :     int err = 0;
     728                 :          0 :     PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
     729                 :            : 
     730         [ #  # ]:          0 :     if (!chan->open) {
     731                 :          0 :         err = ERR_CHANNEL_CLOSED;
     732                 :          0 :         goto done;
     733                 :            :     }
     734         [ #  # ]:          0 :     if (_channelends_associate(chan->ends, interp, 0) != 0) {
     735                 :          0 :         err = ERR_CHANNEL_INTERP_CLOSED;
     736                 :          0 :         goto done;
     737                 :            :     }
     738                 :            : 
     739                 :          0 :     _PyCrossInterpreterData *data = _channelqueue_get(chan->queue);
     740   [ #  #  #  #  :          0 :     if (data == NULL && !PyErr_Occurred() && chan->closing != NULL) {
                   #  # ]
     741                 :          0 :         chan->open = 0;
     742                 :            :     }
     743                 :          0 :     *res = data;
     744                 :            : 
     745                 :          0 : done:
     746                 :          0 :     PyThread_release_lock(chan->mutex);
     747         [ #  # ]:          0 :     if (chan->queue->count == 0) {
     748                 :          0 :         _channel_finish_closing(chan);
     749                 :            :     }
     750                 :          0 :     return err;
     751                 :            : }
     752                 :            : 
     753                 :            : static int
     754                 :          0 : _channel_close_interpreter(_PyChannelState *chan, int64_t interp, int end)
     755                 :            : {
     756                 :          0 :     PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
     757                 :            : 
     758                 :          0 :     int res = -1;
     759         [ #  # ]:          0 :     if (!chan->open) {
     760                 :          0 :         res = ERR_CHANNEL_CLOSED;
     761                 :          0 :         goto done;
     762                 :            :     }
     763                 :            : 
     764         [ #  # ]:          0 :     if (_channelends_close_interpreter(chan->ends, interp, end) != 0) {
     765                 :          0 :         goto done;
     766                 :            :     }
     767                 :          0 :     chan->open = _channelends_is_open(chan->ends);
     768                 :            : 
     769                 :          0 :     res = 0;
     770                 :          0 : done:
     771                 :          0 :     PyThread_release_lock(chan->mutex);
     772                 :          0 :     return res;
     773                 :            : }
     774                 :            : 
     775                 :            : static int
     776                 :          0 : _channel_close_all(_PyChannelState *chan, int end, int force)
     777                 :            : {
     778                 :          0 :     int res = -1;
     779                 :          0 :     PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
     780                 :            : 
     781         [ #  # ]:          0 :     if (!chan->open) {
     782                 :          0 :         res = ERR_CHANNEL_CLOSED;
     783                 :          0 :         goto done;
     784                 :            :     }
     785                 :            : 
     786   [ #  #  #  # ]:          0 :     if (!force && chan->queue->count > 0) {
     787                 :          0 :         res = ERR_CHANNEL_NOT_EMPTY;
     788                 :          0 :         goto done;
     789                 :            :     }
     790                 :            : 
     791                 :          0 :     chan->open = 0;
     792                 :            : 
     793                 :            :     // We *could* also just leave these in place, since we've marked
     794                 :            :     // the channel as closed already.
     795                 :          0 :     _channelends_close_all(chan->ends, end, force);
     796                 :            : 
     797                 :          0 :     res = 0;
     798                 :          0 : done:
     799                 :          0 :     PyThread_release_lock(chan->mutex);
     800                 :          0 :     return res;
     801                 :            : }
     802                 :            : 
     803                 :            : /* the set of channels */
     804                 :            : 
     805                 :            : struct _channelref;
     806                 :            : 
     807                 :            : typedef struct _channelref {
     808                 :            :     int64_t id;
     809                 :            :     _PyChannelState *chan;
     810                 :            :     struct _channelref *next;
     811                 :            :     Py_ssize_t objcount;
     812                 :            : } _channelref;
     813                 :            : 
     814                 :            : static _channelref *
     815                 :          0 : _channelref_new(int64_t id, _PyChannelState *chan)
     816                 :            : {
     817                 :          0 :     _channelref *ref = PyMem_NEW(_channelref, 1);
     818         [ #  # ]:          0 :     if (ref == NULL) {
     819                 :          0 :         return NULL;
     820                 :            :     }
     821                 :          0 :     ref->id = id;
     822                 :          0 :     ref->chan = chan;
     823                 :          0 :     ref->next = NULL;
     824                 :          0 :     ref->objcount = 0;
     825                 :          0 :     return ref;
     826                 :            : }
     827                 :            : 
     828                 :            : //static void
     829                 :            : //_channelref_clear(_channelref *ref)
     830                 :            : //{
     831                 :            : //    ref->id = -1;
     832                 :            : //    ref->chan = NULL;
     833                 :            : //    ref->next = NULL;
     834                 :            : //    ref->objcount = 0;
     835                 :            : //}
     836                 :            : 
     837                 :            : static void
     838                 :          0 : _channelref_free(_channelref *ref)
     839                 :            : {
     840         [ #  # ]:          0 :     if (ref->chan != NULL) {
     841                 :          0 :         _channel_clear_closing(ref->chan);
     842                 :            :     }
     843                 :            :     //_channelref_clear(ref);
     844                 :          0 :     PyMem_Free(ref);
     845                 :          0 : }
     846                 :            : 
     847                 :            : static _channelref *
     848                 :          0 : _channelref_find(_channelref *first, int64_t id, _channelref **pprev)
     849                 :            : {
     850                 :          0 :     _channelref *prev = NULL;
     851                 :          0 :     _channelref *ref = first;
     852         [ #  # ]:          0 :     while (ref != NULL) {
     853         [ #  # ]:          0 :         if (ref->id == id) {
     854                 :          0 :             break;
     855                 :            :         }
     856                 :          0 :         prev = ref;
     857                 :          0 :         ref = ref->next;
     858                 :            :     }
     859         [ #  # ]:          0 :     if (pprev != NULL) {
     860                 :          0 :         *pprev = prev;
     861                 :            :     }
     862                 :          0 :     return ref;
     863                 :            : }
     864                 :            : 
     865                 :            : typedef struct _channels {
     866                 :            :     PyThread_type_lock mutex;
     867                 :            :     _channelref *head;
     868                 :            :     int64_t numopen;
     869                 :            :     int64_t next_id;
     870                 :            : } _channels;
     871                 :            : 
     872                 :            : static void
     873                 :          1 : _channels_init(_channels *channels, PyThread_type_lock mutex)
     874                 :            : {
     875                 :          1 :     channels->mutex = mutex;
     876                 :          1 :     channels->head = NULL;
     877                 :          1 :     channels->numopen = 0;
     878                 :          1 :     channels->next_id = 0;
     879                 :          1 : }
     880                 :            : 
     881                 :            : static void
     882                 :          1 : _channels_fini(_channels *channels)
     883                 :            : {
     884                 :            :     assert(channels->numopen == 0);
     885                 :            :     assert(channels->head == NULL);
     886         [ +  - ]:          1 :     if (channels->mutex != NULL) {
     887                 :          1 :         PyThread_free_lock(channels->mutex);
     888                 :          1 :         channels->mutex = NULL;
     889                 :            :     }
     890                 :          1 : }
     891                 :            : 
     892                 :            : static int64_t
     893                 :          0 : _channels_next_id(_channels *channels)  // needs lock
     894                 :            : {
     895                 :          0 :     int64_t id = channels->next_id;
     896         [ #  # ]:          0 :     if (id < 0) {
     897                 :            :         /* overflow */
     898                 :          0 :         return -1;
     899                 :            :     }
     900                 :          0 :     channels->next_id += 1;
     901                 :          0 :     return id;
     902                 :            : }
     903                 :            : 
     904                 :            : static int
     905                 :          0 : _channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex,
     906                 :            :                  _PyChannelState **res)
     907                 :            : {
     908                 :          0 :     int err = -1;
     909                 :          0 :     _PyChannelState *chan = NULL;
     910                 :          0 :     PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
     911         [ #  # ]:          0 :     if (pmutex != NULL) {
     912                 :          0 :         *pmutex = NULL;
     913                 :            :     }
     914                 :            : 
     915                 :          0 :     _channelref *ref = _channelref_find(channels->head, id, NULL);
     916         [ #  # ]:          0 :     if (ref == NULL) {
     917                 :          0 :         err = ERR_CHANNEL_NOT_FOUND;
     918                 :          0 :         goto done;
     919                 :            :     }
     920   [ #  #  #  # ]:          0 :     if (ref->chan == NULL || !ref->chan->open) {
     921                 :          0 :         err = ERR_CHANNEL_CLOSED;
     922                 :          0 :         goto done;
     923                 :            :     }
     924                 :            : 
     925         [ #  # ]:          0 :     if (pmutex != NULL) {
     926                 :            :         // The mutex will be closed by the caller.
     927                 :          0 :         *pmutex = channels->mutex;
     928                 :            :     }
     929                 :            : 
     930                 :          0 :     chan = ref->chan;
     931                 :          0 :     err = 0;
     932                 :            : 
     933                 :          0 : done:
     934   [ #  #  #  # ]:          0 :     if (pmutex == NULL || *pmutex == NULL) {
     935                 :          0 :         PyThread_release_lock(channels->mutex);
     936                 :            :     }
     937                 :          0 :     *res = chan;
     938                 :          0 :     return err;
     939                 :            : }
     940                 :            : 
     941                 :            : static int64_t
     942                 :          0 : _channels_add(_channels *channels, _PyChannelState *chan)
     943                 :            : {
     944                 :          0 :     int64_t cid = -1;
     945                 :          0 :     PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
     946                 :            : 
     947                 :            :     // Create a new ref.
     948                 :          0 :     int64_t id = _channels_next_id(channels);
     949         [ #  # ]:          0 :     if (id < 0) {
     950                 :          0 :         cid = ERR_NO_NEXT_CHANNEL_ID;
     951                 :          0 :         goto done;
     952                 :            :     }
     953                 :          0 :     _channelref *ref = _channelref_new(id, chan);
     954         [ #  # ]:          0 :     if (ref == NULL) {
     955                 :          0 :         goto done;
     956                 :            :     }
     957                 :            : 
     958                 :            :     // Add it to the list.
     959                 :            :     // We assume that the channel is a new one (not already in the list).
     960                 :          0 :     ref->next = channels->head;
     961                 :          0 :     channels->head = ref;
     962                 :          0 :     channels->numopen += 1;
     963                 :            : 
     964                 :          0 :     cid = id;
     965                 :          0 : done:
     966                 :          0 :     PyThread_release_lock(channels->mutex);
     967                 :          0 :     return cid;
     968                 :            : }
     969                 :            : 
     970                 :            : /* forward */
     971                 :            : static int _channel_set_closing(struct _channelref *, PyThread_type_lock);
     972                 :            : 
     973                 :            : static int
     974                 :          0 : _channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan,
     975                 :            :                 int end, int force)
     976                 :            : {
     977                 :          0 :     int res = -1;
     978                 :          0 :     PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
     979         [ #  # ]:          0 :     if (pchan != NULL) {
     980                 :          0 :         *pchan = NULL;
     981                 :            :     }
     982                 :            : 
     983                 :          0 :     _channelref *ref = _channelref_find(channels->head, cid, NULL);
     984         [ #  # ]:          0 :     if (ref == NULL) {
     985                 :          0 :         res = ERR_CHANNEL_NOT_FOUND;
     986                 :          0 :         goto done;
     987                 :            :     }
     988                 :            : 
     989         [ #  # ]:          0 :     if (ref->chan == NULL) {
     990                 :          0 :         res = ERR_CHANNEL_CLOSED;
     991                 :          0 :         goto done;
     992                 :            :     }
     993   [ #  #  #  #  :          0 :     else if (!force && end == CHANNEL_SEND && ref->chan->closing != NULL) {
                   #  # ]
     994                 :          0 :         res = ERR_CHANNEL_CLOSED;
     995                 :          0 :         goto done;
     996                 :            :     }
     997                 :            :     else {
     998                 :          0 :         int err = _channel_close_all(ref->chan, end, force);
     999         [ #  # ]:          0 :         if (err != 0) {
    1000   [ #  #  #  # ]:          0 :             if (end == CHANNEL_SEND && err == ERR_CHANNEL_NOT_EMPTY) {
    1001         [ #  # ]:          0 :                 if (ref->chan->closing != NULL) {
    1002                 :          0 :                     res = ERR_CHANNEL_CLOSED;
    1003                 :          0 :                     goto done;
    1004                 :            :                 }
    1005                 :            :                 // Mark the channel as closing and return.  The channel
    1006                 :            :                 // will be cleaned up in _channel_next().
    1007                 :          0 :                 PyErr_Clear();
    1008                 :          0 :                 int err = _channel_set_closing(ref, channels->mutex);
    1009         [ #  # ]:          0 :                 if (err != 0) {
    1010                 :          0 :                     res = err;
    1011                 :          0 :                     goto done;
    1012                 :            :                 }
    1013         [ #  # ]:          0 :                 if (pchan != NULL) {
    1014                 :          0 :                     *pchan = ref->chan;
    1015                 :            :                 }
    1016                 :          0 :                 res = 0;
    1017                 :            :             }
    1018                 :            :             else {
    1019                 :          0 :                 res = err;
    1020                 :            :             }
    1021                 :          0 :             goto done;
    1022                 :            :         }
    1023         [ #  # ]:          0 :         if (pchan != NULL) {
    1024                 :          0 :             *pchan = ref->chan;
    1025                 :            :         }
    1026                 :            :         else  {
    1027                 :          0 :             _channel_free(ref->chan);
    1028                 :            :         }
    1029                 :          0 :         ref->chan = NULL;
    1030                 :            :     }
    1031                 :            : 
    1032                 :          0 :     res = 0;
    1033                 :          0 : done:
    1034                 :          0 :     PyThread_release_lock(channels->mutex);
    1035                 :          0 :     return res;
    1036                 :            : }
    1037                 :            : 
    1038                 :            : static void
    1039                 :          0 : _channels_remove_ref(_channels *channels, _channelref *ref, _channelref *prev,
    1040                 :            :                      _PyChannelState **pchan)
    1041                 :            : {
    1042         [ #  # ]:          0 :     if (ref == channels->head) {
    1043                 :          0 :         channels->head = ref->next;
    1044                 :            :     }
    1045                 :            :     else {
    1046                 :          0 :         prev->next = ref->next;
    1047                 :            :     }
    1048                 :          0 :     channels->numopen -= 1;
    1049                 :            : 
    1050         [ #  # ]:          0 :     if (pchan != NULL) {
    1051                 :          0 :         *pchan = ref->chan;
    1052                 :            :     }
    1053                 :          0 :     _channelref_free(ref);
    1054                 :          0 : }
    1055                 :            : 
    1056                 :            : static int
    1057                 :          0 : _channels_remove(_channels *channels, int64_t id, _PyChannelState **pchan)
    1058                 :            : {
    1059                 :          0 :     int res = -1;
    1060                 :          0 :     PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
    1061                 :            : 
    1062         [ #  # ]:          0 :     if (pchan != NULL) {
    1063                 :          0 :         *pchan = NULL;
    1064                 :            :     }
    1065                 :            : 
    1066                 :          0 :     _channelref *prev = NULL;
    1067                 :          0 :     _channelref *ref = _channelref_find(channels->head, id, &prev);
    1068         [ #  # ]:          0 :     if (ref == NULL) {
    1069                 :          0 :         res = ERR_CHANNEL_NOT_FOUND;
    1070                 :          0 :         goto done;
    1071                 :            :     }
    1072                 :            : 
    1073                 :          0 :     _channels_remove_ref(channels, ref, prev, pchan);
    1074                 :            : 
    1075                 :          0 :     res = 0;
    1076                 :          0 : done:
    1077                 :          0 :     PyThread_release_lock(channels->mutex);
    1078                 :          0 :     return res;
    1079                 :            : }
    1080                 :            : 
    1081                 :            : static int
    1082                 :          0 : _channels_add_id_object(_channels *channels, int64_t id)
    1083                 :            : {
    1084                 :          0 :     int res = -1;
    1085                 :          0 :     PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
    1086                 :            : 
    1087                 :          0 :     _channelref *ref = _channelref_find(channels->head, id, NULL);
    1088         [ #  # ]:          0 :     if (ref == NULL) {
    1089                 :          0 :         res = ERR_CHANNEL_NOT_FOUND;
    1090                 :          0 :         goto done;
    1091                 :            :     }
    1092                 :          0 :     ref->objcount += 1;
    1093                 :            : 
    1094                 :          0 :     res = 0;
    1095                 :          0 : done:
    1096                 :          0 :     PyThread_release_lock(channels->mutex);
    1097                 :          0 :     return res;
    1098                 :            : }
    1099                 :            : 
    1100                 :            : static void
    1101                 :          0 : _channels_drop_id_object(_channels *channels, int64_t id)
    1102                 :            : {
    1103                 :          0 :     PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
    1104                 :            : 
    1105                 :          0 :     _channelref *prev = NULL;
    1106                 :          0 :     _channelref *ref = _channelref_find(channels->head, id, &prev);
    1107         [ #  # ]:          0 :     if (ref == NULL) {
    1108                 :            :         // Already destroyed.
    1109                 :          0 :         goto done;
    1110                 :            :     }
    1111                 :          0 :     ref->objcount -= 1;
    1112                 :            : 
    1113                 :            :     // Destroy if no longer used.
    1114         [ #  # ]:          0 :     if (ref->objcount == 0) {
    1115                 :          0 :         _PyChannelState *chan = NULL;
    1116                 :          0 :         _channels_remove_ref(channels, ref, prev, &chan);
    1117         [ #  # ]:          0 :         if (chan != NULL) {
    1118                 :          0 :             _channel_free(chan);
    1119                 :            :         }
    1120                 :            :     }
    1121                 :            : 
    1122                 :          0 : done:
    1123                 :          0 :     PyThread_release_lock(channels->mutex);
    1124                 :          0 : }
    1125                 :            : 
    1126                 :            : static int64_t *
    1127                 :          0 : _channels_list_all(_channels *channels, int64_t *count)
    1128                 :            : {
    1129                 :          0 :     int64_t *cids = NULL;
    1130                 :          0 :     PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
    1131         [ #  # ]:          0 :     int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen));
    1132         [ #  # ]:          0 :     if (ids == NULL) {
    1133                 :          0 :         goto done;
    1134                 :            :     }
    1135                 :          0 :     _channelref *ref = channels->head;
    1136         [ #  # ]:          0 :     for (int64_t i=0; ref != NULL; ref = ref->next, i++) {
    1137                 :          0 :         ids[i] = ref->id;
    1138                 :            :     }
    1139                 :          0 :     *count = channels->numopen;
    1140                 :            : 
    1141                 :          0 :     cids = ids;
    1142                 :          0 : done:
    1143                 :          0 :     PyThread_release_lock(channels->mutex);
    1144                 :          0 :     return cids;
    1145                 :            : }
    1146                 :            : 
    1147                 :            : /* support for closing non-empty channels */
    1148                 :            : 
    1149                 :            : struct _channel_closing {
    1150                 :            :     struct _channelref *ref;
    1151                 :            : };
    1152                 :            : 
    1153                 :            : static int
    1154                 :          0 : _channel_set_closing(struct _channelref *ref, PyThread_type_lock mutex) {
    1155                 :          0 :     struct _channel *chan = ref->chan;
    1156         [ #  # ]:          0 :     if (chan == NULL) {
    1157                 :            :         // already closed
    1158                 :          0 :         return 0;
    1159                 :            :     }
    1160                 :          0 :     int res = -1;
    1161                 :          0 :     PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
    1162         [ #  # ]:          0 :     if (chan->closing != NULL) {
    1163                 :          0 :         res = ERR_CHANNEL_CLOSED;
    1164                 :          0 :         goto done;
    1165                 :            :     }
    1166                 :          0 :     chan->closing = PyMem_NEW(struct _channel_closing, 1);
    1167         [ #  # ]:          0 :     if (chan->closing == NULL) {
    1168                 :          0 :         goto done;
    1169                 :            :     }
    1170                 :          0 :     chan->closing->ref = ref;
    1171                 :            : 
    1172                 :          0 :     res = 0;
    1173                 :          0 : done:
    1174                 :          0 :     PyThread_release_lock(chan->mutex);
    1175                 :          0 :     return res;
    1176                 :            : }
    1177                 :            : 
    1178                 :            : static void
    1179                 :          0 : _channel_clear_closing(struct _channel *chan) {
    1180                 :          0 :     PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
    1181         [ #  # ]:          0 :     if (chan->closing != NULL) {
    1182                 :          0 :         PyMem_Free(chan->closing);
    1183                 :          0 :         chan->closing = NULL;
    1184                 :            :     }
    1185                 :          0 :     PyThread_release_lock(chan->mutex);
    1186                 :          0 : }
    1187                 :            : 
    1188                 :            : static void
    1189                 :          0 : _channel_finish_closing(struct _channel *chan) {
    1190                 :          0 :     struct _channel_closing *closing = chan->closing;
    1191         [ #  # ]:          0 :     if (closing == NULL) {
    1192                 :          0 :         return;
    1193                 :            :     }
    1194                 :          0 :     _channelref *ref = closing->ref;
    1195                 :          0 :     _channel_clear_closing(chan);
    1196                 :            :     // Do the things that would have been done in _channels_close().
    1197                 :          0 :     ref->chan = NULL;
    1198                 :          0 :     _channel_free(chan);
    1199                 :            : }
    1200                 :            : 
    1201                 :            : /* "high"-level channel-related functions */
    1202                 :            : 
    1203                 :            : static int64_t
    1204                 :          0 : _channel_create(_channels *channels)
    1205                 :            : {
    1206                 :          0 :     PyThread_type_lock mutex = PyThread_allocate_lock();
    1207         [ #  # ]:          0 :     if (mutex == NULL) {
    1208                 :          0 :         return ERR_CHANNEL_MUTEX_INIT;
    1209                 :            :     }
    1210                 :          0 :     _PyChannelState *chan = _channel_new(mutex);
    1211         [ #  # ]:          0 :     if (chan == NULL) {
    1212                 :          0 :         PyThread_free_lock(mutex);
    1213                 :          0 :         return -1;
    1214                 :            :     }
    1215                 :          0 :     int64_t id = _channels_add(channels, chan);
    1216         [ #  # ]:          0 :     if (id < 0) {
    1217                 :          0 :         _channel_free(chan);
    1218                 :            :     }
    1219                 :          0 :     return id;
    1220                 :            : }
    1221                 :            : 
    1222                 :            : static int
    1223                 :          0 : _channel_destroy(_channels *channels, int64_t id)
    1224                 :            : {
    1225                 :          0 :     _PyChannelState *chan = NULL;
    1226                 :          0 :     int err = _channels_remove(channels, id, &chan);
    1227         [ #  # ]:          0 :     if (err != 0) {
    1228                 :          0 :         return err;
    1229                 :            :     }
    1230         [ #  # ]:          0 :     if (chan != NULL) {
    1231                 :          0 :         _channel_free(chan);
    1232                 :            :     }
    1233                 :          0 :     return 0;
    1234                 :            : }
    1235                 :            : 
    1236                 :            : static int
    1237                 :          0 : _channel_send(_channels *channels, int64_t id, PyObject *obj)
    1238                 :            : {
    1239                 :          0 :     PyInterpreterState *interp = _get_current_interp();
    1240         [ #  # ]:          0 :     if (interp == NULL) {
    1241                 :          0 :         return -1;
    1242                 :            :     }
    1243                 :            : 
    1244                 :            :     // Look up the channel.
    1245                 :          0 :     PyThread_type_lock mutex = NULL;
    1246                 :          0 :     _PyChannelState *chan = NULL;
    1247                 :          0 :     int err = _channels_lookup(channels, id, &mutex, &chan);
    1248         [ #  # ]:          0 :     if (err != 0) {
    1249                 :          0 :         return err;
    1250                 :            :     }
    1251                 :            :     assert(chan != NULL);
    1252                 :            :     // Past this point we are responsible for releasing the mutex.
    1253                 :            : 
    1254         [ #  # ]:          0 :     if (chan->closing != NULL) {
    1255                 :          0 :         PyThread_release_lock(mutex);
    1256                 :          0 :         return ERR_CHANNEL_CLOSED;
    1257                 :            :     }
    1258                 :            : 
    1259                 :            :     // Convert the object to cross-interpreter data.
    1260                 :          0 :     _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1);
    1261         [ #  # ]:          0 :     if (data == NULL) {
    1262                 :          0 :         PyThread_release_lock(mutex);
    1263                 :          0 :         return -1;
    1264                 :            :     }
    1265         [ #  # ]:          0 :     if (_PyObject_GetCrossInterpreterData(obj, data) != 0) {
    1266                 :          0 :         PyThread_release_lock(mutex);
    1267                 :          0 :         PyMem_Free(data);
    1268                 :          0 :         return -1;
    1269                 :            :     }
    1270                 :            : 
    1271                 :            :     // Add the data to the channel.
    1272                 :          0 :     int res = _channel_add(chan, PyInterpreterState_GetID(interp), data);
    1273                 :          0 :     PyThread_release_lock(mutex);
    1274         [ #  # ]:          0 :     if (res != 0) {
    1275                 :            :         // We may chain an exception here:
    1276                 :          0 :         (void)_release_xid_data(data, 0);
    1277                 :          0 :         PyMem_Free(data);
    1278                 :          0 :         return res;
    1279                 :            :     }
    1280                 :            : 
    1281                 :          0 :     return 0;
    1282                 :            : }
    1283                 :            : 
    1284                 :            : static int
    1285                 :          0 : _channel_recv(_channels *channels, int64_t id, PyObject **res)
    1286                 :            : {
    1287                 :            :     int err;
    1288                 :          0 :     *res = NULL;
    1289                 :            : 
    1290                 :          0 :     PyInterpreterState *interp = _get_current_interp();
    1291         [ #  # ]:          0 :     if (interp == NULL) {
    1292                 :            :         // XXX Is this always an error?
    1293         [ #  # ]:          0 :         if (PyErr_Occurred()) {
    1294                 :          0 :             return -1;
    1295                 :            :         }
    1296                 :          0 :         return 0;
    1297                 :            :     }
    1298                 :            : 
    1299                 :            :     // Look up the channel.
    1300                 :          0 :     PyThread_type_lock mutex = NULL;
    1301                 :          0 :     _PyChannelState *chan = NULL;
    1302                 :          0 :     err = _channels_lookup(channels, id, &mutex, &chan);
    1303         [ #  # ]:          0 :     if (err != 0) {
    1304                 :          0 :         return err;
    1305                 :            :     }
    1306                 :            :     assert(chan != NULL);
    1307                 :            :     // Past this point we are responsible for releasing the mutex.
    1308                 :            : 
    1309                 :            :     // Pop off the next item from the channel.
    1310                 :          0 :     _PyCrossInterpreterData *data = NULL;
    1311                 :          0 :     err = _channel_next(chan, PyInterpreterState_GetID(interp), &data);
    1312                 :          0 :     PyThread_release_lock(mutex);
    1313         [ #  # ]:          0 :     if (err != 0) {
    1314                 :          0 :         return err;
    1315                 :            :     }
    1316         [ #  # ]:          0 :     else if (data == NULL) {
    1317                 :            :         assert(!PyErr_Occurred());
    1318                 :          0 :         return 0;
    1319                 :            :     }
    1320                 :            : 
    1321                 :            :     // Convert the data back to an object.
    1322                 :          0 :     PyObject *obj = _PyCrossInterpreterData_NewObject(data);
    1323         [ #  # ]:          0 :     if (obj == NULL) {
    1324                 :            :         assert(PyErr_Occurred());
    1325                 :          0 :         (void)_release_xid_data(data, 1);
    1326                 :          0 :         PyMem_Free(data);
    1327                 :          0 :         return -1;
    1328                 :            :     }
    1329                 :          0 :     int release_res = _release_xid_data(data, 0);
    1330                 :          0 :     PyMem_Free(data);
    1331         [ #  # ]:          0 :     if (release_res < 0) {
    1332                 :            :         // The source interpreter has been destroyed already.
    1333                 :            :         assert(PyErr_Occurred());
    1334                 :          0 :         Py_DECREF(obj);
    1335                 :          0 :         return -1;
    1336                 :            :     }
    1337                 :            : 
    1338                 :          0 :     *res = obj;
    1339                 :          0 :     return 0;
    1340                 :            : }
    1341                 :            : 
    1342                 :            : static int
    1343                 :          0 : _channel_drop(_channels *channels, int64_t id, int send, int recv)
    1344                 :            : {
    1345                 :          0 :     PyInterpreterState *interp = _get_current_interp();
    1346         [ #  # ]:          0 :     if (interp == NULL) {
    1347                 :          0 :         return -1;
    1348                 :            :     }
    1349                 :            : 
    1350                 :            :     // Look up the channel.
    1351                 :          0 :     PyThread_type_lock mutex = NULL;
    1352                 :          0 :     _PyChannelState *chan = NULL;
    1353                 :          0 :     int err = _channels_lookup(channels, id, &mutex, &chan);
    1354         [ #  # ]:          0 :     if (err != 0) {
    1355                 :          0 :         return err;
    1356                 :            :     }
    1357                 :            :     // Past this point we are responsible for releasing the mutex.
    1358                 :            : 
    1359                 :            :     // Close one or both of the two ends.
    1360                 :          0 :     int res = _channel_close_interpreter(chan, PyInterpreterState_GetID(interp), send-recv);
    1361                 :          0 :     PyThread_release_lock(mutex);
    1362                 :          0 :     return res;
    1363                 :            : }
    1364                 :            : 
    1365                 :            : static int
    1366                 :          0 : _channel_close(_channels *channels, int64_t id, int end, int force)
    1367                 :            : {
    1368                 :          0 :     return _channels_close(channels, id, NULL, end, force);
    1369                 :            : }
    1370                 :            : 
    1371                 :            : static int
    1372                 :          0 : _channel_is_associated(_channels *channels, int64_t cid, int64_t interp,
    1373                 :            :                        int send)
    1374                 :            : {
    1375                 :          0 :     _PyChannelState *chan = NULL;
    1376                 :          0 :     int err = _channels_lookup(channels, cid, NULL, &chan);
    1377         [ #  # ]:          0 :     if (err != 0) {
    1378                 :          0 :         return err;
    1379                 :            :     }
    1380   [ #  #  #  # ]:          0 :     else if (send && chan->closing != NULL) {
    1381                 :          0 :         return ERR_CHANNEL_CLOSED;
    1382                 :            :     }
    1383                 :            : 
    1384         [ #  # ]:          0 :     _channelend *end = _channelend_find(send ? chan->ends->send : chan->ends->recv,
    1385                 :            :                                         interp, NULL);
    1386                 :            : 
    1387   [ #  #  #  # ]:          0 :     return (end != NULL && end->open);
    1388                 :            : }
    1389                 :            : 
    1390                 :            : /* ChannelID class */
    1391                 :            : 
    1392                 :            : typedef struct channelid {
    1393                 :            :     PyObject_HEAD
    1394                 :            :     int64_t id;
    1395                 :            :     int end;
    1396                 :            :     int resolve;
    1397                 :            :     _channels *channels;
    1398                 :            : } channelid;
    1399                 :            : 
    1400                 :            : struct channel_id_converter_data {
    1401                 :            :     PyObject *module;
    1402                 :            :     int64_t cid;
    1403                 :            : };
    1404                 :            : 
    1405                 :            : static int
    1406                 :          0 : channel_id_converter(PyObject *arg, void *ptr)
    1407                 :            : {
    1408                 :            :     int64_t cid;
    1409                 :          0 :     struct channel_id_converter_data *data = ptr;
    1410                 :          0 :     module_state *state = get_module_state(data->module);
    1411                 :            :     assert(state != NULL);
    1412         [ #  # ]:          0 :     if (PyObject_TypeCheck(arg, state->ChannelIDType)) {
    1413                 :          0 :         cid = ((channelid *)arg)->id;
    1414                 :            :     }
    1415         [ #  # ]:          0 :     else if (PyIndex_Check(arg)) {
    1416                 :          0 :         cid = PyLong_AsLongLong(arg);
    1417   [ #  #  #  # ]:          0 :         if (cid == -1 && PyErr_Occurred()) {
    1418                 :          0 :             return 0;
    1419                 :            :         }
    1420         [ #  # ]:          0 :         if (cid < 0) {
    1421                 :          0 :             PyErr_Format(PyExc_ValueError,
    1422                 :            :                         "channel ID must be a non-negative int, got %R", arg);
    1423                 :          0 :             return 0;
    1424                 :            :         }
    1425                 :            :     }
    1426                 :            :     else {
    1427                 :          0 :         PyErr_Format(PyExc_TypeError,
    1428                 :            :                      "channel ID must be an int, got %.100s",
    1429                 :          0 :                      Py_TYPE(arg)->tp_name);
    1430                 :          0 :         return 0;
    1431                 :            :     }
    1432                 :          0 :     data->cid = cid;
    1433                 :          0 :     return 1;
    1434                 :            : }
    1435                 :            : 
    1436                 :            : static int
    1437                 :          0 : newchannelid(PyTypeObject *cls, int64_t cid, int end, _channels *channels,
    1438                 :            :              int force, int resolve, channelid **res)
    1439                 :            : {
    1440                 :          0 :     *res = NULL;
    1441                 :            : 
    1442                 :          0 :     channelid *self = PyObject_New(channelid, cls);
    1443         [ #  # ]:          0 :     if (self == NULL) {
    1444                 :          0 :         return -1;
    1445                 :            :     }
    1446                 :          0 :     self->id = cid;
    1447                 :          0 :     self->end = end;
    1448                 :          0 :     self->resolve = resolve;
    1449                 :          0 :     self->channels = channels;
    1450                 :            : 
    1451                 :          0 :     int err = _channels_add_id_object(channels, cid);
    1452         [ #  # ]:          0 :     if (err != 0) {
    1453   [ #  #  #  # ]:          0 :         if (force && err == ERR_CHANNEL_NOT_FOUND) {
    1454                 :            :             assert(!PyErr_Occurred());
    1455                 :            :         }
    1456                 :            :         else {
    1457                 :          0 :             Py_DECREF((PyObject *)self);
    1458                 :          0 :             return err;
    1459                 :            :         }
    1460                 :            :     }
    1461                 :            : 
    1462                 :          0 :     *res = self;
    1463                 :          0 :     return 0;
    1464                 :            : }
    1465                 :            : 
    1466                 :            : static _channels * _global_channels(void);
    1467                 :            : 
    1468                 :            : static PyObject *
    1469                 :          0 : _channelid_new(PyObject *mod, PyTypeObject *cls,
    1470                 :            :                PyObject *args, PyObject *kwds)
    1471                 :            : {
    1472                 :            :     static char *kwlist[] = {"id", "send", "recv", "force", "_resolve", NULL};
    1473                 :            :     int64_t cid;
    1474                 :          0 :     struct channel_id_converter_data cid_data = {
    1475                 :            :         .module = mod,
    1476                 :            :     };
    1477                 :          0 :     int send = -1;
    1478                 :          0 :     int recv = -1;
    1479                 :          0 :     int force = 0;
    1480                 :          0 :     int resolve = 0;
    1481         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(args, kwds,
    1482                 :            :                                      "O&|$pppp:ChannelID.__new__", kwlist,
    1483                 :            :                                      channel_id_converter, &cid_data,
    1484                 :            :                                      &send, &recv, &force, &resolve)) {
    1485                 :          0 :         return NULL;
    1486                 :            :     }
    1487                 :          0 :     cid = cid_data.cid;
    1488                 :            : 
    1489                 :            :     // Handle "send" and "recv".
    1490   [ #  #  #  # ]:          0 :     if (send == 0 && recv == 0) {
    1491                 :          0 :         PyErr_SetString(PyExc_ValueError,
    1492                 :            :                         "'send' and 'recv' cannot both be False");
    1493                 :          0 :         return NULL;
    1494                 :            :     }
    1495                 :            : 
    1496                 :          0 :     int end = 0;
    1497         [ #  # ]:          0 :     if (send == 1) {
    1498   [ #  #  #  # ]:          0 :         if (recv == 0 || recv == -1) {
    1499                 :          0 :             end = CHANNEL_SEND;
    1500                 :            :         }
    1501                 :            :     }
    1502         [ #  # ]:          0 :     else if (recv == 1) {
    1503                 :          0 :         end = CHANNEL_RECV;
    1504                 :            :     }
    1505                 :            : 
    1506                 :          0 :     PyObject *id = NULL;
    1507                 :          0 :     int err = newchannelid(cls, cid, end, _global_channels(),
    1508                 :            :                            force, resolve,
    1509                 :            :                            (channelid **)&id);
    1510         [ #  # ]:          0 :     if (handle_channel_error(err, mod, cid)) {
    1511                 :            :         assert(id == NULL);
    1512                 :          0 :         return NULL;
    1513                 :            :     }
    1514                 :            :     assert(id != NULL);
    1515                 :          0 :     return id;
    1516                 :            : }
    1517                 :            : 
    1518                 :            : static void
    1519                 :          0 : channelid_dealloc(PyObject *self)
    1520                 :            : {
    1521                 :          0 :     int64_t cid = ((channelid *)self)->id;
    1522                 :          0 :     _channels *channels = ((channelid *)self)->channels;
    1523                 :            : 
    1524                 :          0 :     PyTypeObject *tp = Py_TYPE(self);
    1525                 :          0 :     tp->tp_free(self);
    1526                 :            :     /* "Instances of heap-allocated types hold a reference to their type."
    1527                 :            :      * See: https://docs.python.org/3.11/howto/isolating-extensions.html#garbage-collection-protocol
    1528                 :            :      * See: https://docs.python.org/3.11/c-api/typeobj.html#c.PyTypeObject.tp_traverse
    1529                 :            :     */
    1530                 :            :     // XXX Why don't we implement Py_TPFLAGS_HAVE_GC, e.g. Py_tp_traverse,
    1531                 :            :     // like we do for _abc._abc_data?
    1532                 :          0 :     Py_DECREF(tp);
    1533                 :            : 
    1534                 :          0 :     _channels_drop_id_object(channels, cid);
    1535                 :          0 : }
    1536                 :            : 
    1537                 :            : static PyObject *
    1538                 :          0 : channelid_repr(PyObject *self)
    1539                 :            : {
    1540                 :          0 :     PyTypeObject *type = Py_TYPE(self);
    1541                 :          0 :     const char *name = _PyType_Name(type);
    1542                 :            : 
    1543                 :          0 :     channelid *cid = (channelid *)self;
    1544                 :            :     const char *fmt;
    1545         [ #  # ]:          0 :     if (cid->end == CHANNEL_SEND) {
    1546                 :          0 :         fmt = "%s(%" PRId64 ", send=True)";
    1547                 :            :     }
    1548         [ #  # ]:          0 :     else if (cid->end == CHANNEL_RECV) {
    1549                 :          0 :         fmt = "%s(%" PRId64 ", recv=True)";
    1550                 :            :     }
    1551                 :            :     else {
    1552                 :          0 :         fmt = "%s(%" PRId64 ")";
    1553                 :            :     }
    1554                 :          0 :     return PyUnicode_FromFormat(fmt, name, cid->id);
    1555                 :            : }
    1556                 :            : 
    1557                 :            : static PyObject *
    1558                 :          0 : channelid_str(PyObject *self)
    1559                 :            : {
    1560                 :          0 :     channelid *cid = (channelid *)self;
    1561                 :          0 :     return PyUnicode_FromFormat("%" PRId64 "", cid->id);
    1562                 :            : }
    1563                 :            : 
    1564                 :            : static PyObject *
    1565                 :          0 : channelid_int(PyObject *self)
    1566                 :            : {
    1567                 :          0 :     channelid *cid = (channelid *)self;
    1568                 :          0 :     return PyLong_FromLongLong(cid->id);
    1569                 :            : }
    1570                 :            : 
    1571                 :            : static Py_hash_t
    1572                 :          0 : channelid_hash(PyObject *self)
    1573                 :            : {
    1574                 :          0 :     channelid *cid = (channelid *)self;
    1575                 :          0 :     PyObject *id = PyLong_FromLongLong(cid->id);
    1576         [ #  # ]:          0 :     if (id == NULL) {
    1577                 :          0 :         return -1;
    1578                 :            :     }
    1579                 :          0 :     Py_hash_t hash = PyObject_Hash(id);
    1580                 :          0 :     Py_DECREF(id);
    1581                 :          0 :     return hash;
    1582                 :            : }
    1583                 :            : 
    1584                 :            : static PyObject *
    1585                 :          0 : channelid_richcompare(PyObject *self, PyObject *other, int op)
    1586                 :            : {
    1587                 :          0 :     PyObject *res = NULL;
    1588   [ #  #  #  # ]:          0 :     if (op != Py_EQ && op != Py_NE) {
    1589                 :          0 :         Py_RETURN_NOTIMPLEMENTED;
    1590                 :            :     }
    1591                 :            : 
    1592                 :          0 :     PyObject *mod = get_module_from_type(Py_TYPE(self));
    1593         [ #  # ]:          0 :     if (mod == NULL) {
    1594                 :          0 :         return NULL;
    1595                 :            :     }
    1596                 :          0 :     module_state *state = get_module_state(mod);
    1597         [ #  # ]:          0 :     if (state == NULL) {
    1598                 :          0 :         goto done;
    1599                 :            :     }
    1600                 :            : 
    1601         [ #  # ]:          0 :     if (!PyObject_TypeCheck(self, state->ChannelIDType)) {
    1602                 :          0 :         res = Py_NewRef(Py_NotImplemented);
    1603                 :          0 :         goto done;
    1604                 :            :     }
    1605                 :            : 
    1606                 :          0 :     channelid *cid = (channelid *)self;
    1607                 :            :     int equal;
    1608         [ #  # ]:          0 :     if (PyObject_TypeCheck(other, state->ChannelIDType)) {
    1609                 :          0 :         channelid *othercid = (channelid *)other;
    1610   [ #  #  #  # ]:          0 :         equal = (cid->end == othercid->end) && (cid->id == othercid->id);
    1611                 :            :     }
    1612         [ #  # ]:          0 :     else if (PyLong_Check(other)) {
    1613                 :            :         /* Fast path */
    1614                 :            :         int overflow;
    1615                 :          0 :         long long othercid = PyLong_AsLongLongAndOverflow(other, &overflow);
    1616   [ #  #  #  # ]:          0 :         if (othercid == -1 && PyErr_Occurred()) {
    1617                 :          0 :             goto done;
    1618                 :            :         }
    1619   [ #  #  #  #  :          0 :         equal = !overflow && (othercid >= 0) && (cid->id == othercid);
                   #  # ]
    1620                 :            :     }
    1621         [ #  # ]:          0 :     else if (PyNumber_Check(other)) {
    1622                 :          0 :         PyObject *pyid = PyLong_FromLongLong(cid->id);
    1623         [ #  # ]:          0 :         if (pyid == NULL) {
    1624                 :          0 :             goto done;
    1625                 :            :         }
    1626                 :          0 :         res = PyObject_RichCompare(pyid, other, op);
    1627                 :          0 :         Py_DECREF(pyid);
    1628                 :          0 :         goto done;
    1629                 :            :     }
    1630                 :            :     else {
    1631                 :          0 :         res = Py_NewRef(Py_NotImplemented);
    1632                 :          0 :         goto done;
    1633                 :            :     }
    1634                 :            : 
    1635   [ #  #  #  #  :          0 :     if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) {
             #  #  #  # ]
    1636                 :          0 :         res = Py_NewRef(Py_True);
    1637                 :            :     }
    1638                 :            :     else {
    1639                 :          0 :         res = Py_NewRef(Py_False);
    1640                 :            :     }
    1641                 :            : 
    1642                 :          0 : done:
    1643                 :          0 :     Py_DECREF(mod);
    1644                 :          0 :     return res;
    1645                 :            : }
    1646                 :            : 
    1647                 :            : static PyObject *
    1648                 :          0 : _channel_from_cid(PyObject *cid, int end)
    1649                 :            : {
    1650                 :          0 :     PyObject *highlevel = PyImport_ImportModule("interpreters");
    1651         [ #  # ]:          0 :     if (highlevel == NULL) {
    1652                 :          0 :         PyErr_Clear();
    1653                 :          0 :         highlevel = PyImport_ImportModule("test.support.interpreters");
    1654         [ #  # ]:          0 :         if (highlevel == NULL) {
    1655                 :          0 :             return NULL;
    1656                 :            :         }
    1657                 :            :     }
    1658         [ #  # ]:          0 :     const char *clsname = (end == CHANNEL_RECV) ? "RecvChannel" :
    1659                 :            :                                                   "SendChannel";
    1660                 :          0 :     PyObject *cls = PyObject_GetAttrString(highlevel, clsname);
    1661                 :          0 :     Py_DECREF(highlevel);
    1662         [ #  # ]:          0 :     if (cls == NULL) {
    1663                 :          0 :         return NULL;
    1664                 :            :     }
    1665                 :          0 :     PyObject *chan = PyObject_CallFunctionObjArgs(cls, cid, NULL);
    1666                 :          0 :     Py_DECREF(cls);
    1667         [ #  # ]:          0 :     if (chan == NULL) {
    1668                 :          0 :         return NULL;
    1669                 :            :     }
    1670                 :          0 :     return chan;
    1671                 :            : }
    1672                 :            : 
    1673                 :            : struct _channelid_xid {
    1674                 :            :     int64_t id;
    1675                 :            :     int end;
    1676                 :            :     int resolve;
    1677                 :            : };
    1678                 :            : 
    1679                 :            : static PyObject *
    1680                 :          0 : _channelid_from_xid(_PyCrossInterpreterData *data)
    1681                 :            : {
    1682                 :          0 :     struct _channelid_xid *xid = (struct _channelid_xid *)data->data;
    1683                 :            : 
    1684                 :            :     // It might not be imported yet, so we can't use _get_current_module().
    1685                 :          0 :     PyObject *mod = PyImport_ImportModule(MODULE_NAME);
    1686         [ #  # ]:          0 :     if (mod == NULL) {
    1687                 :          0 :         return NULL;
    1688                 :            :     }
    1689                 :            :     assert(mod != Py_None);
    1690                 :          0 :     module_state *state = get_module_state(mod);
    1691         [ #  # ]:          0 :     if (state == NULL) {
    1692                 :          0 :         return NULL;
    1693                 :            :     }
    1694                 :            : 
    1695                 :            :     // Note that we do not preserve the "resolve" flag.
    1696                 :          0 :     PyObject *cid = NULL;
    1697                 :          0 :     int err = newchannelid(state->ChannelIDType, xid->id, xid->end,
    1698                 :            :                            _global_channels(), 0, 0,
    1699                 :            :                            (channelid **)&cid);
    1700         [ #  # ]:          0 :     if (err != 0) {
    1701                 :            :         assert(cid == NULL);
    1702                 :          0 :         (void)handle_channel_error(err, mod, xid->id);
    1703                 :          0 :         goto done;
    1704                 :            :     }
    1705                 :            :     assert(cid != NULL);
    1706         [ #  # ]:          0 :     if (xid->end == 0) {
    1707                 :          0 :         goto done;
    1708                 :            :     }
    1709         [ #  # ]:          0 :     if (!xid->resolve) {
    1710                 :          0 :         goto done;
    1711                 :            :     }
    1712                 :            : 
    1713                 :            :     /* Try returning a high-level channel end but fall back to the ID. */
    1714                 :          0 :     PyObject *chan = _channel_from_cid(cid, xid->end);
    1715         [ #  # ]:          0 :     if (chan == NULL) {
    1716                 :          0 :         PyErr_Clear();
    1717                 :          0 :         goto done;
    1718                 :            :     }
    1719                 :          0 :     Py_DECREF(cid);
    1720                 :          0 :     cid = chan;
    1721                 :            : 
    1722                 :          0 : done:
    1723                 :          0 :     Py_DECREF(mod);
    1724                 :          0 :     return cid;
    1725                 :            : }
    1726                 :            : 
    1727                 :            : static int
    1728                 :          0 : _channelid_shared(PyThreadState *tstate, PyObject *obj,
    1729                 :            :                   _PyCrossInterpreterData *data)
    1730                 :            : {
    1731         [ #  # ]:          0 :     if (_PyCrossInterpreterData_InitWithSize(
    1732                 :            :             data, tstate->interp, sizeof(struct _channelid_xid), obj,
    1733                 :            :             _channelid_from_xid
    1734                 :            :             ) < 0)
    1735                 :            :     {
    1736                 :          0 :         return -1;
    1737                 :            :     }
    1738                 :          0 :     struct _channelid_xid *xid = (struct _channelid_xid *)data->data;
    1739                 :          0 :     xid->id = ((channelid *)obj)->id;
    1740                 :          0 :     xid->end = ((channelid *)obj)->end;
    1741                 :          0 :     xid->resolve = ((channelid *)obj)->resolve;
    1742                 :          0 :     return 0;
    1743                 :            : }
    1744                 :            : 
    1745                 :            : static PyObject *
    1746                 :          0 : channelid_end(PyObject *self, void *end)
    1747                 :            : {
    1748                 :          0 :     int force = 1;
    1749                 :          0 :     channelid *cid = (channelid *)self;
    1750         [ #  # ]:          0 :     if (end != NULL) {
    1751                 :          0 :         PyObject *id = NULL;
    1752                 :          0 :         int err = newchannelid(Py_TYPE(self), cid->id, *(int *)end,
    1753                 :            :                                cid->channels, force, cid->resolve,
    1754                 :            :                                (channelid **)&id);
    1755         [ #  # ]:          0 :         if (err != 0) {
    1756                 :            :             assert(id == NULL);
    1757                 :          0 :             PyObject *mod = get_module_from_type(Py_TYPE(self));
    1758         [ #  # ]:          0 :             if (mod == NULL) {
    1759                 :          0 :                 return NULL;
    1760                 :            :             }
    1761                 :          0 :             (void)handle_channel_error(err, mod, cid->id);
    1762                 :          0 :             Py_DECREF(mod);
    1763                 :          0 :             return NULL;
    1764                 :            :         }
    1765                 :            :         assert(id != NULL);
    1766                 :          0 :         return id;
    1767                 :            :     }
    1768                 :            : 
    1769         [ #  # ]:          0 :     if (cid->end == CHANNEL_SEND) {
    1770                 :          0 :         return PyUnicode_InternFromString("send");
    1771                 :            :     }
    1772         [ #  # ]:          0 :     if (cid->end == CHANNEL_RECV) {
    1773                 :          0 :         return PyUnicode_InternFromString("recv");
    1774                 :            :     }
    1775                 :          0 :     return PyUnicode_InternFromString("both");
    1776                 :            : }
    1777                 :            : 
    1778                 :            : static int _channelid_end_send = CHANNEL_SEND;
    1779                 :            : static int _channelid_end_recv = CHANNEL_RECV;
    1780                 :            : 
    1781                 :            : static PyGetSetDef channelid_getsets[] = {
    1782                 :            :     {"end", (getter)channelid_end, NULL,
    1783                 :            :      PyDoc_STR("'send', 'recv', or 'both'")},
    1784                 :            :     {"send", (getter)channelid_end, NULL,
    1785                 :            :      PyDoc_STR("the 'send' end of the channel"), &_channelid_end_send},
    1786                 :            :     {"recv", (getter)channelid_end, NULL,
    1787                 :            :      PyDoc_STR("the 'recv' end of the channel"), &_channelid_end_recv},
    1788                 :            :     {NULL}
    1789                 :            : };
    1790                 :            : 
    1791                 :            : PyDoc_STRVAR(channelid_doc,
    1792                 :            : "A channel ID identifies a channel and may be used as an int.");
    1793                 :            : 
    1794                 :            : static PyType_Slot ChannelIDType_slots[] = {
    1795                 :            :     {Py_tp_dealloc, (destructor)channelid_dealloc},
    1796                 :            :     {Py_tp_doc, (void *)channelid_doc},
    1797                 :            :     {Py_tp_repr, (reprfunc)channelid_repr},
    1798                 :            :     {Py_tp_str, (reprfunc)channelid_str},
    1799                 :            :     {Py_tp_hash, channelid_hash},
    1800                 :            :     {Py_tp_richcompare, channelid_richcompare},
    1801                 :            :     {Py_tp_getset, channelid_getsets},
    1802                 :            :     // number slots
    1803                 :            :     {Py_nb_int, (unaryfunc)channelid_int},
    1804                 :            :     {Py_nb_index,  (unaryfunc)channelid_int},
    1805                 :            :     {0, NULL},
    1806                 :            : };
    1807                 :            : 
    1808                 :            : static PyType_Spec ChannelIDType_spec = {
    1809                 :            :     .name = MODULE_NAME ".ChannelID",
    1810                 :            :     .basicsize = sizeof(channelid),
    1811                 :            :     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
    1812                 :            :               Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
    1813                 :            :     .slots = ChannelIDType_slots,
    1814                 :            : };
    1815                 :            : 
    1816                 :            : 
    1817                 :            : /* module level code ********************************************************/
    1818                 :            : 
    1819                 :            : /* globals is the process-global state for the module.  It holds all
    1820                 :            :    the data that we need to share between interpreters, so it cannot
    1821                 :            :    hold PyObject values. */
    1822                 :            : static struct globals {
    1823                 :            :     int module_count;
    1824                 :            :     _channels channels;
    1825                 :            : } _globals = {0};
    1826                 :            : 
    1827                 :            : static int
    1828                 :          1 : _globals_init(void)
    1829                 :            : {
    1830                 :            :     // XXX This isn't thread-safe.
    1831                 :          1 :     _globals.module_count++;
    1832         [ -  + ]:          1 :     if (_globals.module_count > 1) {
    1833                 :            :         // Already initialized.
    1834                 :          0 :         return 0;
    1835                 :            :     }
    1836                 :            : 
    1837                 :            :     assert(_globals.channels.mutex == NULL);
    1838                 :          1 :     PyThread_type_lock mutex = PyThread_allocate_lock();
    1839         [ -  + ]:          1 :     if (mutex == NULL) {
    1840                 :          0 :         return ERR_CHANNELS_MUTEX_INIT;
    1841                 :            :     }
    1842                 :          1 :     _channels_init(&_globals.channels, mutex);
    1843                 :          1 :     return 0;
    1844                 :            : }
    1845                 :            : 
    1846                 :            : static void
    1847                 :          1 : _globals_fini(void)
    1848                 :            : {
    1849                 :            :     // XXX This isn't thread-safe.
    1850                 :          1 :     _globals.module_count--;
    1851         [ -  + ]:          1 :     if (_globals.module_count > 0) {
    1852                 :          0 :         return;
    1853                 :            :     }
    1854                 :            : 
    1855                 :          1 :     _channels_fini(&_globals.channels);
    1856                 :            : }
    1857                 :            : 
    1858                 :            : static _channels *
    1859                 :          0 : _global_channels(void) {
    1860                 :          0 :     return &_globals.channels;
    1861                 :            : }
    1862                 :            : 
    1863                 :            : 
    1864                 :            : static PyObject *
    1865                 :          0 : channel_create(PyObject *self, PyObject *Py_UNUSED(ignored))
    1866                 :            : {
    1867                 :          0 :     int64_t cid = _channel_create(&_globals.channels);
    1868         [ #  # ]:          0 :     if (cid < 0) {
    1869                 :          0 :         (void)handle_channel_error(-1, self, cid);
    1870                 :          0 :         return NULL;
    1871                 :            :     }
    1872                 :          0 :     module_state *state = get_module_state(self);
    1873         [ #  # ]:          0 :     if (state == NULL) {
    1874                 :          0 :         return NULL;
    1875                 :            :     }
    1876                 :          0 :     PyObject *id = NULL;
    1877                 :          0 :     int err = newchannelid(state->ChannelIDType, cid, 0,
    1878                 :            :                            &_globals.channels, 0, 0,
    1879                 :            :                            (channelid **)&id);
    1880         [ #  # ]:          0 :     if (handle_channel_error(err, self, cid)) {
    1881                 :            :         assert(id == NULL);
    1882                 :          0 :         err = _channel_destroy(&_globals.channels, cid);
    1883                 :          0 :         if (handle_channel_error(err, self, cid)) {
    1884                 :            :             // XXX issue a warning?
    1885                 :            :         }
    1886                 :          0 :         return NULL;
    1887                 :            :     }
    1888                 :            :     assert(id != NULL);
    1889                 :            :     assert(((channelid *)id)->channels != NULL);
    1890                 :          0 :     return id;
    1891                 :            : }
    1892                 :            : 
    1893                 :            : PyDoc_STRVAR(channel_create_doc,
    1894                 :            : "channel_create() -> cid\n\
    1895                 :            : \n\
    1896                 :            : Create a new cross-interpreter channel and return a unique generated ID.");
    1897                 :            : 
    1898                 :            : static PyObject *
    1899                 :          0 : channel_destroy(PyObject *self, PyObject *args, PyObject *kwds)
    1900                 :            : {
    1901                 :            :     static char *kwlist[] = {"cid", NULL};
    1902                 :            :     int64_t cid;
    1903                 :          0 :     struct channel_id_converter_data cid_data = {
    1904                 :            :         .module = self,
    1905                 :            :     };
    1906         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:channel_destroy", kwlist,
    1907                 :            :                                      channel_id_converter, &cid_data)) {
    1908                 :          0 :         return NULL;
    1909                 :            :     }
    1910                 :          0 :     cid = cid_data.cid;
    1911                 :            : 
    1912                 :          0 :     int err = _channel_destroy(&_globals.channels, cid);
    1913         [ #  # ]:          0 :     if (handle_channel_error(err, self, cid)) {
    1914                 :          0 :         return NULL;
    1915                 :            :     }
    1916                 :          0 :     Py_RETURN_NONE;
    1917                 :            : }
    1918                 :            : 
    1919                 :            : PyDoc_STRVAR(channel_destroy_doc,
    1920                 :            : "channel_destroy(cid)\n\
    1921                 :            : \n\
    1922                 :            : Close and finalize the channel.  Afterward attempts to use the channel\n\
    1923                 :            : will behave as though it never existed.");
    1924                 :            : 
    1925                 :            : static PyObject *
    1926                 :          0 : channel_list_all(PyObject *self, PyObject *Py_UNUSED(ignored))
    1927                 :            : {
    1928                 :          0 :     int64_t count = 0;
    1929                 :          0 :     int64_t *cids = _channels_list_all(&_globals.channels, &count);
    1930         [ #  # ]:          0 :     if (cids == NULL) {
    1931         [ #  # ]:          0 :         if (count == 0) {
    1932                 :          0 :             return PyList_New(0);
    1933                 :            :         }
    1934                 :          0 :         return NULL;
    1935                 :            :     }
    1936                 :          0 :     PyObject *ids = PyList_New((Py_ssize_t)count);
    1937         [ #  # ]:          0 :     if (ids == NULL) {
    1938                 :          0 :         goto finally;
    1939                 :            :     }
    1940                 :          0 :     module_state *state = get_module_state(self);
    1941         [ #  # ]:          0 :     if (state == NULL) {
    1942                 :          0 :         Py_DECREF(ids);
    1943                 :          0 :         ids = NULL;
    1944                 :          0 :         goto finally;
    1945                 :            :     }
    1946                 :          0 :     int64_t *cur = cids;
    1947         [ #  # ]:          0 :     for (int64_t i=0; i < count; cur++, i++) {
    1948                 :          0 :         PyObject *id = NULL;
    1949                 :          0 :         int err = newchannelid(state->ChannelIDType, *cur, 0,
    1950                 :            :                                &_globals.channels, 0, 0,
    1951                 :            :                                (channelid **)&id);
    1952         [ #  # ]:          0 :         if (handle_channel_error(err, self, *cur)) {
    1953                 :            :             assert(id == NULL);
    1954                 :          0 :             Py_SETREF(ids, NULL);
    1955                 :          0 :             break;
    1956                 :            :         }
    1957                 :            :         assert(id != NULL);
    1958                 :          0 :         PyList_SET_ITEM(ids, (Py_ssize_t)i, id);
    1959                 :            :     }
    1960                 :            : 
    1961                 :          0 : finally:
    1962                 :          0 :     PyMem_Free(cids);
    1963                 :          0 :     return ids;
    1964                 :            : }
    1965                 :            : 
    1966                 :            : PyDoc_STRVAR(channel_list_all_doc,
    1967                 :            : "channel_list_all() -> [cid]\n\
    1968                 :            : \n\
    1969                 :            : Return the list of all IDs for active channels.");
    1970                 :            : 
    1971                 :            : static PyObject *
    1972                 :          0 : channel_list_interpreters(PyObject *self, PyObject *args, PyObject *kwds)
    1973                 :            : {
    1974                 :            :     static char *kwlist[] = {"cid", "send", NULL};
    1975                 :            :     int64_t cid;            /* Channel ID */
    1976                 :          0 :     struct channel_id_converter_data cid_data = {
    1977                 :            :         .module = self,
    1978                 :            :     };
    1979                 :          0 :     int send = 0;           /* Send or receive end? */
    1980                 :            :     int64_t id;
    1981                 :            :     PyObject *ids, *id_obj;
    1982                 :            :     PyInterpreterState *interp;
    1983                 :            : 
    1984         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(
    1985                 :            :             args, kwds, "O&$p:channel_list_interpreters",
    1986                 :            :             kwlist, channel_id_converter, &cid_data, &send)) {
    1987                 :          0 :         return NULL;
    1988                 :            :     }
    1989                 :          0 :     cid = cid_data.cid;
    1990                 :            : 
    1991                 :          0 :     ids = PyList_New(0);
    1992         [ #  # ]:          0 :     if (ids == NULL) {
    1993                 :          0 :         goto except;
    1994                 :            :     }
    1995                 :            : 
    1996                 :          0 :     interp = PyInterpreterState_Head();
    1997         [ #  # ]:          0 :     while (interp != NULL) {
    1998                 :          0 :         id = PyInterpreterState_GetID(interp);
    1999                 :            :         assert(id >= 0);
    2000                 :          0 :         int res = _channel_is_associated(&_globals.channels, cid, id, send);
    2001         [ #  # ]:          0 :         if (res < 0) {
    2002                 :          0 :             (void)handle_channel_error(res, self, cid);
    2003                 :          0 :             goto except;
    2004                 :            :         }
    2005         [ #  # ]:          0 :         if (res) {
    2006                 :          0 :             id_obj = _PyInterpreterState_GetIDObject(interp);
    2007         [ #  # ]:          0 :             if (id_obj == NULL) {
    2008                 :          0 :                 goto except;
    2009                 :            :             }
    2010                 :          0 :             res = PyList_Insert(ids, 0, id_obj);
    2011                 :          0 :             Py_DECREF(id_obj);
    2012         [ #  # ]:          0 :             if (res < 0) {
    2013                 :          0 :                 goto except;
    2014                 :            :             }
    2015                 :            :         }
    2016                 :          0 :         interp = PyInterpreterState_Next(interp);
    2017                 :            :     }
    2018                 :            : 
    2019                 :          0 :     goto finally;
    2020                 :            : 
    2021                 :          0 : except:
    2022         [ #  # ]:          0 :     Py_CLEAR(ids);
    2023                 :            : 
    2024                 :          0 : finally:
    2025                 :          0 :     return ids;
    2026                 :            : }
    2027                 :            : 
    2028                 :            : PyDoc_STRVAR(channel_list_interpreters_doc,
    2029                 :            : "channel_list_interpreters(cid, *, send) -> [id]\n\
    2030                 :            : \n\
    2031                 :            : Return the list of all interpreter IDs associated with an end of the channel.\n\
    2032                 :            : \n\
    2033                 :            : The 'send' argument should be a boolean indicating whether to use the send or\n\
    2034                 :            : receive end.");
    2035                 :            : 
    2036                 :            : 
    2037                 :            : static PyObject *
    2038                 :          0 : channel_send(PyObject *self, PyObject *args, PyObject *kwds)
    2039                 :            : {
    2040                 :            :     static char *kwlist[] = {"cid", "obj", NULL};
    2041                 :            :     int64_t cid;
    2042                 :          0 :     struct channel_id_converter_data cid_data = {
    2043                 :            :         .module = self,
    2044                 :            :     };
    2045                 :            :     PyObject *obj;
    2046         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O:channel_send", kwlist,
    2047                 :            :                                      channel_id_converter, &cid_data, &obj)) {
    2048                 :          0 :         return NULL;
    2049                 :            :     }
    2050                 :          0 :     cid = cid_data.cid;
    2051                 :            : 
    2052                 :          0 :     int err = _channel_send(&_globals.channels, cid, obj);
    2053         [ #  # ]:          0 :     if (handle_channel_error(err, self, cid)) {
    2054                 :          0 :         return NULL;
    2055                 :            :     }
    2056                 :          0 :     Py_RETURN_NONE;
    2057                 :            : }
    2058                 :            : 
    2059                 :            : PyDoc_STRVAR(channel_send_doc,
    2060                 :            : "channel_send(cid, obj)\n\
    2061                 :            : \n\
    2062                 :            : Add the object's data to the channel's queue.");
    2063                 :            : 
    2064                 :            : static PyObject *
    2065                 :          0 : channel_recv(PyObject *self, PyObject *args, PyObject *kwds)
    2066                 :            : {
    2067                 :            :     static char *kwlist[] = {"cid", "default", NULL};
    2068                 :            :     int64_t cid;
    2069                 :          0 :     struct channel_id_converter_data cid_data = {
    2070                 :            :         .module = self,
    2071                 :            :     };
    2072                 :          0 :     PyObject *dflt = NULL;
    2073         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O:channel_recv", kwlist,
    2074                 :            :                                      channel_id_converter, &cid_data, &dflt)) {
    2075                 :          0 :         return NULL;
    2076                 :            :     }
    2077                 :          0 :     cid = cid_data.cid;
    2078                 :            : 
    2079                 :          0 :     PyObject *obj = NULL;
    2080                 :          0 :     int err = _channel_recv(&_globals.channels, cid, &obj);
    2081         [ #  # ]:          0 :     if (handle_channel_error(err, self, cid)) {
    2082                 :          0 :         return NULL;
    2083                 :            :     }
    2084                 :          0 :     Py_XINCREF(dflt);
    2085         [ #  # ]:          0 :     if (obj == NULL) {
    2086                 :            :         // Use the default.
    2087         [ #  # ]:          0 :         if (dflt == NULL) {
    2088                 :          0 :             (void)handle_channel_error(ERR_CHANNEL_EMPTY, self, cid);
    2089                 :          0 :             return NULL;
    2090                 :            :         }
    2091                 :          0 :         obj = Py_NewRef(dflt);
    2092                 :            :     }
    2093                 :          0 :     Py_XDECREF(dflt);
    2094                 :          0 :     return obj;
    2095                 :            : }
    2096                 :            : 
    2097                 :            : PyDoc_STRVAR(channel_recv_doc,
    2098                 :            : "channel_recv(cid, [default]) -> obj\n\
    2099                 :            : \n\
    2100                 :            : Return a new object from the data at the front of the channel's queue.\n\
    2101                 :            : \n\
    2102                 :            : If there is nothing to receive then raise ChannelEmptyError, unless\n\
    2103                 :            : a default value is provided.  In that case return it.");
    2104                 :            : 
    2105                 :            : static PyObject *
    2106                 :          0 : channel_close(PyObject *self, PyObject *args, PyObject *kwds)
    2107                 :            : {
    2108                 :            :     static char *kwlist[] = {"cid", "send", "recv", "force", NULL};
    2109                 :            :     int64_t cid;
    2110                 :          0 :     struct channel_id_converter_data cid_data = {
    2111                 :            :         .module = self,
    2112                 :            :     };
    2113                 :          0 :     int send = 0;
    2114                 :          0 :     int recv = 0;
    2115                 :          0 :     int force = 0;
    2116         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(args, kwds,
    2117                 :            :                                      "O&|$ppp:channel_close", kwlist,
    2118                 :            :                                      channel_id_converter, &cid_data,
    2119                 :            :                                      &send, &recv, &force)) {
    2120                 :          0 :         return NULL;
    2121                 :            :     }
    2122                 :          0 :     cid = cid_data.cid;
    2123                 :            : 
    2124                 :          0 :     int err = _channel_close(&_globals.channels, cid, send-recv, force);
    2125         [ #  # ]:          0 :     if (handle_channel_error(err, self, cid)) {
    2126                 :          0 :         return NULL;
    2127                 :            :     }
    2128                 :          0 :     Py_RETURN_NONE;
    2129                 :            : }
    2130                 :            : 
    2131                 :            : PyDoc_STRVAR(channel_close_doc,
    2132                 :            : "channel_close(cid, *, send=None, recv=None, force=False)\n\
    2133                 :            : \n\
    2134                 :            : Close the channel for all interpreters.\n\
    2135                 :            : \n\
    2136                 :            : If the channel is empty then the keyword args are ignored and both\n\
    2137                 :            : ends are immediately closed.  Otherwise, if 'force' is True then\n\
    2138                 :            : all queued items are released and both ends are immediately\n\
    2139                 :            : closed.\n\
    2140                 :            : \n\
    2141                 :            : If the channel is not empty *and* 'force' is False then following\n\
    2142                 :            : happens:\n\
    2143                 :            : \n\
    2144                 :            :  * recv is True (regardless of send):\n\
    2145                 :            :    - raise ChannelNotEmptyError\n\
    2146                 :            :  * recv is None and send is None:\n\
    2147                 :            :    - raise ChannelNotEmptyError\n\
    2148                 :            :  * send is True and recv is not True:\n\
    2149                 :            :    - fully close the 'send' end\n\
    2150                 :            :    - close the 'recv' end to interpreters not already receiving\n\
    2151                 :            :    - fully close it once empty\n\
    2152                 :            : \n\
    2153                 :            : Closing an already closed channel results in a ChannelClosedError.\n\
    2154                 :            : \n\
    2155                 :            : Once the channel's ID has no more ref counts in any interpreter\n\
    2156                 :            : the channel will be destroyed.");
    2157                 :            : 
    2158                 :            : static PyObject *
    2159                 :          0 : channel_release(PyObject *self, PyObject *args, PyObject *kwds)
    2160                 :            : {
    2161                 :            :     // Note that only the current interpreter is affected.
    2162                 :            :     static char *kwlist[] = {"cid", "send", "recv", "force", NULL};
    2163                 :            :     int64_t cid;
    2164                 :          0 :     struct channel_id_converter_data cid_data = {
    2165                 :            :         .module = self,
    2166                 :            :     };
    2167                 :          0 :     int send = 0;
    2168                 :          0 :     int recv = 0;
    2169                 :          0 :     int force = 0;
    2170         [ #  # ]:          0 :     if (!PyArg_ParseTupleAndKeywords(args, kwds,
    2171                 :            :                                      "O&|$ppp:channel_release", kwlist,
    2172                 :            :                                      channel_id_converter, &cid_data,
    2173                 :            :                                      &send, &recv, &force)) {
    2174                 :          0 :         return NULL;
    2175                 :            :     }
    2176                 :          0 :     cid = cid_data.cid;
    2177   [ #  #  #  # ]:          0 :     if (send == 0 && recv == 0) {
    2178                 :          0 :         send = 1;
    2179                 :          0 :         recv = 1;
    2180                 :            :     }
    2181                 :            : 
    2182                 :            :     // XXX Handle force is True.
    2183                 :            :     // XXX Fix implicit release.
    2184                 :            : 
    2185                 :          0 :     int err = _channel_drop(&_globals.channels, cid, send, recv);
    2186         [ #  # ]:          0 :     if (handle_channel_error(err, self, cid)) {
    2187                 :          0 :         return NULL;
    2188                 :            :     }
    2189                 :          0 :     Py_RETURN_NONE;
    2190                 :            : }
    2191                 :            : 
    2192                 :            : PyDoc_STRVAR(channel_release_doc,
    2193                 :            : "channel_release(cid, *, send=None, recv=None, force=True)\n\
    2194                 :            : \n\
    2195                 :            : Close the channel for the current interpreter.  'send' and 'recv'\n\
    2196                 :            : (bool) may be used to indicate the ends to close.  By default both\n\
    2197                 :            : ends are closed.  Closing an already closed end is a noop.");
    2198                 :            : 
    2199                 :            : static PyObject *
    2200                 :          0 : channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
    2201                 :            : {
    2202                 :          0 :     module_state *state = get_module_state(self);
    2203         [ #  # ]:          0 :     if (state == NULL) {
    2204                 :          0 :         return NULL;
    2205                 :            :     }
    2206                 :          0 :     PyTypeObject *cls = state->ChannelIDType;
    2207                 :          0 :     PyObject *mod = get_module_from_owned_type(cls);
    2208         [ #  # ]:          0 :     if (mod == NULL) {
    2209                 :          0 :         return NULL;
    2210                 :            :     }
    2211                 :          0 :     PyObject *cid = _channelid_new(mod, cls, args, kwds);
    2212                 :          0 :     Py_DECREF(mod);
    2213                 :          0 :     return cid;
    2214                 :            : }
    2215                 :            : 
    2216                 :            : static PyMethodDef module_functions[] = {
    2217                 :            :     {"create",                    channel_create,
    2218                 :            :      METH_NOARGS, channel_create_doc},
    2219                 :            :     {"destroy",                   _PyCFunction_CAST(channel_destroy),
    2220                 :            :      METH_VARARGS | METH_KEYWORDS, channel_destroy_doc},
    2221                 :            :     {"list_all",                  channel_list_all,
    2222                 :            :      METH_NOARGS, channel_list_all_doc},
    2223                 :            :     {"list_interpreters",         _PyCFunction_CAST(channel_list_interpreters),
    2224                 :            :      METH_VARARGS | METH_KEYWORDS, channel_list_interpreters_doc},
    2225                 :            :     {"send",                      _PyCFunction_CAST(channel_send),
    2226                 :            :      METH_VARARGS | METH_KEYWORDS, channel_send_doc},
    2227                 :            :     {"recv",                      _PyCFunction_CAST(channel_recv),
    2228                 :            :      METH_VARARGS | METH_KEYWORDS, channel_recv_doc},
    2229                 :            :     {"close",                     _PyCFunction_CAST(channel_close),
    2230                 :            :      METH_VARARGS | METH_KEYWORDS, channel_close_doc},
    2231                 :            :     {"release",                   _PyCFunction_CAST(channel_release),
    2232                 :            :      METH_VARARGS | METH_KEYWORDS, channel_release_doc},
    2233                 :            :     {"_channel_id",               _PyCFunction_CAST(channel__channel_id),
    2234                 :            :      METH_VARARGS | METH_KEYWORDS, NULL},
    2235                 :            : 
    2236                 :            :     {NULL,                        NULL}           /* sentinel */
    2237                 :            : };
    2238                 :            : 
    2239                 :            : 
    2240                 :            : /* initialization function */
    2241                 :            : 
    2242                 :            : PyDoc_STRVAR(module_doc,
    2243                 :            : "This module provides primitive operations to manage Python interpreters.\n\
    2244                 :            : The 'interpreters' module provides a more convenient interface.");
    2245                 :            : 
    2246                 :            : static int
    2247                 :          1 : module_exec(PyObject *mod)
    2248                 :            : {
    2249         [ -  + ]:          1 :     if (_globals_init() != 0) {
    2250                 :          0 :         return -1;
    2251                 :            :     }
    2252                 :            : 
    2253                 :            :     /* Add exception types */
    2254         [ -  + ]:          1 :     if (exceptions_init(mod) != 0) {
    2255                 :          0 :         goto error;
    2256                 :            :     }
    2257                 :            : 
    2258                 :            :     /* Add other types */
    2259                 :          1 :     module_state *state = get_module_state(mod);
    2260         [ -  + ]:          1 :     if (state == NULL) {
    2261                 :          0 :         goto error;
    2262                 :            :     }
    2263                 :            : 
    2264                 :            :     // ChannelID
    2265                 :          1 :     state->ChannelIDType = add_new_type(
    2266                 :            :             mod, &ChannelIDType_spec, _channelid_shared);
    2267         [ -  + ]:          1 :     if (state->ChannelIDType == NULL) {
    2268                 :          0 :         goto error;
    2269                 :            :     }
    2270                 :            : 
    2271                 :          1 :     return 0;
    2272                 :            : 
    2273                 :          0 : error:
    2274                 :          0 :     _globals_fini();
    2275                 :          0 :     return -1;
    2276                 :            : }
    2277                 :            : 
    2278                 :            : static struct PyModuleDef_Slot module_slots[] = {
    2279                 :            :     {Py_mod_exec, module_exec},
    2280                 :            :     {0, NULL},
    2281                 :            : };
    2282                 :            : 
    2283                 :            : static int
    2284                 :          6 : module_traverse(PyObject *mod, visitproc visit, void *arg)
    2285                 :            : {
    2286                 :          6 :     module_state *state = get_module_state(mod);
    2287                 :            :     assert(state != NULL);
    2288                 :          6 :     traverse_module_state(state, visit, arg);
    2289                 :          6 :     return 0;
    2290                 :            : }
    2291                 :            : 
    2292                 :            : static int
    2293                 :          1 : module_clear(PyObject *mod)
    2294                 :            : {
    2295                 :          1 :     module_state *state = get_module_state(mod);
    2296                 :            :     assert(state != NULL);
    2297                 :          1 :     clear_module_state(state);
    2298                 :          1 :     return 0;
    2299                 :            : }
    2300                 :            : 
    2301                 :            : static void
    2302                 :          1 : module_free(void *mod)
    2303                 :            : {
    2304                 :          1 :     module_state *state = get_module_state(mod);
    2305                 :            :     assert(state != NULL);
    2306                 :          1 :     clear_module_state(state);
    2307                 :          1 :     _globals_fini();
    2308                 :          1 : }
    2309                 :            : 
    2310                 :            : static struct PyModuleDef moduledef = {
    2311                 :            :     .m_base = PyModuleDef_HEAD_INIT,
    2312                 :            :     .m_name = MODULE_NAME,
    2313                 :            :     .m_doc = module_doc,
    2314                 :            :     .m_size = sizeof(module_state),
    2315                 :            :     .m_methods = module_functions,
    2316                 :            :     .m_slots = module_slots,
    2317                 :            :     .m_traverse = module_traverse,
    2318                 :            :     .m_clear = module_clear,
    2319                 :            :     .m_free = (freefunc)module_free,
    2320                 :            : };
    2321                 :            : 
    2322                 :            : PyMODINIT_FUNC
    2323                 :          1 : PyInit__xxinterpchannels(void)
    2324                 :            : {
    2325                 :          1 :     return PyModuleDef_Init(&moduledef);
    2326                 :            : }

Generated by: LCOV version 1.14