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