1 1.1.1.14 mrg /* Copyright (C) 2007-2024 Free Software Foundation, Inc. 2 1.1 mrg Contributed by Richard Henderson <rth (at) redhat.com>. 3 1.1 mrg 4 1.1.1.3 mrg This file is part of the GNU Offloading and Multi Processing Library 5 1.1.1.3 mrg (libgomp). 6 1.1 mrg 7 1.1 mrg Libgomp is free software; you can redistribute it and/or modify it 8 1.1 mrg under the terms of the GNU General Public License as published by 9 1.1 mrg the Free Software Foundation; either version 3, or (at your option) 10 1.1 mrg any later version. 11 1.1 mrg 12 1.1 mrg Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY 13 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 1.1 mrg FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 1.1 mrg more details. 16 1.1 mrg 17 1.1 mrg Under Section 7 of GPL version 3, you are granted additional 18 1.1 mrg permissions described in the GCC Runtime Library Exception, version 19 1.1 mrg 3.1, as published by the Free Software Foundation. 20 1.1 mrg 21 1.1 mrg You should have received a copy of the GNU General Public License and 22 1.1 mrg a copy of the GCC Runtime Library Exception along with this program; 23 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 24 1.1 mrg <http://www.gnu.org/licenses/>. */ 25 1.1 mrg 26 1.1.1.10 mrg /* This file handles the maintenance of tasks in response to task 27 1.1 mrg creation and termination. */ 28 1.1 mrg 29 1.1 mrg #include "libgomp.h" 30 1.1 mrg #include <stdlib.h> 31 1.1 mrg #include <string.h> 32 1.1.1.13 mrg #include <assert.h> 33 1.1.1.4 mrg #include "gomp-constants.h" 34 1.1 mrg 35 1.1.1.3 mrg typedef struct gomp_task_depend_entry *hash_entry_type; 36 1.1.1.3 mrg 37 1.1.1.3 mrg static inline void * 38 1.1.1.3 mrg htab_alloc (size_t size) 39 1.1.1.3 mrg { 40 1.1.1.3 mrg return gomp_malloc (size); 41 1.1.1.3 mrg } 42 1.1.1.3 mrg 43 1.1.1.3 mrg static inline void 44 1.1.1.3 mrg htab_free (void *ptr) 45 1.1.1.3 mrg { 46 1.1.1.3 mrg free (ptr); 47 1.1.1.3 mrg } 48 1.1.1.3 mrg 49 1.1.1.3 mrg #include "hashtab.h" 50 1.1.1.3 mrg 51 1.1.1.3 mrg static inline hashval_t 52 1.1.1.3 mrg htab_hash (hash_entry_type element) 53 1.1.1.3 mrg { 54 1.1.1.3 mrg return hash_pointer (element->addr); 55 1.1.1.3 mrg } 56 1.1.1.3 mrg 57 1.1.1.3 mrg static inline bool 58 1.1.1.3 mrg htab_eq (hash_entry_type x, hash_entry_type y) 59 1.1.1.3 mrg { 60 1.1.1.3 mrg return x->addr == y->addr; 61 1.1.1.3 mrg } 62 1.1 mrg 63 1.1 mrg /* Create a new task data structure. */ 64 1.1 mrg 65 1.1 mrg void 66 1.1 mrg gomp_init_task (struct gomp_task *task, struct gomp_task *parent_task, 67 1.1 mrg struct gomp_task_icv *prev_icv) 68 1.1 mrg { 69 1.1.1.4 mrg /* It would seem that using memset here would be a win, but it turns 70 1.1.1.4 mrg out that partially filling gomp_task allows us to keep the 71 1.1.1.4 mrg overhead of task creation low. In the nqueens-1.c test, for a 72 1.1.1.4 mrg sufficiently large N, we drop the overhead from 5-6% to 1%. 73 1.1.1.4 mrg 74 1.1.1.4 mrg Note, the nqueens-1.c test in serial mode is a good test to 75 1.1.1.4 mrg benchmark the overhead of creating tasks as there are millions of 76 1.1.1.4 mrg tiny tasks created that all run undeferred. */ 77 1.1 mrg task->parent = parent_task; 78 1.1.1.13 mrg priority_queue_init (&task->children_queue); 79 1.1.1.13 mrg task->taskgroup = NULL; 80 1.1.1.13 mrg task->dependers = NULL; 81 1.1.1.13 mrg task->depend_hash = NULL; 82 1.1.1.13 mrg task->taskwait = NULL; 83 1.1.1.14 mrg task->depend_all_memory = NULL; 84 1.1.1.13 mrg task->depend_count = 0; 85 1.1.1.13 mrg task->completion_sem = NULL; 86 1.1.1.13 mrg task->deferred_p = false; 87 1.1 mrg task->icv = *prev_icv; 88 1.1 mrg task->kind = GOMP_TASK_IMPLICIT; 89 1.1 mrg task->in_tied_task = false; 90 1.1.1.2 mrg task->final_task = false; 91 1.1.1.3 mrg task->copy_ctors_done = false; 92 1.1.1.3 mrg task->parent_depends_on = false; 93 1.1 mrg } 94 1.1 mrg 95 1.1 mrg /* Clean up a task, after completing it. */ 96 1.1 mrg 97 1.1 mrg void 98 1.1 mrg gomp_end_task (void) 99 1.1 mrg { 100 1.1 mrg struct gomp_thread *thr = gomp_thread (); 101 1.1 mrg struct gomp_task *task = thr->task; 102 1.1 mrg 103 1.1 mrg gomp_finish_task (task); 104 1.1 mrg thr->task = task->parent; 105 1.1 mrg } 106 1.1 mrg 107 1.1.1.4 mrg /* Clear the parent field of every task in LIST. */ 108 1.1.1.4 mrg 109 1.1 mrg static inline void 110 1.1.1.4 mrg gomp_clear_parent_in_list (struct priority_list *list) 111 1.1 mrg { 112 1.1.1.4 mrg struct priority_node *p = list->tasks; 113 1.1.1.4 mrg if (p) 114 1.1 mrg do 115 1.1 mrg { 116 1.1.1.4 mrg priority_node_to_task (PQ_CHILDREN, p)->parent = NULL; 117 1.1.1.4 mrg p = p->next; 118 1.1 mrg } 119 1.1.1.4 mrg while (p != list->tasks); 120 1.1.1.4 mrg } 121 1.1.1.4 mrg 122 1.1.1.4 mrg /* Splay tree version of gomp_clear_parent_in_list. 123 1.1.1.4 mrg 124 1.1.1.4 mrg Clear the parent field of every task in NODE within SP, and free 125 1.1.1.4 mrg the node when done. */ 126 1.1.1.4 mrg 127 1.1.1.4 mrg static void 128 1.1.1.4 mrg gomp_clear_parent_in_tree (prio_splay_tree sp, prio_splay_tree_node node) 129 1.1.1.4 mrg { 130 1.1.1.4 mrg if (!node) 131 1.1.1.4 mrg return; 132 1.1.1.4 mrg prio_splay_tree_node left = node->left, right = node->right; 133 1.1.1.4 mrg gomp_clear_parent_in_list (&node->key.l); 134 1.1.1.4 mrg #if _LIBGOMP_CHECKING_ 135 1.1.1.4 mrg memset (node, 0xaf, sizeof (*node)); 136 1.1.1.4 mrg #endif 137 1.1.1.4 mrg /* No need to remove the node from the tree. We're nuking 138 1.1.1.4 mrg everything, so just free the nodes and our caller can clear the 139 1.1.1.4 mrg entire splay tree. */ 140 1.1.1.4 mrg free (node); 141 1.1.1.4 mrg gomp_clear_parent_in_tree (sp, left); 142 1.1.1.4 mrg gomp_clear_parent_in_tree (sp, right); 143 1.1.1.4 mrg } 144 1.1.1.4 mrg 145 1.1.1.4 mrg /* Clear the parent field of every task in Q and remove every task 146 1.1.1.4 mrg from Q. */ 147 1.1.1.4 mrg 148 1.1.1.4 mrg static inline void 149 1.1.1.4 mrg gomp_clear_parent (struct priority_queue *q) 150 1.1.1.4 mrg { 151 1.1.1.4 mrg if (priority_queue_multi_p (q)) 152 1.1.1.4 mrg { 153 1.1.1.4 mrg gomp_clear_parent_in_tree (&q->t, q->t.root); 154 1.1.1.4 mrg /* All the nodes have been cleared in gomp_clear_parent_in_tree. 155 1.1.1.4 mrg No need to remove anything. We can just nuke everything. */ 156 1.1.1.4 mrg q->t.root = NULL; 157 1.1.1.4 mrg } 158 1.1.1.4 mrg else 159 1.1.1.4 mrg gomp_clear_parent_in_list (&q->l); 160 1.1 mrg } 161 1.1 mrg 162 1.1.1.4 mrg /* Helper function for GOMP_task and gomp_create_target_task. 163 1.1.1.4 mrg 164 1.1.1.4 mrg For a TASK with in/out dependencies, fill in the various dependency 165 1.1.1.4 mrg queues. PARENT is the parent of said task. DEPEND is as in 166 1.1.1.4 mrg GOMP_task. */ 167 1.1.1.4 mrg 168 1.1.1.4 mrg static void 169 1.1.1.4 mrg gomp_task_handle_depend (struct gomp_task *task, struct gomp_task *parent, 170 1.1.1.4 mrg void **depend) 171 1.1.1.4 mrg { 172 1.1.1.4 mrg size_t ndepend = (uintptr_t) depend[0]; 173 1.1.1.4 mrg size_t i; 174 1.1.1.4 mrg hash_entry_type ent; 175 1.1.1.14 mrg bool all_memory = false; 176 1.1.1.4 mrg 177 1.1.1.9 mrg if (ndepend) 178 1.1.1.9 mrg { 179 1.1.1.9 mrg /* depend[0] is total # */ 180 1.1.1.9 mrg size_t nout = (uintptr_t) depend[1]; /* # of out: and inout: */ 181 1.1.1.9 mrg /* ndepend - nout is # of in: */ 182 1.1.1.9 mrg for (i = 0; i < ndepend; i++) 183 1.1.1.9 mrg { 184 1.1.1.9 mrg task->depend[i].addr = depend[2 + i]; 185 1.1.1.9 mrg task->depend[i].is_in = i >= nout; 186 1.1.1.14 mrg all_memory |= i < nout && depend[2 + i] == NULL; 187 1.1.1.9 mrg } 188 1.1.1.9 mrg } 189 1.1.1.9 mrg else 190 1.1.1.9 mrg { 191 1.1.1.9 mrg ndepend = (uintptr_t) depend[1]; /* total # */ 192 1.1.1.9 mrg size_t nout = (uintptr_t) depend[2]; /* # of out: and inout: */ 193 1.1.1.9 mrg size_t nmutexinoutset = (uintptr_t) depend[3]; /* # of mutexinoutset: */ 194 1.1.1.9 mrg /* For now we treat mutexinoutset like out, which is compliant, but 195 1.1.1.9 mrg inefficient. */ 196 1.1.1.9 mrg size_t nin = (uintptr_t) depend[4]; /* # of in: */ 197 1.1.1.9 mrg /* ndepend - nout - nmutexinoutset - nin is # of depobjs */ 198 1.1.1.9 mrg size_t normal = nout + nmutexinoutset + nin; 199 1.1.1.9 mrg size_t n = 0; 200 1.1.1.14 mrg bool has_in = false; 201 1.1.1.9 mrg for (i = normal; i < ndepend; i++) 202 1.1.1.9 mrg { 203 1.1.1.9 mrg void **d = (void **) (uintptr_t) depend[5 + i]; 204 1.1.1.9 mrg switch ((uintptr_t) d[1]) 205 1.1.1.9 mrg { 206 1.1.1.9 mrg case GOMP_DEPEND_OUT: 207 1.1.1.9 mrg case GOMP_DEPEND_INOUT: 208 1.1.1.14 mrg all_memory |= d[0] == NULL; 209 1.1.1.14 mrg break; 210 1.1.1.9 mrg case GOMP_DEPEND_MUTEXINOUTSET: 211 1.1.1.9 mrg break; 212 1.1.1.9 mrg case GOMP_DEPEND_IN: 213 1.1.1.14 mrg case GOMP_DEPEND_INOUTSET: 214 1.1.1.14 mrg has_in = true; 215 1.1.1.9 mrg continue; 216 1.1.1.9 mrg default: 217 1.1.1.9 mrg gomp_fatal ("unknown omp_depend_t dependence type %d", 218 1.1.1.9 mrg (int) (uintptr_t) d[1]); 219 1.1.1.9 mrg } 220 1.1.1.9 mrg task->depend[n].addr = d[0]; 221 1.1.1.9 mrg task->depend[n++].is_in = 0; 222 1.1.1.9 mrg } 223 1.1.1.9 mrg for (i = 0; i < normal; i++) 224 1.1.1.9 mrg { 225 1.1.1.9 mrg task->depend[n].addr = depend[5 + i]; 226 1.1.1.9 mrg task->depend[n++].is_in = i >= nout + nmutexinoutset; 227 1.1.1.9 mrg } 228 1.1.1.14 mrg if (has_in) 229 1.1.1.14 mrg for (i = normal; i < ndepend; i++) 230 1.1.1.14 mrg { 231 1.1.1.14 mrg void **d = (void **) (uintptr_t) depend[5 + i]; 232 1.1.1.14 mrg if ((uintptr_t) d[1] != GOMP_DEPEND_IN 233 1.1.1.14 mrg && (uintptr_t) d[1] != GOMP_DEPEND_INOUTSET) 234 1.1.1.14 mrg continue; 235 1.1.1.14 mrg task->depend[n].addr = d[0]; 236 1.1.1.14 mrg task->depend[n++].is_in 237 1.1.1.14 mrg = 1 + ((uintptr_t) d[1] == GOMP_DEPEND_INOUTSET); 238 1.1.1.14 mrg } 239 1.1.1.14 mrg } 240 1.1.1.14 mrg task->num_dependees = 0; 241 1.1.1.14 mrg if (__builtin_expect (parent->depend_all_memory && ndepend, false)) 242 1.1.1.14 mrg { 243 1.1.1.14 mrg struct gomp_task *tsk = parent->depend_all_memory; 244 1.1.1.14 mrg if (tsk->dependers == NULL) 245 1.1.1.9 mrg { 246 1.1.1.14 mrg tsk->dependers 247 1.1.1.14 mrg = gomp_malloc (sizeof (struct gomp_dependers_vec) 248 1.1.1.14 mrg + 6 * sizeof (struct gomp_task *)); 249 1.1.1.14 mrg tsk->dependers->n_elem = 1; 250 1.1.1.14 mrg tsk->dependers->allocated = 6; 251 1.1.1.14 mrg tsk->dependers->elem[0] = task; 252 1.1.1.9 mrg } 253 1.1.1.14 mrg else 254 1.1.1.14 mrg { 255 1.1.1.14 mrg if (tsk->dependers->n_elem == tsk->dependers->allocated) 256 1.1.1.14 mrg { 257 1.1.1.14 mrg tsk->dependers->allocated 258 1.1.1.14 mrg = tsk->dependers->allocated * 2 + 2; 259 1.1.1.14 mrg tsk->dependers 260 1.1.1.14 mrg = gomp_realloc (tsk->dependers, 261 1.1.1.14 mrg sizeof (struct gomp_dependers_vec) 262 1.1.1.14 mrg + (tsk->dependers->allocated 263 1.1.1.14 mrg * sizeof (struct gomp_task *))); 264 1.1.1.14 mrg } 265 1.1.1.14 mrg tsk->dependers->elem[tsk->dependers->n_elem++] = task; 266 1.1.1.14 mrg } 267 1.1.1.14 mrg task->num_dependees++; 268 1.1.1.14 mrg } 269 1.1.1.14 mrg if (__builtin_expect (all_memory, false)) 270 1.1.1.14 mrg { 271 1.1.1.14 mrg /* A task with depend(inout: omp_all_memory) depends on all previous 272 1.1.1.14 mrg sibling tasks which have any dependencies and all later sibling 273 1.1.1.14 mrg tasks which have any dependencies depend on it. */ 274 1.1.1.14 mrg task->depend_count = 1; 275 1.1.1.14 mrg task->depend[0].addr = NULL; 276 1.1.1.14 mrg task->depend[0].next = NULL; 277 1.1.1.14 mrg task->depend[0].prev = NULL; 278 1.1.1.14 mrg task->depend[0].task = task; 279 1.1.1.14 mrg task->depend[0].redundant = true; 280 1.1.1.14 mrg task->depend[0].redundant_out = false; 281 1.1.1.14 mrg if (parent->depend_hash) 282 1.1.1.14 mrg { 283 1.1.1.14 mrg /* Inlined htab_traverse + htab_clear. All newer siblings can 284 1.1.1.14 mrg just depend on this task. Add dependencies on all previous 285 1.1.1.14 mrg sibling tasks with dependencies and make them redundant and 286 1.1.1.14 mrg clear the hash table. */ 287 1.1.1.14 mrg hash_entry_type *slot = &parent->depend_hash->entries[0]; 288 1.1.1.14 mrg hash_entry_type *end = slot + htab_size (parent->depend_hash); 289 1.1.1.14 mrg for (; slot != end; ++slot) 290 1.1.1.14 mrg { 291 1.1.1.14 mrg if (*slot == HTAB_EMPTY_ENTRY) 292 1.1.1.14 mrg continue; 293 1.1.1.14 mrg if (*slot != HTAB_DELETED_ENTRY) 294 1.1.1.14 mrg { 295 1.1.1.14 mrg for (ent = *slot; ent; ent = ent->next) 296 1.1.1.14 mrg { 297 1.1.1.14 mrg struct gomp_task *tsk = ent->task; 298 1.1.1.14 mrg 299 1.1.1.14 mrg if (ent->redundant_out) 300 1.1.1.14 mrg break; 301 1.1.1.14 mrg 302 1.1.1.14 mrg ent->redundant = true; 303 1.1.1.14 mrg if (tsk->dependers == NULL) 304 1.1.1.14 mrg { 305 1.1.1.14 mrg tsk->dependers 306 1.1.1.14 mrg = gomp_malloc (sizeof (struct gomp_dependers_vec) 307 1.1.1.14 mrg + 6 * sizeof (struct gomp_task *)); 308 1.1.1.14 mrg tsk->dependers->n_elem = 1; 309 1.1.1.14 mrg tsk->dependers->allocated = 6; 310 1.1.1.14 mrg tsk->dependers->elem[0] = task; 311 1.1.1.14 mrg task->num_dependees++; 312 1.1.1.14 mrg continue; 313 1.1.1.14 mrg } 314 1.1.1.14 mrg /* We already have some other dependency on tsk from 315 1.1.1.14 mrg earlier depend clause. */ 316 1.1.1.14 mrg else if (tsk->dependers->n_elem 317 1.1.1.14 mrg && (tsk->dependers->elem[tsk->dependers->n_elem 318 1.1.1.14 mrg - 1] == task)) 319 1.1.1.14 mrg continue; 320 1.1.1.14 mrg else if (tsk->dependers->n_elem 321 1.1.1.14 mrg == tsk->dependers->allocated) 322 1.1.1.14 mrg { 323 1.1.1.14 mrg tsk->dependers->allocated 324 1.1.1.14 mrg = tsk->dependers->allocated * 2 + 2; 325 1.1.1.14 mrg tsk->dependers 326 1.1.1.14 mrg = gomp_realloc (tsk->dependers, 327 1.1.1.14 mrg sizeof (struct gomp_dependers_vec) 328 1.1.1.14 mrg + (tsk->dependers->allocated 329 1.1.1.14 mrg * sizeof (struct gomp_task *))); 330 1.1.1.14 mrg } 331 1.1.1.14 mrg tsk->dependers->elem[tsk->dependers->n_elem++] = task; 332 1.1.1.14 mrg task->num_dependees++; 333 1.1.1.14 mrg } 334 1.1.1.14 mrg while (ent) 335 1.1.1.14 mrg { 336 1.1.1.14 mrg ent->redundant = true; 337 1.1.1.14 mrg ent = ent->next; 338 1.1.1.14 mrg } 339 1.1.1.14 mrg } 340 1.1.1.14 mrg *slot = HTAB_EMPTY_ENTRY; 341 1.1.1.14 mrg } 342 1.1.1.14 mrg if (htab_size (parent->depend_hash) <= 32) 343 1.1.1.14 mrg { 344 1.1.1.14 mrg parent->depend_hash->n_elements = 0; 345 1.1.1.14 mrg parent->depend_hash->n_deleted = 0; 346 1.1.1.14 mrg } 347 1.1.1.14 mrg else 348 1.1.1.14 mrg { 349 1.1.1.14 mrg /* Shrink the hash table if it would be too large. 350 1.1.1.14 mrg We don't want to walk e.g. megabytes of empty hash 351 1.1.1.14 mrg table for every depend(inout: omp_all_memory). */ 352 1.1.1.14 mrg free (parent->depend_hash); 353 1.1.1.14 mrg parent->depend_hash = htab_create (12); 354 1.1.1.14 mrg } 355 1.1.1.14 mrg } 356 1.1.1.14 mrg parent->depend_all_memory = task; 357 1.1.1.14 mrg return; 358 1.1.1.9 mrg } 359 1.1.1.4 mrg task->depend_count = ndepend; 360 1.1.1.4 mrg if (parent->depend_hash == NULL) 361 1.1.1.4 mrg parent->depend_hash = htab_create (2 * ndepend > 12 ? 2 * ndepend : 12); 362 1.1.1.4 mrg for (i = 0; i < ndepend; i++) 363 1.1.1.4 mrg { 364 1.1.1.4 mrg task->depend[i].next = NULL; 365 1.1.1.4 mrg task->depend[i].prev = NULL; 366 1.1.1.4 mrg task->depend[i].task = task; 367 1.1.1.4 mrg task->depend[i].redundant = false; 368 1.1.1.4 mrg task->depend[i].redundant_out = false; 369 1.1.1.4 mrg 370 1.1.1.4 mrg hash_entry_type *slot = htab_find_slot (&parent->depend_hash, 371 1.1.1.4 mrg &task->depend[i], INSERT); 372 1.1.1.4 mrg hash_entry_type out = NULL, last = NULL; 373 1.1.1.4 mrg if (*slot) 374 1.1.1.4 mrg { 375 1.1.1.4 mrg /* If multiple depends on the same task are the same, all but the 376 1.1.1.4 mrg first one are redundant. As inout/out come first, if any of them 377 1.1.1.4 mrg is inout/out, it will win, which is the right semantics. */ 378 1.1.1.4 mrg if ((*slot)->task == task) 379 1.1.1.4 mrg { 380 1.1.1.4 mrg task->depend[i].redundant = true; 381 1.1.1.4 mrg continue; 382 1.1.1.4 mrg } 383 1.1.1.4 mrg for (ent = *slot; ent; ent = ent->next) 384 1.1.1.4 mrg { 385 1.1.1.4 mrg if (ent->redundant_out) 386 1.1.1.4 mrg break; 387 1.1.1.4 mrg 388 1.1.1.4 mrg last = ent; 389 1.1.1.4 mrg 390 1.1.1.14 mrg /* depend(in:...) doesn't depend on earlier depend(in:...). 391 1.1.1.14 mrg Similarly depend(inoutset:...) doesn't depend on earlier 392 1.1.1.14 mrg depend(inoutset:...). */ 393 1.1.1.14 mrg if (task->depend[i].is_in && task->depend[i].is_in == ent->is_in) 394 1.1.1.4 mrg continue; 395 1.1.1.4 mrg 396 1.1.1.4 mrg if (!ent->is_in) 397 1.1.1.4 mrg out = ent; 398 1.1.1.4 mrg 399 1.1.1.4 mrg struct gomp_task *tsk = ent->task; 400 1.1.1.4 mrg if (tsk->dependers == NULL) 401 1.1.1.4 mrg { 402 1.1.1.4 mrg tsk->dependers 403 1.1.1.4 mrg = gomp_malloc (sizeof (struct gomp_dependers_vec) 404 1.1.1.4 mrg + 6 * sizeof (struct gomp_task *)); 405 1.1.1.4 mrg tsk->dependers->n_elem = 1; 406 1.1.1.4 mrg tsk->dependers->allocated = 6; 407 1.1.1.4 mrg tsk->dependers->elem[0] = task; 408 1.1.1.4 mrg task->num_dependees++; 409 1.1.1.4 mrg continue; 410 1.1.1.4 mrg } 411 1.1.1.4 mrg /* We already have some other dependency on tsk from earlier 412 1.1.1.4 mrg depend clause. */ 413 1.1.1.4 mrg else if (tsk->dependers->n_elem 414 1.1.1.4 mrg && (tsk->dependers->elem[tsk->dependers->n_elem - 1] 415 1.1.1.4 mrg == task)) 416 1.1.1.4 mrg continue; 417 1.1.1.4 mrg else if (tsk->dependers->n_elem == tsk->dependers->allocated) 418 1.1.1.4 mrg { 419 1.1.1.4 mrg tsk->dependers->allocated 420 1.1.1.4 mrg = tsk->dependers->allocated * 2 + 2; 421 1.1.1.4 mrg tsk->dependers 422 1.1.1.4 mrg = gomp_realloc (tsk->dependers, 423 1.1.1.4 mrg sizeof (struct gomp_dependers_vec) 424 1.1.1.4 mrg + (tsk->dependers->allocated 425 1.1.1.4 mrg * sizeof (struct gomp_task *))); 426 1.1.1.4 mrg } 427 1.1.1.4 mrg tsk->dependers->elem[tsk->dependers->n_elem++] = task; 428 1.1.1.4 mrg task->num_dependees++; 429 1.1.1.4 mrg } 430 1.1.1.4 mrg task->depend[i].next = *slot; 431 1.1.1.4 mrg (*slot)->prev = &task->depend[i]; 432 1.1.1.4 mrg } 433 1.1.1.4 mrg *slot = &task->depend[i]; 434 1.1.1.4 mrg 435 1.1.1.4 mrg /* There is no need to store more than one depend({,in}out:) task per 436 1.1.1.4 mrg address in the hash table chain for the purpose of creation of 437 1.1.1.4 mrg deferred tasks, because each out depends on all earlier outs, thus it 438 1.1.1.4 mrg is enough to record just the last depend({,in}out:). For depend(in:), 439 1.1.1.4 mrg we need to keep all of the previous ones not terminated yet, because 440 1.1.1.4 mrg a later depend({,in}out:) might need to depend on all of them. So, if 441 1.1.1.4 mrg the new task's clause is depend({,in}out:), we know there is at most 442 1.1.1.4 mrg one other depend({,in}out:) clause in the list (out). For 443 1.1.1.4 mrg non-deferred tasks we want to see all outs, so they are moved to the 444 1.1.1.4 mrg end of the chain, after first redundant_out entry all following 445 1.1.1.4 mrg entries should be redundant_out. */ 446 1.1.1.4 mrg if (!task->depend[i].is_in && out) 447 1.1.1.4 mrg { 448 1.1.1.4 mrg if (out != last) 449 1.1.1.4 mrg { 450 1.1.1.4 mrg out->next->prev = out->prev; 451 1.1.1.4 mrg out->prev->next = out->next; 452 1.1.1.4 mrg out->next = last->next; 453 1.1.1.4 mrg out->prev = last; 454 1.1.1.4 mrg last->next = out; 455 1.1.1.4 mrg if (out->next) 456 1.1.1.4 mrg out->next->prev = out; 457 1.1.1.4 mrg } 458 1.1.1.4 mrg out->redundant_out = true; 459 1.1.1.4 mrg } 460 1.1.1.4 mrg } 461 1.1.1.4 mrg } 462 1.1.1.3 mrg 463 1.1.1.14 mrg /* Body of empty task like taskwait nowait depend. */ 464 1.1.1.14 mrg 465 1.1.1.14 mrg static void 466 1.1.1.14 mrg empty_task (void *data __attribute__((unused))) 467 1.1.1.14 mrg { 468 1.1.1.14 mrg } 469 1.1.1.14 mrg 470 1.1.1.14 mrg static void gomp_task_run_post_handle_depend_hash (struct gomp_task *); 471 1.1.1.14 mrg static inline size_t gomp_task_run_post_handle_depend (struct gomp_task *, 472 1.1.1.14 mrg struct gomp_team *); 473 1.1.1.14 mrg 474 1.1 mrg /* Called when encountering an explicit task directive. If IF_CLAUSE is 475 1.1 mrg false, then we must not delay in executing the task. If UNTIED is true, 476 1.1.1.4 mrg then the task may be executed by any member of the team. 477 1.1.1.4 mrg 478 1.1.1.4 mrg DEPEND is an array containing: 479 1.1.1.9 mrg if depend[0] is non-zero, then: 480 1.1.1.4 mrg depend[0]: number of depend elements. 481 1.1.1.9 mrg depend[1]: number of depend elements of type "out/inout". 482 1.1.1.9 mrg depend[2..N+1]: address of [1..N]th depend element. 483 1.1.1.9 mrg otherwise, when depend[0] is zero, then: 484 1.1.1.9 mrg depend[1]: number of depend elements. 485 1.1.1.9 mrg depend[2]: number of depend elements of type "out/inout". 486 1.1.1.9 mrg depend[3]: number of depend elements of type "mutexinoutset". 487 1.1.1.9 mrg depend[4]: number of depend elements of type "in". 488 1.1.1.9 mrg depend[5..4+depend[2]+depend[3]+depend[4]]: address of depend elements 489 1.1.1.9 mrg depend[5+depend[2]+depend[3]+depend[4]..4+depend[1]]: address of 490 1.1.1.9 mrg omp_depend_t objects. */ 491 1.1 mrg 492 1.1 mrg void 493 1.1 mrg GOMP_task (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *), 494 1.1.1.3 mrg long arg_size, long arg_align, bool if_clause, unsigned flags, 495 1.1.1.13 mrg void **depend, int priority_arg, void *detach) 496 1.1 mrg { 497 1.1 mrg struct gomp_thread *thr = gomp_thread (); 498 1.1 mrg struct gomp_team *team = thr->ts.team; 499 1.1.1.13 mrg int priority = 0; 500 1.1 mrg 501 1.1 mrg #ifdef HAVE_BROKEN_POSIX_SEMAPHORES 502 1.1 mrg /* If pthread_mutex_* is used for omp_*lock*, then each task must be 503 1.1 mrg tied to one thread all the time. This means UNTIED tasks must be 504 1.1 mrg tied and if CPYFN is non-NULL IF(0) must be forced, as CPYFN 505 1.1 mrg might be running on different thread than FN. */ 506 1.1 mrg if (cpyfn) 507 1.1 mrg if_clause = false; 508 1.1.1.4 mrg flags &= ~GOMP_TASK_FLAG_UNTIED; 509 1.1 mrg #endif 510 1.1 mrg 511 1.1.1.3 mrg /* If parallel or taskgroup has been cancelled, don't start new tasks. */ 512 1.1.1.9 mrg if (__builtin_expect (gomp_cancel_var, 0) && team) 513 1.1.1.9 mrg { 514 1.1.1.9 mrg if (gomp_team_barrier_cancelled (&team->barrier)) 515 1.1.1.9 mrg return; 516 1.1.1.9 mrg if (thr->task->taskgroup) 517 1.1.1.9 mrg { 518 1.1.1.9 mrg if (thr->task->taskgroup->cancelled) 519 1.1.1.9 mrg return; 520 1.1.1.9 mrg if (thr->task->taskgroup->workshare 521 1.1.1.9 mrg && thr->task->taskgroup->prev 522 1.1.1.9 mrg && thr->task->taskgroup->prev->cancelled) 523 1.1.1.9 mrg return; 524 1.1.1.9 mrg } 525 1.1.1.9 mrg } 526 1.1.1.3 mrg 527 1.1.1.13 mrg if (__builtin_expect ((flags & GOMP_TASK_FLAG_PRIORITY) != 0, 0)) 528 1.1.1.13 mrg { 529 1.1.1.13 mrg priority = priority_arg; 530 1.1.1.13 mrg if (priority > gomp_max_task_priority_var) 531 1.1.1.13 mrg priority = gomp_max_task_priority_var; 532 1.1.1.13 mrg } 533 1.1.1.4 mrg 534 1.1 mrg if (!if_clause || team == NULL 535 1.1.1.2 mrg || (thr->task && thr->task->final_task) 536 1.1 mrg || team->task_count > 64 * team->nthreads) 537 1.1 mrg { 538 1.1 mrg struct gomp_task task; 539 1.1.1.13 mrg gomp_sem_t completion_sem; 540 1.1 mrg 541 1.1.1.3 mrg /* If there are depend clauses and earlier deferred sibling tasks 542 1.1.1.3 mrg with depend clauses, check if there isn't a dependency. If there 543 1.1.1.3 mrg is, we need to wait for them. There is no need to handle 544 1.1.1.3 mrg depend clauses for non-deferred tasks other than this, because 545 1.1.1.3 mrg the parent task is suspended until the child task finishes and thus 546 1.1.1.3 mrg it can't start further child tasks. */ 547 1.1.1.4 mrg if ((flags & GOMP_TASK_FLAG_DEPEND) 548 1.1.1.4 mrg && thr->task && thr->task->depend_hash) 549 1.1.1.3 mrg gomp_task_maybe_wait_for_dependencies (depend); 550 1.1.1.3 mrg 551 1.1 mrg gomp_init_task (&task, thr->task, gomp_icv (false)); 552 1.1.1.4 mrg task.kind = GOMP_TASK_UNDEFERRED; 553 1.1.1.4 mrg task.final_task = (thr->task && thr->task->final_task) 554 1.1.1.4 mrg || (flags & GOMP_TASK_FLAG_FINAL); 555 1.1.1.4 mrg task.priority = priority; 556 1.1.1.13 mrg 557 1.1.1.13 mrg if ((flags & GOMP_TASK_FLAG_DETACH) != 0) 558 1.1.1.13 mrg { 559 1.1.1.13 mrg gomp_sem_init (&completion_sem, 0); 560 1.1.1.13 mrg task.completion_sem = &completion_sem; 561 1.1.1.13 mrg *(void **) detach = &task; 562 1.1.1.13 mrg if (data) 563 1.1.1.13 mrg *(void **) data = &task; 564 1.1.1.13 mrg 565 1.1.1.13 mrg gomp_debug (0, "Thread %d: new event: %p\n", 566 1.1.1.13 mrg thr->ts.team_id, &task); 567 1.1.1.13 mrg } 568 1.1.1.13 mrg 569 1.1 mrg if (thr->task) 570 1.1.1.3 mrg { 571 1.1.1.3 mrg task.in_tied_task = thr->task->in_tied_task; 572 1.1.1.3 mrg task.taskgroup = thr->task->taskgroup; 573 1.1.1.3 mrg } 574 1.1 mrg thr->task = &task; 575 1.1 mrg if (__builtin_expect (cpyfn != NULL, 0)) 576 1.1 mrg { 577 1.1 mrg char buf[arg_size + arg_align - 1]; 578 1.1 mrg char *arg = (char *) (((uintptr_t) buf + arg_align - 1) 579 1.1 mrg & ~(uintptr_t) (arg_align - 1)); 580 1.1 mrg cpyfn (arg, data); 581 1.1 mrg fn (arg); 582 1.1 mrg } 583 1.1 mrg else 584 1.1 mrg fn (data); 585 1.1.1.13 mrg 586 1.1.1.13 mrg if ((flags & GOMP_TASK_FLAG_DETACH) != 0) 587 1.1.1.13 mrg { 588 1.1.1.13 mrg gomp_sem_wait (&completion_sem); 589 1.1.1.13 mrg gomp_sem_destroy (&completion_sem); 590 1.1.1.13 mrg } 591 1.1.1.13 mrg 592 1.1.1.2 mrg /* Access to "children" is normally done inside a task_lock 593 1.1.1.2 mrg mutex region, but the only way this particular task.children 594 1.1.1.2 mrg can be set is if this thread's task work function (fn) 595 1.1.1.2 mrg creates children. So since the setter is *this* thread, we 596 1.1.1.2 mrg need no barriers here when testing for non-NULL. We can have 597 1.1.1.2 mrg task.children set by the current thread then changed by a 598 1.1.1.2 mrg child thread, but seeing a stale non-NULL value is not a 599 1.1.1.2 mrg problem. Once past the task_lock acquisition, this thread 600 1.1.1.2 mrg will see the real value of task.children. */ 601 1.1.1.4 mrg if (!priority_queue_empty_p (&task.children_queue, MEMMODEL_RELAXED)) 602 1.1 mrg { 603 1.1 mrg gomp_mutex_lock (&team->task_lock); 604 1.1.1.4 mrg gomp_clear_parent (&task.children_queue); 605 1.1 mrg gomp_mutex_unlock (&team->task_lock); 606 1.1 mrg } 607 1.1 mrg gomp_end_task (); 608 1.1 mrg } 609 1.1 mrg else 610 1.1 mrg { 611 1.1 mrg struct gomp_task *task; 612 1.1 mrg struct gomp_task *parent = thr->task; 613 1.1.1.3 mrg struct gomp_taskgroup *taskgroup = parent->taskgroup; 614 1.1 mrg char *arg; 615 1.1 mrg bool do_wake; 616 1.1.1.3 mrg size_t depend_size = 0; 617 1.1 mrg 618 1.1.1.4 mrg if (flags & GOMP_TASK_FLAG_DEPEND) 619 1.1.1.9 mrg depend_size = ((uintptr_t) (depend[0] ? depend[0] : depend[1]) 620 1.1.1.3 mrg * sizeof (struct gomp_task_depend_entry)); 621 1.1.1.3 mrg task = gomp_malloc (sizeof (*task) + depend_size 622 1.1.1.3 mrg + arg_size + arg_align - 1); 623 1.1.1.3 mrg arg = (char *) (((uintptr_t) (task + 1) + depend_size + arg_align - 1) 624 1.1 mrg & ~(uintptr_t) (arg_align - 1)); 625 1.1 mrg gomp_init_task (task, parent, gomp_icv (false)); 626 1.1.1.4 mrg task->priority = priority; 627 1.1.1.4 mrg task->kind = GOMP_TASK_UNDEFERRED; 628 1.1 mrg task->in_tied_task = parent->in_tied_task; 629 1.1.1.3 mrg task->taskgroup = taskgroup; 630 1.1.1.13 mrg task->deferred_p = true; 631 1.1.1.13 mrg if ((flags & GOMP_TASK_FLAG_DETACH) != 0) 632 1.1.1.13 mrg { 633 1.1.1.13 mrg task->detach_team = team; 634 1.1.1.13 mrg 635 1.1.1.13 mrg *(void **) detach = task; 636 1.1.1.13 mrg if (data) 637 1.1.1.13 mrg *(void **) data = task; 638 1.1.1.13 mrg 639 1.1.1.13 mrg gomp_debug (0, "Thread %d: new event: %p\n", thr->ts.team_id, task); 640 1.1.1.13 mrg } 641 1.1 mrg thr->task = task; 642 1.1 mrg if (cpyfn) 643 1.1.1.3 mrg { 644 1.1.1.3 mrg cpyfn (arg, data); 645 1.1.1.3 mrg task->copy_ctors_done = true; 646 1.1.1.3 mrg } 647 1.1 mrg else 648 1.1 mrg memcpy (arg, data, arg_size); 649 1.1 mrg thr->task = parent; 650 1.1 mrg task->kind = GOMP_TASK_WAITING; 651 1.1 mrg task->fn = fn; 652 1.1 mrg task->fn_data = arg; 653 1.1.1.4 mrg task->final_task = (flags & GOMP_TASK_FLAG_FINAL) >> 1; 654 1.1 mrg gomp_mutex_lock (&team->task_lock); 655 1.1.1.3 mrg /* If parallel or taskgroup has been cancelled, don't start new 656 1.1.1.3 mrg tasks. */ 657 1.1.1.9 mrg if (__builtin_expect (gomp_cancel_var, 0) 658 1.1.1.9 mrg && !task->copy_ctors_done) 659 1.1.1.3 mrg { 660 1.1.1.9 mrg if (gomp_team_barrier_cancelled (&team->barrier)) 661 1.1.1.9 mrg { 662 1.1.1.9 mrg do_cancel: 663 1.1.1.9 mrg gomp_mutex_unlock (&team->task_lock); 664 1.1.1.9 mrg gomp_finish_task (task); 665 1.1.1.9 mrg free (task); 666 1.1.1.9 mrg return; 667 1.1.1.9 mrg } 668 1.1.1.9 mrg if (taskgroup) 669 1.1.1.9 mrg { 670 1.1.1.9 mrg if (taskgroup->cancelled) 671 1.1.1.9 mrg goto do_cancel; 672 1.1.1.9 mrg if (taskgroup->workshare 673 1.1.1.9 mrg && taskgroup->prev 674 1.1.1.9 mrg && taskgroup->prev->cancelled) 675 1.1.1.9 mrg goto do_cancel; 676 1.1.1.9 mrg } 677 1.1.1.3 mrg } 678 1.1.1.3 mrg if (taskgroup) 679 1.1.1.3 mrg taskgroup->num_children++; 680 1.1.1.3 mrg if (depend_size) 681 1.1.1.3 mrg { 682 1.1.1.4 mrg gomp_task_handle_depend (task, parent, depend); 683 1.1.1.3 mrg if (task->num_dependees) 684 1.1.1.3 mrg { 685 1.1.1.4 mrg /* Tasks that depend on other tasks are not put into the 686 1.1.1.4 mrg various waiting queues, so we are done for now. Said 687 1.1.1.4 mrg tasks are instead put into the queues via 688 1.1.1.4 mrg gomp_task_run_post_handle_dependers() after their 689 1.1.1.4 mrg dependencies have been satisfied. After which, they 690 1.1.1.4 mrg can be picked up by the various scheduling 691 1.1.1.4 mrg points. */ 692 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 693 1.1.1.3 mrg return; 694 1.1.1.3 mrg } 695 1.1.1.14 mrg /* Check for taskwait nowait depend which doesn't need to wait for 696 1.1.1.14 mrg anything. */ 697 1.1.1.14 mrg if (__builtin_expect (fn == empty_task, 0)) 698 1.1.1.14 mrg { 699 1.1.1.14 mrg if (taskgroup) 700 1.1.1.14 mrg taskgroup->num_children--; 701 1.1.1.14 mrg gomp_task_run_post_handle_depend_hash (task); 702 1.1.1.14 mrg gomp_mutex_unlock (&team->task_lock); 703 1.1.1.14 mrg gomp_finish_task (task); 704 1.1.1.14 mrg free (task); 705 1.1.1.14 mrg return; 706 1.1.1.14 mrg } 707 1.1.1.3 mrg } 708 1.1.1.4 mrg 709 1.1.1.4 mrg priority_queue_insert (PQ_CHILDREN, &parent->children_queue, 710 1.1.1.4 mrg task, priority, 711 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, 712 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 713 1.1.1.4 mrg task->parent_depends_on); 714 1.1.1.3 mrg if (taskgroup) 715 1.1.1.4 mrg priority_queue_insert (PQ_TASKGROUP, &taskgroup->taskgroup_queue, 716 1.1.1.4 mrg task, priority, 717 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, 718 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 719 1.1.1.4 mrg task->parent_depends_on); 720 1.1.1.4 mrg 721 1.1.1.4 mrg priority_queue_insert (PQ_TEAM, &team->task_queue, 722 1.1.1.4 mrg task, priority, 723 1.1.1.4 mrg PRIORITY_INSERT_END, 724 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 725 1.1.1.4 mrg task->parent_depends_on); 726 1.1.1.4 mrg 727 1.1 mrg ++team->task_count; 728 1.1.1.3 mrg ++team->task_queued_count; 729 1.1 mrg gomp_team_barrier_set_task_pending (&team->barrier); 730 1.1 mrg do_wake = team->task_running_count + !parent->in_tied_task 731 1.1 mrg < team->nthreads; 732 1.1 mrg gomp_mutex_unlock (&team->task_lock); 733 1.1 mrg if (do_wake) 734 1.1 mrg gomp_team_barrier_wake (&team->barrier, 1); 735 1.1 mrg } 736 1.1 mrg } 737 1.1 mrg 738 1.1.1.14 mrg ialias (GOMP_task) 739 1.1.1.4 mrg ialias (GOMP_taskgroup_start) 740 1.1.1.4 mrg ialias (GOMP_taskgroup_end) 741 1.1.1.9 mrg ialias (GOMP_taskgroup_reduction_register) 742 1.1.1.4 mrg 743 1.1.1.4 mrg #define TYPE long 744 1.1.1.4 mrg #define UTYPE unsigned long 745 1.1.1.4 mrg #define TYPE_is_long 1 746 1.1.1.4 mrg #include "taskloop.c" 747 1.1.1.4 mrg #undef TYPE 748 1.1.1.4 mrg #undef UTYPE 749 1.1.1.4 mrg #undef TYPE_is_long 750 1.1.1.4 mrg 751 1.1.1.4 mrg #define TYPE unsigned long long 752 1.1.1.4 mrg #define UTYPE TYPE 753 1.1.1.4 mrg #define GOMP_taskloop GOMP_taskloop_ull 754 1.1.1.4 mrg #include "taskloop.c" 755 1.1.1.4 mrg #undef TYPE 756 1.1.1.4 mrg #undef UTYPE 757 1.1.1.4 mrg #undef GOMP_taskloop 758 1.1.1.4 mrg 759 1.1.1.4 mrg static void inline 760 1.1.1.4 mrg priority_queue_move_task_first (enum priority_queue_type type, 761 1.1.1.4 mrg struct priority_queue *head, 762 1.1.1.4 mrg struct gomp_task *task) 763 1.1.1.4 mrg { 764 1.1.1.4 mrg #if _LIBGOMP_CHECKING_ 765 1.1.1.4 mrg if (!priority_queue_task_in_queue_p (type, head, task)) 766 1.1.1.4 mrg gomp_fatal ("Attempt to move first missing task %p", task); 767 1.1.1.4 mrg #endif 768 1.1.1.4 mrg struct priority_list *list; 769 1.1.1.4 mrg if (priority_queue_multi_p (head)) 770 1.1.1.4 mrg { 771 1.1.1.4 mrg list = priority_queue_lookup_priority (head, task->priority); 772 1.1.1.4 mrg #if _LIBGOMP_CHECKING_ 773 1.1.1.4 mrg if (!list) 774 1.1.1.4 mrg gomp_fatal ("Unable to find priority %d", task->priority); 775 1.1.1.4 mrg #endif 776 1.1.1.4 mrg } 777 1.1.1.4 mrg else 778 1.1.1.4 mrg list = &head->l; 779 1.1.1.4 mrg priority_list_remove (list, task_to_priority_node (type, task), 0); 780 1.1.1.4 mrg priority_list_insert (type, list, task, task->priority, 781 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, type == PQ_CHILDREN, 782 1.1.1.4 mrg task->parent_depends_on); 783 1.1.1.4 mrg } 784 1.1.1.4 mrg 785 1.1.1.4 mrg /* Actual body of GOMP_PLUGIN_target_task_completion that is executed 786 1.1.1.4 mrg with team->task_lock held, or is executed in the thread that called 787 1.1.1.4 mrg gomp_target_task_fn if GOMP_PLUGIN_target_task_completion has been 788 1.1.1.4 mrg run before it acquires team->task_lock. */ 789 1.1.1.4 mrg 790 1.1.1.4 mrg static void 791 1.1.1.4 mrg gomp_target_task_completion (struct gomp_team *team, struct gomp_task *task) 792 1.1.1.3 mrg { 793 1.1.1.4 mrg struct gomp_task *parent = task->parent; 794 1.1.1.3 mrg if (parent) 795 1.1.1.4 mrg priority_queue_move_task_first (PQ_CHILDREN, &parent->children_queue, 796 1.1.1.4 mrg task); 797 1.1.1.4 mrg 798 1.1.1.4 mrg struct gomp_taskgroup *taskgroup = task->taskgroup; 799 1.1.1.4 mrg if (taskgroup) 800 1.1.1.4 mrg priority_queue_move_task_first (PQ_TASKGROUP, &taskgroup->taskgroup_queue, 801 1.1.1.4 mrg task); 802 1.1.1.4 mrg 803 1.1.1.4 mrg priority_queue_insert (PQ_TEAM, &team->task_queue, task, task->priority, 804 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, false, 805 1.1.1.4 mrg task->parent_depends_on); 806 1.1.1.4 mrg task->kind = GOMP_TASK_WAITING; 807 1.1.1.4 mrg if (parent && parent->taskwait) 808 1.1.1.3 mrg { 809 1.1.1.4 mrg if (parent->taskwait->in_taskwait) 810 1.1.1.4 mrg { 811 1.1.1.4 mrg /* One more task has had its dependencies met. 812 1.1.1.4 mrg Inform any waiters. */ 813 1.1.1.4 mrg parent->taskwait->in_taskwait = false; 814 1.1.1.4 mrg gomp_sem_post (&parent->taskwait->taskwait_sem); 815 1.1.1.4 mrg } 816 1.1.1.4 mrg else if (parent->taskwait->in_depend_wait) 817 1.1.1.4 mrg { 818 1.1.1.4 mrg /* One more task has had its dependencies met. 819 1.1.1.4 mrg Inform any waiters. */ 820 1.1.1.4 mrg parent->taskwait->in_depend_wait = false; 821 1.1.1.4 mrg gomp_sem_post (&parent->taskwait->taskwait_sem); 822 1.1.1.3 mrg } 823 1.1.1.3 mrg } 824 1.1.1.4 mrg if (taskgroup && taskgroup->in_taskgroup_wait) 825 1.1.1.4 mrg { 826 1.1.1.4 mrg /* One more task has had its dependencies met. 827 1.1.1.4 mrg Inform any waiters. */ 828 1.1.1.4 mrg taskgroup->in_taskgroup_wait = false; 829 1.1.1.4 mrg gomp_sem_post (&taskgroup->taskgroup_sem); 830 1.1.1.4 mrg } 831 1.1.1.4 mrg 832 1.1.1.4 mrg ++team->task_queued_count; 833 1.1.1.4 mrg gomp_team_barrier_set_task_pending (&team->barrier); 834 1.1.1.4 mrg /* I'm afraid this can't be done after releasing team->task_lock, 835 1.1.1.4 mrg as gomp_target_task_completion is run from unrelated thread and 836 1.1.1.4 mrg therefore in between gomp_mutex_unlock and gomp_team_barrier_wake 837 1.1.1.4 mrg the team could be gone already. */ 838 1.1.1.4 mrg if (team->nthreads > team->task_running_count) 839 1.1.1.4 mrg gomp_team_barrier_wake (&team->barrier, 1); 840 1.1.1.4 mrg } 841 1.1.1.4 mrg 842 1.1.1.4 mrg /* Signal that a target task TTASK has completed the asynchronously 843 1.1.1.4 mrg running phase and should be requeued as a task to handle the 844 1.1.1.4 mrg variable unmapping. */ 845 1.1.1.4 mrg 846 1.1.1.4 mrg void 847 1.1.1.4 mrg GOMP_PLUGIN_target_task_completion (void *data) 848 1.1.1.4 mrg { 849 1.1.1.4 mrg struct gomp_target_task *ttask = (struct gomp_target_task *) data; 850 1.1.1.4 mrg struct gomp_task *task = ttask->task; 851 1.1.1.4 mrg struct gomp_team *team = ttask->team; 852 1.1.1.4 mrg 853 1.1.1.4 mrg gomp_mutex_lock (&team->task_lock); 854 1.1.1.4 mrg if (ttask->state == GOMP_TARGET_TASK_READY_TO_RUN) 855 1.1.1.4 mrg { 856 1.1.1.4 mrg ttask->state = GOMP_TARGET_TASK_FINISHED; 857 1.1.1.4 mrg gomp_mutex_unlock (&team->task_lock); 858 1.1.1.4 mrg return; 859 1.1.1.4 mrg } 860 1.1.1.4 mrg ttask->state = GOMP_TARGET_TASK_FINISHED; 861 1.1.1.4 mrg gomp_target_task_completion (team, task); 862 1.1.1.4 mrg gomp_mutex_unlock (&team->task_lock); 863 1.1.1.4 mrg } 864 1.1.1.4 mrg 865 1.1.1.4 mrg /* Called for nowait target tasks. */ 866 1.1.1.4 mrg 867 1.1.1.4 mrg bool 868 1.1.1.4 mrg gomp_create_target_task (struct gomp_device_descr *devicep, 869 1.1.1.4 mrg void (*fn) (void *), size_t mapnum, void **hostaddrs, 870 1.1.1.4 mrg size_t *sizes, unsigned short *kinds, 871 1.1.1.4 mrg unsigned int flags, void **depend, void **args, 872 1.1.1.4 mrg enum gomp_target_task_state state) 873 1.1.1.4 mrg { 874 1.1.1.4 mrg struct gomp_thread *thr = gomp_thread (); 875 1.1.1.4 mrg struct gomp_team *team = thr->ts.team; 876 1.1.1.4 mrg 877 1.1.1.4 mrg /* If parallel or taskgroup has been cancelled, don't start new tasks. */ 878 1.1.1.9 mrg if (__builtin_expect (gomp_cancel_var, 0) && team) 879 1.1.1.9 mrg { 880 1.1.1.9 mrg if (gomp_team_barrier_cancelled (&team->barrier)) 881 1.1.1.9 mrg return true; 882 1.1.1.9 mrg if (thr->task->taskgroup) 883 1.1.1.9 mrg { 884 1.1.1.9 mrg if (thr->task->taskgroup->cancelled) 885 1.1.1.9 mrg return true; 886 1.1.1.9 mrg if (thr->task->taskgroup->workshare 887 1.1.1.9 mrg && thr->task->taskgroup->prev 888 1.1.1.9 mrg && thr->task->taskgroup->prev->cancelled) 889 1.1.1.9 mrg return true; 890 1.1.1.9 mrg } 891 1.1.1.9 mrg } 892 1.1.1.4 mrg 893 1.1.1.4 mrg struct gomp_target_task *ttask; 894 1.1.1.4 mrg struct gomp_task *task; 895 1.1.1.4 mrg struct gomp_task *parent = thr->task; 896 1.1.1.4 mrg struct gomp_taskgroup *taskgroup = parent->taskgroup; 897 1.1.1.4 mrg bool do_wake; 898 1.1.1.4 mrg size_t depend_size = 0; 899 1.1.1.4 mrg uintptr_t depend_cnt = 0; 900 1.1.1.4 mrg size_t tgt_align = 0, tgt_size = 0; 901 1.1.1.13 mrg uintptr_t args_cnt = 0; 902 1.1.1.4 mrg 903 1.1.1.4 mrg if (depend != NULL) 904 1.1.1.4 mrg { 905 1.1.1.9 mrg depend_cnt = (uintptr_t) (depend[0] ? depend[0] : depend[1]); 906 1.1.1.4 mrg depend_size = depend_cnt * sizeof (struct gomp_task_depend_entry); 907 1.1.1.4 mrg } 908 1.1.1.4 mrg if (fn) 909 1.1.1.3 mrg { 910 1.1.1.4 mrg /* GOMP_MAP_FIRSTPRIVATE need to be copied first, as they are 911 1.1.1.4 mrg firstprivate on the target task. */ 912 1.1.1.4 mrg size_t i; 913 1.1.1.4 mrg for (i = 0; i < mapnum; i++) 914 1.1.1.4 mrg if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE) 915 1.1.1.4 mrg { 916 1.1.1.4 mrg size_t align = (size_t) 1 << (kinds[i] >> 8); 917 1.1.1.4 mrg if (tgt_align < align) 918 1.1.1.4 mrg tgt_align = align; 919 1.1.1.4 mrg tgt_size = (tgt_size + align - 1) & ~(align - 1); 920 1.1.1.4 mrg tgt_size += sizes[i]; 921 1.1.1.4 mrg } 922 1.1.1.4 mrg if (tgt_align) 923 1.1.1.4 mrg tgt_size += tgt_align - 1; 924 1.1.1.3 mrg else 925 1.1.1.4 mrg tgt_size = 0; 926 1.1.1.13 mrg if (args) 927 1.1.1.13 mrg { 928 1.1.1.13 mrg void **cargs = args; 929 1.1.1.13 mrg while (*cargs) 930 1.1.1.13 mrg { 931 1.1.1.13 mrg intptr_t id = (intptr_t) *cargs++; 932 1.1.1.13 mrg if (id & GOMP_TARGET_ARG_SUBSEQUENT_PARAM) 933 1.1.1.13 mrg cargs++; 934 1.1.1.13 mrg } 935 1.1.1.13 mrg args_cnt = cargs + 1 - args; 936 1.1.1.13 mrg } 937 1.1.1.4 mrg } 938 1.1.1.4 mrg 939 1.1.1.4 mrg task = gomp_malloc (sizeof (*task) + depend_size 940 1.1.1.4 mrg + sizeof (*ttask) 941 1.1.1.13 mrg + args_cnt * sizeof (void *) 942 1.1.1.4 mrg + mapnum * (sizeof (void *) + sizeof (size_t) 943 1.1.1.4 mrg + sizeof (unsigned short)) 944 1.1.1.4 mrg + tgt_size); 945 1.1.1.4 mrg gomp_init_task (task, parent, gomp_icv (false)); 946 1.1.1.4 mrg task->priority = 0; 947 1.1.1.4 mrg task->kind = GOMP_TASK_WAITING; 948 1.1.1.4 mrg task->in_tied_task = parent->in_tied_task; 949 1.1.1.4 mrg task->taskgroup = taskgroup; 950 1.1.1.4 mrg ttask = (struct gomp_target_task *) &task->depend[depend_cnt]; 951 1.1.1.4 mrg ttask->devicep = devicep; 952 1.1.1.4 mrg ttask->fn = fn; 953 1.1.1.4 mrg ttask->mapnum = mapnum; 954 1.1.1.4 mrg memcpy (ttask->hostaddrs, hostaddrs, mapnum * sizeof (void *)); 955 1.1.1.13 mrg if (args_cnt) 956 1.1.1.13 mrg { 957 1.1.1.13 mrg ttask->args = (void **) &ttask->hostaddrs[mapnum]; 958 1.1.1.13 mrg memcpy (ttask->args, args, args_cnt * sizeof (void *)); 959 1.1.1.13 mrg ttask->sizes = (size_t *) &ttask->args[args_cnt]; 960 1.1.1.13 mrg } 961 1.1.1.13 mrg else 962 1.1.1.13 mrg { 963 1.1.1.13 mrg ttask->args = args; 964 1.1.1.13 mrg ttask->sizes = (size_t *) &ttask->hostaddrs[mapnum]; 965 1.1.1.13 mrg } 966 1.1.1.4 mrg memcpy (ttask->sizes, sizes, mapnum * sizeof (size_t)); 967 1.1.1.4 mrg ttask->kinds = (unsigned short *) &ttask->sizes[mapnum]; 968 1.1.1.4 mrg memcpy (ttask->kinds, kinds, mapnum * sizeof (unsigned short)); 969 1.1.1.4 mrg if (tgt_align) 970 1.1.1.4 mrg { 971 1.1.1.4 mrg char *tgt = (char *) &ttask->kinds[mapnum]; 972 1.1.1.4 mrg size_t i; 973 1.1.1.4 mrg uintptr_t al = (uintptr_t) tgt & (tgt_align - 1); 974 1.1.1.4 mrg if (al) 975 1.1.1.4 mrg tgt += tgt_align - al; 976 1.1.1.4 mrg tgt_size = 0; 977 1.1.1.4 mrg for (i = 0; i < mapnum; i++) 978 1.1.1.4 mrg if ((kinds[i] & 0xff) == GOMP_MAP_FIRSTPRIVATE) 979 1.1.1.4 mrg { 980 1.1.1.4 mrg size_t align = (size_t) 1 << (kinds[i] >> 8); 981 1.1.1.4 mrg tgt_size = (tgt_size + align - 1) & ~(align - 1); 982 1.1.1.4 mrg memcpy (tgt + tgt_size, hostaddrs[i], sizes[i]); 983 1.1.1.4 mrg ttask->hostaddrs[i] = tgt + tgt_size; 984 1.1.1.4 mrg tgt_size = tgt_size + sizes[i]; 985 1.1.1.4 mrg } 986 1.1.1.4 mrg } 987 1.1.1.4 mrg ttask->flags = flags; 988 1.1.1.4 mrg ttask->state = state; 989 1.1.1.4 mrg ttask->task = task; 990 1.1.1.4 mrg ttask->team = team; 991 1.1.1.4 mrg task->fn = NULL; 992 1.1.1.4 mrg task->fn_data = ttask; 993 1.1.1.4 mrg task->final_task = 0; 994 1.1.1.4 mrg gomp_mutex_lock (&team->task_lock); 995 1.1.1.4 mrg /* If parallel or taskgroup has been cancelled, don't start new tasks. */ 996 1.1.1.9 mrg if (__builtin_expect (gomp_cancel_var, 0)) 997 1.1.1.4 mrg { 998 1.1.1.9 mrg if (gomp_team_barrier_cancelled (&team->barrier)) 999 1.1.1.9 mrg { 1000 1.1.1.9 mrg do_cancel: 1001 1.1.1.9 mrg gomp_mutex_unlock (&team->task_lock); 1002 1.1.1.9 mrg gomp_finish_task (task); 1003 1.1.1.9 mrg free (task); 1004 1.1.1.9 mrg return true; 1005 1.1.1.9 mrg } 1006 1.1.1.9 mrg if (taskgroup) 1007 1.1.1.9 mrg { 1008 1.1.1.9 mrg if (taskgroup->cancelled) 1009 1.1.1.9 mrg goto do_cancel; 1010 1.1.1.9 mrg if (taskgroup->workshare 1011 1.1.1.9 mrg && taskgroup->prev 1012 1.1.1.9 mrg && taskgroup->prev->cancelled) 1013 1.1.1.9 mrg goto do_cancel; 1014 1.1.1.9 mrg } 1015 1.1.1.4 mrg } 1016 1.1.1.4 mrg if (depend_size) 1017 1.1.1.4 mrg { 1018 1.1.1.4 mrg gomp_task_handle_depend (task, parent, depend); 1019 1.1.1.4 mrg if (task->num_dependees) 1020 1.1.1.4 mrg { 1021 1.1.1.4 mrg if (taskgroup) 1022 1.1.1.4 mrg taskgroup->num_children++; 1023 1.1.1.4 mrg gomp_mutex_unlock (&team->task_lock); 1024 1.1.1.4 mrg return true; 1025 1.1.1.4 mrg } 1026 1.1.1.4 mrg } 1027 1.1.1.4 mrg if (state == GOMP_TARGET_TASK_DATA) 1028 1.1.1.4 mrg { 1029 1.1.1.4 mrg gomp_task_run_post_handle_depend_hash (task); 1030 1.1.1.4 mrg gomp_mutex_unlock (&team->task_lock); 1031 1.1.1.4 mrg gomp_finish_task (task); 1032 1.1.1.4 mrg free (task); 1033 1.1.1.4 mrg return false; 1034 1.1.1.3 mrg } 1035 1.1.1.4 mrg if (taskgroup) 1036 1.1.1.4 mrg taskgroup->num_children++; 1037 1.1.1.4 mrg /* For async offloading, if we don't need to wait for dependencies, 1038 1.1.1.4 mrg run the gomp_target_task_fn right away, essentially schedule the 1039 1.1.1.4 mrg mapping part of the task in the current thread. */ 1040 1.1.1.4 mrg if (devicep != NULL 1041 1.1.1.4 mrg && (devicep->capabilities & GOMP_OFFLOAD_CAP_OPENMP_400)) 1042 1.1.1.4 mrg { 1043 1.1.1.4 mrg priority_queue_insert (PQ_CHILDREN, &parent->children_queue, task, 0, 1044 1.1.1.4 mrg PRIORITY_INSERT_END, 1045 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 1046 1.1.1.4 mrg task->parent_depends_on); 1047 1.1.1.4 mrg if (taskgroup) 1048 1.1.1.4 mrg priority_queue_insert (PQ_TASKGROUP, &taskgroup->taskgroup_queue, 1049 1.1.1.4 mrg task, 0, PRIORITY_INSERT_END, 1050 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 1051 1.1.1.4 mrg task->parent_depends_on); 1052 1.1.1.4 mrg task->pnode[PQ_TEAM].next = NULL; 1053 1.1.1.4 mrg task->pnode[PQ_TEAM].prev = NULL; 1054 1.1.1.4 mrg task->kind = GOMP_TASK_TIED; 1055 1.1.1.4 mrg ++team->task_count; 1056 1.1.1.4 mrg gomp_mutex_unlock (&team->task_lock); 1057 1.1.1.4 mrg 1058 1.1.1.4 mrg thr->task = task; 1059 1.1.1.4 mrg gomp_target_task_fn (task->fn_data); 1060 1.1.1.4 mrg thr->task = parent; 1061 1.1.1.4 mrg 1062 1.1.1.4 mrg gomp_mutex_lock (&team->task_lock); 1063 1.1.1.4 mrg task->kind = GOMP_TASK_ASYNC_RUNNING; 1064 1.1.1.4 mrg /* If GOMP_PLUGIN_target_task_completion has run already 1065 1.1.1.4 mrg in between gomp_target_task_fn and the mutex lock, 1066 1.1.1.4 mrg perform the requeuing here. */ 1067 1.1.1.4 mrg if (ttask->state == GOMP_TARGET_TASK_FINISHED) 1068 1.1.1.4 mrg gomp_target_task_completion (team, task); 1069 1.1.1.4 mrg else 1070 1.1.1.4 mrg ttask->state = GOMP_TARGET_TASK_RUNNING; 1071 1.1.1.4 mrg gomp_mutex_unlock (&team->task_lock); 1072 1.1.1.4 mrg return true; 1073 1.1.1.4 mrg } 1074 1.1.1.4 mrg priority_queue_insert (PQ_CHILDREN, &parent->children_queue, task, 0, 1075 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, 1076 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 1077 1.1.1.4 mrg task->parent_depends_on); 1078 1.1.1.4 mrg if (taskgroup) 1079 1.1.1.4 mrg priority_queue_insert (PQ_TASKGROUP, &taskgroup->taskgroup_queue, task, 0, 1080 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, 1081 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 1082 1.1.1.4 mrg task->parent_depends_on); 1083 1.1.1.4 mrg priority_queue_insert (PQ_TEAM, &team->task_queue, task, 0, 1084 1.1.1.4 mrg PRIORITY_INSERT_END, 1085 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 1086 1.1.1.4 mrg task->parent_depends_on); 1087 1.1.1.4 mrg ++team->task_count; 1088 1.1.1.4 mrg ++team->task_queued_count; 1089 1.1.1.4 mrg gomp_team_barrier_set_task_pending (&team->barrier); 1090 1.1.1.4 mrg do_wake = team->task_running_count + !parent->in_tied_task 1091 1.1.1.4 mrg < team->nthreads; 1092 1.1.1.4 mrg gomp_mutex_unlock (&team->task_lock); 1093 1.1.1.4 mrg if (do_wake) 1094 1.1.1.4 mrg gomp_team_barrier_wake (&team->barrier, 1); 1095 1.1.1.4 mrg return true; 1096 1.1.1.4 mrg } 1097 1.1.1.4 mrg 1098 1.1.1.4 mrg /* Given a parent_depends_on task in LIST, move it to the front of its 1099 1.1.1.4 mrg priority so it is run as soon as possible. 1100 1.1.1.4 mrg 1101 1.1.1.4 mrg Care is taken to update the list's LAST_PARENT_DEPENDS_ON field. 1102 1.1.1.4 mrg 1103 1.1.1.4 mrg We rearrange the queue such that all parent_depends_on tasks are 1104 1.1.1.4 mrg first, and last_parent_depends_on points to the last such task we 1105 1.1.1.4 mrg rearranged. For example, given the following tasks in a queue 1106 1.1.1.4 mrg where PD[123] are the parent_depends_on tasks: 1107 1.1.1.4 mrg 1108 1.1.1.4 mrg task->children 1109 1.1.1.4 mrg | 1110 1.1.1.4 mrg V 1111 1.1.1.4 mrg C1 -> C2 -> C3 -> PD1 -> PD2 -> PD3 -> C4 1112 1.1.1.4 mrg 1113 1.1.1.4 mrg We rearrange such that: 1114 1.1.1.4 mrg 1115 1.1.1.4 mrg task->children 1116 1.1.1.4 mrg | +--- last_parent_depends_on 1117 1.1.1.4 mrg | | 1118 1.1.1.4 mrg V V 1119 1.1.1.4 mrg PD1 -> PD2 -> PD3 -> C1 -> C2 -> C3 -> C4. */ 1120 1.1.1.4 mrg 1121 1.1.1.4 mrg static void inline 1122 1.1.1.4 mrg priority_list_upgrade_task (struct priority_list *list, 1123 1.1.1.4 mrg struct priority_node *node) 1124 1.1.1.4 mrg { 1125 1.1.1.4 mrg struct priority_node *last_parent_depends_on 1126 1.1.1.4 mrg = list->last_parent_depends_on; 1127 1.1.1.4 mrg if (last_parent_depends_on) 1128 1.1.1.4 mrg { 1129 1.1.1.4 mrg node->prev->next = node->next; 1130 1.1.1.4 mrg node->next->prev = node->prev; 1131 1.1.1.4 mrg node->prev = last_parent_depends_on; 1132 1.1.1.4 mrg node->next = last_parent_depends_on->next; 1133 1.1.1.4 mrg node->prev->next = node; 1134 1.1.1.4 mrg node->next->prev = node; 1135 1.1.1.4 mrg } 1136 1.1.1.4 mrg else if (node != list->tasks) 1137 1.1.1.4 mrg { 1138 1.1.1.4 mrg node->prev->next = node->next; 1139 1.1.1.4 mrg node->next->prev = node->prev; 1140 1.1.1.4 mrg node->prev = list->tasks->prev; 1141 1.1.1.4 mrg node->next = list->tasks; 1142 1.1.1.4 mrg list->tasks = node; 1143 1.1.1.4 mrg node->prev->next = node; 1144 1.1.1.4 mrg node->next->prev = node; 1145 1.1.1.4 mrg } 1146 1.1.1.4 mrg list->last_parent_depends_on = node; 1147 1.1.1.4 mrg } 1148 1.1.1.4 mrg 1149 1.1.1.4 mrg /* Given a parent_depends_on TASK in its parent's children_queue, move 1150 1.1.1.4 mrg it to the front of its priority so it is run as soon as possible. 1151 1.1.1.4 mrg 1152 1.1.1.4 mrg PARENT is passed as an optimization. 1153 1.1.1.4 mrg 1154 1.1.1.4 mrg (This function could be defined in priority_queue.c, but we want it 1155 1.1.1.4 mrg inlined, and putting it in priority_queue.h is not an option, given 1156 1.1.1.4 mrg that gomp_task has not been properly defined at that point). */ 1157 1.1.1.4 mrg 1158 1.1.1.4 mrg static void inline 1159 1.1.1.4 mrg priority_queue_upgrade_task (struct gomp_task *task, 1160 1.1.1.4 mrg struct gomp_task *parent) 1161 1.1.1.4 mrg { 1162 1.1.1.4 mrg struct priority_queue *head = &parent->children_queue; 1163 1.1.1.4 mrg struct priority_node *node = &task->pnode[PQ_CHILDREN]; 1164 1.1.1.4 mrg #if _LIBGOMP_CHECKING_ 1165 1.1.1.4 mrg if (!task->parent_depends_on) 1166 1.1.1.4 mrg gomp_fatal ("priority_queue_upgrade_task: task must be a " 1167 1.1.1.4 mrg "parent_depends_on task"); 1168 1.1.1.4 mrg if (!priority_queue_task_in_queue_p (PQ_CHILDREN, head, task)) 1169 1.1.1.4 mrg gomp_fatal ("priority_queue_upgrade_task: cannot find task=%p", task); 1170 1.1.1.4 mrg #endif 1171 1.1.1.4 mrg if (priority_queue_multi_p (head)) 1172 1.1.1.4 mrg { 1173 1.1.1.4 mrg struct priority_list *list 1174 1.1.1.4 mrg = priority_queue_lookup_priority (head, task->priority); 1175 1.1.1.4 mrg priority_list_upgrade_task (list, node); 1176 1.1.1.4 mrg } 1177 1.1.1.4 mrg else 1178 1.1.1.4 mrg priority_list_upgrade_task (&head->l, node); 1179 1.1.1.4 mrg } 1180 1.1.1.4 mrg 1181 1.1.1.4 mrg /* Given a CHILD_TASK in LIST that is about to be executed, move it out of 1182 1.1.1.4 mrg the way in LIST so that other tasks can be considered for 1183 1.1.1.4 mrg execution. LIST contains tasks of type TYPE. 1184 1.1.1.4 mrg 1185 1.1.1.4 mrg Care is taken to update the queue's LAST_PARENT_DEPENDS_ON field 1186 1.1.1.4 mrg if applicable. */ 1187 1.1.1.4 mrg 1188 1.1.1.4 mrg static void inline 1189 1.1.1.4 mrg priority_list_downgrade_task (enum priority_queue_type type, 1190 1.1.1.4 mrg struct priority_list *list, 1191 1.1.1.4 mrg struct gomp_task *child_task) 1192 1.1.1.4 mrg { 1193 1.1.1.4 mrg struct priority_node *node = task_to_priority_node (type, child_task); 1194 1.1.1.4 mrg if (list->tasks == node) 1195 1.1.1.4 mrg list->tasks = node->next; 1196 1.1.1.4 mrg else if (node->next != list->tasks) 1197 1.1.1.4 mrg { 1198 1.1.1.4 mrg /* The task in NODE is about to become TIED and TIED tasks 1199 1.1.1.4 mrg cannot come before WAITING tasks. If we're about to 1200 1.1.1.4 mrg leave the queue in such an indeterminate state, rewire 1201 1.1.1.4 mrg things appropriately. However, a TIED task at the end is 1202 1.1.1.4 mrg perfectly fine. */ 1203 1.1.1.4 mrg struct gomp_task *next_task = priority_node_to_task (type, node->next); 1204 1.1.1.4 mrg if (next_task->kind == GOMP_TASK_WAITING) 1205 1.1.1.4 mrg { 1206 1.1.1.4 mrg /* Remove from list. */ 1207 1.1.1.4 mrg node->prev->next = node->next; 1208 1.1.1.4 mrg node->next->prev = node->prev; 1209 1.1.1.4 mrg /* Rewire at the end. */ 1210 1.1.1.4 mrg node->next = list->tasks; 1211 1.1.1.4 mrg node->prev = list->tasks->prev; 1212 1.1.1.4 mrg list->tasks->prev->next = node; 1213 1.1.1.4 mrg list->tasks->prev = node; 1214 1.1.1.4 mrg } 1215 1.1.1.4 mrg } 1216 1.1.1.4 mrg 1217 1.1.1.4 mrg /* If the current task is the last_parent_depends_on for its 1218 1.1.1.4 mrg priority, adjust last_parent_depends_on appropriately. */ 1219 1.1.1.4 mrg if (__builtin_expect (child_task->parent_depends_on, 0) 1220 1.1.1.4 mrg && list->last_parent_depends_on == node) 1221 1.1.1.4 mrg { 1222 1.1.1.4 mrg struct gomp_task *prev_child = priority_node_to_task (type, node->prev); 1223 1.1.1.4 mrg if (node->prev != node 1224 1.1.1.4 mrg && prev_child->kind == GOMP_TASK_WAITING 1225 1.1.1.4 mrg && prev_child->parent_depends_on) 1226 1.1.1.4 mrg list->last_parent_depends_on = node->prev; 1227 1.1.1.4 mrg else 1228 1.1.1.4 mrg { 1229 1.1.1.4 mrg /* There are no more parent_depends_on entries waiting 1230 1.1.1.4 mrg to run, clear the list. */ 1231 1.1.1.4 mrg list->last_parent_depends_on = NULL; 1232 1.1.1.4 mrg } 1233 1.1.1.4 mrg } 1234 1.1.1.4 mrg } 1235 1.1.1.4 mrg 1236 1.1.1.4 mrg /* Given a TASK in HEAD that is about to be executed, move it out of 1237 1.1.1.4 mrg the way so that other tasks can be considered for execution. HEAD 1238 1.1.1.4 mrg contains tasks of type TYPE. 1239 1.1.1.4 mrg 1240 1.1.1.4 mrg Care is taken to update the queue's LAST_PARENT_DEPENDS_ON field 1241 1.1.1.4 mrg if applicable. 1242 1.1.1.4 mrg 1243 1.1.1.4 mrg (This function could be defined in priority_queue.c, but we want it 1244 1.1.1.4 mrg inlined, and putting it in priority_queue.h is not an option, given 1245 1.1.1.4 mrg that gomp_task has not been properly defined at that point). */ 1246 1.1.1.4 mrg 1247 1.1.1.4 mrg static void inline 1248 1.1.1.4 mrg priority_queue_downgrade_task (enum priority_queue_type type, 1249 1.1.1.4 mrg struct priority_queue *head, 1250 1.1.1.4 mrg struct gomp_task *task) 1251 1.1.1.4 mrg { 1252 1.1.1.4 mrg #if _LIBGOMP_CHECKING_ 1253 1.1.1.4 mrg if (!priority_queue_task_in_queue_p (type, head, task)) 1254 1.1.1.4 mrg gomp_fatal ("Attempt to downgrade missing task %p", task); 1255 1.1.1.4 mrg #endif 1256 1.1.1.4 mrg if (priority_queue_multi_p (head)) 1257 1.1.1.4 mrg { 1258 1.1.1.4 mrg struct priority_list *list 1259 1.1.1.4 mrg = priority_queue_lookup_priority (head, task->priority); 1260 1.1.1.4 mrg priority_list_downgrade_task (type, list, task); 1261 1.1.1.4 mrg } 1262 1.1.1.4 mrg else 1263 1.1.1.4 mrg priority_list_downgrade_task (type, &head->l, task); 1264 1.1.1.4 mrg } 1265 1.1.1.4 mrg 1266 1.1.1.4 mrg /* Setup CHILD_TASK to execute. This is done by setting the task to 1267 1.1.1.4 mrg TIED, and updating all relevant queues so that CHILD_TASK is no 1268 1.1.1.4 mrg longer chosen for scheduling. Also, remove CHILD_TASK from the 1269 1.1.1.4 mrg overall team task queue entirely. 1270 1.1.1.4 mrg 1271 1.1.1.4 mrg Return TRUE if task or its containing taskgroup has been 1272 1.1.1.4 mrg cancelled. */ 1273 1.1.1.4 mrg 1274 1.1.1.4 mrg static inline bool 1275 1.1.1.4 mrg gomp_task_run_pre (struct gomp_task *child_task, struct gomp_task *parent, 1276 1.1.1.4 mrg struct gomp_team *team) 1277 1.1.1.4 mrg { 1278 1.1.1.4 mrg #if _LIBGOMP_CHECKING_ 1279 1.1.1.4 mrg if (child_task->parent) 1280 1.1.1.4 mrg priority_queue_verify (PQ_CHILDREN, 1281 1.1.1.4 mrg &child_task->parent->children_queue, true); 1282 1.1.1.4 mrg if (child_task->taskgroup) 1283 1.1.1.4 mrg priority_queue_verify (PQ_TASKGROUP, 1284 1.1.1.4 mrg &child_task->taskgroup->taskgroup_queue, false); 1285 1.1.1.4 mrg priority_queue_verify (PQ_TEAM, &team->task_queue, false); 1286 1.1.1.4 mrg #endif 1287 1.1.1.4 mrg 1288 1.1.1.4 mrg /* Task is about to go tied, move it out of the way. */ 1289 1.1.1.4 mrg if (parent) 1290 1.1.1.4 mrg priority_queue_downgrade_task (PQ_CHILDREN, &parent->children_queue, 1291 1.1.1.4 mrg child_task); 1292 1.1.1.4 mrg 1293 1.1.1.4 mrg /* Task is about to go tied, move it out of the way. */ 1294 1.1.1.4 mrg struct gomp_taskgroup *taskgroup = child_task->taskgroup; 1295 1.1.1.4 mrg if (taskgroup) 1296 1.1.1.4 mrg priority_queue_downgrade_task (PQ_TASKGROUP, &taskgroup->taskgroup_queue, 1297 1.1.1.4 mrg child_task); 1298 1.1.1.4 mrg 1299 1.1.1.4 mrg priority_queue_remove (PQ_TEAM, &team->task_queue, child_task, 1300 1.1.1.4 mrg MEMMODEL_RELAXED); 1301 1.1.1.4 mrg child_task->pnode[PQ_TEAM].next = NULL; 1302 1.1.1.4 mrg child_task->pnode[PQ_TEAM].prev = NULL; 1303 1.1.1.3 mrg child_task->kind = GOMP_TASK_TIED; 1304 1.1.1.4 mrg 1305 1.1.1.3 mrg if (--team->task_queued_count == 0) 1306 1.1.1.3 mrg gomp_team_barrier_clear_task_pending (&team->barrier); 1307 1.1.1.9 mrg if (__builtin_expect (gomp_cancel_var, 0) 1308 1.1.1.3 mrg && !child_task->copy_ctors_done) 1309 1.1.1.9 mrg { 1310 1.1.1.9 mrg if (gomp_team_barrier_cancelled (&team->barrier)) 1311 1.1.1.9 mrg return true; 1312 1.1.1.9 mrg if (taskgroup) 1313 1.1.1.9 mrg { 1314 1.1.1.9 mrg if (taskgroup->cancelled) 1315 1.1.1.9 mrg return true; 1316 1.1.1.9 mrg if (taskgroup->workshare 1317 1.1.1.9 mrg && taskgroup->prev 1318 1.1.1.9 mrg && taskgroup->prev->cancelled) 1319 1.1.1.9 mrg return true; 1320 1.1.1.9 mrg } 1321 1.1.1.9 mrg } 1322 1.1.1.3 mrg return false; 1323 1.1.1.3 mrg } 1324 1.1.1.3 mrg 1325 1.1.1.3 mrg static void 1326 1.1.1.3 mrg gomp_task_run_post_handle_depend_hash (struct gomp_task *child_task) 1327 1.1.1.3 mrg { 1328 1.1.1.3 mrg struct gomp_task *parent = child_task->parent; 1329 1.1.1.3 mrg size_t i; 1330 1.1.1.3 mrg 1331 1.1.1.14 mrg if (parent->depend_all_memory == child_task) 1332 1.1.1.14 mrg parent->depend_all_memory = NULL; 1333 1.1.1.3 mrg for (i = 0; i < child_task->depend_count; i++) 1334 1.1.1.3 mrg if (!child_task->depend[i].redundant) 1335 1.1.1.3 mrg { 1336 1.1.1.3 mrg if (child_task->depend[i].next) 1337 1.1.1.3 mrg child_task->depend[i].next->prev = child_task->depend[i].prev; 1338 1.1.1.3 mrg if (child_task->depend[i].prev) 1339 1.1.1.3 mrg child_task->depend[i].prev->next = child_task->depend[i].next; 1340 1.1.1.3 mrg else 1341 1.1.1.3 mrg { 1342 1.1.1.3 mrg hash_entry_type *slot 1343 1.1.1.3 mrg = htab_find_slot (&parent->depend_hash, &child_task->depend[i], 1344 1.1.1.3 mrg NO_INSERT); 1345 1.1.1.3 mrg if (*slot != &child_task->depend[i]) 1346 1.1.1.3 mrg abort (); 1347 1.1.1.3 mrg if (child_task->depend[i].next) 1348 1.1.1.3 mrg *slot = child_task->depend[i].next; 1349 1.1.1.3 mrg else 1350 1.1.1.3 mrg htab_clear_slot (parent->depend_hash, slot); 1351 1.1.1.3 mrg } 1352 1.1.1.3 mrg } 1353 1.1.1.3 mrg } 1354 1.1.1.3 mrg 1355 1.1.1.4 mrg /* After a CHILD_TASK has been run, adjust the dependency queue for 1356 1.1.1.4 mrg each task that depends on CHILD_TASK, to record the fact that there 1357 1.1.1.4 mrg is one less dependency to worry about. If a task that depended on 1358 1.1.1.4 mrg CHILD_TASK now has no dependencies, place it in the various queues 1359 1.1.1.4 mrg so it gets scheduled to run. 1360 1.1.1.4 mrg 1361 1.1.1.4 mrg TEAM is the team to which CHILD_TASK belongs to. */ 1362 1.1.1.4 mrg 1363 1.1.1.3 mrg static size_t 1364 1.1.1.3 mrg gomp_task_run_post_handle_dependers (struct gomp_task *child_task, 1365 1.1.1.3 mrg struct gomp_team *team) 1366 1.1.1.3 mrg { 1367 1.1.1.3 mrg struct gomp_task *parent = child_task->parent; 1368 1.1.1.3 mrg size_t i, count = child_task->dependers->n_elem, ret = 0; 1369 1.1.1.3 mrg for (i = 0; i < count; i++) 1370 1.1.1.3 mrg { 1371 1.1.1.3 mrg struct gomp_task *task = child_task->dependers->elem[i]; 1372 1.1.1.4 mrg 1373 1.1.1.4 mrg /* CHILD_TASK satisfies a dependency for TASK. Keep track of 1374 1.1.1.4 mrg TASK's remaining dependencies. Once TASK has no other 1375 1.1.1.10 mrg dependencies, put it into the various queues so it will get 1376 1.1.1.4 mrg scheduled for execution. */ 1377 1.1.1.3 mrg if (--task->num_dependees != 0) 1378 1.1.1.3 mrg continue; 1379 1.1.1.3 mrg 1380 1.1.1.3 mrg struct gomp_taskgroup *taskgroup = task->taskgroup; 1381 1.1.1.14 mrg if (__builtin_expect (task->fn == empty_task, 0)) 1382 1.1.1.14 mrg { 1383 1.1.1.14 mrg if (!parent) 1384 1.1.1.14 mrg task->parent = NULL; 1385 1.1.1.14 mrg else if (__builtin_expect (task->parent_depends_on, 0) 1386 1.1.1.14 mrg && --parent->taskwait->n_depend == 0 1387 1.1.1.14 mrg && parent->taskwait->in_depend_wait) 1388 1.1.1.14 mrg { 1389 1.1.1.14 mrg parent->taskwait->in_depend_wait = false; 1390 1.1.1.14 mrg gomp_sem_post (&parent->taskwait->taskwait_sem); 1391 1.1.1.14 mrg } 1392 1.1.1.14 mrg if (gomp_task_run_post_handle_depend (task, team)) 1393 1.1.1.14 mrg ++ret; 1394 1.1.1.14 mrg if (taskgroup) 1395 1.1.1.14 mrg { 1396 1.1.1.14 mrg if (taskgroup->num_children > 1) 1397 1.1.1.14 mrg --taskgroup->num_children; 1398 1.1.1.14 mrg else 1399 1.1.1.14 mrg { 1400 1.1.1.14 mrg __atomic_store_n (&taskgroup->num_children, 0, 1401 1.1.1.14 mrg MEMMODEL_RELEASE); 1402 1.1.1.14 mrg if (taskgroup->in_taskgroup_wait) 1403 1.1.1.14 mrg { 1404 1.1.1.14 mrg taskgroup->in_taskgroup_wait = false; 1405 1.1.1.14 mrg gomp_sem_post (&taskgroup->taskgroup_sem); 1406 1.1.1.14 mrg } 1407 1.1.1.14 mrg } 1408 1.1.1.14 mrg } 1409 1.1.1.14 mrg gomp_finish_task (task); 1410 1.1.1.14 mrg free (task); 1411 1.1.1.14 mrg continue; 1412 1.1.1.14 mrg } 1413 1.1.1.3 mrg if (parent) 1414 1.1.1.3 mrg { 1415 1.1.1.4 mrg priority_queue_insert (PQ_CHILDREN, &parent->children_queue, 1416 1.1.1.4 mrg task, task->priority, 1417 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, 1418 1.1.1.4 mrg /*adjust_parent_depends_on=*/true, 1419 1.1.1.4 mrg task->parent_depends_on); 1420 1.1.1.3 mrg if (parent->taskwait) 1421 1.1.1.3 mrg { 1422 1.1.1.3 mrg if (parent->taskwait->in_taskwait) 1423 1.1.1.3 mrg { 1424 1.1.1.4 mrg /* One more task has had its dependencies met. 1425 1.1.1.4 mrg Inform any waiters. */ 1426 1.1.1.3 mrg parent->taskwait->in_taskwait = false; 1427 1.1.1.3 mrg gomp_sem_post (&parent->taskwait->taskwait_sem); 1428 1.1.1.3 mrg } 1429 1.1.1.3 mrg else if (parent->taskwait->in_depend_wait) 1430 1.1.1.3 mrg { 1431 1.1.1.4 mrg /* One more task has had its dependencies met. 1432 1.1.1.4 mrg Inform any waiters. */ 1433 1.1.1.3 mrg parent->taskwait->in_depend_wait = false; 1434 1.1.1.3 mrg gomp_sem_post (&parent->taskwait->taskwait_sem); 1435 1.1.1.3 mrg } 1436 1.1.1.3 mrg } 1437 1.1.1.3 mrg } 1438 1.1.1.11 mrg else 1439 1.1.1.11 mrg task->parent = NULL; 1440 1.1.1.3 mrg if (taskgroup) 1441 1.1.1.3 mrg { 1442 1.1.1.4 mrg priority_queue_insert (PQ_TASKGROUP, &taskgroup->taskgroup_queue, 1443 1.1.1.4 mrg task, task->priority, 1444 1.1.1.4 mrg PRIORITY_INSERT_BEGIN, 1445 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 1446 1.1.1.4 mrg task->parent_depends_on); 1447 1.1.1.3 mrg if (taskgroup->in_taskgroup_wait) 1448 1.1.1.3 mrg { 1449 1.1.1.4 mrg /* One more task has had its dependencies met. 1450 1.1.1.4 mrg Inform any waiters. */ 1451 1.1.1.3 mrg taskgroup->in_taskgroup_wait = false; 1452 1.1.1.3 mrg gomp_sem_post (&taskgroup->taskgroup_sem); 1453 1.1.1.3 mrg } 1454 1.1.1.3 mrg } 1455 1.1.1.4 mrg priority_queue_insert (PQ_TEAM, &team->task_queue, 1456 1.1.1.4 mrg task, task->priority, 1457 1.1.1.4 mrg PRIORITY_INSERT_END, 1458 1.1.1.4 mrg /*adjust_parent_depends_on=*/false, 1459 1.1.1.4 mrg task->parent_depends_on); 1460 1.1.1.3 mrg ++team->task_count; 1461 1.1.1.3 mrg ++team->task_queued_count; 1462 1.1.1.3 mrg ++ret; 1463 1.1.1.3 mrg } 1464 1.1.1.3 mrg free (child_task->dependers); 1465 1.1.1.3 mrg child_task->dependers = NULL; 1466 1.1.1.3 mrg if (ret > 1) 1467 1.1.1.3 mrg gomp_team_barrier_set_task_pending (&team->barrier); 1468 1.1.1.3 mrg return ret; 1469 1.1.1.3 mrg } 1470 1.1.1.3 mrg 1471 1.1.1.3 mrg static inline size_t 1472 1.1.1.3 mrg gomp_task_run_post_handle_depend (struct gomp_task *child_task, 1473 1.1.1.3 mrg struct gomp_team *team) 1474 1.1.1.3 mrg { 1475 1.1.1.3 mrg if (child_task->depend_count == 0) 1476 1.1.1.3 mrg return 0; 1477 1.1.1.3 mrg 1478 1.1.1.3 mrg /* If parent is gone already, the hash table is freed and nothing 1479 1.1.1.3 mrg will use the hash table anymore, no need to remove anything from it. */ 1480 1.1.1.3 mrg if (child_task->parent != NULL) 1481 1.1.1.3 mrg gomp_task_run_post_handle_depend_hash (child_task); 1482 1.1.1.3 mrg 1483 1.1.1.3 mrg if (child_task->dependers == NULL) 1484 1.1.1.3 mrg return 0; 1485 1.1.1.3 mrg 1486 1.1.1.3 mrg return gomp_task_run_post_handle_dependers (child_task, team); 1487 1.1.1.3 mrg } 1488 1.1.1.3 mrg 1489 1.1.1.4 mrg /* Remove CHILD_TASK from its parent. */ 1490 1.1.1.4 mrg 1491 1.1.1.3 mrg static inline void 1492 1.1.1.3 mrg gomp_task_run_post_remove_parent (struct gomp_task *child_task) 1493 1.1.1.3 mrg { 1494 1.1.1.3 mrg struct gomp_task *parent = child_task->parent; 1495 1.1.1.3 mrg if (parent == NULL) 1496 1.1.1.3 mrg return; 1497 1.1.1.4 mrg 1498 1.1.1.4 mrg /* If this was the last task the parent was depending on, 1499 1.1.1.4 mrg synchronize with gomp_task_maybe_wait_for_dependencies so it can 1500 1.1.1.4 mrg clean up and return. */ 1501 1.1.1.3 mrg if (__builtin_expect (child_task->parent_depends_on, 0) 1502 1.1.1.3 mrg && --parent->taskwait->n_depend == 0 1503 1.1.1.3 mrg && parent->taskwait->in_depend_wait) 1504 1.1.1.3 mrg { 1505 1.1.1.3 mrg parent->taskwait->in_depend_wait = false; 1506 1.1.1.3 mrg gomp_sem_post (&parent->taskwait->taskwait_sem); 1507 1.1.1.3 mrg } 1508 1.1.1.4 mrg 1509 1.1.1.4 mrg if (priority_queue_remove (PQ_CHILDREN, &parent->children_queue, 1510 1.1.1.4 mrg child_task, MEMMODEL_RELEASE) 1511 1.1.1.4 mrg && parent->taskwait && parent->taskwait->in_taskwait) 1512 1.1.1.3 mrg { 1513 1.1.1.4 mrg parent->taskwait->in_taskwait = false; 1514 1.1.1.4 mrg gomp_sem_post (&parent->taskwait->taskwait_sem); 1515 1.1.1.3 mrg } 1516 1.1.1.4 mrg child_task->pnode[PQ_CHILDREN].next = NULL; 1517 1.1.1.4 mrg child_task->pnode[PQ_CHILDREN].prev = NULL; 1518 1.1.1.3 mrg } 1519 1.1.1.3 mrg 1520 1.1.1.4 mrg /* Remove CHILD_TASK from its taskgroup. */ 1521 1.1.1.4 mrg 1522 1.1.1.3 mrg static inline void 1523 1.1.1.3 mrg gomp_task_run_post_remove_taskgroup (struct gomp_task *child_task) 1524 1.1.1.3 mrg { 1525 1.1.1.3 mrg struct gomp_taskgroup *taskgroup = child_task->taskgroup; 1526 1.1.1.3 mrg if (taskgroup == NULL) 1527 1.1.1.3 mrg return; 1528 1.1.1.4 mrg bool empty = priority_queue_remove (PQ_TASKGROUP, 1529 1.1.1.4 mrg &taskgroup->taskgroup_queue, 1530 1.1.1.4 mrg child_task, MEMMODEL_RELAXED); 1531 1.1.1.4 mrg child_task->pnode[PQ_TASKGROUP].next = NULL; 1532 1.1.1.4 mrg child_task->pnode[PQ_TASKGROUP].prev = NULL; 1533 1.1.1.3 mrg if (taskgroup->num_children > 1) 1534 1.1.1.3 mrg --taskgroup->num_children; 1535 1.1.1.3 mrg else 1536 1.1.1.3 mrg { 1537 1.1.1.3 mrg /* We access taskgroup->num_children in GOMP_taskgroup_end 1538 1.1.1.3 mrg outside of the task lock mutex region, so 1539 1.1.1.3 mrg need a release barrier here to ensure memory 1540 1.1.1.3 mrg written by child_task->fn above is flushed 1541 1.1.1.3 mrg before the NULL is written. */ 1542 1.1.1.3 mrg __atomic_store_n (&taskgroup->num_children, 0, MEMMODEL_RELEASE); 1543 1.1.1.3 mrg } 1544 1.1.1.4 mrg if (empty && taskgroup->in_taskgroup_wait) 1545 1.1.1.3 mrg { 1546 1.1.1.4 mrg taskgroup->in_taskgroup_wait = false; 1547 1.1.1.4 mrg gomp_sem_post (&taskgroup->taskgroup_sem); 1548 1.1.1.3 mrg } 1549 1.1.1.3 mrg } 1550 1.1.1.3 mrg 1551 1.1 mrg void 1552 1.1 mrg gomp_barrier_handle_tasks (gomp_barrier_state_t state) 1553 1.1 mrg { 1554 1.1 mrg struct gomp_thread *thr = gomp_thread (); 1555 1.1 mrg struct gomp_team *team = thr->ts.team; 1556 1.1 mrg struct gomp_task *task = thr->task; 1557 1.1 mrg struct gomp_task *child_task = NULL; 1558 1.1 mrg struct gomp_task *to_free = NULL; 1559 1.1.1.3 mrg int do_wake = 0; 1560 1.1 mrg 1561 1.1 mrg gomp_mutex_lock (&team->task_lock); 1562 1.1 mrg if (gomp_barrier_last_thread (state)) 1563 1.1 mrg { 1564 1.1 mrg if (team->task_count == 0) 1565 1.1 mrg { 1566 1.1 mrg gomp_team_barrier_done (&team->barrier, state); 1567 1.1 mrg gomp_mutex_unlock (&team->task_lock); 1568 1.1 mrg gomp_team_barrier_wake (&team->barrier, 0); 1569 1.1 mrg return; 1570 1.1 mrg } 1571 1.1 mrg gomp_team_barrier_set_waiting_for_tasks (&team->barrier); 1572 1.1 mrg } 1573 1.1 mrg 1574 1.1 mrg while (1) 1575 1.1 mrg { 1576 1.1.1.3 mrg bool cancelled = false; 1577 1.1.1.13 mrg 1578 1.1.1.4 mrg if (!priority_queue_empty_p (&team->task_queue, MEMMODEL_RELAXED)) 1579 1.1 mrg { 1580 1.1.1.4 mrg bool ignored; 1581 1.1.1.4 mrg child_task 1582 1.1.1.4 mrg = priority_queue_next_task (PQ_TEAM, &team->task_queue, 1583 1.1.1.4 mrg PQ_IGNORED, NULL, 1584 1.1.1.4 mrg &ignored); 1585 1.1.1.3 mrg cancelled = gomp_task_run_pre (child_task, child_task->parent, 1586 1.1.1.4 mrg team); 1587 1.1.1.3 mrg if (__builtin_expect (cancelled, 0)) 1588 1.1.1.3 mrg { 1589 1.1.1.3 mrg if (to_free) 1590 1.1.1.3 mrg { 1591 1.1.1.3 mrg gomp_finish_task (to_free); 1592 1.1.1.3 mrg free (to_free); 1593 1.1.1.3 mrg to_free = NULL; 1594 1.1.1.3 mrg } 1595 1.1.1.3 mrg goto finish_cancelled; 1596 1.1.1.3 mrg } 1597 1.1 mrg team->task_running_count++; 1598 1.1.1.3 mrg child_task->in_tied_task = true; 1599 1.1 mrg } 1600 1.1.1.13 mrg else if (team->task_count == 0 1601 1.1.1.13 mrg && gomp_team_barrier_waiting_for_tasks (&team->barrier)) 1602 1.1.1.13 mrg { 1603 1.1.1.13 mrg gomp_team_barrier_done (&team->barrier, state); 1604 1.1.1.13 mrg gomp_mutex_unlock (&team->task_lock); 1605 1.1.1.13 mrg gomp_team_barrier_wake (&team->barrier, 0); 1606 1.1.1.13 mrg if (to_free) 1607 1.1.1.13 mrg { 1608 1.1.1.13 mrg gomp_finish_task (to_free); 1609 1.1.1.13 mrg free (to_free); 1610 1.1.1.13 mrg } 1611 1.1.1.13 mrg return; 1612 1.1.1.13 mrg } 1613 1.1 mrg gomp_mutex_unlock (&team->task_lock); 1614 1.1.1.3 mrg if (do_wake) 1615 1.1.1.3 mrg { 1616 1.1.1.3 mrg gomp_team_barrier_wake (&team->barrier, do_wake); 1617 1.1.1.3 mrg do_wake = 0; 1618 1.1.1.3 mrg } 1619 1.1 mrg if (to_free) 1620 1.1 mrg { 1621 1.1 mrg gomp_finish_task (to_free); 1622 1.1 mrg free (to_free); 1623 1.1 mrg to_free = NULL; 1624 1.1 mrg } 1625 1.1 mrg if (child_task) 1626 1.1 mrg { 1627 1.1 mrg thr->task = child_task; 1628 1.1.1.4 mrg if (__builtin_expect (child_task->fn == NULL, 0)) 1629 1.1.1.4 mrg { 1630 1.1.1.4 mrg if (gomp_target_task_fn (child_task->fn_data)) 1631 1.1.1.4 mrg { 1632 1.1.1.4 mrg thr->task = task; 1633 1.1.1.4 mrg gomp_mutex_lock (&team->task_lock); 1634 1.1.1.4 mrg child_task->kind = GOMP_TASK_ASYNC_RUNNING; 1635 1.1.1.4 mrg team->task_running_count--; 1636 1.1.1.4 mrg struct gomp_target_task *ttask 1637 1.1.1.4 mrg = (struct gomp_target_task *) child_task->fn_data; 1638 1.1.1.4 mrg /* If GOMP_PLUGIN_target_task_completion has run already 1639 1.1.1.4 mrg in between gomp_target_task_fn and the mutex lock, 1640 1.1.1.4 mrg perform the requeuing here. */ 1641 1.1.1.4 mrg if (ttask->state == GOMP_TARGET_TASK_FINISHED) 1642 1.1.1.4 mrg gomp_target_task_completion (team, child_task); 1643 1.1.1.4 mrg else 1644 1.1.1.4 mrg ttask->state = GOMP_TARGET_TASK_RUNNING; 1645 1.1.1.4 mrg child_task = NULL; 1646 1.1.1.4 mrg continue; 1647 1.1.1.4 mrg } 1648 1.1.1.4 mrg } 1649 1.1.1.4 mrg else 1650 1.1.1.4 mrg child_task->fn (child_task->fn_data); 1651 1.1 mrg thr->task = task; 1652 1.1 mrg } 1653 1.1 mrg else 1654 1.1 mrg return; 1655 1.1 mrg gomp_mutex_lock (&team->task_lock); 1656 1.1 mrg if (child_task) 1657 1.1 mrg { 1658 1.1.1.13 mrg if (child_task->detach_team) 1659 1.1.1.13 mrg { 1660 1.1.1.13 mrg assert (child_task->detach_team == team); 1661 1.1.1.13 mrg child_task->kind = GOMP_TASK_DETACHED; 1662 1.1.1.13 mrg ++team->task_detach_count; 1663 1.1.1.13 mrg --team->task_running_count; 1664 1.1.1.13 mrg gomp_debug (0, 1665 1.1.1.13 mrg "thread %d: task with event %p finished without " 1666 1.1.1.13 mrg "completion event fulfilled in team barrier\n", 1667 1.1.1.13 mrg thr->ts.team_id, child_task); 1668 1.1.1.13 mrg child_task = NULL; 1669 1.1.1.13 mrg continue; 1670 1.1.1.13 mrg } 1671 1.1.1.13 mrg 1672 1.1.1.3 mrg finish_cancelled:; 1673 1.1.1.3 mrg size_t new_tasks 1674 1.1.1.3 mrg = gomp_task_run_post_handle_depend (child_task, team); 1675 1.1.1.3 mrg gomp_task_run_post_remove_parent (child_task); 1676 1.1.1.4 mrg gomp_clear_parent (&child_task->children_queue); 1677 1.1.1.3 mrg gomp_task_run_post_remove_taskgroup (child_task); 1678 1.1 mrg to_free = child_task; 1679 1.1.1.3 mrg if (!cancelled) 1680 1.1.1.3 mrg team->task_running_count--; 1681 1.1.1.13 mrg child_task = NULL; 1682 1.1.1.3 mrg if (new_tasks > 1) 1683 1.1.1.3 mrg { 1684 1.1.1.3 mrg do_wake = team->nthreads - team->task_running_count; 1685 1.1.1.3 mrg if (do_wake > new_tasks) 1686 1.1.1.3 mrg do_wake = new_tasks; 1687 1.1.1.3 mrg } 1688 1.1.1.13 mrg --team->task_count; 1689 1.1 mrg } 1690 1.1 mrg } 1691 1.1 mrg } 1692 1.1 mrg 1693 1.1.1.4 mrg /* Called when encountering a taskwait directive. 1694 1.1.1.4 mrg 1695 1.1.1.4 mrg Wait for all children of the current task. */ 1696 1.1 mrg 1697 1.1 mrg void 1698 1.1 mrg GOMP_taskwait (void) 1699 1.1 mrg { 1700 1.1 mrg struct gomp_thread *thr = gomp_thread (); 1701 1.1 mrg struct gomp_team *team = thr->ts.team; 1702 1.1 mrg struct gomp_task *task = thr->task; 1703 1.1 mrg struct gomp_task *child_task = NULL; 1704 1.1 mrg struct gomp_task *to_free = NULL; 1705 1.1.1.3 mrg struct gomp_taskwait taskwait; 1706 1.1.1.3 mrg int do_wake = 0; 1707 1.1 mrg 1708 1.1.1.2 mrg /* The acquire barrier on load of task->children here synchronizes 1709 1.1.1.3 mrg with the write of a NULL in gomp_task_run_post_remove_parent. It is 1710 1.1.1.2 mrg not necessary that we synchronize with other non-NULL writes at 1711 1.1.1.2 mrg this point, but we must ensure that all writes to memory by a 1712 1.1.1.2 mrg child thread task work function are seen before we exit from 1713 1.1.1.2 mrg GOMP_taskwait. */ 1714 1.1.1.2 mrg if (task == NULL 1715 1.1.1.4 mrg || priority_queue_empty_p (&task->children_queue, MEMMODEL_ACQUIRE)) 1716 1.1 mrg return; 1717 1.1.1.2 mrg 1718 1.1.1.3 mrg memset (&taskwait, 0, sizeof (taskwait)); 1719 1.1.1.4 mrg bool child_q = false; 1720 1.1 mrg gomp_mutex_lock (&team->task_lock); 1721 1.1 mrg while (1) 1722 1.1 mrg { 1723 1.1.1.3 mrg bool cancelled = false; 1724 1.1.1.4 mrg if (priority_queue_empty_p (&task->children_queue, MEMMODEL_RELAXED)) 1725 1.1 mrg { 1726 1.1.1.3 mrg bool destroy_taskwait = task->taskwait != NULL; 1727 1.1.1.3 mrg task->taskwait = NULL; 1728 1.1 mrg gomp_mutex_unlock (&team->task_lock); 1729 1.1 mrg if (to_free) 1730 1.1 mrg { 1731 1.1 mrg gomp_finish_task (to_free); 1732 1.1 mrg free (to_free); 1733 1.1 mrg } 1734 1.1.1.3 mrg if (destroy_taskwait) 1735 1.1.1.3 mrg gomp_sem_destroy (&taskwait.taskwait_sem); 1736 1.1 mrg return; 1737 1.1 mrg } 1738 1.1.1.4 mrg struct gomp_task *next_task 1739 1.1.1.4 mrg = priority_queue_next_task (PQ_CHILDREN, &task->children_queue, 1740 1.1.1.4 mrg PQ_TEAM, &team->task_queue, &child_q); 1741 1.1.1.4 mrg if (next_task->kind == GOMP_TASK_WAITING) 1742 1.1 mrg { 1743 1.1.1.4 mrg child_task = next_task; 1744 1.1.1.3 mrg cancelled 1745 1.1.1.4 mrg = gomp_task_run_pre (child_task, task, team); 1746 1.1.1.3 mrg if (__builtin_expect (cancelled, 0)) 1747 1.1 mrg { 1748 1.1.1.3 mrg if (to_free) 1749 1.1.1.3 mrg { 1750 1.1.1.3 mrg gomp_finish_task (to_free); 1751 1.1.1.3 mrg free (to_free); 1752 1.1.1.3 mrg to_free = NULL; 1753 1.1.1.3 mrg } 1754 1.1.1.3 mrg goto finish_cancelled; 1755 1.1 mrg } 1756 1.1 mrg } 1757 1.1 mrg else 1758 1.1.1.3 mrg { 1759 1.1.1.4 mrg /* All tasks we are waiting for are either running in other 1760 1.1.1.13 mrg threads, are detached and waiting for the completion event to be 1761 1.1.1.13 mrg fulfilled, or they are tasks that have not had their 1762 1.1.1.4 mrg dependencies met (so they're not even in the queue). Wait 1763 1.1.1.4 mrg for them. */ 1764 1.1.1.3 mrg if (task->taskwait == NULL) 1765 1.1.1.3 mrg { 1766 1.1.1.3 mrg taskwait.in_depend_wait = false; 1767 1.1.1.3 mrg gomp_sem_init (&taskwait.taskwait_sem, 0); 1768 1.1.1.3 mrg task->taskwait = &taskwait; 1769 1.1.1.3 mrg } 1770 1.1.1.3 mrg taskwait.in_taskwait = true; 1771 1.1.1.3 mrg } 1772 1.1 mrg gomp_mutex_unlock (&team->task_lock); 1773 1.1.1.3 mrg if (do_wake) 1774 1.1.1.3 mrg { 1775 1.1.1.3 mrg gomp_team_barrier_wake (&team->barrier, do_wake); 1776 1.1.1.3 mrg do_wake = 0; 1777 1.1.1.3 mrg } 1778 1.1 mrg if (to_free) 1779 1.1 mrg { 1780 1.1 mrg gomp_finish_task (to_free); 1781 1.1 mrg free (to_free); 1782 1.1 mrg to_free = NULL; 1783 1.1 mrg } 1784 1.1 mrg if (child_task) 1785 1.1 mrg { 1786 1.1 mrg thr->task = child_task; 1787 1.1.1.4 mrg if (__builtin_expect (child_task->fn == NULL, 0)) 1788 1.1.1.4 mrg { 1789 1.1.1.4 mrg if (gomp_target_task_fn (child_task->fn_data)) 1790 1.1.1.4 mrg { 1791 1.1.1.4 mrg thr->task = task; 1792 1.1.1.4 mrg gomp_mutex_lock (&team->task_lock); 1793 1.1.1.4 mrg child_task->kind = GOMP_TASK_ASYNC_RUNNING; 1794 1.1.1.4 mrg struct gomp_target_task *ttask 1795 1.1.1.4 mrg = (struct gomp_target_task *) child_task->fn_data; 1796 1.1.1.4 mrg /* If GOMP_PLUGIN_target_task_completion has run already 1797 1.1.1.4 mrg in between gomp_target_task_fn and the mutex lock, 1798 1.1.1.4 mrg perform the requeuing here. */ 1799 1.1.1.4 mrg if (ttask->state == GOMP_TARGET_TASK_FINISHED) 1800 1.1.1.4 mrg gomp_target_task_completion (team, child_task); 1801 1.1.1.4 mrg else 1802 1.1.1.4 mrg ttask->state = GOMP_TARGET_TASK_RUNNING; 1803 1.1.1.4 mrg child_task = NULL; 1804 1.1.1.4 mrg continue; 1805 1.1.1.4 mrg } 1806 1.1.1.4 mrg } 1807 1.1.1.4 mrg else 1808 1.1.1.4 mrg child_task->fn (child_task->fn_data); 1809 1.1 mrg thr->task = task; 1810 1.1 mrg } 1811 1.1 mrg else 1812 1.1.1.3 mrg gomp_sem_wait (&taskwait.taskwait_sem); 1813 1.1.1.3 mrg gomp_mutex_lock (&team->task_lock); 1814 1.1.1.3 mrg if (child_task) 1815 1.1 mrg { 1816 1.1.1.13 mrg if (child_task->detach_team) 1817 1.1.1.13 mrg { 1818 1.1.1.13 mrg assert (child_task->detach_team == team); 1819 1.1.1.13 mrg child_task->kind = GOMP_TASK_DETACHED; 1820 1.1.1.13 mrg ++team->task_detach_count; 1821 1.1.1.13 mrg gomp_debug (0, 1822 1.1.1.13 mrg "thread %d: task with event %p finished without " 1823 1.1.1.13 mrg "completion event fulfilled in taskwait\n", 1824 1.1.1.13 mrg thr->ts.team_id, child_task); 1825 1.1.1.13 mrg child_task = NULL; 1826 1.1.1.13 mrg continue; 1827 1.1.1.13 mrg } 1828 1.1.1.13 mrg 1829 1.1.1.3 mrg finish_cancelled:; 1830 1.1.1.3 mrg size_t new_tasks 1831 1.1.1.3 mrg = gomp_task_run_post_handle_depend (child_task, team); 1832 1.1.1.4 mrg 1833 1.1.1.4 mrg if (child_q) 1834 1.1.1.4 mrg { 1835 1.1.1.4 mrg priority_queue_remove (PQ_CHILDREN, &task->children_queue, 1836 1.1.1.4 mrg child_task, MEMMODEL_RELAXED); 1837 1.1.1.4 mrg child_task->pnode[PQ_CHILDREN].next = NULL; 1838 1.1.1.4 mrg child_task->pnode[PQ_CHILDREN].prev = NULL; 1839 1.1.1.3 mrg } 1840 1.1.1.4 mrg 1841 1.1.1.4 mrg gomp_clear_parent (&child_task->children_queue); 1842 1.1.1.4 mrg 1843 1.1.1.3 mrg gomp_task_run_post_remove_taskgroup (child_task); 1844 1.1.1.4 mrg 1845 1.1.1.3 mrg to_free = child_task; 1846 1.1.1.3 mrg child_task = NULL; 1847 1.1.1.3 mrg team->task_count--; 1848 1.1.1.3 mrg if (new_tasks > 1) 1849 1.1.1.3 mrg { 1850 1.1.1.3 mrg do_wake = team->nthreads - team->task_running_count 1851 1.1.1.3 mrg - !task->in_tied_task; 1852 1.1.1.3 mrg if (do_wake > new_tasks) 1853 1.1.1.3 mrg do_wake = new_tasks; 1854 1.1.1.3 mrg } 1855 1.1.1.3 mrg } 1856 1.1.1.3 mrg } 1857 1.1.1.3 mrg } 1858 1.1.1.3 mrg 1859 1.1.1.9 mrg /* Called when encountering a taskwait directive with depend clause(s). 1860 1.1.1.9 mrg Wait as if it was an mergeable included task construct with empty body. */ 1861 1.1.1.9 mrg 1862 1.1.1.9 mrg void 1863 1.1.1.9 mrg GOMP_taskwait_depend (void **depend) 1864 1.1.1.9 mrg { 1865 1.1.1.9 mrg struct gomp_thread *thr = gomp_thread (); 1866 1.1.1.9 mrg struct gomp_team *team = thr->ts.team; 1867 1.1.1.9 mrg 1868 1.1.1.9 mrg /* If parallel or taskgroup has been cancelled, return early. */ 1869 1.1.1.9 mrg if (__builtin_expect (gomp_cancel_var, 0) && team) 1870 1.1.1.9 mrg { 1871 1.1.1.9 mrg if (gomp_team_barrier_cancelled (&team->barrier)) 1872 1.1.1.9 mrg return; 1873 1.1.1.9 mrg if (thr->task->taskgroup) 1874 1.1.1.9 mrg { 1875 1.1.1.9 mrg if (thr->task->taskgroup->cancelled) 1876 1.1.1.9 mrg return; 1877 1.1.1.9 mrg if (thr->task->taskgroup->workshare 1878 1.1.1.9 mrg && thr->task->taskgroup->prev 1879 1.1.1.9 mrg && thr->task->taskgroup->prev->cancelled) 1880 1.1.1.9 mrg return; 1881 1.1.1.9 mrg } 1882 1.1.1.9 mrg } 1883 1.1.1.9 mrg 1884 1.1.1.9 mrg if (thr->task && thr->task->depend_hash) 1885 1.1.1.9 mrg gomp_task_maybe_wait_for_dependencies (depend); 1886 1.1.1.9 mrg } 1887 1.1.1.9 mrg 1888 1.1.1.14 mrg /* Called when encountering a taskwait directive with nowait and depend 1889 1.1.1.14 mrg clause(s). Create a possibly deferred task construct with empty body. */ 1890 1.1.1.14 mrg 1891 1.1.1.14 mrg void 1892 1.1.1.14 mrg GOMP_taskwait_depend_nowait (void **depend) 1893 1.1.1.14 mrg { 1894 1.1.1.14 mrg ialias_call (GOMP_task) (empty_task, "", NULL, 0, 1, true, 1895 1.1.1.14 mrg GOMP_TASK_FLAG_DEPEND, depend, 0, NULL); 1896 1.1.1.14 mrg } 1897 1.1.1.14 mrg 1898 1.1.1.4 mrg /* An undeferred task is about to run. Wait for all tasks that this 1899 1.1.1.4 mrg undeferred task depends on. 1900 1.1.1.3 mrg 1901 1.1.1.4 mrg This is done by first putting all known ready dependencies 1902 1.1.1.4 mrg (dependencies that have their own dependencies met) at the top of 1903 1.1.1.4 mrg the scheduling queues. Then we iterate through these imminently 1904 1.1.1.4 mrg ready tasks (and possibly other high priority tasks), and run them. 1905 1.1.1.4 mrg If we run out of ready dependencies to execute, we either wait for 1906 1.1.1.9 mrg the remaining dependencies to finish, or wait for them to get 1907 1.1.1.4 mrg scheduled so we can run them. 1908 1.1.1.4 mrg 1909 1.1.1.4 mrg DEPEND is as in GOMP_task. */ 1910 1.1.1.4 mrg 1911 1.1.1.4 mrg void 1912 1.1.1.3 mrg gomp_task_maybe_wait_for_dependencies (void **depend) 1913 1.1.1.3 mrg { 1914 1.1.1.3 mrg struct gomp_thread *thr = gomp_thread (); 1915 1.1.1.3 mrg struct gomp_task *task = thr->task; 1916 1.1.1.3 mrg struct gomp_team *team = thr->ts.team; 1917 1.1.1.3 mrg struct gomp_task_depend_entry elem, *ent = NULL; 1918 1.1.1.3 mrg struct gomp_taskwait taskwait; 1919 1.1.1.9 mrg size_t orig_ndepend = (uintptr_t) depend[0]; 1920 1.1.1.3 mrg size_t nout = (uintptr_t) depend[1]; 1921 1.1.1.9 mrg size_t ndepend = orig_ndepend; 1922 1.1.1.9 mrg size_t normal = ndepend; 1923 1.1.1.9 mrg size_t n = 2; 1924 1.1.1.3 mrg size_t i; 1925 1.1.1.3 mrg size_t num_awaited = 0; 1926 1.1.1.3 mrg struct gomp_task *child_task = NULL; 1927 1.1.1.3 mrg struct gomp_task *to_free = NULL; 1928 1.1.1.3 mrg int do_wake = 0; 1929 1.1.1.3 mrg 1930 1.1.1.9 mrg if (ndepend == 0) 1931 1.1.1.9 mrg { 1932 1.1.1.9 mrg ndepend = nout; 1933 1.1.1.9 mrg nout = (uintptr_t) depend[2] + (uintptr_t) depend[3]; 1934 1.1.1.9 mrg normal = nout + (uintptr_t) depend[4]; 1935 1.1.1.9 mrg n = 5; 1936 1.1.1.9 mrg } 1937 1.1.1.3 mrg gomp_mutex_lock (&team->task_lock); 1938 1.1.1.14 mrg if (__builtin_expect (task->depend_all_memory && ndepend, false)) 1939 1.1.1.14 mrg { 1940 1.1.1.14 mrg struct gomp_task *tsk = task->depend_all_memory; 1941 1.1.1.14 mrg if (!tsk->parent_depends_on) 1942 1.1.1.14 mrg { 1943 1.1.1.14 mrg tsk->parent_depends_on = true; 1944 1.1.1.14 mrg ++num_awaited; 1945 1.1.1.14 mrg if (tsk->num_dependees == 0 && tsk->kind == GOMP_TASK_WAITING) 1946 1.1.1.14 mrg priority_queue_upgrade_task (tsk, task); 1947 1.1.1.14 mrg } 1948 1.1.1.14 mrg } 1949 1.1.1.3 mrg for (i = 0; i < ndepend; i++) 1950 1.1.1.3 mrg { 1951 1.1.1.9 mrg elem.addr = depend[i + n]; 1952 1.1.1.9 mrg elem.is_in = i >= nout; 1953 1.1.1.9 mrg if (__builtin_expect (i >= normal, 0)) 1954 1.1.1.9 mrg { 1955 1.1.1.9 mrg void **d = (void **) elem.addr; 1956 1.1.1.9 mrg switch ((uintptr_t) d[1]) 1957 1.1.1.9 mrg { 1958 1.1.1.9 mrg case GOMP_DEPEND_IN: 1959 1.1.1.9 mrg break; 1960 1.1.1.9 mrg case GOMP_DEPEND_OUT: 1961 1.1.1.9 mrg case GOMP_DEPEND_INOUT: 1962 1.1.1.9 mrg case GOMP_DEPEND_MUTEXINOUTSET: 1963 1.1.1.9 mrg elem.is_in = 0; 1964 1.1.1.9 mrg break; 1965 1.1.1.14 mrg case GOMP_DEPEND_INOUTSET: 1966 1.1.1.14 mrg elem.is_in = 2; 1967 1.1.1.14 mrg break; 1968 1.1.1.9 mrg default: 1969 1.1.1.9 mrg gomp_fatal ("unknown omp_depend_t dependence type %d", 1970 1.1.1.9 mrg (int) (uintptr_t) d[1]); 1971 1.1.1.9 mrg } 1972 1.1.1.9 mrg elem.addr = d[0]; 1973 1.1.1.9 mrg } 1974 1.1.1.14 mrg if (__builtin_expect (elem.addr == NULL && !elem.is_in, false)) 1975 1.1.1.14 mrg { 1976 1.1.1.14 mrg size_t size = htab_size (task->depend_hash); 1977 1.1.1.14 mrg if (htab_elements (task->depend_hash) * 8 < size && size > 32) 1978 1.1.1.14 mrg htab_expand (task->depend_hash); 1979 1.1.1.14 mrg 1980 1.1.1.14 mrg /* depend(inout: omp_all_memory) - depend on all previous 1981 1.1.1.14 mrg sibling tasks that do have dependencies. Inlined 1982 1.1.1.14 mrg htab_traverse. */ 1983 1.1.1.14 mrg hash_entry_type *slot = &task->depend_hash->entries[0]; 1984 1.1.1.14 mrg hash_entry_type *end = slot + htab_size (task->depend_hash); 1985 1.1.1.14 mrg for (; slot != end; ++slot) 1986 1.1.1.14 mrg { 1987 1.1.1.14 mrg if (*slot == HTAB_EMPTY_ENTRY || *slot == HTAB_DELETED_ENTRY) 1988 1.1.1.14 mrg continue; 1989 1.1.1.14 mrg for (ent = *slot; ent; ent = ent->next) 1990 1.1.1.14 mrg { 1991 1.1.1.14 mrg struct gomp_task *tsk = ent->task; 1992 1.1.1.14 mrg if (!tsk->parent_depends_on) 1993 1.1.1.14 mrg { 1994 1.1.1.14 mrg tsk->parent_depends_on = true; 1995 1.1.1.14 mrg ++num_awaited; 1996 1.1.1.14 mrg if (tsk->num_dependees == 0 1997 1.1.1.14 mrg && tsk->kind == GOMP_TASK_WAITING) 1998 1.1.1.14 mrg priority_queue_upgrade_task (tsk, task); 1999 1.1.1.14 mrg } 2000 1.1.1.14 mrg } 2001 1.1.1.14 mrg } 2002 1.1.1.14 mrg break; 2003 1.1.1.14 mrg } 2004 1.1.1.3 mrg ent = htab_find (task->depend_hash, &elem); 2005 1.1.1.3 mrg for (; ent; ent = ent->next) 2006 1.1.1.14 mrg if (elem.is_in && elem.is_in == ent->is_in) 2007 1.1.1.3 mrg continue; 2008 1.1.1.3 mrg else 2009 1.1.1.3 mrg { 2010 1.1.1.3 mrg struct gomp_task *tsk = ent->task; 2011 1.1.1.3 mrg if (!tsk->parent_depends_on) 2012 1.1.1.3 mrg { 2013 1.1.1.3 mrg tsk->parent_depends_on = true; 2014 1.1.1.3 mrg ++num_awaited; 2015 1.1.1.10 mrg /* If dependency TSK itself has no dependencies and is 2016 1.1.1.4 mrg ready to run, move it up front so that we run it as 2017 1.1.1.4 mrg soon as possible. */ 2018 1.1.1.3 mrg if (tsk->num_dependees == 0 && tsk->kind == GOMP_TASK_WAITING) 2019 1.1.1.4 mrg priority_queue_upgrade_task (tsk, task); 2020 1.1.1.3 mrg } 2021 1.1.1.3 mrg } 2022 1.1.1.3 mrg } 2023 1.1.1.3 mrg if (num_awaited == 0) 2024 1.1.1.3 mrg { 2025 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 2026 1.1.1.3 mrg return; 2027 1.1.1.3 mrg } 2028 1.1.1.3 mrg 2029 1.1.1.3 mrg memset (&taskwait, 0, sizeof (taskwait)); 2030 1.1.1.3 mrg taskwait.n_depend = num_awaited; 2031 1.1.1.3 mrg gomp_sem_init (&taskwait.taskwait_sem, 0); 2032 1.1.1.3 mrg task->taskwait = &taskwait; 2033 1.1.1.3 mrg 2034 1.1.1.3 mrg while (1) 2035 1.1.1.3 mrg { 2036 1.1.1.3 mrg bool cancelled = false; 2037 1.1.1.3 mrg if (taskwait.n_depend == 0) 2038 1.1.1.3 mrg { 2039 1.1.1.3 mrg task->taskwait = NULL; 2040 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 2041 1.1.1.3 mrg if (to_free) 2042 1.1.1.3 mrg { 2043 1.1.1.3 mrg gomp_finish_task (to_free); 2044 1.1.1.3 mrg free (to_free); 2045 1.1.1.3 mrg } 2046 1.1.1.3 mrg gomp_sem_destroy (&taskwait.taskwait_sem); 2047 1.1 mrg return; 2048 1.1 mrg } 2049 1.1.1.4 mrg 2050 1.1.1.4 mrg /* Theoretically when we have multiple priorities, we should 2051 1.1.1.4 mrg chose between the highest priority item in 2052 1.1.1.4 mrg task->children_queue and team->task_queue here, so we should 2053 1.1.1.4 mrg use priority_queue_next_task(). However, since we are 2054 1.1.1.4 mrg running an undeferred task, perhaps that makes all tasks it 2055 1.1.1.4 mrg depends on undeferred, thus a priority of INF? This would 2056 1.1.1.4 mrg make it unnecessary to take anything into account here, 2057 1.1.1.4 mrg but the dependencies. 2058 1.1.1.4 mrg 2059 1.1.1.4 mrg On the other hand, if we want to use priority_queue_next_task(), 2060 1.1.1.4 mrg care should be taken to only use priority_queue_remove() 2061 1.1.1.4 mrg below if the task was actually removed from the children 2062 1.1.1.4 mrg queue. */ 2063 1.1.1.4 mrg bool ignored; 2064 1.1.1.4 mrg struct gomp_task *next_task 2065 1.1.1.4 mrg = priority_queue_next_task (PQ_CHILDREN, &task->children_queue, 2066 1.1.1.4 mrg PQ_IGNORED, NULL, &ignored); 2067 1.1.1.4 mrg 2068 1.1.1.4 mrg if (next_task->kind == GOMP_TASK_WAITING) 2069 1.1.1.3 mrg { 2070 1.1.1.4 mrg child_task = next_task; 2071 1.1.1.3 mrg cancelled 2072 1.1.1.4 mrg = gomp_task_run_pre (child_task, task, team); 2073 1.1.1.3 mrg if (__builtin_expect (cancelled, 0)) 2074 1.1.1.3 mrg { 2075 1.1.1.3 mrg if (to_free) 2076 1.1.1.3 mrg { 2077 1.1.1.3 mrg gomp_finish_task (to_free); 2078 1.1.1.3 mrg free (to_free); 2079 1.1.1.3 mrg to_free = NULL; 2080 1.1.1.3 mrg } 2081 1.1.1.3 mrg goto finish_cancelled; 2082 1.1.1.3 mrg } 2083 1.1.1.3 mrg } 2084 1.1.1.3 mrg else 2085 1.1.1.4 mrg /* All tasks we are waiting for are either running in other 2086 1.1.1.4 mrg threads, or they are tasks that have not had their 2087 1.1.1.4 mrg dependencies met (so they're not even in the queue). Wait 2088 1.1.1.4 mrg for them. */ 2089 1.1.1.3 mrg taskwait.in_depend_wait = true; 2090 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 2091 1.1.1.3 mrg if (do_wake) 2092 1.1.1.3 mrg { 2093 1.1.1.3 mrg gomp_team_barrier_wake (&team->barrier, do_wake); 2094 1.1.1.3 mrg do_wake = 0; 2095 1.1.1.3 mrg } 2096 1.1.1.3 mrg if (to_free) 2097 1.1.1.3 mrg { 2098 1.1.1.3 mrg gomp_finish_task (to_free); 2099 1.1.1.3 mrg free (to_free); 2100 1.1.1.3 mrg to_free = NULL; 2101 1.1.1.3 mrg } 2102 1.1.1.3 mrg if (child_task) 2103 1.1.1.3 mrg { 2104 1.1.1.3 mrg thr->task = child_task; 2105 1.1.1.4 mrg if (__builtin_expect (child_task->fn == NULL, 0)) 2106 1.1.1.4 mrg { 2107 1.1.1.4 mrg if (gomp_target_task_fn (child_task->fn_data)) 2108 1.1.1.4 mrg { 2109 1.1.1.4 mrg thr->task = task; 2110 1.1.1.4 mrg gomp_mutex_lock (&team->task_lock); 2111 1.1.1.4 mrg child_task->kind = GOMP_TASK_ASYNC_RUNNING; 2112 1.1.1.4 mrg struct gomp_target_task *ttask 2113 1.1.1.4 mrg = (struct gomp_target_task *) child_task->fn_data; 2114 1.1.1.4 mrg /* If GOMP_PLUGIN_target_task_completion has run already 2115 1.1.1.4 mrg in between gomp_target_task_fn and the mutex lock, 2116 1.1.1.4 mrg perform the requeuing here. */ 2117 1.1.1.4 mrg if (ttask->state == GOMP_TARGET_TASK_FINISHED) 2118 1.1.1.4 mrg gomp_target_task_completion (team, child_task); 2119 1.1.1.4 mrg else 2120 1.1.1.4 mrg ttask->state = GOMP_TARGET_TASK_RUNNING; 2121 1.1.1.4 mrg child_task = NULL; 2122 1.1.1.4 mrg continue; 2123 1.1.1.4 mrg } 2124 1.1.1.4 mrg } 2125 1.1.1.4 mrg else 2126 1.1.1.4 mrg child_task->fn (child_task->fn_data); 2127 1.1.1.3 mrg thr->task = task; 2128 1.1.1.3 mrg } 2129 1.1.1.3 mrg else 2130 1.1.1.3 mrg gomp_sem_wait (&taskwait.taskwait_sem); 2131 1.1 mrg gomp_mutex_lock (&team->task_lock); 2132 1.1 mrg if (child_task) 2133 1.1 mrg { 2134 1.1.1.3 mrg finish_cancelled:; 2135 1.1.1.3 mrg size_t new_tasks 2136 1.1.1.3 mrg = gomp_task_run_post_handle_depend (child_task, team); 2137 1.1.1.3 mrg if (child_task->parent_depends_on) 2138 1.1.1.3 mrg --taskwait.n_depend; 2139 1.1.1.4 mrg 2140 1.1.1.4 mrg priority_queue_remove (PQ_CHILDREN, &task->children_queue, 2141 1.1.1.4 mrg child_task, MEMMODEL_RELAXED); 2142 1.1.1.4 mrg child_task->pnode[PQ_CHILDREN].next = NULL; 2143 1.1.1.4 mrg child_task->pnode[PQ_CHILDREN].prev = NULL; 2144 1.1.1.4 mrg 2145 1.1.1.4 mrg gomp_clear_parent (&child_task->children_queue); 2146 1.1.1.3 mrg gomp_task_run_post_remove_taskgroup (child_task); 2147 1.1 mrg to_free = child_task; 2148 1.1 mrg child_task = NULL; 2149 1.1 mrg team->task_count--; 2150 1.1.1.3 mrg if (new_tasks > 1) 2151 1.1.1.3 mrg { 2152 1.1.1.3 mrg do_wake = team->nthreads - team->task_running_count 2153 1.1.1.3 mrg - !task->in_tied_task; 2154 1.1.1.3 mrg if (do_wake > new_tasks) 2155 1.1.1.3 mrg do_wake = new_tasks; 2156 1.1.1.3 mrg } 2157 1.1 mrg } 2158 1.1 mrg } 2159 1.1 mrg } 2160 1.1.1.2 mrg 2161 1.1.1.2 mrg /* Called when encountering a taskyield directive. */ 2162 1.1.1.2 mrg 2163 1.1.1.2 mrg void 2164 1.1.1.2 mrg GOMP_taskyield (void) 2165 1.1.1.2 mrg { 2166 1.1.1.2 mrg /* Nothing at the moment. */ 2167 1.1.1.2 mrg } 2168 1.1.1.2 mrg 2169 1.1.1.9 mrg static inline struct gomp_taskgroup * 2170 1.1.1.9 mrg gomp_taskgroup_init (struct gomp_taskgroup *prev) 2171 1.1.1.9 mrg { 2172 1.1.1.9 mrg struct gomp_taskgroup *taskgroup 2173 1.1.1.9 mrg = gomp_malloc (sizeof (struct gomp_taskgroup)); 2174 1.1.1.9 mrg taskgroup->prev = prev; 2175 1.1.1.9 mrg priority_queue_init (&taskgroup->taskgroup_queue); 2176 1.1.1.9 mrg taskgroup->reductions = prev ? prev->reductions : NULL; 2177 1.1.1.9 mrg taskgroup->in_taskgroup_wait = false; 2178 1.1.1.9 mrg taskgroup->cancelled = false; 2179 1.1.1.9 mrg taskgroup->workshare = false; 2180 1.1.1.9 mrg taskgroup->num_children = 0; 2181 1.1.1.9 mrg gomp_sem_init (&taskgroup->taskgroup_sem, 0); 2182 1.1.1.9 mrg return taskgroup; 2183 1.1.1.9 mrg } 2184 1.1.1.9 mrg 2185 1.1.1.3 mrg void 2186 1.1.1.3 mrg GOMP_taskgroup_start (void) 2187 1.1.1.3 mrg { 2188 1.1.1.3 mrg struct gomp_thread *thr = gomp_thread (); 2189 1.1.1.3 mrg struct gomp_team *team = thr->ts.team; 2190 1.1.1.3 mrg struct gomp_task *task = thr->task; 2191 1.1.1.3 mrg 2192 1.1.1.3 mrg /* If team is NULL, all tasks are executed as 2193 1.1.1.4 mrg GOMP_TASK_UNDEFERRED tasks and thus all children tasks of 2194 1.1.1.3 mrg taskgroup and their descendant tasks will be finished 2195 1.1.1.3 mrg by the time GOMP_taskgroup_end is called. */ 2196 1.1.1.3 mrg if (team == NULL) 2197 1.1.1.3 mrg return; 2198 1.1.1.9 mrg task->taskgroup = gomp_taskgroup_init (task->taskgroup); 2199 1.1.1.3 mrg } 2200 1.1.1.3 mrg 2201 1.1.1.3 mrg void 2202 1.1.1.3 mrg GOMP_taskgroup_end (void) 2203 1.1.1.3 mrg { 2204 1.1.1.3 mrg struct gomp_thread *thr = gomp_thread (); 2205 1.1.1.3 mrg struct gomp_team *team = thr->ts.team; 2206 1.1.1.3 mrg struct gomp_task *task = thr->task; 2207 1.1.1.3 mrg struct gomp_taskgroup *taskgroup; 2208 1.1.1.3 mrg struct gomp_task *child_task = NULL; 2209 1.1.1.3 mrg struct gomp_task *to_free = NULL; 2210 1.1.1.3 mrg int do_wake = 0; 2211 1.1.1.3 mrg 2212 1.1.1.3 mrg if (team == NULL) 2213 1.1.1.3 mrg return; 2214 1.1.1.3 mrg taskgroup = task->taskgroup; 2215 1.1.1.4 mrg if (__builtin_expect (taskgroup == NULL, 0) 2216 1.1.1.4 mrg && thr->ts.level == 0) 2217 1.1.1.4 mrg { 2218 1.1.1.4 mrg /* This can happen if GOMP_taskgroup_start is called when 2219 1.1.1.4 mrg thr->ts.team == NULL, but inside of the taskgroup there 2220 1.1.1.4 mrg is #pragma omp target nowait that creates an implicit 2221 1.1.1.4 mrg team with a single thread. In this case, we want to wait 2222 1.1.1.4 mrg for all outstanding tasks in this team. */ 2223 1.1.1.4 mrg gomp_team_barrier_wait (&team->barrier); 2224 1.1.1.4 mrg return; 2225 1.1.1.4 mrg } 2226 1.1.1.3 mrg 2227 1.1.1.3 mrg /* The acquire barrier on load of taskgroup->num_children here 2228 1.1.1.3 mrg synchronizes with the write of 0 in gomp_task_run_post_remove_taskgroup. 2229 1.1.1.3 mrg It is not necessary that we synchronize with other non-0 writes at 2230 1.1.1.3 mrg this point, but we must ensure that all writes to memory by a 2231 1.1.1.3 mrg child thread task work function are seen before we exit from 2232 1.1.1.3 mrg GOMP_taskgroup_end. */ 2233 1.1.1.3 mrg if (__atomic_load_n (&taskgroup->num_children, MEMMODEL_ACQUIRE) == 0) 2234 1.1.1.3 mrg goto finish; 2235 1.1.1.3 mrg 2236 1.1.1.4 mrg bool unused; 2237 1.1.1.3 mrg gomp_mutex_lock (&team->task_lock); 2238 1.1.1.3 mrg while (1) 2239 1.1.1.3 mrg { 2240 1.1.1.3 mrg bool cancelled = false; 2241 1.1.1.4 mrg if (priority_queue_empty_p (&taskgroup->taskgroup_queue, 2242 1.1.1.4 mrg MEMMODEL_RELAXED)) 2243 1.1.1.3 mrg { 2244 1.1.1.3 mrg if (taskgroup->num_children) 2245 1.1.1.3 mrg { 2246 1.1.1.4 mrg if (priority_queue_empty_p (&task->children_queue, 2247 1.1.1.4 mrg MEMMODEL_RELAXED)) 2248 1.1.1.3 mrg goto do_wait; 2249 1.1.1.4 mrg child_task 2250 1.1.1.4 mrg = priority_queue_next_task (PQ_CHILDREN, &task->children_queue, 2251 1.1.1.4 mrg PQ_TEAM, &team->task_queue, 2252 1.1.1.4 mrg &unused); 2253 1.1.1.4 mrg } 2254 1.1.1.4 mrg else 2255 1.1.1.3 mrg { 2256 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 2257 1.1.1.3 mrg if (to_free) 2258 1.1.1.3 mrg { 2259 1.1.1.3 mrg gomp_finish_task (to_free); 2260 1.1.1.3 mrg free (to_free); 2261 1.1.1.3 mrg } 2262 1.1.1.3 mrg goto finish; 2263 1.1.1.3 mrg } 2264 1.1.1.3 mrg } 2265 1.1.1.3 mrg else 2266 1.1.1.4 mrg child_task 2267 1.1.1.4 mrg = priority_queue_next_task (PQ_TASKGROUP, &taskgroup->taskgroup_queue, 2268 1.1.1.4 mrg PQ_TEAM, &team->task_queue, &unused); 2269 1.1.1.3 mrg if (child_task->kind == GOMP_TASK_WAITING) 2270 1.1.1.3 mrg { 2271 1.1.1.3 mrg cancelled 2272 1.1.1.4 mrg = gomp_task_run_pre (child_task, child_task->parent, team); 2273 1.1.1.3 mrg if (__builtin_expect (cancelled, 0)) 2274 1.1.1.3 mrg { 2275 1.1.1.3 mrg if (to_free) 2276 1.1.1.3 mrg { 2277 1.1.1.3 mrg gomp_finish_task (to_free); 2278 1.1.1.3 mrg free (to_free); 2279 1.1.1.3 mrg to_free = NULL; 2280 1.1.1.3 mrg } 2281 1.1.1.3 mrg goto finish_cancelled; 2282 1.1.1.3 mrg } 2283 1.1.1.3 mrg } 2284 1.1.1.3 mrg else 2285 1.1.1.3 mrg { 2286 1.1.1.3 mrg child_task = NULL; 2287 1.1.1.3 mrg do_wait: 2288 1.1.1.4 mrg /* All tasks we are waiting for are either running in other 2289 1.1.1.4 mrg threads, or they are tasks that have not had their 2290 1.1.1.4 mrg dependencies met (so they're not even in the queue). Wait 2291 1.1.1.4 mrg for them. */ 2292 1.1.1.3 mrg taskgroup->in_taskgroup_wait = true; 2293 1.1.1.3 mrg } 2294 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 2295 1.1.1.3 mrg if (do_wake) 2296 1.1.1.3 mrg { 2297 1.1.1.3 mrg gomp_team_barrier_wake (&team->barrier, do_wake); 2298 1.1.1.3 mrg do_wake = 0; 2299 1.1.1.3 mrg } 2300 1.1.1.3 mrg if (to_free) 2301 1.1.1.3 mrg { 2302 1.1.1.3 mrg gomp_finish_task (to_free); 2303 1.1.1.3 mrg free (to_free); 2304 1.1.1.3 mrg to_free = NULL; 2305 1.1.1.3 mrg } 2306 1.1.1.3 mrg if (child_task) 2307 1.1.1.3 mrg { 2308 1.1.1.3 mrg thr->task = child_task; 2309 1.1.1.4 mrg if (__builtin_expect (child_task->fn == NULL, 0)) 2310 1.1.1.4 mrg { 2311 1.1.1.4 mrg if (gomp_target_task_fn (child_task->fn_data)) 2312 1.1.1.4 mrg { 2313 1.1.1.4 mrg thr->task = task; 2314 1.1.1.4 mrg gomp_mutex_lock (&team->task_lock); 2315 1.1.1.4 mrg child_task->kind = GOMP_TASK_ASYNC_RUNNING; 2316 1.1.1.4 mrg struct gomp_target_task *ttask 2317 1.1.1.4 mrg = (struct gomp_target_task *) child_task->fn_data; 2318 1.1.1.4 mrg /* If GOMP_PLUGIN_target_task_completion has run already 2319 1.1.1.4 mrg in between gomp_target_task_fn and the mutex lock, 2320 1.1.1.4 mrg perform the requeuing here. */ 2321 1.1.1.4 mrg if (ttask->state == GOMP_TARGET_TASK_FINISHED) 2322 1.1.1.4 mrg gomp_target_task_completion (team, child_task); 2323 1.1.1.4 mrg else 2324 1.1.1.4 mrg ttask->state = GOMP_TARGET_TASK_RUNNING; 2325 1.1.1.4 mrg child_task = NULL; 2326 1.1.1.4 mrg continue; 2327 1.1.1.4 mrg } 2328 1.1.1.4 mrg } 2329 1.1.1.4 mrg else 2330 1.1.1.4 mrg child_task->fn (child_task->fn_data); 2331 1.1.1.3 mrg thr->task = task; 2332 1.1.1.3 mrg } 2333 1.1.1.3 mrg else 2334 1.1.1.3 mrg gomp_sem_wait (&taskgroup->taskgroup_sem); 2335 1.1.1.3 mrg gomp_mutex_lock (&team->task_lock); 2336 1.1.1.3 mrg if (child_task) 2337 1.1.1.3 mrg { 2338 1.1.1.13 mrg if (child_task->detach_team) 2339 1.1.1.13 mrg { 2340 1.1.1.13 mrg assert (child_task->detach_team == team); 2341 1.1.1.13 mrg child_task->kind = GOMP_TASK_DETACHED; 2342 1.1.1.13 mrg ++team->task_detach_count; 2343 1.1.1.13 mrg gomp_debug (0, 2344 1.1.1.13 mrg "thread %d: task with event %p finished without " 2345 1.1.1.13 mrg "completion event fulfilled in taskgroup\n", 2346 1.1.1.13 mrg thr->ts.team_id, child_task); 2347 1.1.1.13 mrg child_task = NULL; 2348 1.1.1.13 mrg continue; 2349 1.1.1.13 mrg } 2350 1.1.1.13 mrg 2351 1.1.1.3 mrg finish_cancelled:; 2352 1.1.1.3 mrg size_t new_tasks 2353 1.1.1.3 mrg = gomp_task_run_post_handle_depend (child_task, team); 2354 1.1.1.3 mrg gomp_task_run_post_remove_parent (child_task); 2355 1.1.1.4 mrg gomp_clear_parent (&child_task->children_queue); 2356 1.1.1.3 mrg gomp_task_run_post_remove_taskgroup (child_task); 2357 1.1.1.3 mrg to_free = child_task; 2358 1.1.1.3 mrg child_task = NULL; 2359 1.1.1.3 mrg team->task_count--; 2360 1.1.1.3 mrg if (new_tasks > 1) 2361 1.1.1.3 mrg { 2362 1.1.1.3 mrg do_wake = team->nthreads - team->task_running_count 2363 1.1.1.3 mrg - !task->in_tied_task; 2364 1.1.1.3 mrg if (do_wake > new_tasks) 2365 1.1.1.3 mrg do_wake = new_tasks; 2366 1.1.1.3 mrg } 2367 1.1.1.3 mrg } 2368 1.1.1.3 mrg } 2369 1.1.1.3 mrg 2370 1.1.1.3 mrg finish: 2371 1.1.1.3 mrg task->taskgroup = taskgroup->prev; 2372 1.1.1.3 mrg gomp_sem_destroy (&taskgroup->taskgroup_sem); 2373 1.1.1.3 mrg free (taskgroup); 2374 1.1.1.3 mrg } 2375 1.1.1.3 mrg 2376 1.1.1.9 mrg static inline __attribute__((always_inline)) void 2377 1.1.1.9 mrg gomp_reduction_register (uintptr_t *data, uintptr_t *old, uintptr_t *orig, 2378 1.1.1.9 mrg unsigned nthreads) 2379 1.1.1.9 mrg { 2380 1.1.1.9 mrg size_t total_cnt = 0; 2381 1.1.1.9 mrg uintptr_t *d = data; 2382 1.1.1.9 mrg struct htab *old_htab = NULL, *new_htab; 2383 1.1.1.9 mrg do 2384 1.1.1.9 mrg { 2385 1.1.1.9 mrg if (__builtin_expect (orig != NULL, 0)) 2386 1.1.1.9 mrg { 2387 1.1.1.9 mrg /* For worksharing task reductions, memory has been allocated 2388 1.1.1.9 mrg already by some other thread that encountered the construct 2389 1.1.1.9 mrg earlier. */ 2390 1.1.1.9 mrg d[2] = orig[2]; 2391 1.1.1.9 mrg d[6] = orig[6]; 2392 1.1.1.9 mrg orig = (uintptr_t *) orig[4]; 2393 1.1.1.9 mrg } 2394 1.1.1.9 mrg else 2395 1.1.1.9 mrg { 2396 1.1.1.9 mrg size_t sz = d[1] * nthreads; 2397 1.1.1.9 mrg /* Should use omp_alloc if d[3] is not -1. */ 2398 1.1.1.9 mrg void *ptr = gomp_aligned_alloc (d[2], sz); 2399 1.1.1.9 mrg memset (ptr, '\0', sz); 2400 1.1.1.9 mrg d[2] = (uintptr_t) ptr; 2401 1.1.1.9 mrg d[6] = d[2] + sz; 2402 1.1.1.9 mrg } 2403 1.1.1.9 mrg d[5] = 0; 2404 1.1.1.9 mrg total_cnt += d[0]; 2405 1.1.1.9 mrg if (d[4] == 0) 2406 1.1.1.9 mrg { 2407 1.1.1.9 mrg d[4] = (uintptr_t) old; 2408 1.1.1.9 mrg break; 2409 1.1.1.9 mrg } 2410 1.1.1.9 mrg else 2411 1.1.1.9 mrg d = (uintptr_t *) d[4]; 2412 1.1.1.9 mrg } 2413 1.1.1.9 mrg while (1); 2414 1.1.1.9 mrg if (old && old[5]) 2415 1.1.1.9 mrg { 2416 1.1.1.9 mrg old_htab = (struct htab *) old[5]; 2417 1.1.1.9 mrg total_cnt += htab_elements (old_htab); 2418 1.1.1.9 mrg } 2419 1.1.1.9 mrg new_htab = htab_create (total_cnt); 2420 1.1.1.9 mrg if (old_htab) 2421 1.1.1.9 mrg { 2422 1.1.1.9 mrg /* Copy old hash table, like in htab_expand. */ 2423 1.1.1.9 mrg hash_entry_type *p, *olimit; 2424 1.1.1.9 mrg new_htab->n_elements = htab_elements (old_htab); 2425 1.1.1.9 mrg olimit = old_htab->entries + old_htab->size; 2426 1.1.1.9 mrg p = old_htab->entries; 2427 1.1.1.9 mrg do 2428 1.1.1.9 mrg { 2429 1.1.1.9 mrg hash_entry_type x = *p; 2430 1.1.1.9 mrg if (x != HTAB_EMPTY_ENTRY && x != HTAB_DELETED_ENTRY) 2431 1.1.1.9 mrg *find_empty_slot_for_expand (new_htab, htab_hash (x)) = x; 2432 1.1.1.9 mrg p++; 2433 1.1.1.9 mrg } 2434 1.1.1.9 mrg while (p < olimit); 2435 1.1.1.9 mrg } 2436 1.1.1.9 mrg d = data; 2437 1.1.1.9 mrg do 2438 1.1.1.9 mrg { 2439 1.1.1.9 mrg size_t j; 2440 1.1.1.9 mrg for (j = 0; j < d[0]; ++j) 2441 1.1.1.9 mrg { 2442 1.1.1.9 mrg uintptr_t *p = d + 7 + j * 3; 2443 1.1.1.9 mrg p[2] = (uintptr_t) d; 2444 1.1.1.9 mrg /* Ugly hack, hash_entry_type is defined for the task dependencies, 2445 1.1.1.9 mrg which hash on the first element which is a pointer. We need 2446 1.1.1.9 mrg to hash also on the first sizeof (uintptr_t) bytes which contain 2447 1.1.1.9 mrg a pointer. Hide the cast from the compiler. */ 2448 1.1.1.9 mrg hash_entry_type n; 2449 1.1.1.9 mrg __asm ("" : "=g" (n) : "0" (p)); 2450 1.1.1.9 mrg *htab_find_slot (&new_htab, n, INSERT) = n; 2451 1.1.1.9 mrg } 2452 1.1.1.9 mrg if (d[4] == (uintptr_t) old) 2453 1.1.1.9 mrg break; 2454 1.1.1.9 mrg else 2455 1.1.1.9 mrg d = (uintptr_t *) d[4]; 2456 1.1.1.9 mrg } 2457 1.1.1.9 mrg while (1); 2458 1.1.1.9 mrg d[5] = (uintptr_t) new_htab; 2459 1.1.1.9 mrg } 2460 1.1.1.9 mrg 2461 1.1.1.9 mrg static void 2462 1.1.1.9 mrg gomp_create_artificial_team (void) 2463 1.1.1.9 mrg { 2464 1.1.1.9 mrg struct gomp_thread *thr = gomp_thread (); 2465 1.1.1.9 mrg struct gomp_task_icv *icv; 2466 1.1.1.9 mrg struct gomp_team *team = gomp_new_team (1); 2467 1.1.1.9 mrg struct gomp_task *task = thr->task; 2468 1.1.1.12 mrg struct gomp_task **implicit_task = &task; 2469 1.1.1.9 mrg icv = task ? &task->icv : &gomp_global_icv; 2470 1.1.1.9 mrg team->prev_ts = thr->ts; 2471 1.1.1.9 mrg thr->ts.team = team; 2472 1.1.1.9 mrg thr->ts.team_id = 0; 2473 1.1.1.9 mrg thr->ts.work_share = &team->work_shares[0]; 2474 1.1.1.9 mrg thr->ts.last_work_share = NULL; 2475 1.1.1.9 mrg #ifdef HAVE_SYNC_BUILTINS 2476 1.1.1.9 mrg thr->ts.single_count = 0; 2477 1.1.1.9 mrg #endif 2478 1.1.1.9 mrg thr->ts.static_trip = 0; 2479 1.1.1.9 mrg thr->task = &team->implicit_task[0]; 2480 1.1.1.9 mrg gomp_init_task (thr->task, NULL, icv); 2481 1.1.1.12 mrg while (*implicit_task 2482 1.1.1.12 mrg && (*implicit_task)->kind != GOMP_TASK_IMPLICIT) 2483 1.1.1.12 mrg implicit_task = &(*implicit_task)->parent; 2484 1.1.1.12 mrg if (*implicit_task) 2485 1.1.1.9 mrg { 2486 1.1.1.12 mrg thr->task = *implicit_task; 2487 1.1.1.9 mrg gomp_end_task (); 2488 1.1.1.12 mrg free (*implicit_task); 2489 1.1.1.9 mrg thr->task = &team->implicit_task[0]; 2490 1.1.1.9 mrg } 2491 1.1.1.9 mrg #ifdef LIBGOMP_USE_PTHREADS 2492 1.1.1.9 mrg else 2493 1.1.1.9 mrg pthread_setspecific (gomp_thread_destructor, thr); 2494 1.1.1.9 mrg #endif 2495 1.1.1.12 mrg if (implicit_task != &task) 2496 1.1.1.12 mrg { 2497 1.1.1.12 mrg *implicit_task = thr->task; 2498 1.1.1.12 mrg thr->task = task; 2499 1.1.1.12 mrg } 2500 1.1.1.9 mrg } 2501 1.1.1.9 mrg 2502 1.1.1.9 mrg /* The format of data is: 2503 1.1.1.9 mrg data[0] cnt 2504 1.1.1.9 mrg data[1] size 2505 1.1.1.9 mrg data[2] alignment (on output array pointer) 2506 1.1.1.9 mrg data[3] allocator (-1 if malloc allocator) 2507 1.1.1.9 mrg data[4] next pointer 2508 1.1.1.9 mrg data[5] used internally (htab pointer) 2509 1.1.1.9 mrg data[6] used internally (end of array) 2510 1.1.1.9 mrg cnt times 2511 1.1.1.9 mrg ent[0] address 2512 1.1.1.9 mrg ent[1] offset 2513 1.1.1.9 mrg ent[2] used internally (pointer to data[0]) 2514 1.1.1.9 mrg The entries are sorted by increasing offset, so that a binary 2515 1.1.1.9 mrg search can be performed. Normally, data[8] is 0, exception is 2516 1.1.1.9 mrg for worksharing construct task reductions in cancellable parallel, 2517 1.1.1.9 mrg where at offset 0 there should be space for a pointer and an integer 2518 1.1.1.9 mrg which are used internally. */ 2519 1.1.1.9 mrg 2520 1.1.1.9 mrg void 2521 1.1.1.9 mrg GOMP_taskgroup_reduction_register (uintptr_t *data) 2522 1.1.1.9 mrg { 2523 1.1.1.9 mrg struct gomp_thread *thr = gomp_thread (); 2524 1.1.1.9 mrg struct gomp_team *team = thr->ts.team; 2525 1.1.1.9 mrg struct gomp_task *task; 2526 1.1.1.9 mrg unsigned nthreads; 2527 1.1.1.9 mrg if (__builtin_expect (team == NULL, 0)) 2528 1.1.1.9 mrg { 2529 1.1.1.9 mrg /* The task reduction code needs a team and task, so for 2530 1.1.1.9 mrg orphaned taskgroups just create the implicit team. */ 2531 1.1.1.9 mrg gomp_create_artificial_team (); 2532 1.1.1.9 mrg ialias_call (GOMP_taskgroup_start) (); 2533 1.1.1.9 mrg team = thr->ts.team; 2534 1.1.1.9 mrg } 2535 1.1.1.9 mrg nthreads = team->nthreads; 2536 1.1.1.9 mrg task = thr->task; 2537 1.1.1.9 mrg gomp_reduction_register (data, task->taskgroup->reductions, NULL, nthreads); 2538 1.1.1.9 mrg task->taskgroup->reductions = data; 2539 1.1.1.9 mrg } 2540 1.1.1.9 mrg 2541 1.1.1.9 mrg void 2542 1.1.1.9 mrg GOMP_taskgroup_reduction_unregister (uintptr_t *data) 2543 1.1.1.9 mrg { 2544 1.1.1.9 mrg uintptr_t *d = data; 2545 1.1.1.9 mrg htab_free ((struct htab *) data[5]); 2546 1.1.1.9 mrg do 2547 1.1.1.9 mrg { 2548 1.1.1.9 mrg gomp_aligned_free ((void *) d[2]); 2549 1.1.1.9 mrg d = (uintptr_t *) d[4]; 2550 1.1.1.9 mrg } 2551 1.1.1.9 mrg while (d && !d[5]); 2552 1.1.1.9 mrg } 2553 1.1.1.9 mrg ialias (GOMP_taskgroup_reduction_unregister) 2554 1.1.1.9 mrg 2555 1.1.1.9 mrg /* For i = 0 to cnt-1, remap ptrs[i] which is either address of the 2556 1.1.1.9 mrg original list item or address of previously remapped original list 2557 1.1.1.9 mrg item to address of the private copy, store that to ptrs[i]. 2558 1.1.1.9 mrg For i < cntorig, additionally set ptrs[cnt+i] to the address of 2559 1.1.1.9 mrg the original list item. */ 2560 1.1.1.9 mrg 2561 1.1.1.9 mrg void 2562 1.1.1.9 mrg GOMP_task_reduction_remap (size_t cnt, size_t cntorig, void **ptrs) 2563 1.1.1.9 mrg { 2564 1.1.1.9 mrg struct gomp_thread *thr = gomp_thread (); 2565 1.1.1.9 mrg struct gomp_task *task = thr->task; 2566 1.1.1.9 mrg unsigned id = thr->ts.team_id; 2567 1.1.1.9 mrg uintptr_t *data = task->taskgroup->reductions; 2568 1.1.1.9 mrg uintptr_t *d; 2569 1.1.1.9 mrg struct htab *reduction_htab = (struct htab *) data[5]; 2570 1.1.1.9 mrg size_t i; 2571 1.1.1.9 mrg for (i = 0; i < cnt; ++i) 2572 1.1.1.9 mrg { 2573 1.1.1.9 mrg hash_entry_type ent, n; 2574 1.1.1.9 mrg __asm ("" : "=g" (ent) : "0" (ptrs + i)); 2575 1.1.1.9 mrg n = htab_find (reduction_htab, ent); 2576 1.1.1.9 mrg if (n) 2577 1.1.1.9 mrg { 2578 1.1.1.9 mrg uintptr_t *p; 2579 1.1.1.9 mrg __asm ("" : "=g" (p) : "0" (n)); 2580 1.1.1.9 mrg /* At this point, p[0] should be equal to (uintptr_t) ptrs[i], 2581 1.1.1.9 mrg p[1] is the offset within the allocated chunk for each 2582 1.1.1.9 mrg thread, p[2] is the array registered with 2583 1.1.1.9 mrg GOMP_taskgroup_reduction_register, d[2] is the base of the 2584 1.1.1.9 mrg allocated memory and d[1] is the size of the allocated chunk 2585 1.1.1.9 mrg for one thread. */ 2586 1.1.1.9 mrg d = (uintptr_t *) p[2]; 2587 1.1.1.9 mrg ptrs[i] = (void *) (d[2] + id * d[1] + p[1]); 2588 1.1.1.9 mrg if (__builtin_expect (i < cntorig, 0)) 2589 1.1.1.9 mrg ptrs[cnt + i] = (void *) p[0]; 2590 1.1.1.9 mrg continue; 2591 1.1.1.9 mrg } 2592 1.1.1.9 mrg d = data; 2593 1.1.1.9 mrg while (d != NULL) 2594 1.1.1.9 mrg { 2595 1.1.1.9 mrg if ((uintptr_t) ptrs[i] >= d[2] && (uintptr_t) ptrs[i] < d[6]) 2596 1.1.1.9 mrg break; 2597 1.1.1.9 mrg d = (uintptr_t *) d[4]; 2598 1.1.1.9 mrg } 2599 1.1.1.9 mrg if (d == NULL) 2600 1.1.1.9 mrg gomp_fatal ("couldn't find matching task_reduction or reduction with " 2601 1.1.1.9 mrg "task modifier for %p", ptrs[i]); 2602 1.1.1.9 mrg uintptr_t off = ((uintptr_t) ptrs[i] - d[2]) % d[1]; 2603 1.1.1.9 mrg ptrs[i] = (void *) (d[2] + id * d[1] + off); 2604 1.1.1.9 mrg if (__builtin_expect (i < cntorig, 0)) 2605 1.1.1.9 mrg { 2606 1.1.1.9 mrg size_t lo = 0, hi = d[0] - 1; 2607 1.1.1.9 mrg while (lo <= hi) 2608 1.1.1.9 mrg { 2609 1.1.1.9 mrg size_t m = (lo + hi) / 2; 2610 1.1.1.9 mrg if (d[7 + 3 * m + 1] < off) 2611 1.1.1.9 mrg lo = m + 1; 2612 1.1.1.9 mrg else if (d[7 + 3 * m + 1] == off) 2613 1.1.1.9 mrg { 2614 1.1.1.9 mrg ptrs[cnt + i] = (void *) d[7 + 3 * m]; 2615 1.1.1.9 mrg break; 2616 1.1.1.9 mrg } 2617 1.1.1.9 mrg else 2618 1.1.1.9 mrg hi = m - 1; 2619 1.1.1.9 mrg } 2620 1.1.1.9 mrg if (lo > hi) 2621 1.1.1.9 mrg gomp_fatal ("couldn't find matching task_reduction or reduction " 2622 1.1.1.9 mrg "with task modifier for %p", ptrs[i]); 2623 1.1.1.9 mrg } 2624 1.1.1.9 mrg } 2625 1.1.1.9 mrg } 2626 1.1.1.9 mrg 2627 1.1.1.9 mrg struct gomp_taskgroup * 2628 1.1.1.9 mrg gomp_parallel_reduction_register (uintptr_t *data, unsigned nthreads) 2629 1.1.1.9 mrg { 2630 1.1.1.9 mrg struct gomp_taskgroup *taskgroup = gomp_taskgroup_init (NULL); 2631 1.1.1.9 mrg gomp_reduction_register (data, NULL, NULL, nthreads); 2632 1.1.1.9 mrg taskgroup->reductions = data; 2633 1.1.1.9 mrg return taskgroup; 2634 1.1.1.9 mrg } 2635 1.1.1.9 mrg 2636 1.1.1.9 mrg void 2637 1.1.1.9 mrg gomp_workshare_task_reduction_register (uintptr_t *data, uintptr_t *orig) 2638 1.1.1.9 mrg { 2639 1.1.1.9 mrg struct gomp_thread *thr = gomp_thread (); 2640 1.1.1.9 mrg struct gomp_team *team = thr->ts.team; 2641 1.1.1.9 mrg struct gomp_task *task = thr->task; 2642 1.1.1.9 mrg unsigned nthreads = team->nthreads; 2643 1.1.1.9 mrg gomp_reduction_register (data, task->taskgroup->reductions, orig, nthreads); 2644 1.1.1.9 mrg task->taskgroup->reductions = data; 2645 1.1.1.9 mrg } 2646 1.1.1.9 mrg 2647 1.1.1.9 mrg void 2648 1.1.1.9 mrg gomp_workshare_taskgroup_start (void) 2649 1.1.1.9 mrg { 2650 1.1.1.9 mrg struct gomp_thread *thr = gomp_thread (); 2651 1.1.1.9 mrg struct gomp_team *team = thr->ts.team; 2652 1.1.1.9 mrg struct gomp_task *task; 2653 1.1.1.9 mrg 2654 1.1.1.9 mrg if (team == NULL) 2655 1.1.1.9 mrg { 2656 1.1.1.9 mrg gomp_create_artificial_team (); 2657 1.1.1.9 mrg team = thr->ts.team; 2658 1.1.1.9 mrg } 2659 1.1.1.9 mrg task = thr->task; 2660 1.1.1.9 mrg task->taskgroup = gomp_taskgroup_init (task->taskgroup); 2661 1.1.1.9 mrg task->taskgroup->workshare = true; 2662 1.1.1.9 mrg } 2663 1.1.1.9 mrg 2664 1.1.1.9 mrg void 2665 1.1.1.9 mrg GOMP_workshare_task_reduction_unregister (bool cancelled) 2666 1.1.1.9 mrg { 2667 1.1.1.9 mrg struct gomp_thread *thr = gomp_thread (); 2668 1.1.1.9 mrg struct gomp_task *task = thr->task; 2669 1.1.1.9 mrg struct gomp_team *team = thr->ts.team; 2670 1.1.1.9 mrg uintptr_t *data = task->taskgroup->reductions; 2671 1.1.1.9 mrg ialias_call (GOMP_taskgroup_end) (); 2672 1.1.1.9 mrg if (thr->ts.team_id == 0) 2673 1.1.1.9 mrg ialias_call (GOMP_taskgroup_reduction_unregister) (data); 2674 1.1.1.9 mrg else 2675 1.1.1.9 mrg htab_free ((struct htab *) data[5]); 2676 1.1.1.9 mrg 2677 1.1.1.9 mrg if (!cancelled) 2678 1.1.1.9 mrg gomp_team_barrier_wait (&team->barrier); 2679 1.1.1.9 mrg } 2680 1.1.1.9 mrg 2681 1.1.1.2 mrg int 2682 1.1.1.2 mrg omp_in_final (void) 2683 1.1.1.2 mrg { 2684 1.1.1.2 mrg struct gomp_thread *thr = gomp_thread (); 2685 1.1.1.2 mrg return thr->task && thr->task->final_task; 2686 1.1.1.2 mrg } 2687 1.1.1.2 mrg 2688 1.1.1.2 mrg ialias (omp_in_final) 2689 1.1.1.13 mrg 2690 1.1.1.14 mrg int 2691 1.1.1.14 mrg omp_in_explicit_task (void) 2692 1.1.1.14 mrg { 2693 1.1.1.14 mrg struct gomp_thread *thr = gomp_thread (); 2694 1.1.1.14 mrg struct gomp_task *task = thr->task; 2695 1.1.1.14 mrg return task && task->kind != GOMP_TASK_IMPLICIT; 2696 1.1.1.14 mrg } 2697 1.1.1.14 mrg 2698 1.1.1.14 mrg ialias (omp_in_explicit_task) 2699 1.1.1.14 mrg 2700 1.1.1.13 mrg void 2701 1.1.1.13 mrg omp_fulfill_event (omp_event_handle_t event) 2702 1.1.1.13 mrg { 2703 1.1.1.13 mrg struct gomp_task *task = (struct gomp_task *) event; 2704 1.1.1.13 mrg if (!task->deferred_p) 2705 1.1.1.13 mrg { 2706 1.1.1.13 mrg if (gomp_sem_getcount (task->completion_sem) > 0) 2707 1.1.1.13 mrg gomp_fatal ("omp_fulfill_event: %p event already fulfilled!\n", task); 2708 1.1.1.13 mrg 2709 1.1.1.13 mrg gomp_debug (0, "omp_fulfill_event: %p event for undeferred task\n", 2710 1.1.1.13 mrg task); 2711 1.1.1.13 mrg gomp_sem_post (task->completion_sem); 2712 1.1.1.13 mrg return; 2713 1.1.1.13 mrg } 2714 1.1.1.13 mrg 2715 1.1.1.13 mrg struct gomp_team *team = __atomic_load_n (&task->detach_team, 2716 1.1.1.13 mrg MEMMODEL_RELAXED); 2717 1.1.1.13 mrg if (!team) 2718 1.1.1.13 mrg gomp_fatal ("omp_fulfill_event: %p event is invalid or has already " 2719 1.1.1.13 mrg "been fulfilled!\n", task); 2720 1.1.1.13 mrg 2721 1.1.1.13 mrg gomp_mutex_lock (&team->task_lock); 2722 1.1.1.13 mrg if (task->kind != GOMP_TASK_DETACHED) 2723 1.1.1.13 mrg { 2724 1.1.1.13 mrg /* The task has not finished running yet. */ 2725 1.1.1.13 mrg gomp_debug (0, 2726 1.1.1.13 mrg "omp_fulfill_event: %p event fulfilled for unfinished " 2727 1.1.1.13 mrg "task\n", task); 2728 1.1.1.13 mrg __atomic_store_n (&task->detach_team, NULL, MEMMODEL_RELAXED); 2729 1.1.1.13 mrg gomp_mutex_unlock (&team->task_lock); 2730 1.1.1.13 mrg return; 2731 1.1.1.13 mrg } 2732 1.1.1.13 mrg 2733 1.1.1.13 mrg gomp_debug (0, "omp_fulfill_event: %p event fulfilled for finished task\n", 2734 1.1.1.13 mrg task); 2735 1.1.1.13 mrg size_t new_tasks = gomp_task_run_post_handle_depend (task, team); 2736 1.1.1.13 mrg gomp_task_run_post_remove_parent (task); 2737 1.1.1.13 mrg gomp_clear_parent (&task->children_queue); 2738 1.1.1.13 mrg gomp_task_run_post_remove_taskgroup (task); 2739 1.1.1.13 mrg team->task_count--; 2740 1.1.1.13 mrg team->task_detach_count--; 2741 1.1.1.13 mrg 2742 1.1.1.13 mrg int do_wake = 0; 2743 1.1.1.13 mrg bool shackled_thread_p = team == gomp_thread ()->ts.team; 2744 1.1.1.13 mrg if (new_tasks > 0) 2745 1.1.1.13 mrg { 2746 1.1.1.13 mrg /* Wake up threads to run new tasks. */ 2747 1.1.1.13 mrg gomp_team_barrier_set_task_pending (&team->barrier); 2748 1.1.1.13 mrg do_wake = team->nthreads - team->task_running_count; 2749 1.1.1.13 mrg if (do_wake > new_tasks) 2750 1.1.1.13 mrg do_wake = new_tasks; 2751 1.1.1.13 mrg } 2752 1.1.1.13 mrg 2753 1.1.1.13 mrg if (!shackled_thread_p 2754 1.1.1.13 mrg && !do_wake 2755 1.1.1.13 mrg && team->task_detach_count == 0 2756 1.1.1.13 mrg && gomp_team_barrier_waiting_for_tasks (&team->barrier)) 2757 1.1.1.13 mrg /* Ensure that at least one thread is woken up to signal that the 2758 1.1.1.13 mrg barrier can finish. */ 2759 1.1.1.13 mrg do_wake = 1; 2760 1.1.1.13 mrg 2761 1.1.1.13 mrg /* If we are running in an unshackled thread, the team might vanish before 2762 1.1.1.13 mrg gomp_team_barrier_wake is run if we release the lock first, so keep the 2763 1.1.1.13 mrg lock for the call in that case. */ 2764 1.1.1.13 mrg if (shackled_thread_p) 2765 1.1.1.13 mrg gomp_mutex_unlock (&team->task_lock); 2766 1.1.1.13 mrg if (do_wake) 2767 1.1.1.13 mrg gomp_team_barrier_wake (&team->barrier, do_wake); 2768 1.1.1.13 mrg if (!shackled_thread_p) 2769 1.1.1.13 mrg gomp_mutex_unlock (&team->task_lock); 2770 1.1.1.13 mrg 2771 1.1.1.13 mrg gomp_finish_task (task); 2772 1.1.1.13 mrg free (task); 2773 1.1.1.13 mrg } 2774 1.1.1.13 mrg 2775 1.1.1.13 mrg ialias (omp_fulfill_event) 2776