@@ -177,7 +177,7 @@ PyMUTEX_UNLOCK(PyMUTEX_T *cs)
177177typedef 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
183183Py_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)
246250Py_LOCAL_INLINE (int )
247251PyCOND_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)
260268Py_LOCAL_INLINE (int )
261269PyCOND_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