priority_queue.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /* Copyright (C) 2015-2022 Free Software Foundation, Inc.
  2. Contributed by Aldy Hernandez <aldyh@redhat.com>.
  3. This file is part of the GNU Offloading and Multi Processing Library
  4. (libgomp).
  5. Libgomp is free software; you can redistribute it and/or modify it
  6. under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3, or (at your option)
  8. any later version.
  9. Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12. more details.
  13. Under Section 7 of GPL version 3, you are granted additional
  14. permissions described in the GCC Runtime Library Exception, version
  15. 3.1, as published by the Free Software Foundation.
  16. You should have received a copy of the GNU General Public License and
  17. a copy of the GCC Runtime Library Exception along with this program;
  18. see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  19. <http://www.gnu.org/licenses/>. */
  20. /* Priority queue implementation of GOMP tasks. */
  21. #include "libgomp.h"
  22. #if _LIBGOMP_CHECKING_
  23. #include <stdio.h>
  24. /* Sanity check to verify whether a TASK is in LIST. Return TRUE if
  25. found, FALSE otherwise.
  26. TYPE is the type of priority queue this task resides in. */
  27. static inline bool
  28. priority_queue_task_in_list_p (enum priority_queue_type type,
  29. struct priority_list *list,
  30. struct gomp_task *task)
  31. {
  32. struct priority_node *p = list->tasks;
  33. do
  34. {
  35. if (priority_node_to_task (type, p) == task)
  36. return true;
  37. p = p->next;
  38. }
  39. while (p != list->tasks);
  40. return false;
  41. }
  42. /* Tree version of priority_queue_task_in_list_p. */
  43. static inline bool
  44. priority_queue_task_in_tree_p (enum priority_queue_type type,
  45. struct priority_queue *head,
  46. struct gomp_task *task)
  47. {
  48. struct priority_list *list
  49. = priority_queue_lookup_priority (head, task->priority);
  50. if (!list)
  51. return false;
  52. return priority_queue_task_in_list_p (type, list, task);
  53. }
  54. /* Generic version of priority_queue_task_in_list_p that works for
  55. trees or lists. */
  56. bool
  57. priority_queue_task_in_queue_p (enum priority_queue_type type,
  58. struct priority_queue *head,
  59. struct gomp_task *task)
  60. {
  61. if (priority_queue_empty_p (head, MEMMODEL_RELAXED))
  62. return false;
  63. if (priority_queue_multi_p (head))
  64. return priority_queue_task_in_tree_p (type, head, task);
  65. else
  66. return priority_queue_task_in_list_p (type, &head->l, task);
  67. }
  68. /* Sanity check LIST to make sure the tasks therein are in the right
  69. order. LIST is a priority list of type TYPE.
  70. The expected order is that GOMP_TASK_WAITING tasks come before
  71. GOMP_TASK_TIED/GOMP_TASK_ASYNC_RUNNING ones.
  72. If CHECK_DEPS is TRUE, we also check that parent_depends_on WAITING
  73. tasks come before !parent_depends_on WAITING tasks. This is only
  74. applicable to the children queue, and the caller is expected to
  75. ensure that we are verifying the children queue. */
  76. static void
  77. priority_list_verify (enum priority_queue_type type,
  78. struct priority_list *list, bool check_deps)
  79. {
  80. bool seen_tied = false;
  81. bool seen_plain_waiting = false;
  82. struct priority_node *p = list->tasks;
  83. while (1)
  84. {
  85. struct gomp_task *t = priority_node_to_task (type, p);
  86. if (seen_tied && t->kind == GOMP_TASK_WAITING)
  87. gomp_fatal ("priority_queue_verify: WAITING task after TIED");
  88. if (t->kind >= GOMP_TASK_TIED)
  89. seen_tied = true;
  90. else if (check_deps && t->kind == GOMP_TASK_WAITING)
  91. {
  92. if (t->parent_depends_on)
  93. {
  94. if (seen_plain_waiting)
  95. gomp_fatal ("priority_queue_verify: "
  96. "parent_depends_on after !parent_depends_on");
  97. }
  98. else
  99. seen_plain_waiting = true;
  100. }
  101. p = p->next;
  102. if (p == list->tasks)
  103. break;
  104. }
  105. }
  106. /* Callback type for priority_tree_verify_callback. */
  107. struct cbtype
  108. {
  109. enum priority_queue_type type;
  110. bool check_deps;
  111. };
  112. /* Verify every task in NODE.
  113. Callback for splay_tree_foreach. */
  114. static void
  115. priority_tree_verify_callback (prio_splay_tree_key key, void *data)
  116. {
  117. struct cbtype *cb = (struct cbtype *) data;
  118. priority_list_verify (cb->type, &key->l, cb->check_deps);
  119. }
  120. /* Generic version of priority_list_verify.
  121. Sanity check HEAD to make sure the tasks therein are in the right
  122. order. The priority_queue holds tasks of type TYPE.
  123. If CHECK_DEPS is TRUE, we also check that parent_depends_on WAITING
  124. tasks come before !parent_depends_on WAITING tasks. This is only
  125. applicable to the children queue, and the caller is expected to
  126. ensure that we are verifying the children queue. */
  127. void
  128. priority_queue_verify (enum priority_queue_type type,
  129. struct priority_queue *head, bool check_deps)
  130. {
  131. if (priority_queue_empty_p (head, MEMMODEL_RELAXED))
  132. return;
  133. if (priority_queue_multi_p (head))
  134. {
  135. struct cbtype cb = { type, check_deps };
  136. prio_splay_tree_foreach (&head->t,
  137. priority_tree_verify_callback, &cb);
  138. }
  139. else
  140. priority_list_verify (type, &head->l, check_deps);
  141. }
  142. #endif /* _LIBGOMP_CHECKING_ */
  143. /* Tree version of priority_queue_find. */
  144. static struct gomp_task *
  145. priority_tree_find (enum priority_queue_type type,
  146. prio_splay_tree_node node,
  147. priority_queue_predicate pred)
  148. {
  149. again:
  150. if (!node)
  151. return NULL;
  152. struct gomp_task *task = priority_tree_find (type, node->right, pred);
  153. if (task)
  154. return task;
  155. task = priority_node_to_task (type, node->key.l.tasks);
  156. if (pred (task))
  157. return task;
  158. node = node->left;
  159. goto again;
  160. }
  161. /* List version of priority_queue_find. */
  162. static struct gomp_task *
  163. priority_list_find (enum priority_queue_type type,
  164. struct priority_list *list,
  165. priority_queue_predicate pred)
  166. {
  167. struct priority_node *node = list->tasks;
  168. if (!node)
  169. return NULL;
  170. do
  171. {
  172. struct gomp_task *task = priority_node_to_task (type, node);
  173. if (pred (task))
  174. return task;
  175. node = node->next;
  176. }
  177. while (node != list->tasks);
  178. return NULL;
  179. }
  180. /* Return the highest priority task in the priority queue HEAD that
  181. satisfies the predicate PRED. HEAD contains tasks of type TYPE. */
  182. struct gomp_task *
  183. priority_queue_find (enum priority_queue_type type,
  184. struct priority_queue *head,
  185. priority_queue_predicate pred)
  186. {
  187. if (priority_queue_multi_p (head))
  188. return priority_tree_find (type, head->t.root, pred);
  189. else
  190. return priority_list_find (type, &head->l, pred);
  191. }
  192. /* Remove NODE from priority queue HEAD, wherever it may be inside the
  193. tree. HEAD contains tasks of type TYPE. */
  194. void
  195. priority_tree_remove (enum priority_queue_type type,
  196. struct priority_queue *head,
  197. struct priority_node *node)
  198. {
  199. /* ?? The only reason this function is not inlined is because we
  200. need to find the priority within gomp_task (which has not been
  201. completely defined in the header file). If the lack of inlining
  202. is a concern, we could pass the priority number as a
  203. parameter, or we could move this to libgomp.h. */
  204. int priority = priority_node_to_task (type, node)->priority;
  205. /* ?? We could avoid this lookup by keeping a pointer to the key in
  206. the priority_node. */
  207. struct priority_list *list
  208. = priority_queue_lookup_priority (head, priority);
  209. #if _LIBGOMP_CHECKING_
  210. if (!list)
  211. gomp_fatal ("Unable to find priority %d", priority);
  212. #endif
  213. /* If NODE was the last in its priority, clean up the priority. */
  214. if (priority_list_remove (list, node, MEMMODEL_RELAXED))
  215. {
  216. prio_splay_tree_remove (&head->t, (prio_splay_tree_key) list);
  217. list->tasks = NULL;
  218. #if _LIBGOMP_CHECKING_
  219. memset (list, 0xaf, sizeof (*list));
  220. #endif
  221. free (list);
  222. }
  223. }
  224. /* Return the highest priority WAITING task in a splay tree NODE. If
  225. there are no WAITING tasks available, return NULL.
  226. NODE is a priority list containing tasks of type TYPE.
  227. The right most node in a tree contains the highest priority.
  228. Recurse down to find such a node. If the task at that max node is
  229. not WAITING, bubble back up and look at the remaining tasks
  230. in-order. */
  231. static struct gomp_task *
  232. priority_tree_next_task_1 (enum priority_queue_type type,
  233. prio_splay_tree_node node)
  234. {
  235. again:
  236. if (!node)
  237. return NULL;
  238. struct gomp_task *ret = priority_tree_next_task_1 (type, node->right);
  239. if (ret)
  240. return ret;
  241. ret = priority_node_to_task (type, node->key.l.tasks);
  242. if (ret->kind == GOMP_TASK_WAITING)
  243. return ret;
  244. node = node->left;
  245. goto again;
  246. }
  247. /* Return the highest priority WAITING task from within Q1 and Q2,
  248. while giving preference to tasks from Q1. Q1 is a queue containing
  249. items of type TYPE1. Q2 is a queue containing items of type TYPE2.
  250. Since we are mostly interested in Q1, if there are no WAITING tasks
  251. in Q1, we don't bother checking Q2, and just return NULL.
  252. As a special case, Q2 can be NULL, in which case, we just choose
  253. the highest priority WAITING task in Q1. This is an optimization
  254. to speed up looking through only one queue.
  255. If the returned task is chosen from Q1, *Q1_CHOSEN_P is set to
  256. TRUE, otherwise it is set to FALSE. */
  257. struct gomp_task *
  258. priority_tree_next_task (enum priority_queue_type type1,
  259. struct priority_queue *q1,
  260. enum priority_queue_type type2,
  261. struct priority_queue *q2,
  262. bool *q1_chosen_p)
  263. {
  264. struct gomp_task *t1 = priority_tree_next_task_1 (type1, q1->t.root);
  265. if (!t1
  266. /* Special optimization when only searching through one queue. */
  267. || !q2)
  268. {
  269. *q1_chosen_p = true;
  270. return t1;
  271. }
  272. struct gomp_task *t2 = priority_tree_next_task_1 (type2, q2->t.root);
  273. if (!t2 || t1->priority > t2->priority)
  274. {
  275. *q1_chosen_p = true;
  276. return t1;
  277. }
  278. if (t2->priority > t1->priority)
  279. {
  280. *q1_chosen_p = false;
  281. return t2;
  282. }
  283. /* If we get here, the priorities are the same, so we must look at
  284. parent_depends_on to make our decision. */
  285. #if _LIBGOMP_CHECKING_
  286. if (t1 != t2)
  287. gomp_fatal ("priority_tree_next_task: t1 != t2");
  288. #endif
  289. if (t2->parent_depends_on && !t1->parent_depends_on)
  290. {
  291. *q1_chosen_p = false;
  292. return t2;
  293. }
  294. *q1_chosen_p = true;
  295. return t1;
  296. }
  297. /* Priority splay trees comparison function. */
  298. static inline int
  299. prio_splay_compare (prio_splay_tree_key x, prio_splay_tree_key y)
  300. {
  301. if (x->l.priority == y->l.priority)
  302. return 0;
  303. return x->l.priority < y->l.priority ? -1 : 1;
  304. }
  305. /* Define another splay tree instantiation, for priority_list's. */
  306. #define splay_tree_prefix prio
  307. #define splay_tree_c
  308. #include "splay-tree.h"