2019-01-01 13:31:55 +01:00
|
|
|
/* Copyright (C) 2015-2019 Free Software Foundation, Inc.
|
omp-low.c (lower_omp_ordered): Add argument to GOMP_SMD_ORDERED_* internal calls - 0 if...
gcc/
2015-11-14 Jakub Jelinek <jakub@redhat.com>
* omp-low.c (lower_omp_ordered): Add argument to GOMP_SMD_ORDERED_*
internal calls - 0 if ordered simd and 1 for ordered threads simd.
* tree-vectorizer.c (adjust_simduid_builtins): If GOMP_SIMD_ORDERED_*
argument is 1, replace it with GOMP_ordered_* call instead of removing
it.
gcc/c/
2015-11-14 Jakub Jelinek <jakub@redhat.com>
* c-typeck.c (c_finish_omp_clauses): Don't mark
GOMP_MAP_FIRSTPRIVATE_POINTER decls addressable.
gcc/cp/
2015-11-14 Jakub Jelinek <jakub@redhat.com>
* semantics.c (finish_omp_clauses): Don't mark
GOMP_MAP_FIRSTPRIVATE_POINTER decls addressable.
libgomp/
2015-11-14 Jakub Jelinek <jakub@redhat.com>
Aldy Hernandez <aldyh@redhat.com>
Ilya Verbin <ilya.verbin@intel.com>
* ordered.c (gomp_doacross_init, GOMP_doacross_post,
GOMP_doacross_wait, gomp_doacross_ull_init, GOMP_doacross_ull_post,
GOMP_doacross_ull_wait): For GFS_GUIDED don't divide number of
iterators or IV by chunk size.
* parallel.c (gomp_resolve_num_threads): Don't assume that
if thr->ts.team is non-NULL, then pool must be non-NULL.
* libgomp-plugin.h (GOMP_PLUGIN_target_task_completion): Declare.
* libgomp.map (GOMP_PLUGIN_1.1): New symbol version, export
GOMP_PLUGIN_target_task_completion.
* Makefile.am (libgomp_la_SOURCES): Add priority_queue.c.
* Makefile.in: Regenerate.
* libgomp.h: Shuffle prototypes and forward definitions around so
priority queues can be defined.
(enum gomp_task_kind): Add GOMP_TASK_ASYNC_RUNNING.
(enum gomp_target_task_state): New enum.
(struct gomp_target_task): Add state, tgt, task and team fields.
(gomp_create_target_task): Change return type to bool, add
state argument.
(gomp_target_task_fn): Change return type to bool.
(struct gomp_device_descr): Add async_run_func.
(struct gomp_task): Remove children, next_child, prev_child,
next_queue, prev_queue, next_taskgroup, prev_taskgroup.
Add pnode field.
(struct gomp_taskgroup): Remove children.
Add taskgroup_queue.
(struct gomp_team): Change task_queue type to a priority queue.
(splay_compare): Define inline.
(priority_queue_offset): New.
(priority_node_to_task): New.
(task_to_priority_node): New.
* oacc-mem.c: Do not include splay-tree.h.
* priority_queue.c: New file.
* priority_queue.h: New file.
* splay-tree.c: Do not include splay-tree.h.
(splay_tree_foreach_internal): New.
(splay_tree_foreach): New.
* splay-tree.h: Become re-entrant if splay_tree_prefix is defined.
(splay_tree_callback): Define typedef.
* target.c (splay_compare): Move to libgomp.h.
(GOMP_target): Don't adjust *thr in any way around running offloaded
task.
(GOMP_target_ext): Likewise. Handle target nowait.
(GOMP_target_update_ext, GOMP_target_enter_exit_data): Check
return value from gomp_create_target_task, if false, fallthrough
as if no dependencies exist.
(gomp_target_task_fn): Change return type to bool, return true
if the task should have another part scheduled later. Handle
target nowait.
(gomp_load_plugin_for_device): Initialize async_run.
* task.c (gomp_init_task): Initialize children_queue.
(gomp_clear_parent_in_list): New.
(gomp_clear_parent_in_tree): New.
(gomp_clear_parent): Handle priorities.
(GOMP_task): Likewise.
(priority_queue_move_task_first,
gomp_target_task_completion, GOMP_PLUGIN_target_task_completion):
New functions.
(gomp_create_target_task): Use priority queues. Change return type
to bool, add state argument, return false if for async
{{enter,exit} data,update} constructs no dependencies need to be
waited for, handle target nowait. Set task->fn to NULL instead of
gomp_target_task_fn.
(verify_children_queue): Remove.
(priority_list_upgrade_task): New.
(priority_queue_upgrade_task): New.
(verify_task_queue): Remove.
(priority_list_downgrade_task): New.
(priority_queue_downgrade_task): New.
(gomp_task_run_pre): Use priority queues.
Abstract code out to priority_queue_downgrade_task.
(gomp_task_run_post_handle_dependers): Use priority queues.
(gomp_task_run_post_remove_parent): Likewise.
(gomp_task_run_post_remove_taskgroup): Likewise.
(gomp_barrier_handle_tasks): Likewise. Handle target nowait target
tasks specially.
(GOMP_taskwait): Likewise.
(gomp_task_maybe_wait_for_dependencies): Likewise. Abstract code to
priority-queue_upgrade_task.
(GOMP_taskgroup_start): Use priority queues.
(GOMP_taskgroup_end): Likewise. Handle target nowait target tasks
specially. If taskgroup is NULL, and thr->ts.level is 0, act as a
barrier.
* taskloop.c (GOMP_taskloop): Handle priorities.
* team.c (gomp_new_team): Call priority_queue_init.
(free_team): Call priority_queue_free.
(gomp_free_thread): Call gomp_team_end if thr->ts.team is artificial
team created for target nowait in implicit parallel region.
(gomp_team_start): For nested check, test thr->ts.level instead of
thr->ts.team != NULL.
* testsuite/libgomp.c/doacross-3.c: New test.
* testsuite/libgomp.c/ordered-5.c: New test.
* testsuite/libgomp.c/priority.c: New test.
* testsuite/libgomp.c/target-31.c: New test.
* testsuite/libgomp.c/target-32.c: New test.
* testsuite/libgomp.c/target-33.c: New test.
* testsuite/libgomp.c/target-34.c: New test.
liboffloadmic/
2015-11-14 Ilya Verbin <ilya.verbin@intel.com>
* runtime/offload_host.cpp (task_completion_callback): New
variable.
(offload_proxy_task_completed_ooo): Call task_completion_callback.
(__offload_register_task_callback): New function.
* runtime/offload_host.h (__offload_register_task_callback): New
declaration.
* plugin/libgomp-plugin-intelmic.cpp (offload): Add async_data
argument, handle async offloading.
(register_main_image): Call register_main_image.
(GOMP_OFFLOAD_init_device, get_target_table, GOMP_OFFLOAD_alloc,
GOMP_OFFLOAD_free, GOMP_OFFLOAD_host2dev, GOMP_OFFLOAD_dev2host,
GOMP_OFFLOAD_dev2dev) Adjust offload callers.
(GOMP_OFFLOAD_async_run): New function.
(GOMP_OFFLOAD_run): Implement using GOMP_OFFLOAD_async_run.
From-SVN: r230381
2015-11-14 19:42:13 +01:00
|
|
|
Contributed by Aldy Hernandez <aldyh@redhat.com>.
|
|
|
|
|
|
|
|
This file is part of the GNU Offloading and Multi Processing Library
|
|
|
|
(libgomp).
|
|
|
|
|
|
|
|
Libgomp is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
more details.
|
|
|
|
|
|
|
|
Under Section 7 of GPL version 3, you are granted additional
|
|
|
|
permissions described in the GCC Runtime Library Exception, version
|
|
|
|
3.1, as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License and
|
|
|
|
a copy of the GCC Runtime Library Exception along with this program;
|
|
|
|
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
/* Header file for a priority queue of GOMP tasks. */
|
|
|
|
|
|
|
|
/* ?? Perhaps all the priority_tree_* functions are complex and rare
|
|
|
|
enough to go out-of-line and be moved to priority_queue.c. ?? */
|
|
|
|
|
|
|
|
#ifndef _PRIORITY_QUEUE_H_
|
|
|
|
#define _PRIORITY_QUEUE_H_
|
|
|
|
|
|
|
|
/* One task. */
|
|
|
|
|
|
|
|
struct priority_node
|
|
|
|
{
|
|
|
|
/* Next and previous chains in a circular doubly linked list for
|
|
|
|
tasks within this task's priority. */
|
|
|
|
struct priority_node *next, *prev;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* All tasks within the same priority. */
|
|
|
|
|
|
|
|
struct priority_list
|
|
|
|
{
|
|
|
|
/* Priority of the tasks in this set. */
|
|
|
|
int priority;
|
|
|
|
|
|
|
|
/* Tasks. */
|
|
|
|
struct priority_node *tasks;
|
|
|
|
|
|
|
|
/* This points to the last of the higher priority WAITING tasks.
|
|
|
|
Remember that for the children queue, we have:
|
|
|
|
|
|
|
|
parent_depends_on WAITING tasks.
|
|
|
|
!parent_depends_on WAITING tasks.
|
|
|
|
TIED tasks.
|
|
|
|
|
|
|
|
This is a pointer to the last of the parent_depends_on WAITING
|
|
|
|
tasks which are essentially, higher priority items within their
|
|
|
|
priority. */
|
|
|
|
struct priority_node *last_parent_depends_on;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Another splay tree instantiation, for priority_list's. */
|
|
|
|
typedef struct prio_splay_tree_node_s *prio_splay_tree_node;
|
|
|
|
typedef struct prio_splay_tree_s *prio_splay_tree;
|
|
|
|
typedef struct prio_splay_tree_key_s *prio_splay_tree_key;
|
|
|
|
struct prio_splay_tree_key_s {
|
|
|
|
/* This structure must only containing a priority_list, as we cast
|
|
|
|
prio_splay_tree_key to priority_list throughout. */
|
|
|
|
struct priority_list l;
|
|
|
|
};
|
|
|
|
#define splay_tree_prefix prio
|
|
|
|
#include "splay-tree.h"
|
|
|
|
|
|
|
|
/* The entry point into a priority queue of tasks.
|
|
|
|
|
|
|
|
There are two alternate implementations with which to store tasks:
|
|
|
|
as a balanced tree of sorts, or as a simple list of tasks. If
|
|
|
|
there are only priority-0 items (ROOT is NULL), we use the simple
|
|
|
|
list, otherwise (ROOT is non-NULL) we use the tree. */
|
|
|
|
|
|
|
|
struct priority_queue
|
|
|
|
{
|
|
|
|
/* If t.root != NULL, this is a splay tree of priority_lists to hold
|
|
|
|
all tasks. This is only used if multiple priorities are in play,
|
|
|
|
otherwise we use the priority_list `l' below to hold all
|
|
|
|
(priority-0) tasks. */
|
|
|
|
struct prio_splay_tree_s t;
|
|
|
|
|
|
|
|
/* If T above is NULL, only priority-0 items exist, so keep them
|
|
|
|
in a simple list. */
|
|
|
|
struct priority_list l;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum priority_insert_type {
|
|
|
|
/* Insert at the beginning of a priority list. */
|
|
|
|
PRIORITY_INSERT_BEGIN,
|
|
|
|
/* Insert at the end of a priority list. */
|
|
|
|
PRIORITY_INSERT_END
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Used to determine in which queue a given priority node belongs in.
|
|
|
|
See pnode field of gomp_task. */
|
|
|
|
|
|
|
|
enum priority_queue_type
|
|
|
|
{
|
|
|
|
PQ_TEAM, /* Node belongs in gomp_team's task_queue. */
|
|
|
|
PQ_CHILDREN, /* Node belongs in parent's children_queue. */
|
|
|
|
PQ_TASKGROUP, /* Node belongs in taskgroup->taskgroup_queue. */
|
|
|
|
PQ_IGNORED = 999
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Priority queue implementation prototypes. */
|
|
|
|
|
|
|
|
extern bool priority_queue_task_in_queue_p (enum priority_queue_type,
|
|
|
|
struct priority_queue *,
|
|
|
|
struct gomp_task *);
|
|
|
|
extern void priority_queue_dump (enum priority_queue_type,
|
|
|
|
struct priority_queue *);
|
|
|
|
extern void priority_queue_verify (enum priority_queue_type,
|
|
|
|
struct priority_queue *, bool);
|
|
|
|
extern void priority_tree_remove (enum priority_queue_type,
|
|
|
|
struct priority_queue *,
|
|
|
|
struct priority_node *);
|
|
|
|
extern struct gomp_task *priority_tree_next_task (enum priority_queue_type,
|
|
|
|
struct priority_queue *,
|
|
|
|
enum priority_queue_type,
|
|
|
|
struct priority_queue *,
|
|
|
|
bool *);
|
|
|
|
|
|
|
|
/* Return TRUE if there is more than one priority in HEAD. This is
|
|
|
|
used throughout to to choose between the fast path (priority 0 only
|
|
|
|
items) and a world with multiple priorities. */
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
priority_queue_multi_p (struct priority_queue *head)
|
|
|
|
{
|
|
|
|
return __builtin_expect (head->t.root != NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize a priority queue. */
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
priority_queue_init (struct priority_queue *head)
|
|
|
|
{
|
|
|
|
head->t.root = NULL;
|
|
|
|
/* To save a few microseconds, we don't initialize head->l.priority
|
|
|
|
to 0 here. It is implied that priority will be 0 if head->t.root
|
|
|
|
== NULL.
|
|
|
|
|
|
|
|
priority_tree_insert() will fix this when we encounter multiple
|
|
|
|
priorities. */
|
|
|
|
head->l.tasks = NULL;
|
|
|
|
head->l.last_parent_depends_on = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
priority_queue_free (struct priority_queue *head)
|
|
|
|
{
|
|
|
|
/* There's nothing to do, as tasks were freed as they were removed
|
|
|
|
in priority_queue_remove. */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Forward declarations. */
|
|
|
|
static inline size_t priority_queue_offset (enum priority_queue_type);
|
|
|
|
static inline struct gomp_task *priority_node_to_task
|
|
|
|
(enum priority_queue_type,
|
|
|
|
struct priority_node *);
|
|
|
|
static inline struct priority_node *task_to_priority_node
|
|
|
|
(enum priority_queue_type,
|
|
|
|
struct gomp_task *);
|
|
|
|
|
|
|
|
/* Return TRUE if priority queue HEAD is empty.
|
|
|
|
|
|
|
|
MODEL IS MEMMODEL_ACQUIRE if we should use an acquire atomic to
|
|
|
|
read from the root of the queue, otherwise MEMMODEL_RELAXED if we
|
|
|
|
should use a plain load. */
|
|
|
|
|
|
|
|
static inline _Bool
|
|
|
|
priority_queue_empty_p (struct priority_queue *head, enum memmodel model)
|
|
|
|
{
|
|
|
|
/* Note: The acquire barriers on the loads here synchronize with
|
|
|
|
the write of a NULL in gomp_task_run_post_remove_parent. It is
|
|
|
|
not necessary that we synchronize with other non-NULL writes at
|
|
|
|
this point, but we must ensure that all writes to memory by a
|
|
|
|
child thread task work function are seen before we exit from
|
|
|
|
GOMP_taskwait. */
|
|
|
|
if (priority_queue_multi_p (head))
|
|
|
|
{
|
|
|
|
if (model == MEMMODEL_ACQUIRE)
|
|
|
|
return __atomic_load_n (&head->t.root, MEMMODEL_ACQUIRE) == NULL;
|
|
|
|
return head->t.root == NULL;
|
|
|
|
}
|
|
|
|
if (model == MEMMODEL_ACQUIRE)
|
|
|
|
return __atomic_load_n (&head->l.tasks, MEMMODEL_ACQUIRE) == NULL;
|
|
|
|
return head->l.tasks == NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look for a given PRIORITY in HEAD. Return it if found, otherwise
|
|
|
|
return NULL. This only applies to the tree variant in HEAD. There
|
|
|
|
is no point in searching for priorities in HEAD->L. */
|
|
|
|
|
|
|
|
static inline struct priority_list *
|
|
|
|
priority_queue_lookup_priority (struct priority_queue *head, int priority)
|
|
|
|
{
|
|
|
|
if (head->t.root == NULL)
|
|
|
|
return NULL;
|
|
|
|
struct prio_splay_tree_key_s k;
|
|
|
|
k.l.priority = priority;
|
|
|
|
return (struct priority_list *)
|
|
|
|
prio_splay_tree_lookup (&head->t, &k);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Insert task in DATA, with PRIORITY, in the priority list in LIST.
|
|
|
|
LIST contains items of type TYPE.
|
|
|
|
|
|
|
|
If POS is PRIORITY_INSERT_BEGIN, the new task is inserted at the
|
|
|
|
top of its respective priority. If POS is PRIORITY_INSERT_END, the
|
|
|
|
task is inserted at the end of its priority.
|
|
|
|
|
|
|
|
If ADJUST_PARENT_DEPENDS_ON is TRUE, LIST is a children queue, and
|
|
|
|
we must keep track of higher and lower priority WAITING tasks by
|
|
|
|
keeping the queue's last_parent_depends_on field accurate. This
|
|
|
|
only applies to the children queue, and the caller must ensure LIST
|
|
|
|
is a children queue in this case.
|
|
|
|
|
|
|
|
If ADJUST_PARENT_DEPENDS_ON is TRUE, TASK_IS_PARENT_DEPENDS_ON is
|
|
|
|
set to the task's parent_depends_on field. If
|
|
|
|
ADJUST_PARENT_DEPENDS_ON is FALSE, this field is irrelevant.
|
|
|
|
|
|
|
|
Return the new priority_node. */
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
priority_list_insert (enum priority_queue_type type,
|
|
|
|
struct priority_list *list,
|
|
|
|
struct gomp_task *task,
|
|
|
|
int priority,
|
|
|
|
enum priority_insert_type pos,
|
|
|
|
bool adjust_parent_depends_on,
|
|
|
|
bool task_is_parent_depends_on)
|
|
|
|
{
|
|
|
|
struct priority_node *node = task_to_priority_node (type, task);
|
|
|
|
if (list->tasks)
|
|
|
|
{
|
|
|
|
/* If we are keeping track of higher/lower priority items,
|
|
|
|
but this is a lower priority WAITING task
|
|
|
|
(parent_depends_on != NULL), put it after all ready to
|
|
|
|
run tasks. See the comment in
|
|
|
|
priority_queue_upgrade_task for a visual on how tasks
|
|
|
|
should be organized. */
|
|
|
|
if (adjust_parent_depends_on
|
|
|
|
&& pos == PRIORITY_INSERT_BEGIN
|
|
|
|
&& list->last_parent_depends_on
|
|
|
|
&& !task_is_parent_depends_on)
|
|
|
|
{
|
|
|
|
struct priority_node *last_parent_depends_on
|
|
|
|
= list->last_parent_depends_on;
|
|
|
|
node->next = last_parent_depends_on->next;
|
|
|
|
node->prev = last_parent_depends_on;
|
|
|
|
}
|
|
|
|
/* Otherwise, put it at the top/bottom of the queue. */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
node->next = list->tasks;
|
|
|
|
node->prev = list->tasks->prev;
|
|
|
|
if (pos == PRIORITY_INSERT_BEGIN)
|
|
|
|
list->tasks = node;
|
|
|
|
}
|
|
|
|
node->next->prev = node;
|
|
|
|
node->prev->next = node;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
node->next = node;
|
|
|
|
node->prev = node;
|
|
|
|
list->tasks = node;
|
|
|
|
}
|
|
|
|
if (adjust_parent_depends_on
|
|
|
|
&& list->last_parent_depends_on == NULL
|
|
|
|
&& task_is_parent_depends_on)
|
|
|
|
list->last_parent_depends_on = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tree version of priority_list_insert. */
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
priority_tree_insert (enum priority_queue_type type,
|
|
|
|
struct priority_queue *head,
|
|
|
|
struct gomp_task *task,
|
|
|
|
int priority,
|
|
|
|
enum priority_insert_type pos,
|
|
|
|
bool adjust_parent_depends_on,
|
|
|
|
bool task_is_parent_depends_on)
|
|
|
|
{
|
|
|
|
if (__builtin_expect (head->t.root == NULL, 0))
|
|
|
|
{
|
|
|
|
/* The first time around, transfer any priority 0 items to the
|
|
|
|
tree. */
|
|
|
|
if (head->l.tasks != NULL)
|
|
|
|
{
|
|
|
|
prio_splay_tree_node k = gomp_malloc (sizeof (*k));
|
|
|
|
k->left = NULL;
|
|
|
|
k->right = NULL;
|
|
|
|
k->key.l.priority = 0;
|
|
|
|
k->key.l.tasks = head->l.tasks;
|
|
|
|
k->key.l.last_parent_depends_on = head->l.last_parent_depends_on;
|
|
|
|
prio_splay_tree_insert (&head->t, k);
|
|
|
|
head->l.tasks = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
struct priority_list *list
|
|
|
|
= priority_queue_lookup_priority (head, priority);
|
|
|
|
if (!list)
|
|
|
|
{
|
|
|
|
prio_splay_tree_node k = gomp_malloc (sizeof (*k));
|
|
|
|
k->left = NULL;
|
|
|
|
k->right = NULL;
|
|
|
|
k->key.l.priority = priority;
|
|
|
|
k->key.l.tasks = NULL;
|
|
|
|
k->key.l.last_parent_depends_on = NULL;
|
|
|
|
prio_splay_tree_insert (&head->t, k);
|
|
|
|
list = &k->key.l;
|
|
|
|
}
|
|
|
|
priority_list_insert (type, list, task, priority, pos,
|
|
|
|
adjust_parent_depends_on,
|
|
|
|
task_is_parent_depends_on);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generic version of priority_*_insert. */
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
priority_queue_insert (enum priority_queue_type type,
|
|
|
|
struct priority_queue *head,
|
|
|
|
struct gomp_task *task,
|
|
|
|
int priority,
|
|
|
|
enum priority_insert_type pos,
|
|
|
|
bool adjust_parent_depends_on,
|
|
|
|
bool task_is_parent_depends_on)
|
|
|
|
{
|
|
|
|
#if _LIBGOMP_CHECKING_
|
|
|
|
if (priority_queue_task_in_queue_p (type, head, task))
|
|
|
|
gomp_fatal ("Attempt to insert existing task %p", task);
|
|
|
|
#endif
|
|
|
|
if (priority_queue_multi_p (head) || __builtin_expect (priority > 0, 0))
|
|
|
|
priority_tree_insert (type, head, task, priority, pos,
|
|
|
|
adjust_parent_depends_on,
|
|
|
|
task_is_parent_depends_on);
|
|
|
|
else
|
|
|
|
priority_list_insert (type, &head->l, task, priority, pos,
|
|
|
|
adjust_parent_depends_on,
|
|
|
|
task_is_parent_depends_on);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If multiple priorities are in play, return the highest priority
|
|
|
|
task from within Q1 and Q2, while giving preference to tasks from
|
|
|
|
Q1. If the returned task is chosen from Q1, *Q1_CHOSEN_P is set to
|
|
|
|
TRUE, otherwise it is set to FALSE.
|
|
|
|
|
|
|
|
If multiple priorities are not in play (only 0 priorities are
|
|
|
|
available), the next task is chosen exclusively from Q1.
|
|
|
|
|
|
|
|
As a special case, Q2 can be NULL, in which case, we just choose
|
|
|
|
the highest priority WAITING task in Q1. This is an optimization
|
|
|
|
to speed up looking through only one queue.
|
|
|
|
|
|
|
|
We assume Q1 has at least one item. */
|
|
|
|
|
|
|
|
static inline struct gomp_task *
|
|
|
|
priority_queue_next_task (enum priority_queue_type t1,
|
|
|
|
struct priority_queue *q1,
|
|
|
|
enum priority_queue_type t2,
|
|
|
|
struct priority_queue *q2,
|
|
|
|
bool *q1_chosen_p)
|
|
|
|
{
|
|
|
|
#if _LIBGOMP_CHECKING_
|
|
|
|
if (priority_queue_empty_p (q1, MEMMODEL_RELAXED))
|
|
|
|
gomp_fatal ("priority_queue_next_task: Q1 is empty");
|
|
|
|
#endif
|
|
|
|
if (priority_queue_multi_p (q1))
|
|
|
|
{
|
|
|
|
struct gomp_task *t
|
|
|
|
= priority_tree_next_task (t1, q1, t2, q2, q1_chosen_p);
|
|
|
|
/* If T is NULL, there are no WAITING tasks in Q1. In which
|
|
|
|
case, return any old (non-waiting) task which will cause the
|
|
|
|
caller to do the right thing when checking T->KIND ==
|
|
|
|
GOMP_TASK_WAITING. */
|
|
|
|
if (!t)
|
|
|
|
{
|
|
|
|
#if _LIBGOMP_CHECKING_
|
|
|
|
if (*q1_chosen_p == false)
|
|
|
|
gomp_fatal ("priority_queue_next_task inconsistency");
|
|
|
|
#endif
|
|
|
|
return priority_node_to_task (t1, q1->t.root->key.l.tasks);
|
|
|
|
}
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*q1_chosen_p = true;
|
|
|
|
return priority_node_to_task (t1, q1->l.tasks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove NODE from LIST.
|
|
|
|
|
|
|
|
If we are removing the one and only item in the list, and MODEL is
|
|
|
|
MEMMODEL_RELEASE, use an atomic release to clear the list.
|
|
|
|
|
|
|
|
If the list becomes empty after the remove, return TRUE. */
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
priority_list_remove (struct priority_list *list,
|
|
|
|
struct priority_node *node,
|
|
|
|
enum memmodel model)
|
|
|
|
{
|
|
|
|
bool empty = false;
|
|
|
|
node->prev->next = node->next;
|
|
|
|
node->next->prev = node->prev;
|
|
|
|
if (list->tasks == node)
|
|
|
|
{
|
|
|
|
if (node->next != node)
|
|
|
|
list->tasks = node->next;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* We access task->children in GOMP_taskwait outside of
|
|
|
|
the task lock mutex region, so need a release barrier
|
|
|
|
here to ensure memory written by child_task->fn above
|
|
|
|
is flushed before the NULL is written. */
|
|
|
|
if (model == MEMMODEL_RELEASE)
|
|
|
|
__atomic_store_n (&list->tasks, NULL, MEMMODEL_RELEASE);
|
|
|
|
else
|
|
|
|
list->tasks = NULL;
|
|
|
|
empty = true;
|
|
|
|
goto remove_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
remove_out:
|
|
|
|
#if _LIBGOMP_CHECKING_
|
|
|
|
memset (node, 0xaf, sizeof (*node));
|
|
|
|
#endif
|
|
|
|
return empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is the generic version of priority_list_remove.
|
|
|
|
|
|
|
|
Remove NODE from priority queue HEAD. HEAD contains tasks of type TYPE.
|
|
|
|
|
|
|
|
If we are removing the one and only item in the priority queue and
|
|
|
|
MODEL is MEMMODEL_RELEASE, use an atomic release to clear the queue.
|
|
|
|
|
|
|
|
If the queue becomes empty after the remove, return TRUE. */
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
priority_queue_remove (enum priority_queue_type type,
|
|
|
|
struct priority_queue *head,
|
|
|
|
struct gomp_task *task,
|
|
|
|
enum memmodel model)
|
|
|
|
{
|
|
|
|
#if _LIBGOMP_CHECKING_
|
|
|
|
if (!priority_queue_task_in_queue_p (type, head, task))
|
|
|
|
gomp_fatal ("Attempt to remove missing task %p", task);
|
|
|
|
#endif
|
|
|
|
if (priority_queue_multi_p (head))
|
|
|
|
{
|
|
|
|
priority_tree_remove (type, head, task_to_priority_node (type, task));
|
|
|
|
if (head->t.root == NULL)
|
|
|
|
{
|
|
|
|
if (model == MEMMODEL_RELEASE)
|
|
|
|
/* Errr, we store NULL twice, the alternative would be to
|
|
|
|
use an atomic release directly in the splay tree
|
|
|
|
routines. Worth it? */
|
|
|
|
__atomic_store_n (&head->t.root, NULL, MEMMODEL_RELEASE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return priority_list_remove (&head->l,
|
|
|
|
task_to_priority_node (type, task), model);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* _PRIORITY_QUEUE_H_ */
|