Skip to content

Commit aa5a9b5

Browse files
gh-101659: Use the Raw Allocator in the _xxinterpchannels Module (gh-103287)
Using the raw allocator for any of the global state makes sense, especially as we move to a per-interpreter obmalloc state (gh-101660).
1 parent de18267 commit aa5a9b5

File tree

1 file changed

+93
-22
lines changed

1 file changed

+93
-22
lines changed

Modules/_xxinterpchannelsmodule.c

+93-22
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,77 @@
1010
#include "pycore_interpreteridobject.h"
1111

1212

13+
/*
14+
This module has the following process-global state:
15+
16+
_globals (static struct globals):
17+
module_count (int)
18+
channels (struct _channels):
19+
numopen (int64_t)
20+
next_id; (int64_t)
21+
mutex (PyThread_type_lock)
22+
head (linked list of struct _channelref *):
23+
id (int64_t)
24+
objcount (Py_ssize_t)
25+
next (struct _channelref *):
26+
...
27+
chan (struct _channel *):
28+
open (int)
29+
mutex (PyThread_type_lock)
30+
closing (struct _channel_closing *):
31+
ref (struct _channelref *):
32+
...
33+
ends (struct _channelends *):
34+
numsendopen (int64_t)
35+
numrecvopen (int64_t)
36+
send (struct _channelend *):
37+
interp (int64_t)
38+
open (int)
39+
next (struct _channelend *)
40+
recv (struct _channelend *):
41+
...
42+
queue (struct _channelqueue *):
43+
count (int64_t)
44+
first (struct _channelitem *):
45+
next (struct _channelitem *):
46+
...
47+
data (_PyCrossInterpreterData *):
48+
data (void *)
49+
obj (PyObject *)
50+
interp (int64_t)
51+
new_object (xid_newobjectfunc)
52+
free (xid_freefunc)
53+
last (struct _channelitem *):
54+
...
55+
56+
The above state includes the following allocations by the module:
57+
58+
* 1 top-level mutex (to protect the rest of the state)
59+
* for each channel:
60+
* 1 struct _channelref
61+
* 1 struct _channel
62+
* 0-1 struct _channel_closing
63+
* 1 struct _channelends
64+
* 2 struct _channelend
65+
* 1 struct _channelqueue
66+
* for each item in each channel:
67+
* 1 struct _channelitem
68+
* 1 _PyCrossInterpreterData
69+
70+
The only objects in that global state are the references held by each
71+
channel's queue, which are safely managed via the _PyCrossInterpreterData_*()
72+
API.. The module does not create any objects that are shared globally.
73+
*/
74+
1375
#define MODULE_NAME "_xxinterpchannels"
1476

1577

