8000 gh-125716: Use A Global Mutex When Initializing Global State For The … · python/cpython@4848b0b · GitHub
[go: up one dir, main page]

Skip to content

Commit 4848b0b

Browse files
gh-125716: Use A Global Mutex When Initializing Global State For The _interpqueues Module (gh-125803)
This includes a drive-by cleanup in _queues_init() and _queues_fini(). This change also applies to the _interpchannels module.
1 parent d48cc82 commit 4848b0b

File tree

2 files changed

+79
-57
lines changed

2 files changed

+79
-57
lines changed

Modules/_interpchannelsmodule.c

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
This module has the following process-global state:
2929
3030
_globals (static struct globals):
31+
mutex (PyMutex)
3132
module_count (int)
3233
channels (struct _channels):
3334
numopen (int64_t)
@@ -1349,21 +1350,29 @@ typedef struct _channels {
13491350
static void
13501351
_channels_init(_channels *channels, PyThread_type_lock mutex)
13511352
{
1352-
channels->mutex = mutex;
1353-
channels->head = NULL;
1354-
channels->numopen = 0;
1355-
channels->next_id = 0;
1353+
assert(mutex != NULL);
1354+
assert(channels->mutex == NULL);
1355+
*channels = (_channels){
1356+
.mutex = mutex,
1357+
.head = NULL,
1358+
.numopen = 0,
1359+
.next_id = 0,
1360+
};
13561361
}
13571362

13581363
static void
1359-
_channels_fini(_channels *channels)
1364+
_channels_fini(_channels *channels, PyThread_type_lock *p_mutex)
13601365
{
1366+
PyThread_type_lock mutex = channels->mutex;
1367+
assert(mutex != NULL);
1368+
1369+
PyThread_acquire_lock(mutex, WAIT_LOCK);
13611370
assert(channels->numopen == 0);
13621371
assert(channels->head == NULL);
1363-
if (channels->mutex != NULL) {
1364-
PyThread_free_lock(channels->mutex);
1365-
channels->mutex = NULL;
1366-
}
1372+
*channels = (_channels){0};
1373+
PyThread_release_lock(mutex);
1374+
1375+
*p_mutex = mutex;
13671376
}
13681377

13691378
static int64_t
@@ -2812,39 +2821,44 @@ set_channelend_types(PyObject *mod, PyTypeObject *send, PyTypeObject *recv)
28122821
the data that we need to share between interpreters, so it cannot
28132822
hold PyObject values. */
28142823
static struct globals {
2824+
PyMutex mutex;
28152825
int module_count;
28162826
_channels channels;
28172827
} _globals = {0};
28182828

28192829
static int
28202830
_globals_init(void)
28212831
{
2822-
// XXX This isn't thread-safe.
2832+
PyMutex_Lock(&_globals.mutex);
2833+
assert(_globals.module_count >= 0);
28232834
_globals.module_count++;
2824-
if (_globals.module_count > 1) {
2825-
// Already initialized.
2826-
return 0;
2827-
}
2828-
2829-
assert(_globals.channels.mutex == NULL);
2830-
PyThread_type_lock mutex = PyThread_allocate_lock();
2831-
if (mutex == NULL) {
2832-
return ERR_CHANNELS_MUTEX_INIT;
2835+
if (_globals.module_count == 1) {
2836+
// Called for the first time.
2837+
PyThread_type_lock mutex = PyThread_allocate_lock();
2838+
if (mutex == NULL) {
2839+
_globals.module_count--;
2840+
PyMutex_Unlock(&_globals.mutex);
2841+
return ERR_CHANNELS_MUTEX_INIT;
2842+
}
2843+
_channels_init(&_globals.channels, mutex);
28332844
}
2834-
_channels_init(&_globals.channels, mutex);
2845+
PyMutex_Unlock(&_globals.mutex);
28352846
return 0;
28362847
}
28372848

28382849
static void
28392850
_globals_fini(void)
28402851
{
2841-
// XXX This isn't thread-safe.
2852+
PyMutex_Lock(&_globals.mutex);
2853+
assert(_globals.module_count > 0);
28422854
_globals.module_count--;
2843-
if (_globals.module_count > 0) {
2844-
return;
2855+
if (_globals.module_count == 0) {
2856+
PyThread_type_lock mutex;
2857+
_channels_fini(&_globals.channels, &mutex);
2858+
assert(mutex != NULL);
2859+
PyThread_free_lock(mutex);
28452860
}
2846-
2847-
_channels_fini(&_globals.channels);
2861+
PyMutex_Unlock(&_globals.mutex);
28482862
}
28492863

28502864
static _channels *

Modules/_interpqueuesmodule.c

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -845,28 +845,31 @@ typedef struct _queues {
845845
static void
846846
_queues_init(_queues *queues, PyThread_type_lock mutex)
847847
{
848-
queues->mutex = mutex;
849-
queues->head = NULL;
850-
queues->count = 0;
851-
queues->next_id = 1;
848+
assert(mutex != NULL);
849+
assert(queues->mutex == NULL);
850+
*queues = (_queues){
851+
.mutex = mutex,
852+
.head = NULL,
853+
.count = 0,
854+
.next_id = 1,
855+
};
852856
}
853857

854858
static void
855-
_queues_f F438 ini(_queues *queues)
859+
_queues_fini(_queues *queues, PyThread_type_lock *p_mutex)
856860
{
861+
PyThread_type_lock mutex = queues->mutex;
862+
assert(mutex != NULL);
863+
864+
PyThread_acquire_lock(mutex, WAIT_LOCK);
857865
if (queues->count > 0) {
858-
PyThread_acquire_lock(queues->mutex, WAIT_LOCK);
859-
assert((queues->count == 0) != (queues->head != NULL));
860-
_queueref *head = queues->head;
861-
queues->head = NULL;
862-
queues->count = 0;
863-
PyThread_release_lock(queues->mutex);
864-
_queuerefs_clear(head);
865-
}
866-
if (queues->mutex != NULL) {
867-
PyThread_free_lock(queues->mutex);
868-
queues->mutex = NULL;
866+
assert(queues->head != NULL);
867+
_queuerefs_clear(queues->head);
869868
}
869+
*queues = (_queues){0};
870+
PyThread_release_lock(mutex);
871+
872+
*p_mutex = mutex;
870873
}
871874

872875
static int64_t
@@ -1398,39 +1401,44 @@ _queueobj_shared(PyThreadState *tstate, PyObject *queueobj,
13981401
the data that we need to share between interpreters, so it cannot
13991402
hold PyObject values. */
14001403
static struct globals {
1404+
PyMutex mutex;
14011405
int module_count;
14021406
_queues queues;
14031407
} _globals = {0};
14041408

14051409
static int
14061410
_globals_init(void)
14071411
{
1408-
// XXX This isn't thread-safe.
1412+
PyMutex_Lock(&_globals.mutex);
1413+
assert(_globals.module_count >= 0);
14091414
_globals.module_count++;
1410-
if (_globals.module_count > 1) {
1411-
// Already initialized.
1412-
return 0;
1413-
}
1414-
1415-
assert(_globals.queues.mutex == NULL);
1416-
PyThread_type_lock mutex = PyThread_allocate_lock();
1417-
if (mutex == NULL) {
1418-
return ERR_QUEUES_ALLOC;
1415+
if (_globals.module_count == 1) {
1416+
// Called for the first time.
1417+
PyThread_type_lock mutex = PyThread_allocate_lock();
1418+
if (mutex == NULL) {
1419+
_globals.module_count--;
1420+
PyMutex_Unlock(&_globals.mutex);
1421+
return ERR_QUEUES_ALLOC;
1422+
}
1423+
_queues_init(&_globals.queues, mutex);
14191424
}
1420-
_queues_init(&_globals.queues, mutex);
1425+
PyMutex_Unlock(&_globals.mutex);
14211426
return 0;
14221427
}
14231428

14241429
static void
14251430
_globals_fini(void)
14261431
{
1427-
// XXX This isn't thread-safe.
1432+
PyMutex_Lock(&_globals.mutex);
1433+
assert(_globals.module_count > 0);
14281434
_globals.module_count--;
1429-
if (_globals.module_count > 0) {
1430-
return;
1435+
if (_globals.module_count == 0) {
1436+
PyThread_type_lock mutex;
1437+
_queues_fini(&_globals.queues, &mutex);
1438+
assert(mutex != NULL);
1439+
PyThread_free_lock(mutex);
14311440
}
1432-
1433-
_queues_fini(&_globals.queues);
1441+
PyMutex_Unlock(&_globals.mutex);
14341442
}
14351443

14361444
static _queues *

0 commit comments

Comments
 (0)
0