#include "pycore_interpreteridobject.h"
+/*
+This module has the following process-global state:
+
+_globals (static struct globals):
+ module_count (int)
+ channels (struct _channels):
+ numopen (int64_t)
+ next_id; (int64_t)
+ mutex (PyThread_type_lock)
+ head (linked list of struct _channelref *):
+ id (int64_t)
+ objcount (Py_ssize_t)
+ next (struct _channelref *):
+ ...
+ chan (struct _channel *):
+ open (int)
+ mutex (PyThread_type_lock)
+ closing (struct _channel_closing *):
+ ref (struct _channelref *):
+ ...
+ ends (struct _channelends *):
+ numsendopen (int64_t)
+ numrecvopen (int64_t)
+ send (struct _channelend *):
+ interp (int64_t)
+ open (int)
+ next (struct _channelend *)
+ recv (struct _channelend *):
+ ...
+ queue (struct _channelqueue *):
+ count (int64_t)
+ first (struct _channelitem *):
+ next (struct _channelitem *):
+ ...
+ data (_PyCrossInterpreterData *):
+ data (void *)
+ obj (PyObject *)
+ interp (int64_t)
+ new_object (xid_newobjectfunc)
+ free (xid_freefunc)
+ last (struct _channelitem *):
+ ...
+
+The above state includes the following allocations by the module:
+
+* 1 top-level mutex (to protect the rest of the state)
+* for each channel:
+ * 1 struct _channelref
+ * 1 struct _channel
+ * 0-1 struct _channel_closing
+ * 1 struct _channelends
+ * 2 struct _channelend
+ * 1 struct _channelqueue
+* for each item in each channel:
+ * 1 struct _channelitem
+ * 1 _PyCrossInterpreterData
+
+The only objects in that global state are the references held by each
+channel's queue, which are safely managed via the _PyCrossInterpreterData_*()
+API.. The module does not create any objects that are shared globally.
+*/
+
#define MODULE_NAME "_xxinterpchannels"
+#define GLOBAL_MALLOC(TYPE) \
+ PyMem_RawMalloc(sizeof(TYPE))
+#define GLOBAL_FREE(VAR) \
+ PyMem_RawFree(VAR)
+
+
static PyInterpreterState *
_get_current_interp(void)
{
static _channelitem *
_channelitem_new(void)
{
- _channelitem *item = PyMem_NEW(_channelitem, 1);
+ _channelitem *item = GLOBAL_MALLOC(_channelitem);
if (item == NULL) {
PyErr_NoMemory();
return NULL;
{
if (item->data != NULL) {
(void)_release_xid_data(item->data, 1);
- PyMem_Free(item->data);
+ // It was allocated in _channel_send().
+ GLOBAL_FREE(item->data);
item->data = NULL;
}
item->next = NULL;
_channelitem_free(_channelitem *item)
{
_channelitem_clear(item);
- PyMem_Free(item);
+ GLOBAL_FREE(item);
}
static void
static _channelqueue *
_channelqueue_new(void)
{
- _channelqueue *queue = PyMem_NEW(_channelqueue, 1);
+ _channelqueue *queue = GLOBAL_MALLOC(_channelqueue);
if (queue == NULL) {
PyErr_NoMemory();
return NULL;
_channelqueue_free(_channelqueue *queue)
{
_channelqueue_clear(queue);
- PyMem_Free(queue);
+ GLOBAL_FREE(queue);
}
static int
static _channelend *
_channelend_new(int64_t interp)
{
- _channelend *end = PyMem_NEW(_channelend, 1);
+ _channelend *end = GLOBAL_MALLOC(_channelend);
if (end == NULL) {
PyErr_NoMemory();
return NULL;
static void
_channelend_free(_channelend *end)
{
- PyMem_Free(end);
+ GLOBAL_FREE(end);
}
static void
static _channelends *
_channelends_new(void)
{
- _channelends *ends = PyMem_NEW(_channelends, 1);
+ _channelends *ends = GLOBAL_MALLOC(_channelends);
if (ends== NULL) {
return NULL;
}
_channelends_free(_channelends *ends)
{
_channelends_clear(ends);
- PyMem_Free(ends);
+ GLOBAL_FREE(ends);
}
static _channelend *
static _PyChannelState *
_channel_new(PyThread_type_lock mutex)
{
- _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1);
+ _PyChannelState *chan = GLOBAL_MALLOC(_PyChannelState);
if (chan == NULL) {
return NULL;
}
chan->mutex = mutex;
chan->queue = _channelqueue_new();
if (chan->queue == NULL) {
- PyMem_Free(chan);
+ GLOBAL_FREE(chan);
return NULL;
}
chan->ends = _channelends_new();
if (chan->ends == NULL) {
_channelqueue_free(chan->queue);
- PyMem_Free(chan);
+ GLOBAL_FREE(chan);
return NULL;
}
chan->open = 1;
PyThread_release_lock(chan->mutex);
PyThread_free_lock(chan->mutex);
- PyMem_Free(chan);
+ GLOBAL_FREE(chan);
}
static int
static _channelref *
_channelref_new(int64_t id, _PyChannelState *chan)
{
- _channelref *ref = PyMem_NEW(_channelref, 1);
+ _channelref *ref = GLOBAL_MALLOC(_channelref);
if (ref == NULL) {
return NULL;
}
_channel_clear_closing(ref->chan);
}
//_channelref_clear(ref);
- PyMem_Free(ref);
+ GLOBAL_FREE(ref);
}
static _channelref *
res = ERR_CHANNEL_CLOSED;
goto done;
}
- chan->closing = PyMem_NEW(struct _channel_closing, 1);
+ chan->closing = GLOBAL_MALLOC(struct _channel_closing);
if (chan->closing == NULL) {
goto done;
}
_channel_clear_closing(struct _channel *chan) {
PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
if (chan->closing != NULL) {
- PyMem_Free(chan->closing);
+ GLOBAL_FREE(chan->closing);
chan->closing = NULL;
}
PyThread_release_lock(chan->mutex);
}
// Convert the object to cross-interpreter data.
- _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1);
+ _PyCrossInterpreterData *data = GLOBAL_MALLOC(_PyCrossInterpreterData);
if (data == NULL) {
PyThread_release_lock(mutex);
return -1;
}
if (_PyObject_GetCrossInterpreterData(obj, data) != 0) {
PyThread_release_lock(mutex);
- PyMem_Free(data);
+ GLOBAL_FREE(data);
return -1;
}
if (res != 0) {
// We may chain an exception here:
(void)_release_xid_data(data, 0);
- PyMem_Free(data);
+ GLOBAL_FREE(data);
return res;
}
if (obj == NULL) {
assert(PyErr_Occurred());
(void)_release_xid_data(data, 1);
- PyMem_Free(data);
+ // It was allocated in _channel_send().
+ GLOBAL_FREE(data);
return -1;
}
int release_res = _release_xid_data(data, 0);
- PyMem_Free(data);
+ // It was allocated in _channel_send().
+ GLOBAL_FREE(data);
if (release_res < 0) {
// The source interpreter has been destroyed already.
assert(PyErr_Occurred());