78+
#define GLOBAL_MALLOC(TYPE) \
79+
PyMem_RawMalloc(sizeof(TYPE))
80+
#define GLOBAL_FREE(VAR) \
81+
PyMem_RawFree(VAR)
82+
83+
1684
static PyInterpreterState *
1785
_get_current_interp(void)
1886
{
@@ -301,7 +369,7 @@ typedef struct _channelitem {
301369
static _channelitem *
302370
_channelitem_new(void)
303371
{
304-
_channelitem *item = PyMem_NEW(_channelitem, 1);
372+
_channelitem *item = GLOBAL_MALLOC(_channelitem);
305373
if (item == NULL) {
306374
PyErr_NoMemory();
307375
return NULL;
@@ -316,7 +384,8 @@ _channelitem_clear(_channelitem *item)
316384
{
317385
if (item->data != NULL) {
318386
(void)_release_xid_data(item->data, 1);
319-
PyMem_Free(item->data);
387+
// It was allocated in _channel_send().
388+
GLOBAL_FREE(item->data);
320389
item->data = NULL;
321390
}
322391
item->next = NULL;
@@ -326,7 +395,7 @@ static void
326395
_channelitem_free(_channelitem *item)
327396
{
328397
_channelitem_clear(item);
329-
PyMem_Free(item);
398+
GLOBAL_FREE(item);
330399
}
331400

332401
static void
@@ -357,7 +426,7 @@ typedef struct _channelqueue {
357426
static _channelqueue *
358427
_channelqueue_new(void)
359428
{
360-
_channelqueue *queue = PyMem_NEW(_channelqueue, 1);
429+
_channelqueue *queue = GLOBAL_MALLOC(_channelqueue);
361430
if (queue == NULL) {
362431
PyErr_NoMemory();
363432
return NULL;
@@ -381,7 +450,7 @@ static void
381450
_channelqueue_free(_channelqueue *queue)
382451
{
383452
_channelqueue_clear(queue);
384-
PyMem_Free(queue);
453+
GLOBAL_FREE(queue);
385454
}
386455

387456
static int
@@ -433,7 +502,7 @@ typedef struct _channelend {
433502
static _channelend *
434503
_channelend_new(int64_t interp)
435504
{
436-
_channelend *end = PyMem_NEW(_channelend, 1);
505+
_channelend *end = GLOBAL_MALLOC(_channelend);
437506
if (end == NULL) {
438507
PyErr_NoMemory();
439508
return NULL;
@@ -447,7 +516,7 @@ _channelend_new(int64_t interp)
447516
static void
448517
_channelend_free(_channelend *end)
449518
{
450-
PyMem_Free(end);
519+
GLOBAL_FREE(end);
451520
}
452521

453522
static void
@@ -492,7 +561,7 @@ typedef struct _channelassociations {
492561
static _channelends *
493562
_channelends_new(void)
494563
{
495-
_channelends *ends = PyMem_NEW(_channelends, 1);
564+
_channelends *ends = GLOBAL_MALLOC(_channelends);
496565
if (ends== NULL) {
497566
return NULL;
498567
}
@@ -519,7 +588,7 @@ static void
519588
_channelends_free(_channelends *ends)
520589
{
521590
_channelends_clear(ends);
522-
PyMem_Free(ends);
591+
GLOBAL_FREE(ends);
523592
}
524593

525594
static _channelend *
@@ -660,20 +729,20 @@ typedef struct _channel {
660729
static _PyChannelState *
661730
_channel_new(PyThread_type_lock mutex)
662731
{
663-
_PyChannelState *chan = PyMem_NEW(_PyChannelState, 1);
732+
_PyChannelState *chan = GLOBAL_MALLOC(_PyChannelState);
664733
if (chan == NULL) {
665734
return NULL;
666735
}
667736
chan->mutex = mutex;
668737
chan->queue = _channelqueue_new();
669738
if (chan->queue == NULL) {
670-
PyMem_Free(chan);
739+
GLOBAL_FREE(chan);
671740
return NULL;
672741
}
673742
chan->ends = _channelends_new();
674743
if (chan->ends == NULL) {
675744
_channelqueue_free(chan->queue);
676-
PyMem_Free(chan);
745+
GLOBAL_FREE(chan);
677746
return NULL;
678747
}
679748
chan->open = 1;
@@ -691,7 +760,7 @@ _channel_free(_PyChannelState *chan)
691760
PyThread_release_lock(chan->mutex);
692761

693762
PyThread_free_lock(chan->mutex);
694-
PyMem_Free(chan);
763+
GLOBAL_FREE(chan);
695764
}
696765

697766
static int
@@ -814,7 +883,7 @@ typedef struct _channelref {
814883
static _channelref *
815884
_channelref_new(int64_t id, _PyChannelState *chan)
816885
{
817-
_channelref *ref = PyMem_NEW(_channelref, 1);
886+
_channelref *ref = GLOBAL_MALLOC(_channelref);
818887
if (ref == NULL) {
819888
return NULL;
820889
}
@@ -841,7 +910,7 @@ _channelref_free(_channelref *ref)
841910
_channel_clear_closing(ref->chan);
842911
}
843912
//_channelref_clear(ref);
844-
PyMem_Free(ref);
913+
GLOBAL_FREE(ref);
845914
}
846915

847916
static _channelref *
@@ -1163,7 +1232,7 @@ _channel_set_closing(struct _channelref *ref, PyThread_type_lock mutex) {
11631232
res = ERR_CHANNEL_CLOSED;
11641233
goto done;
11651234
}
1166-
chan->closing = PyMem_NEW(struct _channel_closing, 1);
1235+
chan->closing = GLOBAL_MALLOC(struct _channel_closing);
11671236
if (chan->closing == NULL) {
11681237
goto done;
11691238
}
@@ -1179,7 +1248,7 @@ static void
11791248
_channel_clear_closing(struct _channel *chan) {
11801249
PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
11811250
if (chan->closing != NULL) {
1182-
PyMem_Free(chan->closing);
1251+
GLOBAL_FREE(chan->closing);
11831252
chan->closing = NULL;
11841253
}
11851254
PyThread_release_lock(chan->mutex);
@@ -1257,14 +1326,14 @@ _channel_send(_channels *channels, int64_t id, PyObject *obj)
12571326
}
12581327

12591328
// Convert the object to cross-interpreter data.
1260-
_PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1);
1329+
_PyCrossInterpreterData *data = GLOBAL_MALLOC(_PyCrossInterpreterData);
12611330
if (data == NULL) {
12621331
PyThread_release_lock(mutex);
12631332
return -1;
12641333
}
12651334
if (_PyObject_GetCrossInterpreterData(obj, data) != 0) {
12661335
PyThread_release_lock(mutex);
1267-
PyMem_Free(data);
1336+
GLOBAL_FREE(data);
12681337
return -1;
12691338
}
12701339

@@ -1274,7 +1343,7 @@ _channel_send(_channels *channels, int64_t id, PyObject *obj)
12741343
if (res != 0) {
12751344
// We may chain an exception here:
12761345
(void)_release_xid_data(data, 0);
1277-
PyMem_Free(data);
1346+
GLOBAL_FREE(data);
12781347
return res;
12791348
}
12801349

@@ -1323,11 +1392,13 @@ _channel_recv(_channels *channels, int64_t id, PyObject **res)
13231392
if (obj == NULL) {
13241393
assert(PyErr_Occurred());
13251394
(void)_release_xid_data(data, 1);
1326-
PyMem_Free(data);
1395+
// It was allocated in _channel_send().
1396+
GLOBAL_FREE(data);
13271397
return -1;
13281398
}
13291399
int release_res = _release_xid_data(data, 0);
1330-
PyMem_Free(data);
1400+
// It was allocated in _channel_send().
1401+
GLOBAL_FREE(data);
13311402
if (release_res < 0) {
13321403
// The source interpreter has been destroyed already.
13331404
assert(PyErr_Occurred());

0 commit comments

Comments
 (0)