12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217 |
- /* Copyright (C) 2007-2015 Free Software Foundation, Inc.
- Contributed by Richard Henderson <rth@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/>. */
- /* This file handles the maintainence of tasks in response to task
- creation and termination. */
- #include "libgomp.h"
- #include <stdlib.h>
- #include <string.h>
- typedef struct gomp_task_depend_entry *hash_entry_type;
- static inline void *
- htab_alloc (size_t size)
- {
- return gomp_malloc (size);
- }
- static inline void
- htab_free (void *ptr)
- {
- free (ptr);
- }
- #include "hashtab.h"
- static inline hashval_t
- htab_hash (hash_entry_type element)
- {
- return hash_pointer (element->addr);
- }
- static inline bool
- htab_eq (hash_entry_type x, hash_entry_type y)
- {
- return x->addr == y->addr;
- }
- /* Create a new task data structure. */
- void
- gomp_init_task (struct gomp_task *task, struct gomp_task *parent_task,
- struct gomp_task_icv *prev_icv)
- {
- task->parent = parent_task;
- task->icv = *prev_icv;
- task->kind = GOMP_TASK_IMPLICIT;
- task->taskwait = NULL;
- task->in_tied_task = false;
- task->final_task = false;
- task->copy_ctors_done = false;
- task->parent_depends_on = false;
- task->children = NULL;
- task->taskgroup = NULL;
- task->dependers = NULL;
- task->depend_hash = NULL;
- task->depend_count = 0;
- }
- /* Clean up a task, after completing it. */
- void
- gomp_end_task (void)
- {
- struct gomp_thread *thr = gomp_thread ();
- struct gomp_task *task = thr->task;
- gomp_finish_task (task);
- thr->task = task->parent;
- }
- static inline void
- gomp_clear_parent (struct gomp_task *children)
- {
- struct gomp_task *task = children;
- if (task)
- do
- {
- task->parent = NULL;
- task = task->next_child;
- }
- while (task != children);
- }
- static void gomp_task_maybe_wait_for_dependencies (void **depend);
- /* Called when encountering an explicit task directive. If IF_CLAUSE is
- false, then we must not delay in executing the task. If UNTIED is true,
- then the task may be executed by any member of the team. */
- void
- GOMP_task (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *),
- long arg_size, long arg_align, bool if_clause, unsigned flags,
- void **depend)
- {
- struct gomp_thread *thr = gomp_thread ();
- struct gomp_team *team = thr->ts.team;
- #ifdef HAVE_BROKEN_POSIX_SEMAPHORES
- /* If pthread_mutex_* is used for omp_*lock*, then each task must be
- tied to one thread all the time. This means UNTIED tasks must be
- tied and if CPYFN is non-NULL IF(0) must be forced, as CPYFN
- might be running on different thread than FN. */
- if (cpyfn)
- if_clause = false;
- if (flags & 1)
- flags &= ~1;
- #endif
- /* If parallel or taskgroup has been cancelled, don't start new tasks. */
- if (team
- && (gomp_team_barrier_cancelled (&team->barrier)
- || (thr->task->taskgroup && thr->task->taskgroup->cancelled)))
- return;
- if (!if_clause || team == NULL
- || (thr->task && thr->task->final_task)
- || team->task_count > 64 * team->nthreads)
- {
- struct gomp_task task;
- /* If there are depend clauses and earlier deferred sibling tasks
- with depend clauses, check if there isn't a dependency. If there
- is, we need to wait for them. There is no need to handle
- depend clauses for non-deferred tasks other than this, because
- the parent task is suspended until the child task finishes and thus
- it can't start further child tasks. */
- if ((flags & 8) && thr->task && thr->task->depend_hash)
- gomp_task_maybe_wait_for_dependencies (depend);
- gomp_init_task (&task, thr->task, gomp_icv (false));
- task.kind = GOMP_TASK_IFFALSE;
- task.final_task = (thr->task && thr->task->final_task) || (flags & 2);
- if (thr->task)
- {
- task.in_tied_task = thr->task->in_tied_task;
- task.taskgroup = thr->task->taskgroup;
- }
- thr->task = &task;
- if (__builtin_expect (cpyfn != NULL, 0))
- {
- char buf[arg_size + arg_align - 1];
- char *arg = (char *) (((uintptr_t) buf + arg_align - 1)
- & ~(uintptr_t) (arg_align - 1));
- cpyfn (arg, data);
- fn (arg);
- }
- else
- fn (data);
- /* Access to "children" is normally done inside a task_lock
- mutex region, but the only way this particular task.children
- can be set is if this thread's task work function (fn)
- creates children. So since the setter is *this* thread, we
- need no barriers here when testing for non-NULL. We can have
- task.children set by the current thread then changed by a
- child thread, but seeing a stale non-NULL value is not a
- problem. Once past the task_lock acquisition, this thread
- will see the real value of task.children. */
- if (task.children != NULL)
- {
- gomp_mutex_lock (&team->task_lock);
- gomp_clear_parent (task.children);
- gomp_mutex_unlock (&team->task_lock);
- }
- gomp_end_task ();
- }
- else
- {
- struct gomp_task *task;
- struct gomp_task *parent = thr->task;
- struct gomp_taskgroup *taskgroup = parent->taskgroup;
- char *arg;
- bool do_wake;
- size_t depend_size = 0;
- if (flags & 8)
- depend_size = ((uintptr_t) depend[0]
- * sizeof (struct gomp_task_depend_entry));
- task = gomp_malloc (sizeof (*task) + depend_size
- + arg_size + arg_align - 1);
- arg = (char *) (((uintptr_t) (task + 1) + depend_size + arg_align - 1)
- & ~(uintptr_t) (arg_align - 1));
- gomp_init_task (task, parent, gomp_icv (false));
- task->kind = GOMP_TASK_IFFALSE;
- task->in_tied_task = parent->in_tied_task;
- task->taskgroup = taskgroup;
- thr->task = task;
- if (cpyfn)
- {
- cpyfn (arg, data);
- task->copy_ctors_done = true;
- }
- else
- memcpy (arg, data, arg_size);
- thr->task = parent;
- task->kind = GOMP_TASK_WAITING;
- task->fn = fn;
- task->fn_data = arg;
- task->final_task = (flags & 2) >> 1;
- gomp_mutex_lock (&team->task_lock);
- /* If parallel or taskgroup has been cancelled, don't start new
- tasks. */
- if (__builtin_expect ((gomp_team_barrier_cancelled (&team->barrier)
- || (taskgroup && taskgroup->cancelled))
- && !task->copy_ctors_done, 0))
- {
- gomp_mutex_unlock (&team->task_lock);
- gomp_finish_task (task);
- free (task);
- return;
- }
- if (taskgroup)
- taskgroup->num_children++;
- if (depend_size)
- {
- size_t ndepend = (uintptr_t) depend[0];
- size_t nout = (uintptr_t) depend[1];
- size_t i;
- hash_entry_type ent;
- task->depend_count = ndepend;
- task->num_dependees = 0;
- if (parent->depend_hash == NULL)
- parent->depend_hash
- = htab_create (2 * ndepend > 12 ? 2 * ndepend : 12);
- for (i = 0; i < ndepend; i++)
- {
- task->depend[i].addr = depend[2 + i];
- task->depend[i].next = NULL;
- task->depend[i].prev = NULL;
- task->depend[i].task = task;
- task->depend[i].is_in = i >= nout;
- task->depend[i].redundant = false;
- task->depend[i].redundant_out = false;
- hash_entry_type *slot
- = htab_find_slot (&parent->depend_hash, &task->depend[i],
- INSERT);
- hash_entry_type out = NULL, last = NULL;
- if (*slot)
- {
- /* If multiple depends on the same task are the
- same, all but the first one are redundant.
- As inout/out come first, if any of them is
- inout/out, it will win, which is the right
- semantics. */
- if ((*slot)->task == task)
- {
- task->depend[i].redundant = true;
- continue;
- }
- for (ent = *slot; ent; ent = ent->next)
- {
- if (ent->redundant_out)
- break;
- last = ent;
- /* depend(in:...) doesn't depend on earlier
- depend(in:...). */
- if (i >= nout && ent->is_in)
- continue;
- if (!ent->is_in)
- out = ent;
- struct gomp_task *tsk = ent->task;
- if (tsk->dependers == NULL)
- {
- tsk->dependers
- = gomp_malloc (sizeof (struct gomp_dependers_vec)
- + 6 * sizeof (struct gomp_task *));
- tsk->dependers->n_elem = 1;
- tsk->dependers->allocated = 6;
- tsk->dependers->elem[0] = task;
- task->num_dependees++;
- continue;
- }
- /* We already have some other dependency on tsk
- from earlier depend clause. */
- else if (tsk->dependers->n_elem
- && (tsk->dependers->elem[tsk->dependers->n_elem
- - 1]
- == task))
- continue;
- else if (tsk->dependers->n_elem
- == tsk->dependers->allocated)
- {
- tsk->dependers->allocated
- = tsk->dependers->allocated * 2 + 2;
- tsk->dependers
- = gomp_realloc (tsk->dependers,
- sizeof (struct gomp_dependers_vec)
- + (tsk->dependers->allocated
- * sizeof (struct gomp_task *)));
- }
- tsk->dependers->elem[tsk->dependers->n_elem++] = task;
- task->num_dependees++;
- }
- task->depend[i].next = *slot;
- (*slot)->prev = &task->depend[i];
- }
- *slot = &task->depend[i];
- /* There is no need to store more than one depend({,in}out:)
- task per address in the hash table chain for the purpose
- of creation of deferred tasks, because each out
- depends on all earlier outs, thus it is enough to record
- just the last depend({,in}out:). For depend(in:), we need
- to keep all of the previous ones not terminated yet, because
- a later depend({,in}out:) might need to depend on all of
- them. So, if the new task's clause is depend({,in}out:),
- we know there is at most one other depend({,in}out:) clause
- in the list (out). For non-deferred tasks we want to see
- all outs, so they are moved to the end of the chain,
- after first redundant_out entry all following entries
- should be redundant_out. */
- if (!task->depend[i].is_in && out)
- {
- if (out != last)
- {
- out->next->prev = out->prev;
- out->prev->next = out->next;
- out->next = last->next;
- out->prev = last;
- last->next = out;
- if (out->next)
- out->next->prev = out;
- }
- out->redundant_out = true;
- }
- }
- if (task->num_dependees)
- {
- gomp_mutex_unlock (&team->task_lock);
- return;
- }
- }
- if (parent->children)
- {
- task->next_child = parent->children;
- task->prev_child = parent->children->prev_child;
- task->next_child->prev_child = task;
- task->prev_child->next_child = task;
- }
- else
- {
- task->next_child = task;
- task->prev_child = task;
- }
- parent->children = task;
- if (taskgroup)
- {
- if (taskgroup->children)
- {
- task->next_taskgroup = taskgroup->children;
- task->prev_taskgroup = taskgroup->children->prev_taskgroup;
- task->next_taskgroup->prev_taskgroup = task;
- task->prev_taskgroup->next_taskgroup = task;
- }
- else
- {
- task->next_taskgroup = task;
- task->prev_taskgroup = task;
- }
- taskgroup->children = task;
- }
- if (team->task_queue)
- {
- task->next_queue = team->task_queue;
- task->prev_queue = team->task_queue->prev_queue;
- task->next_queue->prev_queue = task;
- task->prev_queue->next_queue = task;
- }
- else
- {
- task->next_queue = task;
- task->prev_queue = task;
- team->task_queue = task;
- }
- ++team->task_count;
- ++team->task_queued_count;
- gomp_team_barrier_set_task_pending (&team->barrier);
- do_wake = team->task_running_count + !parent->in_tied_task
- < team->nthreads;
- gomp_mutex_unlock (&team->task_lock);
- if (do_wake)
- gomp_team_barrier_wake (&team->barrier, 1);
- }
- }
- static inline bool
- gomp_task_run_pre (struct gomp_task *child_task, struct gomp_task *parent,
- struct gomp_taskgroup *taskgroup, struct gomp_team *team)
- {
- if (parent)
- {
- if (parent->children == child_task)
- parent->children = child_task->next_child;
- if (__builtin_expect (child_task->parent_depends_on, 0)
- && parent->taskwait->last_parent_depends_on == child_task)
- {
- if (child_task->prev_child->kind == GOMP_TASK_WAITING
- && child_task->prev_child->parent_depends_on)
- parent->taskwait->last_parent_depends_on = child_task->prev_child;
- else
- parent->taskwait->last_parent_depends_on = NULL;
- }
- }
- if (taskgroup && taskgroup->children == child_task)
- taskgroup->children = child_task->next_taskgroup;
- child_task->prev_queue->next_queue = child_task->next_queue;
- child_task->next_queue->prev_queue = child_task->prev_queue;
- if (team->task_queue == child_task)
- {
- if (child_task->next_queue != child_task)
- team->task_queue = child_task->next_queue;
- else
- team->task_queue = NULL;
- }
- child_task->kind = GOMP_TASK_TIED;
- if (--team->task_queued_count == 0)
- gomp_team_barrier_clear_task_pending (&team->barrier);
- if ((gomp_team_barrier_cancelled (&team->barrier)
- || (taskgroup && taskgroup->cancelled))
- && !child_task->copy_ctors_done)
- return true;
- return false;
- }
- static void
- gomp_task_run_post_handle_depend_hash (struct gomp_task *child_task)
- {
- struct gomp_task *parent = child_task->parent;
- size_t i;
- for (i = 0; i < child_task->depend_count; i++)
- if (!child_task->depend[i].redundant)
- {
- if (child_task->depend[i].next)
- child_task->depend[i].next->prev = child_task->depend[i].prev;
- if (child_task->depend[i].prev)
- child_task->depend[i].prev->next = child_task->depend[i].next;
- else
- {
- hash_entry_type *slot
- = htab_find_slot (&parent->depend_hash, &child_task->depend[i],
- NO_INSERT);
- if (*slot != &child_task->depend[i])
- abort ();
- if (child_task->depend[i].next)
- *slot = child_task->depend[i].next;
- else
- htab_clear_slot (parent->depend_hash, slot);
- }
- }
- }
- static size_t
- gomp_task_run_post_handle_dependers (struct gomp_task *child_task,
- struct gomp_team *team)
- {
- struct gomp_task *parent = child_task->parent;
- size_t i, count = child_task->dependers->n_elem, ret = 0;
- for (i = 0; i < count; i++)
- {
- struct gomp_task *task = child_task->dependers->elem[i];
- if (--task->num_dependees != 0)
- continue;
- struct gomp_taskgroup *taskgroup = task->taskgroup;
- if (parent)
- {
- if (parent->children)
- {
- /* If parent is in gomp_task_maybe_wait_for_dependencies
- and it doesn't need to wait for this task, put it after
- all ready to run tasks it needs to wait for. */
- if (parent->taskwait && parent->taskwait->last_parent_depends_on
- && !task->parent_depends_on)
- {
- struct gomp_task *last_parent_depends_on
- = parent->taskwait->last_parent_depends_on;
- task->next_child = last_parent_depends_on->next_child;
- task->prev_child = last_parent_depends_on;
- }
- else
- {
- task->next_child = parent->children;
- task->prev_child = parent->children->prev_child;
- parent->children = task;
- }
- task->next_child->prev_child = task;
- task->prev_child->next_child = task;
- }
- else
- {
- task->next_child = task;
- task->prev_child = task;
- parent->children = task;
- }
- if (parent->taskwait)
- {
- if (parent->taskwait->in_taskwait)
- {
- parent->taskwait->in_taskwait = false;
- gomp_sem_post (&parent->taskwait->taskwait_sem);
- }
- else if (parent->taskwait->in_depend_wait)
- {
- parent->taskwait->in_depend_wait = false;
- gomp_sem_post (&parent->taskwait->taskwait_sem);
- }
- if (parent->taskwait->last_parent_depends_on == NULL
- && task->parent_depends_on)
- parent->taskwait->last_parent_depends_on = task;
- }
- }
- if (taskgroup)
- {
- if (taskgroup->children)
- {
- task->next_taskgroup = taskgroup->children;
- task->prev_taskgroup = taskgroup->children->prev_taskgroup;
- task->next_taskgroup->prev_taskgroup = task;
- task->prev_taskgroup->next_taskgroup = task;
- }
- else
- {
- task->next_taskgroup = task;
- task->prev_taskgroup = task;
- }
- taskgroup->children = task;
- if (taskgroup->in_taskgroup_wait)
- {
- taskgroup->in_taskgroup_wait = false;
- gomp_sem_post (&taskgroup->taskgroup_sem);
- }
- }
- if (team->task_queue)
- {
- task->next_queue = team->task_queue;
- task->prev_queue = team->task_queue->prev_queue;
- task->next_queue->prev_queue = task;
- task->prev_queue->next_queue = task;
- }
- else
- {
- task->next_queue = task;
- task->prev_queue = task;
- team->task_queue = task;
- }
- ++team->task_count;
- ++team->task_queued_count;
- ++ret;
- }
- free (child_task->dependers);
- child_task->dependers = NULL;
- if (ret > 1)
- gomp_team_barrier_set_task_pending (&team->barrier);
- return ret;
- }
- static inline size_t
- gomp_task_run_post_handle_depend (struct gomp_task *child_task,
- struct gomp_team *team)
- {
- if (child_task->depend_count == 0)
- return 0;
- /* If parent is gone already, the hash table is freed and nothing
- will use the hash table anymore, no need to remove anything from it. */
- if (child_task->parent != NULL)
- gomp_task_run_post_handle_depend_hash (child_task);
- if (child_task->dependers == NULL)
- return 0;
- return gomp_task_run_post_handle_dependers (child_task, team);
- }
- static inline void
- gomp_task_run_post_remove_parent (struct gomp_task *child_task)
- {
- struct gomp_task *parent = child_task->parent;
- if (parent == NULL)
- return;
- if (__builtin_expect (child_task->parent_depends_on, 0)
- && --parent->taskwait->n_depend == 0
- && parent->taskwait->in_depend_wait)
- {
- parent->taskwait->in_depend_wait = false;
- gomp_sem_post (&parent->taskwait->taskwait_sem);
- }
- child_task->prev_child->next_child = child_task->next_child;
- child_task->next_child->prev_child = child_task->prev_child;
- if (parent->children != child_task)
- return;
- if (child_task->next_child != child_task)
- parent->children = child_task->next_child;
- 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. */
- __atomic_store_n (&parent->children, NULL, MEMMODEL_RELEASE);
- if (parent->taskwait && parent->taskwait->in_taskwait)
- {
- parent->taskwait->in_taskwait = false;
- gomp_sem_post (&parent->taskwait->taskwait_sem);
- }
- }
- }
- static inline void
- gomp_task_run_post_remove_taskgroup (struct gomp_task *child_task)
- {
- struct gomp_taskgroup *taskgroup = child_task->taskgroup;
- if (taskgroup == NULL)
- return;
- child_task->prev_taskgroup->next_taskgroup = child_task->next_taskgroup;
- child_task->next_taskgroup->prev_taskgroup = child_task->prev_taskgroup;
- if (taskgroup->num_children > 1)
- --taskgroup->num_children;
- else
- {
- /* We access taskgroup->num_children in GOMP_taskgroup_end
- 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. */
- __atomic_store_n (&taskgroup->num_children, 0, MEMMODEL_RELEASE);
- }
- if (taskgroup->children != child_task)
- return;
- if (child_task->next_taskgroup != child_task)
- taskgroup->children = child_task->next_taskgroup;
- else
- {
- taskgroup->children = NULL;
- if (taskgroup->in_taskgroup_wait)
- {
- taskgroup->in_taskgroup_wait = false;
- gomp_sem_post (&taskgroup->taskgroup_sem);
- }
- }
- }
- void
- gomp_barrier_handle_tasks (gomp_barrier_state_t state)
- {
- struct gomp_thread *thr = gomp_thread ();
- struct gomp_team *team = thr->ts.team;
- struct gomp_task *task = thr->task;
- struct gomp_task *child_task = NULL;
- struct gomp_task *to_free = NULL;
- int do_wake = 0;
- gomp_mutex_lock (&team->task_lock);
- if (gomp_barrier_last_thread (state))
- {
- if (team->task_count == 0)
- {
- gomp_team_barrier_done (&team->barrier, state);
- gomp_mutex_unlock (&team->task_lock);
- gomp_team_barrier_wake (&team->barrier, 0);
- return;
- }
- gomp_team_barrier_set_waiting_for_tasks (&team->barrier);
- }
- while (1)
- {
- bool cancelled = false;
- if (team->task_queue != NULL)
- {
- child_task = team->task_queue;
- cancelled = gomp_task_run_pre (child_task, child_task->parent,
- child_task->taskgroup, team);
- if (__builtin_expect (cancelled, 0))
- {
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- goto finish_cancelled;
- }
- team->task_running_count++;
- child_task->in_tied_task = true;
- }
- gomp_mutex_unlock (&team->task_lock);
- if (do_wake)
- {
- gomp_team_barrier_wake (&team->barrier, do_wake);
- do_wake = 0;
- }
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- if (child_task)
- {
- thr->task = child_task;
- child_task->fn (child_task->fn_data);
- thr->task = task;
- }
- else
- return;
- gomp_mutex_lock (&team->task_lock);
- if (child_task)
- {
- finish_cancelled:;
- size_t new_tasks
- = gomp_task_run_post_handle_depend (child_task, team);
- gomp_task_run_post_remove_parent (child_task);
- gomp_clear_parent (child_task->children);
- gomp_task_run_post_remove_taskgroup (child_task);
- to_free = child_task;
- child_task = NULL;
- if (!cancelled)
- team->task_running_count--;
- if (new_tasks > 1)
- {
- do_wake = team->nthreads - team->task_running_count;
- if (do_wake > new_tasks)
- do_wake = new_tasks;
- }
- if (--team->task_count == 0
- && gomp_team_barrier_waiting_for_tasks (&team->barrier))
- {
- gomp_team_barrier_done (&team->barrier, state);
- gomp_mutex_unlock (&team->task_lock);
- gomp_team_barrier_wake (&team->barrier, 0);
- gomp_mutex_lock (&team->task_lock);
- }
- }
- }
- }
- /* Called when encountering a taskwait directive. */
- void
- GOMP_taskwait (void)
- {
- struct gomp_thread *thr = gomp_thread ();
- struct gomp_team *team = thr->ts.team;
- struct gomp_task *task = thr->task;
- struct gomp_task *child_task = NULL;
- struct gomp_task *to_free = NULL;
- struct gomp_taskwait taskwait;
- int do_wake = 0;
- /* The acquire barrier on load of task->children here synchronizes
- 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 (task == NULL
- || __atomic_load_n (&task->children, MEMMODEL_ACQUIRE) == NULL)
- return;
- memset (&taskwait, 0, sizeof (taskwait));
- gomp_mutex_lock (&team->task_lock);
- while (1)
- {
- bool cancelled = false;
- if (task->children == NULL)
- {
- bool destroy_taskwait = task->taskwait != NULL;
- task->taskwait = NULL;
- gomp_mutex_unlock (&team->task_lock);
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- }
- if (destroy_taskwait)
- gomp_sem_destroy (&taskwait.taskwait_sem);
- return;
- }
- if (task->children->kind == GOMP_TASK_WAITING)
- {
- child_task = task->children;
- cancelled
- = gomp_task_run_pre (child_task, task, child_task->taskgroup,
- team);
- if (__builtin_expect (cancelled, 0))
- {
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- goto finish_cancelled;
- }
- }
- else
- {
- /* All tasks we are waiting for are already running
- in other threads. Wait for them. */
- if (task->taskwait == NULL)
- {
- taskwait.in_depend_wait = false;
- gomp_sem_init (&taskwait.taskwait_sem, 0);
- task->taskwait = &taskwait;
- }
- taskwait.in_taskwait = true;
- }
- gomp_mutex_unlock (&team->task_lock);
- if (do_wake)
- {
- gomp_team_barrier_wake (&team->barrier, do_wake);
- do_wake = 0;
- }
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- if (child_task)
- {
- thr->task = child_task;
- child_task->fn (child_task->fn_data);
- thr->task = task;
- }
- else
- gomp_sem_wait (&taskwait.taskwait_sem);
- gomp_mutex_lock (&team->task_lock);
- if (child_task)
- {
- finish_cancelled:;
- size_t new_tasks
- = gomp_task_run_post_handle_depend (child_task, team);
- child_task->prev_child->next_child = child_task->next_child;
- child_task->next_child->prev_child = child_task->prev_child;
- if (task->children == child_task)
- {
- if (child_task->next_child != child_task)
- task->children = child_task->next_child;
- else
- task->children = NULL;
- }
- gomp_clear_parent (child_task->children);
- gomp_task_run_post_remove_taskgroup (child_task);
- to_free = child_task;
- child_task = NULL;
- team->task_count--;
- if (new_tasks > 1)
- {
- do_wake = team->nthreads - team->task_running_count
- - !task->in_tied_task;
- if (do_wake > new_tasks)
- do_wake = new_tasks;
- }
- }
- }
- }
- /* This is like GOMP_taskwait, but we only wait for tasks that the
- upcoming task depends on. */
- static void
- gomp_task_maybe_wait_for_dependencies (void **depend)
- {
- struct gomp_thread *thr = gomp_thread ();
- struct gomp_task *task = thr->task;
- struct gomp_team *team = thr->ts.team;
- struct gomp_task_depend_entry elem, *ent = NULL;
- struct gomp_taskwait taskwait;
- struct gomp_task *last_parent_depends_on = NULL;
- size_t ndepend = (uintptr_t) depend[0];
- size_t nout = (uintptr_t) depend[1];
- size_t i;
- size_t num_awaited = 0;
- struct gomp_task *child_task = NULL;
- struct gomp_task *to_free = NULL;
- int do_wake = 0;
- gomp_mutex_lock (&team->task_lock);
- for (i = 0; i < ndepend; i++)
- {
- elem.addr = depend[i + 2];
- ent = htab_find (task->depend_hash, &elem);
- for (; ent; ent = ent->next)
- if (i >= nout && ent->is_in)
- continue;
- else
- {
- struct gomp_task *tsk = ent->task;
- if (!tsk->parent_depends_on)
- {
- tsk->parent_depends_on = true;
- ++num_awaited;
- if (tsk->num_dependees == 0 && tsk->kind == GOMP_TASK_WAITING)
- {
- /* If a task we need to wait for is not already
- running and is ready to be scheduled, move it
- to front, so that we run it as soon as possible. */
- if (last_parent_depends_on)
- {
- tsk->prev_child->next_child = tsk->next_child;
- tsk->next_child->prev_child = tsk->prev_child;
- tsk->prev_child = last_parent_depends_on;
- tsk->next_child = last_parent_depends_on->next_child;
- tsk->prev_child->next_child = tsk;
- tsk->next_child->prev_child = tsk;
- }
- else if (tsk != task->children)
- {
- tsk->prev_child->next_child = tsk->next_child;
- tsk->next_child->prev_child = tsk->prev_child;
- tsk->prev_child = task->children;
- tsk->next_child = task->children->next_child;
- task->children = tsk;
- tsk->prev_child->next_child = tsk;
- tsk->next_child->prev_child = tsk;
- }
- last_parent_depends_on = tsk;
- }
- }
- }
- }
- if (num_awaited == 0)
- {
- gomp_mutex_unlock (&team->task_lock);
- return;
- }
- memset (&taskwait, 0, sizeof (taskwait));
- taskwait.n_depend = num_awaited;
- taskwait.last_parent_depends_on = last_parent_depends_on;
- gomp_sem_init (&taskwait.taskwait_sem, 0);
- task->taskwait = &taskwait;
- while (1)
- {
- bool cancelled = false;
- if (taskwait.n_depend == 0)
- {
- task->taskwait = NULL;
- gomp_mutex_unlock (&team->task_lock);
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- }
- gomp_sem_destroy (&taskwait.taskwait_sem);
- return;
- }
- if (task->children->kind == GOMP_TASK_WAITING)
- {
- child_task = task->children;
- cancelled
- = gomp_task_run_pre (child_task, task, child_task->taskgroup,
- team);
- if (__builtin_expect (cancelled, 0))
- {
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- goto finish_cancelled;
- }
- }
- else
- /* All tasks we are waiting for are already running
- in other threads. Wait for them. */
- taskwait.in_depend_wait = true;
- gomp_mutex_unlock (&team->task_lock);
- if (do_wake)
- {
- gomp_team_barrier_wake (&team->barrier, do_wake);
- do_wake = 0;
- }
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- if (child_task)
- {
- thr->task = child_task;
- child_task->fn (child_task->fn_data);
- thr->task = task;
- }
- else
- gomp_sem_wait (&taskwait.taskwait_sem);
- gomp_mutex_lock (&team->task_lock);
- if (child_task)
- {
- finish_cancelled:;
- size_t new_tasks
- = gomp_task_run_post_handle_depend (child_task, team);
- if (child_task->parent_depends_on)
- --taskwait.n_depend;
- child_task->prev_child->next_child = child_task->next_child;
- child_task->next_child->prev_child = child_task->prev_child;
- if (task->children == child_task)
- {
- if (child_task->next_child != child_task)
- task->children = child_task->next_child;
- else
- task->children = NULL;
- }
- gomp_clear_parent (child_task->children);
- gomp_task_run_post_remove_taskgroup (child_task);
- to_free = child_task;
- child_task = NULL;
- team->task_count--;
- if (new_tasks > 1)
- {
- do_wake = team->nthreads - team->task_running_count
- - !task->in_tied_task;
- if (do_wake > new_tasks)
- do_wake = new_tasks;
- }
- }
- }
- }
- /* Called when encountering a taskyield directive. */
- void
- GOMP_taskyield (void)
- {
- /* Nothing at the moment. */
- }
- void
- GOMP_taskgroup_start (void)
- {
- struct gomp_thread *thr = gomp_thread ();
- struct gomp_team *team = thr->ts.team;
- struct gomp_task *task = thr->task;
- struct gomp_taskgroup *taskgroup;
- /* If team is NULL, all tasks are executed as
- GOMP_TASK_IFFALSE tasks and thus all children tasks of
- taskgroup and their descendant tasks will be finished
- by the time GOMP_taskgroup_end is called. */
- if (team == NULL)
- return;
- taskgroup = gomp_malloc (sizeof (struct gomp_taskgroup));
- taskgroup->prev = task->taskgroup;
- taskgroup->children = NULL;
- taskgroup->in_taskgroup_wait = false;
- taskgroup->cancelled = false;
- taskgroup->num_children = 0;
- gomp_sem_init (&taskgroup->taskgroup_sem, 0);
- task->taskgroup = taskgroup;
- }
- void
- GOMP_taskgroup_end (void)
- {
- struct gomp_thread *thr = gomp_thread ();
- struct gomp_team *team = thr->ts.team;
- struct gomp_task *task = thr->task;
- struct gomp_taskgroup *taskgroup;
- struct gomp_task *child_task = NULL;
- struct gomp_task *to_free = NULL;
- int do_wake = 0;
- if (team == NULL)
- return;
- taskgroup = task->taskgroup;
- /* The acquire barrier on load of taskgroup->num_children here
- synchronizes with the write of 0 in gomp_task_run_post_remove_taskgroup.
- It is not necessary that we synchronize with other non-0 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_taskgroup_end. */
- if (__atomic_load_n (&taskgroup->num_children, MEMMODEL_ACQUIRE) == 0)
- goto finish;
- gomp_mutex_lock (&team->task_lock);
- while (1)
- {
- bool cancelled = false;
- if (taskgroup->children == NULL)
- {
- if (taskgroup->num_children)
- {
- if (task->children == NULL)
- goto do_wait;
- child_task = task->children;
- }
- else
- {
- gomp_mutex_unlock (&team->task_lock);
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- }
- goto finish;
- }
- }
- else
- child_task = taskgroup->children;
- if (child_task->kind == GOMP_TASK_WAITING)
- {
- cancelled
- = gomp_task_run_pre (child_task, child_task->parent, taskgroup,
- team);
- if (__builtin_expect (cancelled, 0))
- {
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- goto finish_cancelled;
- }
- }
- else
- {
- child_task = NULL;
- do_wait:
- /* All tasks we are waiting for are already running
- in other threads. Wait for them. */
- taskgroup->in_taskgroup_wait = true;
- }
- gomp_mutex_unlock (&team->task_lock);
- if (do_wake)
- {
- gomp_team_barrier_wake (&team->barrier, do_wake);
- do_wake = 0;
- }
- if (to_free)
- {
- gomp_finish_task (to_free);
- free (to_free);
- to_free = NULL;
- }
- if (child_task)
- {
- thr->task = child_task;
- child_task->fn (child_task->fn_data);
- thr->task = task;
- }
- else
- gomp_sem_wait (&taskgroup->taskgroup_sem);
- gomp_mutex_lock (&team->task_lock);
- if (child_task)
- {
- finish_cancelled:;
- size_t new_tasks
- = gomp_task_run_post_handle_depend (child_task, team);
- gomp_task_run_post_remove_parent (child_task);
- gomp_clear_parent (child_task->children);
- gomp_task_run_post_remove_taskgroup (child_task);
- to_free = child_task;
- child_task = NULL;
- team->task_count--;
- if (new_tasks > 1)
- {
- do_wake = team->nthreads - team->task_running_count
- - !task->in_tied_task;
- if (do_wake > new_tasks)
- do_wake = new_tasks;
- }
- }
- }
- finish:
- task->taskgroup = taskgroup->prev;
- gomp_sem_destroy (&taskgroup->taskgroup_sem);
- free (taskgroup);
- }
- int
- omp_in_final (void)
- {
- struct gomp_thread *thr = gomp_thread ();
- return thr->task && thr->task->final_task;
- }
- ialias (omp_in_final)
|