annotate libgomp/priority_queue.h @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 131
diff changeset
1 /* Copyright (C) 2015-2020 Free Software Foundation, Inc.
111
kono
parents:
diff changeset
2 Contributed by Aldy Hernandez <aldyh@redhat.com>.
kono
parents:
diff changeset
3
kono
parents:
diff changeset
4 This file is part of the GNU Offloading and Multi Processing Library
kono
parents:
diff changeset
5 (libgomp).
kono
parents:
diff changeset
6
kono
parents:
diff changeset
7 Libgomp is free software; you can redistribute it and/or modify it
kono
parents:
diff changeset
8 under the terms of the GNU General Public License as published by
kono
parents:
diff changeset
9 the Free Software Foundation; either version 3, or (at your option)
kono
parents:
diff changeset
10 any later version.
kono
parents:
diff changeset
11
kono
parents:
diff changeset
12 Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
kono
parents:
diff changeset
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
kono
parents:
diff changeset
14 FOR A PARTICULAR PURPOSE. See the GNU General Public License for
kono
parents:
diff changeset
15 more details.
kono
parents:
diff changeset
16
kono
parents:
diff changeset
17 Under Section 7 of GPL version 3, you are granted additional
kono
parents:
diff changeset
18 permissions described in the GCC Runtime Library Exception, version
kono
parents:
diff changeset
19 3.1, as published by the Free Software Foundation.
kono
parents:
diff changeset
20
kono
parents:
diff changeset
21 You should have received a copy of the GNU General Public License and
kono
parents:
diff changeset
22 a copy of the GCC Runtime Library Exception along with this program;
kono
parents:
diff changeset
23 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
kono
parents:
diff changeset
24 <http://www.gnu.org/licenses/>. */
kono
parents:
diff changeset
25
kono
parents:
diff changeset
26 /* Header file for a priority queue of GOMP tasks. */
kono
parents:
diff changeset
27
kono
parents:
diff changeset
28 /* ?? Perhaps all the priority_tree_* functions are complex and rare
kono
parents:
diff changeset
29 enough to go out-of-line and be moved to priority_queue.c. ?? */
kono
parents:
diff changeset
30
kono
parents:
diff changeset
31 #ifndef _PRIORITY_QUEUE_H_
kono
parents:
diff changeset
32 #define _PRIORITY_QUEUE_H_
kono
parents:
diff changeset
33
kono
parents:
diff changeset
34 /* One task. */
kono
parents:
diff changeset
35
kono
parents:
diff changeset
36 struct priority_node
kono
parents:
diff changeset
37 {
kono
parents:
diff changeset
38 /* Next and previous chains in a circular doubly linked list for
kono
parents:
diff changeset
39 tasks within this task's priority. */
kono
parents:
diff changeset
40 struct priority_node *next, *prev;
kono
parents:
diff changeset
41 };
kono
parents:
diff changeset
42
kono
parents:
diff changeset
43 /* All tasks within the same priority. */
kono
parents:
diff changeset
44
kono
parents:
diff changeset
45 struct priority_list
kono
parents:
diff changeset
46 {
kono
parents:
diff changeset
47 /* Priority of the tasks in this set. */
kono
parents:
diff changeset
48 int priority;
kono
parents:
diff changeset
49
kono
parents:
diff changeset
50 /* Tasks. */
kono
parents:
diff changeset
51 struct priority_node *tasks;
kono
parents:
diff changeset
52
kono
parents:
diff changeset
53 /* This points to the last of the higher priority WAITING tasks.
kono
parents:
diff changeset
54 Remember that for the children queue, we have:
kono
parents:
diff changeset
55
kono
parents:
diff changeset
56 parent_depends_on WAITING tasks.
kono
parents:
diff changeset
57 !parent_depends_on WAITING tasks.
kono
parents:
diff changeset
58 TIED tasks.
kono
parents:
diff changeset
59
kono
parents:
diff changeset
60 This is a pointer to the last of the parent_depends_on WAITING
kono
parents:
diff changeset
61 tasks which are essentially, higher priority items within their
kono
parents:
diff changeset
62 priority. */
kono
parents:
diff changeset
63 struct priority_node *last_parent_depends_on;
kono
parents:
diff changeset
64 };
kono
parents:
diff changeset
65
kono
parents:
diff changeset
66 /* Another splay tree instantiation, for priority_list's. */
kono
parents:
diff changeset
67 typedef struct prio_splay_tree_node_s *prio_splay_tree_node;
kono
parents:
diff changeset
68 typedef struct prio_splay_tree_s *prio_splay_tree;
kono
parents:
diff changeset
69 typedef struct prio_splay_tree_key_s *prio_splay_tree_key;
kono
parents:
diff changeset
70 struct prio_splay_tree_key_s {
kono
parents:
diff changeset
71 /* This structure must only containing a priority_list, as we cast
kono
parents:
diff changeset
72 prio_splay_tree_key to priority_list throughout. */
kono
parents:
diff changeset
73 struct priority_list l;
kono
parents:
diff changeset
74 };
kono
parents:
diff changeset
75 #define splay_tree_prefix prio
kono
parents:
diff changeset
76 #include "splay-tree.h"
kono
parents:
diff changeset
77
kono
parents:
diff changeset
78 /* The entry point into a priority queue of tasks.
kono
parents:
diff changeset
79
kono
parents:
diff changeset
80 There are two alternate implementations with which to store tasks:
kono
parents:
diff changeset
81 as a balanced tree of sorts, or as a simple list of tasks. If
kono
parents:
diff changeset
82 there are only priority-0 items (ROOT is NULL), we use the simple
kono
parents:
diff changeset
83 list, otherwise (ROOT is non-NULL) we use the tree. */
kono
parents:
diff changeset
84
kono
parents:
diff changeset
85 struct priority_queue
kono
parents:
diff changeset
86 {
kono
parents:
diff changeset
87 /* If t.root != NULL, this is a splay tree of priority_lists to hold
kono
parents:
diff changeset
88 all tasks. This is only used if multiple priorities are in play,
kono
parents:
diff changeset
89 otherwise we use the priority_list `l' below to hold all
kono
parents:
diff changeset
90 (priority-0) tasks. */
kono
parents:
diff changeset
91 struct prio_splay_tree_s t;
kono
parents:
diff changeset
92
kono
parents:
diff changeset
93 /* If T above is NULL, only priority-0 items exist, so keep them
kono
parents:
diff changeset
94 in a simple list. */
kono
parents:
diff changeset
95 struct priority_list l;
kono
parents:
diff changeset
96 };
kono
parents:
diff changeset
97
kono
parents:
diff changeset
98 enum priority_insert_type {
kono
parents:
diff changeset
99 /* Insert at the beginning of a priority list. */
kono
parents:
diff changeset
100 PRIORITY_INSERT_BEGIN,
kono
parents:
diff changeset
101 /* Insert at the end of a priority list. */
kono
parents:
diff changeset
102 PRIORITY_INSERT_END
kono
parents:
diff changeset
103 };
kono
parents:
diff changeset
104
kono
parents:
diff changeset
105 /* Used to determine in which queue a given priority node belongs in.
kono
parents:
diff changeset
106 See pnode field of gomp_task. */
kono
parents:
diff changeset
107
kono
parents:
diff changeset
108 enum priority_queue_type
kono
parents:
diff changeset
109 {
kono
parents:
diff changeset
110 PQ_TEAM, /* Node belongs in gomp_team's task_queue. */
kono
parents:
diff changeset
111 PQ_CHILDREN, /* Node belongs in parent's children_queue. */
kono
parents:
diff changeset
112 PQ_TASKGROUP, /* Node belongs in taskgroup->taskgroup_queue. */
kono
parents:
diff changeset
113 PQ_IGNORED = 999
kono
parents:
diff changeset
114 };
kono
parents:
diff changeset
115
kono
parents:
diff changeset
116 /* Priority queue implementation prototypes. */
kono
parents:
diff changeset
117
kono
parents:
diff changeset
118 extern bool priority_queue_task_in_queue_p (enum priority_queue_type,
kono
parents:
diff changeset
119 struct priority_queue *,
kono
parents:
diff changeset
120 struct gomp_task *);
kono
parents:
diff changeset
121 extern void priority_queue_dump (enum priority_queue_type,
kono
parents:
diff changeset
122 struct priority_queue *);
kono
parents:
diff changeset
123 extern void priority_queue_verify (enum priority_queue_type,
kono
parents:
diff changeset
124 struct priority_queue *, bool);
kono
parents:
diff changeset
125 extern void priority_tree_remove (enum priority_queue_type,
kono
parents:
diff changeset
126 struct priority_queue *,
kono
parents:
diff changeset
127 struct priority_node *);
kono
parents:
diff changeset
128 extern struct gomp_task *priority_tree_next_task (enum priority_queue_type,
kono
parents:
diff changeset
129 struct priority_queue *,
kono
parents:
diff changeset
130 enum priority_queue_type,
kono
parents:
diff changeset
131 struct priority_queue *,
kono
parents:
diff changeset
132 bool *);
kono
parents:
diff changeset
133
kono
parents:
diff changeset
134 /* Return TRUE if there is more than one priority in HEAD. This is
kono
parents:
diff changeset
135 used throughout to to choose between the fast path (priority 0 only
kono
parents:
diff changeset
136 items) and a world with multiple priorities. */
kono
parents:
diff changeset
137
kono
parents:
diff changeset
138 static inline bool
kono
parents:
diff changeset
139 priority_queue_multi_p (struct priority_queue *head)
kono
parents:
diff changeset
140 {
kono
parents:
diff changeset
141 return __builtin_expect (head->t.root != NULL, 0);
kono
parents:
diff changeset
142 }
kono
parents:
diff changeset
143
kono
parents:
diff changeset
144 /* Initialize a priority queue. */
kono
parents:
diff changeset
145
kono
parents:
diff changeset
146 static inline void
kono
parents:
diff changeset
147 priority_queue_init (struct priority_queue *head)
kono
parents:
diff changeset
148 {
kono
parents:
diff changeset
149 head->t.root = NULL;
kono
parents:
diff changeset
150 /* To save a few microseconds, we don't initialize head->l.priority
kono
parents:
diff changeset
151 to 0 here. It is implied that priority will be 0 if head->t.root
kono
parents:
diff changeset
152 == NULL.
kono
parents:
diff changeset
153
kono
parents:
diff changeset
154 priority_tree_insert() will fix this when we encounter multiple
kono
parents:
diff changeset
155 priorities. */
kono
parents:
diff changeset
156 head->l.tasks = NULL;
kono
parents:
diff changeset
157 head->l.last_parent_depends_on = NULL;
kono
parents:
diff changeset
158 }
kono
parents:
diff changeset
159
kono
parents:
diff changeset
160 static inline void
kono
parents:
diff changeset
161 priority_queue_free (struct priority_queue *head)
kono
parents:
diff changeset
162 {
kono
parents:
diff changeset
163 /* There's nothing to do, as tasks were freed as they were removed
kono
parents:
diff changeset
164 in priority_queue_remove. */
kono
parents:
diff changeset
165 }
kono
parents:
diff changeset
166
kono
parents:
diff changeset
167 /* Forward declarations. */
kono
parents:
diff changeset
168 static inline size_t priority_queue_offset (enum priority_queue_type);
kono
parents:
diff changeset
169 static inline struct gomp_task *priority_node_to_task
kono
parents:
diff changeset
170 (enum priority_queue_type,
kono
parents:
diff changeset
171 struct priority_node *);
kono
parents:
diff changeset
172 static inline struct priority_node *task_to_priority_node
kono
parents:
diff changeset
173 (enum priority_queue_type,
kono
parents:
diff changeset
174 struct gomp_task *);
kono
parents:
diff changeset
175
kono
parents:
diff changeset
176 /* Return TRUE if priority queue HEAD is empty.
kono
parents:
diff changeset
177
kono
parents:
diff changeset
178 MODEL IS MEMMODEL_ACQUIRE if we should use an acquire atomic to
kono
parents:
diff changeset
179 read from the root of the queue, otherwise MEMMODEL_RELAXED if we
kono
parents:
diff changeset
180 should use a plain load. */
kono
parents:
diff changeset
181
kono
parents:
diff changeset
182 static inline _Bool
kono
parents:
diff changeset
183 priority_queue_empty_p (struct priority_queue *head, enum memmodel model)
kono
parents:
diff changeset
184 {
kono
parents:
diff changeset
185 /* Note: The acquire barriers on the loads here synchronize with
kono
parents:
diff changeset
186 the write of a NULL in gomp_task_run_post_remove_parent. It is
kono
parents:
diff changeset
187 not necessary that we synchronize with other non-NULL writes at
kono
parents:
diff changeset
188 this point, but we must ensure that all writes to memory by a
kono
parents:
diff changeset
189 child thread task work function are seen before we exit from
kono
parents:
diff changeset
190 GOMP_taskwait. */
kono
parents:
diff changeset
191 if (priority_queue_multi_p (head))
kono
parents:
diff changeset
192 {
kono
parents:
diff changeset
193 if (model == MEMMODEL_ACQUIRE)
kono
parents:
diff changeset
194 return __atomic_load_n (&head->t.root, MEMMODEL_ACQUIRE) == NULL;
kono
parents:
diff changeset
195 return head->t.root == NULL;
kono
parents:
diff changeset
196 }
kono
parents:
diff changeset
197 if (model == MEMMODEL_ACQUIRE)
kono
parents:
diff changeset
198 return __atomic_load_n (&head->l.tasks, MEMMODEL_ACQUIRE) == NULL;
kono
parents:
diff changeset
199 return head->l.tasks == NULL;
kono
parents:
diff changeset
200 }
kono
parents:
diff changeset
201
kono
parents:
diff changeset
202 /* Look for a given PRIORITY in HEAD. Return it if found, otherwise
kono
parents:
diff changeset
203 return NULL. This only applies to the tree variant in HEAD. There
kono
parents:
diff changeset
204 is no point in searching for priorities in HEAD->L. */
kono
parents:
diff changeset
205
kono
parents:
diff changeset
206 static inline struct priority_list *
kono
parents:
diff changeset
207 priority_queue_lookup_priority (struct priority_queue *head, int priority)
kono
parents:
diff changeset
208 {
kono
parents:
diff changeset
209 if (head->t.root == NULL)
kono
parents:
diff changeset
210 return NULL;
kono
parents:
diff changeset
211 struct prio_splay_tree_key_s k;
kono
parents:
diff changeset
212 k.l.priority = priority;
kono
parents:
diff changeset
213 return (struct priority_list *)
kono
parents:
diff changeset
214 prio_splay_tree_lookup (&head->t, &k);
kono
parents:
diff changeset
215 }
kono
parents:
diff changeset
216
kono
parents:
diff changeset
217 /* Insert task in DATA, with PRIORITY, in the priority list in LIST.
kono
parents:
diff changeset
218 LIST contains items of type TYPE.
kono
parents:
diff changeset
219
kono
parents:
diff changeset
220 If POS is PRIORITY_INSERT_BEGIN, the new task is inserted at the
kono
parents:
diff changeset
221 top of its respective priority. If POS is PRIORITY_INSERT_END, the
kono
parents:
diff changeset
222 task is inserted at the end of its priority.
kono
parents:
diff changeset
223
kono
parents:
diff changeset
224 If ADJUST_PARENT_DEPENDS_ON is TRUE, LIST is a children queue, and
kono
parents:
diff changeset
225 we must keep track of higher and lower priority WAITING tasks by
kono
parents:
diff changeset
226 keeping the queue's last_parent_depends_on field accurate. This
kono
parents:
diff changeset
227 only applies to the children queue, and the caller must ensure LIST
kono
parents:
diff changeset
228 is a children queue in this case.
kono
parents:
diff changeset
229
kono
parents:
diff changeset
230 If ADJUST_PARENT_DEPENDS_ON is TRUE, TASK_IS_PARENT_DEPENDS_ON is
kono
parents:
diff changeset
231 set to the task's parent_depends_on field. If
kono
parents:
diff changeset
232 ADJUST_PARENT_DEPENDS_ON is FALSE, this field is irrelevant.
kono
parents:
diff changeset
233
kono
parents:
diff changeset
234 Return the new priority_node. */
kono
parents:
diff changeset
235
kono
parents:
diff changeset
236 static inline void
kono
parents:
diff changeset
237 priority_list_insert (enum priority_queue_type type,
kono
parents:
diff changeset
238 struct priority_list *list,
kono
parents:
diff changeset
239 struct gomp_task *task,
kono
parents:
diff changeset
240 int priority,
kono
parents:
diff changeset
241 enum priority_insert_type pos,
kono
parents:
diff changeset
242 bool adjust_parent_depends_on,
kono
parents:
diff changeset
243 bool task_is_parent_depends_on)
kono
parents:
diff changeset
244 {
kono
parents:
diff changeset
245 struct priority_node *node = task_to_priority_node (type, task);
kono
parents:
diff changeset
246 if (list->tasks)
kono
parents:
diff changeset
247 {
kono
parents:
diff changeset
248 /* If we are keeping track of higher/lower priority items,
kono
parents:
diff changeset
249 but this is a lower priority WAITING task
kono
parents:
diff changeset
250 (parent_depends_on != NULL), put it after all ready to
kono
parents:
diff changeset
251 run tasks. See the comment in
kono
parents:
diff changeset
252 priority_queue_upgrade_task for a visual on how tasks
kono
parents:
diff changeset
253 should be organized. */
kono
parents:
diff changeset
254 if (adjust_parent_depends_on
kono
parents:
diff changeset
255 && pos == PRIORITY_INSERT_BEGIN
kono
parents:
diff changeset
256 && list->last_parent_depends_on
kono
parents:
diff changeset
257 && !task_is_parent_depends_on)
kono
parents:
diff changeset
258 {
kono
parents:
diff changeset
259 struct priority_node *last_parent_depends_on
kono
parents:
diff changeset
260 = list->last_parent_depends_on;
kono
parents:
diff changeset
261 node->next = last_parent_depends_on->next;
kono
parents:
diff changeset
262 node->prev = last_parent_depends_on;
kono
parents:
diff changeset
263 }
kono
parents:
diff changeset
264 /* Otherwise, put it at the top/bottom of the queue. */
kono
parents:
diff changeset
265 else
kono
parents:
diff changeset
266 {
kono
parents:
diff changeset
267 node->next = list->tasks;
kono
parents:
diff changeset
268 node->prev = list->tasks->prev;
kono
parents:
diff changeset
269 if (pos == PRIORITY_INSERT_BEGIN)
kono
parents:
diff changeset
270 list->tasks = node;
kono
parents:
diff changeset
271 }
kono
parents:
diff changeset
272 node->next->prev = node;
kono
parents:
diff changeset
273 node->prev->next = node;
kono
parents:
diff changeset
274 }
kono
parents:
diff changeset
275 else
kono
parents:
diff changeset
276 {
kono
parents:
diff changeset
277 node->next = node;
kono
parents:
diff changeset
278 node->prev = node;
kono
parents:
diff changeset
279 list->tasks = node;
kono
parents:
diff changeset
280 }
kono
parents:
diff changeset
281 if (adjust_parent_depends_on
kono
parents:
diff changeset
282 && list->last_parent_depends_on == NULL
kono
parents:
diff changeset
283 && task_is_parent_depends_on)
kono
parents:
diff changeset
284 list->last_parent_depends_on = node;
kono
parents:
diff changeset
285 }
kono
parents:
diff changeset
286
kono
parents:
diff changeset
287 /* Tree version of priority_list_insert. */
kono
parents:
diff changeset
288
kono
parents:
diff changeset
289 static inline void
kono
parents:
diff changeset
290 priority_tree_insert (enum priority_queue_type type,
kono
parents:
diff changeset
291 struct priority_queue *head,
kono
parents:
diff changeset
292 struct gomp_task *task,
kono
parents:
diff changeset
293 int priority,
kono
parents:
diff changeset
294 enum priority_insert_type pos,
kono
parents:
diff changeset
295 bool adjust_parent_depends_on,
kono
parents:
diff changeset
296 bool task_is_parent_depends_on)
kono
parents:
diff changeset
297 {
kono
parents:
diff changeset
298 if (__builtin_expect (head->t.root == NULL, 0))
kono
parents:
diff changeset
299 {
kono
parents:
diff changeset
300 /* The first time around, transfer any priority 0 items to the
kono
parents:
diff changeset
301 tree. */
kono
parents:
diff changeset
302 if (head->l.tasks != NULL)
kono
parents:
diff changeset
303 {
kono
parents:
diff changeset
304 prio_splay_tree_node k = gomp_malloc (sizeof (*k));
kono
parents:
diff changeset
305 k->left = NULL;
kono
parents:
diff changeset
306 k->right = NULL;
kono
parents:
diff changeset
307 k->key.l.priority = 0;
kono
parents:
diff changeset
308 k->key.l.tasks = head->l.tasks;
kono
parents:
diff changeset
309 k->key.l.last_parent_depends_on = head->l.last_parent_depends_on;
kono
parents:
diff changeset
310 prio_splay_tree_insert (&head->t, k);
kono
parents:
diff changeset
311 head->l.tasks = NULL;
kono
parents:
diff changeset
312 }
kono
parents:
diff changeset
313 }
kono
parents:
diff changeset
314 struct priority_list *list
kono
parents:
diff changeset
315 = priority_queue_lookup_priority (head, priority);
kono
parents:
diff changeset
316 if (!list)
kono
parents:
diff changeset
317 {
kono
parents:
diff changeset
318 prio_splay_tree_node k = gomp_malloc (sizeof (*k));
kono
parents:
diff changeset
319 k->left = NULL;
kono
parents:
diff changeset
320 k->right = NULL;
kono
parents:
diff changeset
321 k->key.l.priority = priority;
kono
parents:
diff changeset
322 k->key.l.tasks = NULL;
kono
parents:
diff changeset
323 k->key.l.last_parent_depends_on = NULL;
kono
parents:
diff changeset
324 prio_splay_tree_insert (&head->t, k);
kono
parents:
diff changeset
325 list = &k->key.l;
kono
parents:
diff changeset
326 }
kono
parents:
diff changeset
327 priority_list_insert (type, list, task, priority, pos,
kono
parents:
diff changeset
328 adjust_parent_depends_on,
kono
parents:
diff changeset
329 task_is_parent_depends_on);
kono
parents:
diff changeset
330 }
kono
parents:
diff changeset
331
kono
parents:
diff changeset
332 /* Generic version of priority_*_insert. */
kono
parents:
diff changeset
333
kono
parents:
diff changeset
334 static inline void
kono
parents:
diff changeset
335 priority_queue_insert (enum priority_queue_type type,
kono
parents:
diff changeset
336 struct priority_queue *head,
kono
parents:
diff changeset
337 struct gomp_task *task,
kono
parents:
diff changeset
338 int priority,
kono
parents:
diff changeset
339 enum priority_insert_type pos,
kono
parents:
diff changeset
340 bool adjust_parent_depends_on,
kono
parents:
diff changeset
341 bool task_is_parent_depends_on)
kono
parents:
diff changeset
342 {
kono
parents:
diff changeset
343 #if _LIBGOMP_CHECKING_
kono
parents:
diff changeset
344 if (priority_queue_task_in_queue_p (type, head, task))
kono
parents:
diff changeset
345 gomp_fatal ("Attempt to insert existing task %p", task);
kono
parents:
diff changeset
346 #endif
kono
parents:
diff changeset
347 if (priority_queue_multi_p (head) || __builtin_expect (priority > 0, 0))
kono
parents:
diff changeset
348 priority_tree_insert (type, head, task, priority, pos,
kono
parents:
diff changeset
349 adjust_parent_depends_on,
kono
parents:
diff changeset
350 task_is_parent_depends_on);
kono
parents:
diff changeset
351 else
kono
parents:
diff changeset
352 priority_list_insert (type, &head->l, task, priority, pos,
kono
parents:
diff changeset
353 adjust_parent_depends_on,
kono
parents:
diff changeset
354 task_is_parent_depends_on);
kono
parents:
diff changeset
355 }
kono
parents:
diff changeset
356
kono
parents:
diff changeset
357 /* If multiple priorities are in play, return the highest priority
kono
parents:
diff changeset
358 task from within Q1 and Q2, while giving preference to tasks from
kono
parents:
diff changeset
359 Q1. If the returned task is chosen from Q1, *Q1_CHOSEN_P is set to
kono
parents:
diff changeset
360 TRUE, otherwise it is set to FALSE.
kono
parents:
diff changeset
361
kono
parents:
diff changeset
362 If multiple priorities are not in play (only 0 priorities are
kono
parents:
diff changeset
363 available), the next task is chosen exclusively from Q1.
kono
parents:
diff changeset
364
kono
parents:
diff changeset
365 As a special case, Q2 can be NULL, in which case, we just choose
kono
parents:
diff changeset
366 the highest priority WAITING task in Q1. This is an optimization
kono
parents:
diff changeset
367 to speed up looking through only one queue.
kono
parents:
diff changeset
368
kono
parents:
diff changeset
369 We assume Q1 has at least one item. */
kono
parents:
diff changeset
370
kono
parents:
diff changeset
371 static inline struct gomp_task *
kono
parents:
diff changeset
372 priority_queue_next_task (enum priority_queue_type t1,
kono
parents:
diff changeset
373 struct priority_queue *q1,
kono
parents:
diff changeset
374 enum priority_queue_type t2,
kono
parents:
diff changeset
375 struct priority_queue *q2,
kono
parents:
diff changeset
376 bool *q1_chosen_p)
kono
parents:
diff changeset
377 {
kono
parents:
diff changeset
378 #if _LIBGOMP_CHECKING_
kono
parents:
diff changeset
379 if (priority_queue_empty_p (q1, MEMMODEL_RELAXED))
kono
parents:
diff changeset
380 gomp_fatal ("priority_queue_next_task: Q1 is empty");
kono
parents:
diff changeset
381 #endif
kono
parents:
diff changeset
382 if (priority_queue_multi_p (q1))
kono
parents:
diff changeset
383 {
kono
parents:
diff changeset
384 struct gomp_task *t
kono
parents:
diff changeset
385 = priority_tree_next_task (t1, q1, t2, q2, q1_chosen_p);
kono
parents:
diff changeset
386 /* If T is NULL, there are no WAITING tasks in Q1. In which
kono
parents:
diff changeset
387 case, return any old (non-waiting) task which will cause the
kono
parents:
diff changeset
388 caller to do the right thing when checking T->KIND ==
kono
parents:
diff changeset
389 GOMP_TASK_WAITING. */
kono
parents:
diff changeset
390 if (!t)
kono
parents:
diff changeset
391 {
kono
parents:
diff changeset
392 #if _LIBGOMP_CHECKING_
kono
parents:
diff changeset
393 if (*q1_chosen_p == false)
kono
parents:
diff changeset
394 gomp_fatal ("priority_queue_next_task inconsistency");
kono
parents:
diff changeset
395 #endif
kono
parents:
diff changeset
396 return priority_node_to_task (t1, q1->t.root->key.l.tasks);
kono
parents:
diff changeset
397 }
kono
parents:
diff changeset
398 return t;
kono
parents:
diff changeset
399 }
kono
parents:
diff changeset
400 else
kono
parents:
diff changeset
401 {
kono
parents:
diff changeset
402 *q1_chosen_p = true;
kono
parents:
diff changeset
403 return priority_node_to_task (t1, q1->l.tasks);
kono
parents:
diff changeset
404 }
kono
parents:
diff changeset
405 }
kono
parents:
diff changeset
406
kono
parents:
diff changeset
407 /* Remove NODE from LIST.
kono
parents:
diff changeset
408
kono
parents:
diff changeset
409 If we are removing the one and only item in the list, and MODEL is
kono
parents:
diff changeset
410 MEMMODEL_RELEASE, use an atomic release to clear the list.
kono
parents:
diff changeset
411
kono
parents:
diff changeset
412 If the list becomes empty after the remove, return TRUE. */
kono
parents:
diff changeset
413
kono
parents:
diff changeset
414 static inline bool
kono
parents:
diff changeset
415 priority_list_remove (struct priority_list *list,
kono
parents:
diff changeset
416 struct priority_node *node,
kono
parents:
diff changeset
417 enum memmodel model)
kono
parents:
diff changeset
418 {
kono
parents:
diff changeset
419 bool empty = false;
kono
parents:
diff changeset
420 node->prev->next = node->next;
kono
parents:
diff changeset
421 node->next->prev = node->prev;
kono
parents:
diff changeset
422 if (list->tasks == node)
kono
parents:
diff changeset
423 {
kono
parents:
diff changeset
424 if (node->next != node)
kono
parents:
diff changeset
425 list->tasks = node->next;
kono
parents:
diff changeset
426 else
kono
parents:
diff changeset
427 {
kono
parents:
diff changeset
428 /* We access task->children in GOMP_taskwait outside of
kono
parents:
diff changeset
429 the task lock mutex region, so need a release barrier
kono
parents:
diff changeset
430 here to ensure memory written by child_task->fn above
kono
parents:
diff changeset
431 is flushed before the NULL is written. */
kono
parents:
diff changeset
432 if (model == MEMMODEL_RELEASE)
kono
parents:
diff changeset
433 __atomic_store_n (&list->tasks, NULL, MEMMODEL_RELEASE);
kono
parents:
diff changeset
434 else
kono
parents:
diff changeset
435 list->tasks = NULL;
kono
parents:
diff changeset
436 empty = true;
kono
parents:
diff changeset
437 goto remove_out;
kono
parents:
diff changeset
438 }
kono
parents:
diff changeset
439 }
kono
parents:
diff changeset
440 remove_out:
kono
parents:
diff changeset
441 #if _LIBGOMP_CHECKING_
kono
parents:
diff changeset
442 memset (node, 0xaf, sizeof (*node));
kono
parents:
diff changeset
443 #endif
kono
parents:
diff changeset
444 return empty;
kono
parents:
diff changeset
445 }
kono
parents:
diff changeset
446
kono
parents:
diff changeset
447 /* This is the generic version of priority_list_remove.
kono
parents:
diff changeset
448
kono
parents:
diff changeset
449 Remove NODE from priority queue HEAD. HEAD contains tasks of type TYPE.
kono
parents:
diff changeset
450
kono
parents:
diff changeset
451 If we are removing the one and only item in the priority queue and
kono
parents:
diff changeset
452 MODEL is MEMMODEL_RELEASE, use an atomic release to clear the queue.
kono
parents:
diff changeset
453
kono
parents:
diff changeset
454 If the queue becomes empty after the remove, return TRUE. */
kono
parents:
diff changeset
455
kono
parents:
diff changeset
456 static inline bool
kono
parents:
diff changeset
457 priority_queue_remove (enum priority_queue_type type,
kono
parents:
diff changeset
458 struct priority_queue *head,
kono
parents:
diff changeset
459 struct gomp_task *task,
kono
parents:
diff changeset
460 enum memmodel model)
kono
parents:
diff changeset
461 {
kono
parents:
diff changeset
462 #if _LIBGOMP_CHECKING_
kono
parents:
diff changeset
463 if (!priority_queue_task_in_queue_p (type, head, task))
kono
parents:
diff changeset
464 gomp_fatal ("Attempt to remove missing task %p", task);
kono
parents:
diff changeset
465 #endif
kono
parents:
diff changeset
466 if (priority_queue_multi_p (head))
kono
parents:
diff changeset
467 {
kono
parents:
diff changeset
468 priority_tree_remove (type, head, task_to_priority_node (type, task));
kono
parents:
diff changeset
469 if (head->t.root == NULL)
kono
parents:
diff changeset
470 {
kono
parents:
diff changeset
471 if (model == MEMMODEL_RELEASE)
kono
parents:
diff changeset
472 /* Errr, we store NULL twice, the alternative would be to
kono
parents:
diff changeset
473 use an atomic release directly in the splay tree
kono
parents:
diff changeset
474 routines. Worth it? */
kono
parents:
diff changeset
475 __atomic_store_n (&head->t.root, NULL, MEMMODEL_RELEASE);
kono
parents:
diff changeset
476 return true;
kono
parents:
diff changeset
477 }
kono
parents:
diff changeset
478 return false;
kono
parents:
diff changeset
479 }
kono
parents:
diff changeset
480 else
kono
parents:
diff changeset
481 return priority_list_remove (&head->l,
kono
parents:
diff changeset
482 task_to_priority_node (type, task), model);
kono
parents:
diff changeset
483 }
kono
parents:
diff changeset
484
kono
parents:
diff changeset
485 #endif /* _PRIORITY_QUEUE_H_ */