proclist.h 6.5 KB

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