After a process decides not to wait for a condition variable, it can
still consume a signal before it reaches ConditionVariableCancelSleep().
In that case, pass the signal on to another waiter if possible, so that
a signal doesn't go missing when there is another process ready to
receive it.
Author: Thomas Munro
Reviewed-by: Shawn Debnath
Discussion: https://postgr.es/m/CA%2BhUKGLQ_RW%2BXs8znDn36e-%2Bmq2--zrPemBqTQ8eKT-VO1OF4Q%40mail.gmail.com
ConditionVariableCancelSleep(void)
{
ConditionVariable *cv = cv_sleep_target;
+ bool signaled = false;
if (cv == NULL)
return;
SpinLockAcquire(&cv->mutex);
if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
+ else
+ signaled = true;
SpinLockRelease(&cv->mutex);
+ /*
+ * If we've received a signal, pass it on to another waiting process, if
+ * there is one. Otherwise a call to ConditionVariableSignal() might get
+ * lost, despite there being another process ready to handle it.
+ */
+ if (signaled)
+ ConditionVariableSignal(cv);
+
cv_sleep_target = NULL;
}