Skip to content

Commit 1617077

Browse files
committed
Issue #15038:
Fix incorrect test of the condition variable state, spotted by Richard Oudkerk. This could cause the internal condition variable to grow without bounds.
1 parent 0d0a1de commit 1617077

1 file changed

Lines changed: 13 additions & 5 deletions

File tree

Python/condvar.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ PyMUTEX_UNLOCK(PyMUTEX_T *cs)
177177
typedef struct _PyCOND_T
178178
{
179179
HANDLE sem;
180-
int waiting;
180+
int waiting; /* to allow PyCOND_SIGNAL to be a no-op */
181181
} PyCOND_T;
182182

183183
Py_LOCAL_INLINE(int)
@@ -222,6 +222,10 @@ _PyCOND_WAIT_MS(PyCOND_T *cv, PyMUTEX_T *cs, DWORD ms)
222222
* PyCOND_SIGNAL also decrements this value
223223
* and signals releases the mutex. This is benign because it
224224
* just means an extra spurious wakeup for a waiting thread.
225+
* ('waiting' corresponds to the semaphore's "negative" count and
226+
* we may end up with e.g. (waiting == -1 && sem.count == 1). When
227+
* a new thread comes along, it will pass right throuhgh, having
228+
* adjusted it to (waiting == 0 && sem.count == 0).
225229
*/
226230

227231
if (wait == WAIT_FAILED)
@@ -246,10 +250,14 @@ PyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long us)
246250
Py_LOCAL_INLINE(int)
247251
PyCOND_SIGNAL(PyCOND_T *cv)
248252
{
249-
if (cv->waiting) {
253+
/* this test allows PyCOND_SIGNAL to be a no-op unless required
254+
* to wake someone up, thus preventing an unbounded increase of
255+
* the semaphore's internal counter.
256+
*/
257+
if (cv->waiting > 0) {
250258
/* notifying thread decreases the cv->waiting count so that
251-
* a delay between notify and wakeup doesn't cause a number
252-
* of extra ReleaseSemaphore calls
259+
* a delay between notify and actual wakeup of the target thread
260+
* doesn't cause a number of extra ReleaseSemaphore calls.
253261
*/
254262
cv->waiting--;
255263
return ReleaseSemaphore(cv->sem, 1, NULL) ? 0 : -1;
@@ -260,7 +268,7 @@ PyCOND_SIGNAL(PyCOND_T *cv)
260268
Py_LOCAL_INLINE(int)
261269
PyCOND_BROADCAST(PyCOND_T *cv)
262270
{
263-
if (cv->waiting) {
271+
if (cv->waiting > 0) {
264272
return ReleaseSemaphore(cv->sem, cv->waiting, NULL) ? 0 : -1;
265273
cv->waiting = 0;
266274
}

0 commit comments

Comments
 (0)