diff options
author | mpage <mpage@meta.com> | 2024-02-05 21:48:37 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-05 21:48:37 (GMT) |
commit | c32bae52904723d99e1f98e2ef54570268d86467 (patch) | |
tree | a7c789d955c6a67cd49c93e75ea76b5205f45c55 /Python | |
parent | 652fbf88c4c422ff17fefd4dcb5e06b5c0e26e74 (diff) | |
download | cpython-c32bae52904723d99e1f98e2ef54570268d86467.zip cpython-c32bae52904723d99e1f98e2ef54570268d86467.tar.gz cpython-c32bae52904723d99e1f98e2ef54570268d86467.tar.bz2 |
gh-114944: Fix race between `_PyParkingLot_Park` and `_PyParkingLot_UnparkAll` when handling interrupts (#114945)
Fix race between `_PyParkingLot_Park` and `_PyParkingLot_UnparkAll` when handling interrupts
There is a potential race when `_PyParkingLot_UnparkAll` is executing in
one thread and another thread is unblocked because of an interrupt in
`_PyParkingLot_Park`. Consider the following scenario:
1. Thread T0 is blocked[^1] in `_PyParkingLot_Park` on address `A`.
2. Thread T1 executes `_PyParkingLot_UnparkAll` on address `A`. It
finds the `wait_entry` for `T0` and unlinks[^2] its list node.
3. Immediately after (2), T0 is woken up due to an interrupt. It
then segfaults trying to unlink[^3] the node that was previously
unlinked in (2).
To fix this we mark each waiter as unparking before releasing the bucket
lock. `_PyParkingLot_Park` will wait to handle the coming wakeup, and not
attempt to unlink the node, when this field is set. `_PyParkingLot_Unpark`
does this already, presumably to handle this case.
Diffstat (limited to 'Python')
-rw-r--r-- | Python/parking_lot.c | 4 |
1 files changed, 2 insertions, 2 deletions
diff --git a/Python/parking_lot.c b/Python/parking_lot.c index c83d744..8ba50fc 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -244,6 +244,7 @@ dequeue(Bucket *bucket, const void *address) if (wait->addr == (uintptr_t)address) { llist_remove(node); --bucket->num_waiters; + wait->is_unparking = true; return wait; } } @@ -262,6 +263,7 @@ dequeue_all(Bucket *bucket, const void *address, struct llist_node *dst) llist_remove(node); llist_insert_tail(dst, node); --bucket->num_waiters; + wait->is_unparking = true; } } } @@ -337,8 +339,6 @@ _PyParkingLot_Unpark(const void *addr, _Py_unpark_fn_t *fn, void *arg) _PyRawMutex_Lock(&bucket->mutex); struct wait_entry *waiter = dequeue(bucket, addr); if (waiter) { - waiter->is_unparking = true; - int has_more_waiters = (bucket->num_waiters > 0); fn(arg, waiter->park_arg, has_more_waiters); } |