| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * proclist.h |
| 4 | * operations on doubly-linked lists of pgprocnos |
| 5 | * |
| 6 | * The interface is similar to dlist from ilist.h, but uses pgprocno instead |
| 7 | * of pointers. This allows proclist_head to be mapped at different addresses |
| 8 | * in different backends. |
| 9 | * |
| 10 | * See proclist_types.h for the structs that these functions operate on. They |
| 11 | * are separated to break a header dependency cycle with proc.h. |
| 12 | * |
| 13 | * Portions Copyright (c) 2016-2019, PostgreSQL Global Development Group |
| 14 | * |
| 15 | * IDENTIFICATION |
| 16 | * src/include/storage/proclist.h |
| 17 | *------------------------------------------------------------------------- |
| 18 | */ |
| 19 | #ifndef PROCLIST_H |
| 20 | #define PROCLIST_H |
| 21 | |
| 22 | #include "storage/proc.h" |
| 23 | #include "storage/proclist_types.h" |
| 24 | |
| 25 | /* |
| 26 | * Initialize a proclist. |
| 27 | */ |
| 28 | static inline void |
| 29 | proclist_init(proclist_head *list) |
| 30 | { |
| 31 | list->head = list->tail = INVALID_PGPROCNO; |
| 32 | } |
| 33 | |
| 34 | /* |
| 35 | * Is the list empty? |
| 36 | */ |
| 37 | static inline bool |
| 38 | proclist_is_empty(proclist_head *list) |
| 39 | { |
| 40 | return list->head == INVALID_PGPROCNO; |
| 41 | } |
| 42 | |
| 43 | /* |
| 44 | * Get a pointer to a proclist_node inside a given PGPROC, given a procno and |
| 45 | * the proclist_node field's offset within struct PGPROC. |
| 46 | */ |
| 47 | static inline proclist_node * |
| 48 | proclist_node_get(int procno, size_t node_offset) |
| 49 | { |
| 50 | char *entry = (char *) GetPGProcByNumber(procno); |
| 51 | |
| 52 | return (proclist_node *) (entry + node_offset); |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | * Insert a process at the beginning of a list. |
| 57 | */ |
| 58 | static inline void |
| 59 | proclist_push_head_offset(proclist_head *list, int procno, size_t node_offset) |
| 60 | { |
| 61 | proclist_node *node = proclist_node_get(procno, node_offset); |
| 62 | |
| 63 | Assert(node->next == 0 && node->prev == 0); |
| 64 | |
| 65 | if (list->head == INVALID_PGPROCNO) |
| 66 | { |
| 67 | Assert(list->tail == INVALID_PGPROCNO); |
| 68 | node->next = node->prev = INVALID_PGPROCNO; |
| 69 | list->head = list->tail = procno; |
| 70 | } |
| 71 | else |
| 72 | { |
| 73 | Assert(list->tail != INVALID_PGPROCNO); |
| 74 | Assert(list->head != procno); |
| 75 | Assert(list->tail != procno); |
| 76 | node->next = list->head; |
| 77 | proclist_node_get(node->next, node_offset)->prev = procno; |
| 78 | node->prev = INVALID_PGPROCNO; |
| 79 | list->head = procno; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /* |
| 84 | * Insert a process at the end of a list. |
| 85 | */ |
| 86 | static inline void |
| 87 | proclist_push_tail_offset(proclist_head *list, int procno, size_t node_offset) |
| 88 | { |
| 89 | proclist_node *node = proclist_node_get(procno, node_offset); |
| 90 | |
| 91 | Assert(node->next == 0 && node->prev == 0); |
| 92 | |
| 93 | if (list->tail == INVALID_PGPROCNO) |
| 94 | { |
| 95 | Assert(list->head == INVALID_PGPROCNO); |
| 96 | node->next = node->prev = INVALID_PGPROCNO; |
| 97 | list->head = list->tail = procno; |
| 98 | } |
| 99 | else |
| 100 | { |
| 101 | Assert(list->head != INVALID_PGPROCNO); |
| 102 | Assert(list->head != procno); |
| 103 | Assert(list->tail != procno); |
| 104 | node->prev = list->tail; |
| 105 | proclist_node_get(node->prev, node_offset)->next = procno; |
| 106 | node->next = INVALID_PGPROCNO; |
| 107 | list->tail = procno; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | * Delete a process from a list --- it must be in the list! |
| 113 | */ |
| 114 | static inline void |
| 115 | proclist_delete_offset(proclist_head *list, int procno, size_t node_offset) |
| 116 | { |
| 117 | proclist_node *node = proclist_node_get(procno, node_offset); |
| 118 | |
| 119 | Assert(node->next != 0 || node->prev != 0); |
| 120 | |
| 121 | if (node->prev == INVALID_PGPROCNO) |
| 122 | { |
| 123 | Assert(list->head == procno); |
| 124 | list->head = node->next; |
| 125 | } |
| 126 | else |
| 127 | proclist_node_get(node->prev, node_offset)->next = node->next; |
| 128 | |
| 129 | if (node->next == INVALID_PGPROCNO) |
| 130 | { |
| 131 | Assert(list->tail == procno); |
| 132 | list->tail = node->prev; |
| 133 | } |
| 134 | else |
| 135 | proclist_node_get(node->next, node_offset)->prev = node->prev; |
| 136 | |
| 137 | node->next = node->prev = 0; |
| 138 | } |
| 139 | |
| 140 | /* |
| 141 | * Check if a process is currently in a list. It must be known that the |
| 142 | * process is not in any _other_ proclist that uses the same proclist_node, |
| 143 | * so that the only possibilities are that it is in this list or none. |
| 144 | */ |
| 145 | static inline bool |
| 146 | proclist_contains_offset(proclist_head *list, int procno, |
| 147 | size_t node_offset) |
| 148 | { |
| 149 | proclist_node *node = proclist_node_get(procno, node_offset); |
| 150 | |
| 151 | /* If it's not in any list, it's definitely not in this one. */ |
| 152 | if (node->prev == 0 && node->next == 0) |
| 153 | return false; |
| 154 | |
| 155 | /* |
| 156 | * It must, in fact, be in this list. Ideally, in assert-enabled builds, |
| 157 | * we'd verify that. But since this function is typically used while |
| 158 | * holding a spinlock, crawling the whole list is unacceptable. However, |
| 159 | * we can verify matters in O(1) time when the node is a list head or |
| 160 | * tail, and that seems worth doing, since in practice that should often |
| 161 | * be enough to catch mistakes. |
| 162 | */ |
| 163 | Assert(node->prev != INVALID_PGPROCNO || list->head == procno); |
| 164 | Assert(node->next != INVALID_PGPROCNO || list->tail == procno); |
| 165 | |
| 166 | return true; |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | * Remove and return the first process from a list (there must be one). |
| 171 | */ |
| 172 | static inline PGPROC * |
| 173 | proclist_pop_head_node_offset(proclist_head *list, size_t node_offset) |
| 174 | { |
| 175 | PGPROC *proc; |
| 176 | |
| 177 | Assert(!proclist_is_empty(list)); |
| 178 | proc = GetPGProcByNumber(list->head); |
| 179 | proclist_delete_offset(list, list->head, node_offset); |
| 180 | return proc; |
| 181 | } |
| 182 | |
| 183 | /* |
| 184 | * Helper macros to avoid repetition of offsetof(PGPROC, <member>). |
| 185 | * 'link_member' is the name of a proclist_node member in PGPROC. |
| 186 | */ |
| 187 | #define proclist_delete(list, procno, link_member) \ |
| 188 | proclist_delete_offset((list), (procno), offsetof(PGPROC, link_member)) |
| 189 | #define proclist_push_head(list, procno, link_member) \ |
| 190 | proclist_push_head_offset((list), (procno), offsetof(PGPROC, link_member)) |
| 191 | #define proclist_push_tail(list, procno, link_member) \ |
| 192 | proclist_push_tail_offset((list), (procno), offsetof(PGPROC, link_member)) |
| 193 | #define proclist_pop_head_node(list, link_member) \ |
| 194 | proclist_pop_head_node_offset((list), offsetof(PGPROC, link_member)) |
| 195 | #define proclist_contains(list, procno, link_member) \ |
| 196 | proclist_contains_offset((list), (procno), offsetof(PGPROC, link_member)) |
| 197 | |
| 198 | /* |
| 199 | * Iterate through the list pointed at by 'lhead', storing the current |
| 200 | * position in 'iter'. 'link_member' is the name of a proclist_node member in |
| 201 | * PGPROC. Access the current position with iter.cur. |
| 202 | * |
| 203 | * The only list modification allowed while iterating is deleting the current |
| 204 | * node with proclist_delete(list, iter.cur, node_offset). |
| 205 | */ |
| 206 | #define proclist_foreach_modify(iter, lhead, link_member) \ |
| 207 | for (AssertVariableIsOfTypeMacro(iter, proclist_mutable_iter), \ |
| 208 | AssertVariableIsOfTypeMacro(lhead, proclist_head *), \ |
| 209 | (iter).cur = (lhead)->head, \ |
| 210 | (iter).next = (iter).cur == INVALID_PGPROCNO ? INVALID_PGPROCNO : \ |
| 211 | proclist_node_get((iter).cur, \ |
| 212 | offsetof(PGPROC, link_member))->next; \ |
| 213 | (iter).cur != INVALID_PGPROCNO; \ |
| 214 | (iter).cur = (iter).next, \ |
| 215 | (iter).next = (iter).cur == INVALID_PGPROCNO ? INVALID_PGPROCNO : \ |
| 216 | proclist_node_get((iter).cur, \ |
| 217 | offsetof(PGPROC, link_member))->next) |
| 218 | |
| 219 | #endif /* PROCLIST_H */ |
| 220 | |