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