Skip to content

Commit c32bae5

Browse files
authored
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.
1 parent 652fbf8 commit c32bae5

File tree

2 files changed

+3
-2
lines changed

2 files changed

+3
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixes a race between ``PyParkingLot_Park`` and ``_PyParkingLot_UnparkAll``.

Python/parking_lot.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ dequeue(Bucket *bucket, const void *address)
244244
if (wait->addr == (uintptr_t)address) {
245245
llist_remove(node);
246246
--bucket->num_waiters;
247+
wait->is_unparking = true;
247248
return wait;
248249
}
249250
}
@@ -262,6 +263,7 @@ dequeue_all(Bucket *bucket, const void *address, struct llist_node *dst)
262263
llist_remove(node);
263264
llist_insert_tail(dst, node);
264265
--bucket->num_waiters;
266+
wait->is_unparking = true;
265267
}
266268
}
267269
}
@@ -337,8 +339,6 @@ _PyParkingLot_Unpark(const void *addr, _Py_unpark_fn_t *fn, void *arg)
337339
_PyRawMutex_Lock(&bucket->mutex);
338340
struct wait_entry *waiter = dequeue(bucket, addr);
339341
if (waiter) {
340-
waiter->is_unparking = true;
341-
342342
int has_more_waiters = (bucket->num_waiters > 0);
343343
fn(arg, waiter->park_arg, has_more_waiters);
344344
}

0 commit comments

Comments
 (0)