Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit 083382d

Browse files
committed
ParkingLot: use thread-local waiter instead of PyThreadState
Sometimes we need to acquire locks before the current PyThreadState is set. This moves the waiter data to it's own struct. It's shared between PyThreadStates on the same thread, such as in multi-interpreter settings. Fixes #85
1 parent b0a0700 commit 083382d

File tree

9 files changed

+224
-227
lines changed

9 files changed

+224
-227
lines changed

Include/cpython/pystate.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ typedef struct mi_heap_s mi_heap_t;
5656
struct PyThreadStateOS;
5757
typedef struct PyThreadStateOS PyThreadStateOS;
5858

59+
struct Waiter;
5960
typedef struct _PyEventRC _PyEventRC;
6061

6162

@@ -69,7 +70,7 @@ struct _ts {
6970

7071
/* OS-specific state (for locking and parking) */
7172
PyThreadStateOS *os;
72-
uintptr_t handoff_elem;
73+
uintptr_t _unused_handoff_elem; // TODO: delete before release, but gonna require recompiling conda binaries
7374

7475
/* thread status */
7576
int32_t status;
@@ -152,6 +153,8 @@ struct _ts {
152153
/* Unique thread state id. */
153154
uint64_t id;
154155

156+
struct Waiter *waiter;
157+
155158
/* XXX signal handlers should also be here */
156159
struct method_cache_entry method_cache[(1 << MCACHE_SIZE_EXP)];
157160
};

Include/internal/pycore_pystate.h

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -365,35 +365,16 @@ _PyThreadState_CheckForShutdown(PyThreadState *tstate)
365365

366366

367367
/* Other */
368-
struct PyThreadStateWaiter {
369-
struct PyThreadStateWaiter *next;
370-
struct PyThreadStateWaiter *prev;
371-
uintptr_t key;
372-
int64_t time_to_be_fair;
373-
};
374-
375368
struct brc_queued_object;
376369

377370
struct PyThreadStateOS {
378-
struct PyThreadStateWaiter waiter;
379-
380371
PyThreadState *tstate;
381-
PyThreadState *next_waiter;
382-
PyMUTEX_T waiter_mutex;
383-
PyCOND_T waiter_cond;
384-
int waiter_counter;
385372

386373
struct _PyBrcState {
387374
struct llist_node node;
388375
uintptr_t thread_id;
389376
struct brc_queued_object *queue;
390377
} brc;
391-
392-
/* DEBUG info */
393-
PyThreadState *last_notifier;
394-
const char *last_notifier_msg;
395-
void *last_notifier_data;
396-
int64_t counter;
397378
};
398379

399380
PyAPI_FUNC(void) _PyThreadState_Init(

Include/lock.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
extern "C" {
88
#endif
99

10-
struct _ts;
11-
typedef struct _ts PyThreadState;
12-
1310
typedef struct {
1411
uintptr_t v;
1512
} _PyRawMutex;
@@ -52,19 +49,18 @@ void _PyRecursiveMutex_lock_slow(_PyRecursiveMutex *m);
5249
void _PyRecursiveMutex_unlock_slow(_PyRecursiveMutex *m);
5350

5451
void _PyRawEvent_Notify(_PyRawEvent *o);
55-
void _PyRawEvent_Wait(_PyRawEvent *o, PyThreadState *tstate);
56-
int _PyRawEvent_TimedWait(_PyRawEvent *o, PyThreadState *tstate, int64_t ns);
52+
void _PyRawEvent_Wait(_PyRawEvent *o);
53+
int _PyRawEvent_TimedWait(_PyRawEvent *o, int64_t ns);
5754
void _PyRawEvent_Reset(_PyRawEvent *o);
5855

5956
void _PyEvent_Notify(_PyEvent *o);
60-
void _PyEvent_Wait(_PyEvent *o, PyThreadState *tstate);
61-
int _PyEvent_TimedWait(_PyEvent *o, PyThreadState *tstate, int64_t ns);
57+
void _PyEvent_Wait(_PyEvent *o);
58+
int _PyEvent_TimedWait(_PyEvent *o, int64_t ns);
6259

6360
int _PyBeginOnce_slow(_PyOnceFlag *o);
6461
void _PyEndOnce(_PyOnceFlag *o);
6562
void _PyEndOnceFailed(_PyOnceFlag *o);
6663

67-
6864
static inline int
6965
_PyMutex_is_locked(_PyMutex *m)
7066
{
@@ -80,7 +76,6 @@ _PyRawMutex_is_locked(_PyRawMutex *m)
8076
static inline void
8177
_PyRawMutex_lock(_PyRawMutex *m)
8278
{
83-
// lock_count++;
8479
if (_Py_atomic_compare_exchange_uintptr(&m->v, UNLOCKED, LOCKED)) {
8580
return;
8681
}

Include/parking_lot.h

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#define Py_PARKING_LOT_H
33

44
#include "pyatomic.h"
5+
#include "pycore_llist.h"
6+
#include "pycore_condvar.h"
57

68
#ifdef __cplusplus
79
extern "C" {
@@ -14,29 +16,50 @@ enum {
1416
PY_PARK_OK = 0,
1517
};
1618

17-
int
18-
_PySemaphore_Wait(PyThreadState *tstate, int64_t ns);
19+
typedef struct Waiter {
20+
struct llist_node node; // wait queue node
21+
Py_ssize_t refcount;
22+
struct Waiter *next_waiter; // for "raw" locks
23+
PyMUTEX_T mutex;
24+
PyCOND_T cond;
25+
int counter;
26+
uintptr_t key;
27+
int64_t time_to_be_fair;
28+
uintptr_t thread_id;
29+
uintptr_t handoff_elem;
30+
} Waiter;
31+
32+
Waiter *
33+
_PyParkingLot_InitThread(void);
1934

2035
void
21-
_PySemaphore_Signal(PyThreadStateOS *os, const char *msg, void *data);
36+
_PyParkingLot_DeinitThread(Waiter *waiter);
37+
38+
Waiter *
39+
_PyParkingLot_ThisWaiter(void);
40+
41+
void
42+
_PySemaphore_Signal(Waiter *waiter, const char *msg, void *data);
43+
44+
int
45+
_PySemaphore_Wait(Waiter *waiter, int64_t ns);
2246

23-
/* Functions for waking and parking threads */
2447
int
2548
_PyParkingLot_ParkInt32(const int32_t *key, int32_t expected);
2649

2750
int
28-
_PyParkingLot_Park(const uintptr_t *key, uintptr_t expected,
29-
_PyTime_t start_time, int64_t timeout_ns);
51+
_PyParkingLot_Park(const void *key, uintptr_t expected,
52+
_PyTime_t start_time, int64_t ns);
3053

3154
void
3255
_PyParkingLot_UnparkAll(const void *key);
3356

3457
void
35-
_PyParkingLot_BeginUnpark(const void *key, PyThreadState **tstate,
36-
int *more_waiters, int *time_to_be_fair);
58+
_PyParkingLot_BeginUnpark(const void *key, struct Waiter **out,
59+
int *more_waiters, int *should_be_fair);
3760

3861
void
39-
_PyParkingLot_FinishUnpark(const void *key, PyThreadState *tstate);
62+
_PyParkingLot_FinishUnpark(const void *key, struct Waiter *waiter);
4063

4164
void
4265
_PyParkingLot_AfterFork(void);

Modules/_queuemodule.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ _queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item,
154154
if (self->waiting) {
155155
int more_waiters;
156156
int should_be_fair;
157-
PyThreadState *waiter;
157+
Waiter *waiter;
158158

159159
/* If there is a waiter, handoff the item directly */
160160
_PyParkingLot_BeginUnpark(&self->waiting, &waiter, &more_waiters, &should_be_fair);
@@ -279,8 +279,8 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, int block,
279279
}
280280
}
281281

282-
PyThreadState *tstate = PyThreadState_GET();
283-
tstate->handoff_elem = (uintptr_t)&item;
282+
Waiter *this_waiter = _PyParkingLot_ThisWaiter();
283+
this_waiter->handoff_elem = (uintptr_t)&item;
284284
int ret = _PyParkingLot_Park(&self->waiting, 1, 0, timeout_ns);
285285
if (ret == PY_PARK_OK) {
286286
assert(item);

Modules/_threadmodule.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -702,8 +702,6 @@ event_set(eventobject *self, PyObject *Py_UNUSED(ignored))
702702
static PyObject *
703703
event_wait(eventobject *self, PyObject *args)
704704
{
705-
PyThreadState *tstate = PyThreadState_Get();
706-
707705
PyObject *timeout_obj = NULL;
708706
if (!PyArg_ParseTuple(args, "|O:wait", &timeout_obj)) {
709707
return NULL;
@@ -721,7 +719,7 @@ event_wait(eventobject *self, PyObject *args)
721719
}
722720
}
723721

724-
int ok = _PyEvent_TimedWait(&self->erc->event, tstate, timeout_ns);
722+
int ok = _PyEvent_TimedWait(&self->erc->event, timeout_ns);
725723
if (ok) {
726724
Py_RETURN_TRUE;
727725
}

0 commit comments

Comments
 (0)