diff options
| author | Robert Haas | 2016-11-22 19:26:40 +0000 |
|---|---|---|
| committer | Robert Haas | 2016-11-22 19:27:11 +0000 |
| commit | e8ac886c24776295dd9b025386a821061da8e4d1 (patch) | |
| tree | cdb4306afce149e7f9c8c0708d18ddee7737fc29 /src/include | |
| parent | 1c7861e81b4220364bef75d2445e9c0619f3f3f8 (diff) | |
Support condition variables.
Condition variables provide a flexible way to sleep until a
cooperating process causes an arbitrary condition to become true. In
simple cases, this can be accomplished with a WaitLatch/ResetLatch
loop; the cooperating process can call SetLatch after performing work
that might cause the condition to be satisfied, and the waiting
process can recheck the condition each time. However, if the process
performing the work doesn't have an easy way to identify which
processes might be waiting, this doesn't work, because it can't
identify which latches to set. Condition variables solve that problem
by internally maintaining a list of waiters; a process that may have
caused some waiter's condition to be satisfied must "signal" or
"broadcast" on the condition variable.
Robert Haas and Thomas Munro
Diffstat (limited to 'src/include')
| -rw-r--r-- | src/include/storage/condition_variable.h | 59 | ||||
| -rw-r--r-- | src/include/storage/proc.h | 3 | ||||
| -rw-r--r-- | src/include/storage/proclist.h | 56 |
3 files changed, 117 insertions, 1 deletions
diff --git a/src/include/storage/condition_variable.h b/src/include/storage/condition_variable.h new file mode 100644 index 00000000000..685df4323d8 --- /dev/null +++ b/src/include/storage/condition_variable.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------- + * + * 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 cancelled 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-2016, 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/s_lock.h" +#include "storage/proclist_types.h" + +typedef struct +{ + slock_t mutex; + proclist_head wakeup; +} ConditionVariable; + +/* Initialize a condition variable. */ +extern void ConditionVariableInit(ConditionVariable *); + +/* + * 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 should + * be called to ensure that the process is no longer in the wait list for + * the condition variable. + */ +extern void ConditionVariableSleep(ConditionVariable *, uint32 wait_event_info); +extern void ConditionVariableCancelSleep(void); + +/* + * The use of this function is optional and not necessary for correctness; + * for efficiency, it should be called prior entering the loop described above + * if it is thought that the condition is unlikely to hold immediately. + */ +extern void ConditionVariablePrepareToSleep(ConditionVariable *); + +/* Wake up a single waiter (via signal) or all waiters (via broadcast). */ +extern bool ConditionVariableSignal(ConditionVariable *); +extern int ConditionVariableBroadcast(ConditionVariable *); + +#endif /* CONDITION_VARIABLE_H */ diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 7dc8dac6d1e..6fa71253d86 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -115,6 +115,9 @@ struct PGPROC uint8 lwWaitMode; /* lwlock mode being waited for */ proclist_node lwWaitLink; /* position in LW lock wait list */ + /* Support for condition variables. */ + proclist_node cvWaitLink; /* position in CV wait list */ + /* Info about lock the process is currently waiting for, if any. */ /* waitLock and waitProcLock are NULL if not currently waiting. */ LOCK *waitLock; /* Lock object we're sleeping on ... */ diff --git a/src/include/storage/proclist.h b/src/include/storage/proclist.h index 2013a406a3c..8666c27cf8c 100644 --- a/src/include/storage/proclist.h +++ b/src/include/storage/proclist.h @@ -69,6 +69,8 @@ proclist_push_head_offset(proclist_head *list, int procno, size_t node_offset) else { Assert(list->tail != INVALID_PGPROCNO); + Assert(list->head != procno); + Assert(list->tail != procno); node->next = list->head; proclist_node_get(node->next, node_offset)->prev = procno; node->prev = INVALID_PGPROCNO; @@ -77,7 +79,7 @@ proclist_push_head_offset(proclist_head *list, int procno, size_t node_offset) } /* - * Insert a node a the end of a list. + * Insert a node at the end of a list. */ static inline void proclist_push_tail_offset(proclist_head *list, int procno, size_t node_offset) @@ -93,6 +95,8 @@ proclist_push_tail_offset(proclist_head *list, int procno, size_t node_offset) else { Assert(list->head != INVALID_PGPROCNO); + Assert(list->head != procno); + Assert(list->tail != procno); node->prev = list->tail; proclist_node_get(node->prev, node_offset)->next = procno; node->next = INVALID_PGPROCNO; @@ -117,6 +121,52 @@ proclist_delete_offset(proclist_head *list, int procno, size_t node_offset) list->tail = node->prev; else proclist_node_get(node->next, node_offset)->prev = node->prev; + + node->next = node->prev = INVALID_PGPROCNO; +} + +/* + * Check if a node is currently in a list. It must be known that the node is + * not in any _other_ proclist that uses the same proclist_node, so that the + * only possibilities are that it is in this list or none. + */ +static inline bool +proclist_contains_offset(proclist_head *list, int procno, + size_t node_offset) +{ + proclist_node *node = proclist_node_get(procno, node_offset); + + /* + * If this is not a member of a proclist, then the next and prev pointers + * should be 0. Circular lists are not allowed so this condition is not + * confusable with a real pgprocno 0. + */ + if (node->prev == 0 && node->next == 0) + return false; + + /* If there is a previous node, then this node must be in the list. */ + if (node->prev != INVALID_PGPROCNO) + return true; + + /* + * There is no previous node, so the only way this node can be in the list + * is if it's the head node. + */ + return list->head == procno; +} + +/* + * Remove and return the first node from a list (there must be one). + */ +static inline PGPROC * +proclist_pop_head_node_offset(proclist_head *list, size_t node_offset) +{ + PGPROC *proc; + + Assert(!proclist_is_empty(list)); + proc = GetPGProcByNumber(list->head); + proclist_delete_offset(list, list->head, node_offset); + return proc; } /* @@ -129,6 +179,10 @@ proclist_delete_offset(proclist_head *list, int procno, size_t node_offset) proclist_push_head_offset((list), (procno), offsetof(PGPROC, link_member)) #define proclist_push_tail(list, procno, link_member) \ proclist_push_tail_offset((list), (procno), offsetof(PGPROC, link_member)) +#define proclist_pop_head_node(list, link_member) \ + proclist_pop_head_node_offset((list), offsetof(PGPROC, link_member)) +#define proclist_contains(list, procno, link_member) \ + proclist_contains_offset((list), (procno), offsetof(PGPROC, link_member)) /* * Iterate through the list pointed at by 'lhead', storing the current |
