postgresql/src/include/storage/condition_variable.h
Thomas Munro 5ffb7c7750 De-pessimize ConditionVariableCancelSleep().
Commit b91dd9de was concerned with a theoretical problem with our
non-atomic condition variable operations.  If you stop sleeping, and
then cancel the sleep in a separate step, you might be signaled in
between, and that could be lost.  That doesn't matter for callers of
ConditionVariableBroadcast(), but callers of ConditionVariableSignal()
might be upset if a signal went missing like this.

Commit bc971f4025 interacted badly with that logic, because it doesn't
use ConditionVariableSleep(), which would normally put us back in the
wait list.  ConditionVariableCancelSleep() would be confused and think
we'd received an extra signal, and try to forward it to another backend,
resulting in wakeup storms.

New idea: ConditionVariableCancelSleep() can just return true if we've
been signaled.  Hypothetical users of ConditionVariableSignal() would
then still have a way to deal with rare lost signals if they are
concerned about that problem.

Back-patch to 16, where bc971f4025 arrived.

Reported-by: Tomas Vondra <tomas.vondra@enterprisedb.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/2840876b-4cfe-240f-0a7e-29ffd66711e7%40enterprisedb.com
2023-08-15 10:23:47 +12:00

74 lines
2.9 KiB
C

/*-------------------------------------------------------------------------
*
* condition_variable.h
* Condition variables
*
* A condition variable is a method of waiting until a certain condition
* becomes true. Conventionally, a condition variable supports three
* operations: (1) sleep; (2) signal, which wakes up one process sleeping
* on the condition variable; and (3) broadcast, which wakes up every
* process sleeping on the condition variable. In our implementation,
* condition variables put a process into an interruptible sleep (so it
* can be canceled prior to the fulfillment of the condition) and do not
* use pointers internally (so that they are safe to use within DSMs).
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/storage/condition_variable.h
*
*-------------------------------------------------------------------------
*/
#ifndef CONDITION_VARIABLE_H
#define CONDITION_VARIABLE_H
#include "storage/proclist_types.h"
#include "storage/spin.h"
typedef struct
{
slock_t mutex; /* spinlock protecting the wakeup list */
proclist_head wakeup; /* list of wake-able processes */
} ConditionVariable;
/*
* Pad a condition variable to a power-of-two size so that an array of
* condition variables does not cross a cache line boundary.
*/
#define CV_MINIMAL_SIZE (sizeof(ConditionVariable) <= 16 ? 16 : 32)
typedef union ConditionVariableMinimallyPadded
{
ConditionVariable cv;
char pad[CV_MINIMAL_SIZE];
} ConditionVariableMinimallyPadded;
/* Initialize a condition variable. */
extern void ConditionVariableInit(ConditionVariable *cv);
/*
* To sleep on a condition variable, a process should use a loop which first
* checks the condition, exiting the loop if it is met, and then calls
* ConditionVariableSleep. Spurious wakeups are possible, but should be
* infrequent. After exiting the loop, ConditionVariableCancelSleep must
* be called to ensure that the process is no longer in the wait list for
* the condition variable.
*/
extern void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info);
extern bool ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
uint32 wait_event_info);
extern bool ConditionVariableCancelSleep(void);
/*
* Optionally, ConditionVariablePrepareToSleep can be called before entering
* the test-and-sleep loop described above. Doing so is more efficient if
* at least one sleep is needed, whereas not doing so is more efficient when
* no sleep is needed because the test condition is true the first time.
*/
extern void ConditionVariablePrepareToSleep(ConditionVariable *cv);
/* Wake up a single waiter (via signal) or all waiters (via broadcast). */
extern void ConditionVariableSignal(ConditionVariable *cv);
extern void ConditionVariableBroadcast(ConditionVariable *cv);
#endif /* CONDITION_VARIABLE_H */