1*629ff9f7SJohn Marino /* Copyright (C) 2007, 2008, 2009, 2011 Free Software Foundation, Inc. 2*629ff9f7SJohn Marino Contributed by Richard Henderson <rth@redhat.com>. 3*629ff9f7SJohn Marino 4*629ff9f7SJohn Marino This file is part of the GNU OpenMP Library (libgomp). 5*629ff9f7SJohn Marino 6*629ff9f7SJohn Marino Libgomp is free software; you can redistribute it and/or modify it 7*629ff9f7SJohn Marino under the terms of the GNU General Public License as published by 8*629ff9f7SJohn Marino the Free Software Foundation; either version 3, or (at your option) 9*629ff9f7SJohn Marino any later version. 10*629ff9f7SJohn Marino 11*629ff9f7SJohn Marino Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY 12*629ff9f7SJohn Marino WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13*629ff9f7SJohn Marino FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14*629ff9f7SJohn Marino more details. 15*629ff9f7SJohn Marino 16*629ff9f7SJohn Marino Under Section 7 of GPL version 3, you are granted additional 17*629ff9f7SJohn Marino permissions described in the GCC Runtime Library Exception, version 18*629ff9f7SJohn Marino 3.1, as published by the Free Software Foundation. 19*629ff9f7SJohn Marino 20*629ff9f7SJohn Marino You should have received a copy of the GNU General Public License and 21*629ff9f7SJohn Marino a copy of the GCC Runtime Library Exception along with this program; 22*629ff9f7SJohn Marino see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23*629ff9f7SJohn Marino <http://www.gnu.org/licenses/>. */ 24*629ff9f7SJohn Marino 25*629ff9f7SJohn Marino /* This file handles the maintainence of tasks in response to task 26*629ff9f7SJohn Marino creation and termination. */ 27*629ff9f7SJohn Marino 28*629ff9f7SJohn Marino #include "libgomp.h" 29*629ff9f7SJohn Marino #include <stdlib.h> 30*629ff9f7SJohn Marino #include <string.h> 31*629ff9f7SJohn Marino 32*629ff9f7SJohn Marino 33*629ff9f7SJohn Marino /* Create a new task data structure. */ 34*629ff9f7SJohn Marino 35*629ff9f7SJohn Marino void 36*629ff9f7SJohn Marino gomp_init_task (struct gomp_task *task, struct gomp_task *parent_task, 37*629ff9f7SJohn Marino struct gomp_task_icv *prev_icv) 38*629ff9f7SJohn Marino { 39*629ff9f7SJohn Marino task->parent = parent_task; 40*629ff9f7SJohn Marino task->icv = *prev_icv; 41*629ff9f7SJohn Marino task->kind = GOMP_TASK_IMPLICIT; 42*629ff9f7SJohn Marino task->in_taskwait = false; 43*629ff9f7SJohn Marino task->in_tied_task = false; 44*629ff9f7SJohn Marino task->final_task = false; 45*629ff9f7SJohn Marino task->children = NULL; 46*629ff9f7SJohn Marino gomp_sem_init (&task->taskwait_sem, 0); 47*629ff9f7SJohn Marino } 48*629ff9f7SJohn Marino 49*629ff9f7SJohn Marino /* Clean up a task, after completing it. */ 50*629ff9f7SJohn Marino 51*629ff9f7SJohn Marino void 52*629ff9f7SJohn Marino gomp_end_task (void) 53*629ff9f7SJohn Marino { 54*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 55*629ff9f7SJohn Marino struct gomp_task *task = thr->task; 56*629ff9f7SJohn Marino 57*629ff9f7SJohn Marino gomp_finish_task (task); 58*629ff9f7SJohn Marino thr->task = task->parent; 59*629ff9f7SJohn Marino } 60*629ff9f7SJohn Marino 61*629ff9f7SJohn Marino static inline void 62*629ff9f7SJohn Marino gomp_clear_parent (struct gomp_task *children) 63*629ff9f7SJohn Marino { 64*629ff9f7SJohn Marino struct gomp_task *task = children; 65*629ff9f7SJohn Marino 66*629ff9f7SJohn Marino if (task) 67*629ff9f7SJohn Marino do 68*629ff9f7SJohn Marino { 69*629ff9f7SJohn Marino task->parent = NULL; 70*629ff9f7SJohn Marino task = task->next_child; 71*629ff9f7SJohn Marino } 72*629ff9f7SJohn Marino while (task != children); 73*629ff9f7SJohn Marino } 74*629ff9f7SJohn Marino 75*629ff9f7SJohn Marino /* Called when encountering an explicit task directive. If IF_CLAUSE is 76*629ff9f7SJohn Marino false, then we must not delay in executing the task. If UNTIED is true, 77*629ff9f7SJohn Marino then the task may be executed by any member of the team. */ 78*629ff9f7SJohn Marino 79*629ff9f7SJohn Marino void 80*629ff9f7SJohn Marino GOMP_task (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *), 81*629ff9f7SJohn Marino long arg_size, long arg_align, bool if_clause, unsigned flags) 82*629ff9f7SJohn Marino { 83*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 84*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 85*629ff9f7SJohn Marino 86*629ff9f7SJohn Marino #ifdef HAVE_BROKEN_POSIX_SEMAPHORES 87*629ff9f7SJohn Marino /* If pthread_mutex_* is used for omp_*lock*, then each task must be 88*629ff9f7SJohn Marino tied to one thread all the time. This means UNTIED tasks must be 89*629ff9f7SJohn Marino tied and if CPYFN is non-NULL IF(0) must be forced, as CPYFN 90*629ff9f7SJohn Marino might be running on different thread than FN. */ 91*629ff9f7SJohn Marino if (cpyfn) 92*629ff9f7SJohn Marino if_clause = false; 93*629ff9f7SJohn Marino if (flags & 1) 94*629ff9f7SJohn Marino flags &= ~1; 95*629ff9f7SJohn Marino #endif 96*629ff9f7SJohn Marino 97*629ff9f7SJohn Marino if (!if_clause || team == NULL 98*629ff9f7SJohn Marino || (thr->task && thr->task->final_task) 99*629ff9f7SJohn Marino || team->task_count > 64 * team->nthreads) 100*629ff9f7SJohn Marino { 101*629ff9f7SJohn Marino struct gomp_task task; 102*629ff9f7SJohn Marino 103*629ff9f7SJohn Marino gomp_init_task (&task, thr->task, gomp_icv (false)); 104*629ff9f7SJohn Marino task.kind = GOMP_TASK_IFFALSE; 105*629ff9f7SJohn Marino task.final_task = (thr->task && thr->task->final_task) || (flags & 2); 106*629ff9f7SJohn Marino if (thr->task) 107*629ff9f7SJohn Marino task.in_tied_task = thr->task->in_tied_task; 108*629ff9f7SJohn Marino thr->task = &task; 109*629ff9f7SJohn Marino if (__builtin_expect (cpyfn != NULL, 0)) 110*629ff9f7SJohn Marino { 111*629ff9f7SJohn Marino char buf[arg_size + arg_align - 1]; 112*629ff9f7SJohn Marino char *arg = (char *) (((uintptr_t) buf + arg_align - 1) 113*629ff9f7SJohn Marino & ~(uintptr_t) (arg_align - 1)); 114*629ff9f7SJohn Marino cpyfn (arg, data); 115*629ff9f7SJohn Marino fn (arg); 116*629ff9f7SJohn Marino } 117*629ff9f7SJohn Marino else 118*629ff9f7SJohn Marino fn (data); 119*629ff9f7SJohn Marino if (team != NULL) 120*629ff9f7SJohn Marino { 121*629ff9f7SJohn Marino gomp_mutex_lock (&team->task_lock); 122*629ff9f7SJohn Marino if (task.children != NULL) 123*629ff9f7SJohn Marino gomp_clear_parent (task.children); 124*629ff9f7SJohn Marino gomp_mutex_unlock (&team->task_lock); 125*629ff9f7SJohn Marino } 126*629ff9f7SJohn Marino gomp_end_task (); 127*629ff9f7SJohn Marino } 128*629ff9f7SJohn Marino else 129*629ff9f7SJohn Marino { 130*629ff9f7SJohn Marino struct gomp_task *task; 131*629ff9f7SJohn Marino struct gomp_task *parent = thr->task; 132*629ff9f7SJohn Marino char *arg; 133*629ff9f7SJohn Marino bool do_wake; 134*629ff9f7SJohn Marino 135*629ff9f7SJohn Marino task = gomp_malloc (sizeof (*task) + arg_size + arg_align - 1); 136*629ff9f7SJohn Marino arg = (char *) (((uintptr_t) (task + 1) + arg_align - 1) 137*629ff9f7SJohn Marino & ~(uintptr_t) (arg_align - 1)); 138*629ff9f7SJohn Marino gomp_init_task (task, parent, gomp_icv (false)); 139*629ff9f7SJohn Marino task->kind = GOMP_TASK_IFFALSE; 140*629ff9f7SJohn Marino task->in_tied_task = parent->in_tied_task; 141*629ff9f7SJohn Marino thr->task = task; 142*629ff9f7SJohn Marino if (cpyfn) 143*629ff9f7SJohn Marino cpyfn (arg, data); 144*629ff9f7SJohn Marino else 145*629ff9f7SJohn Marino memcpy (arg, data, arg_size); 146*629ff9f7SJohn Marino thr->task = parent; 147*629ff9f7SJohn Marino task->kind = GOMP_TASK_WAITING; 148*629ff9f7SJohn Marino task->fn = fn; 149*629ff9f7SJohn Marino task->fn_data = arg; 150*629ff9f7SJohn Marino task->in_tied_task = true; 151*629ff9f7SJohn Marino task->final_task = (flags & 2) >> 1; 152*629ff9f7SJohn Marino gomp_mutex_lock (&team->task_lock); 153*629ff9f7SJohn Marino if (parent->children) 154*629ff9f7SJohn Marino { 155*629ff9f7SJohn Marino task->next_child = parent->children; 156*629ff9f7SJohn Marino task->prev_child = parent->children->prev_child; 157*629ff9f7SJohn Marino task->next_child->prev_child = task; 158*629ff9f7SJohn Marino task->prev_child->next_child = task; 159*629ff9f7SJohn Marino } 160*629ff9f7SJohn Marino else 161*629ff9f7SJohn Marino { 162*629ff9f7SJohn Marino task->next_child = task; 163*629ff9f7SJohn Marino task->prev_child = task; 164*629ff9f7SJohn Marino } 165*629ff9f7SJohn Marino parent->children = task; 166*629ff9f7SJohn Marino if (team->task_queue) 167*629ff9f7SJohn Marino { 168*629ff9f7SJohn Marino task->next_queue = team->task_queue; 169*629ff9f7SJohn Marino task->prev_queue = team->task_queue->prev_queue; 170*629ff9f7SJohn Marino task->next_queue->prev_queue = task; 171*629ff9f7SJohn Marino task->prev_queue->next_queue = task; 172*629ff9f7SJohn Marino } 173*629ff9f7SJohn Marino else 174*629ff9f7SJohn Marino { 175*629ff9f7SJohn Marino task->next_queue = task; 176*629ff9f7SJohn Marino task->prev_queue = task; 177*629ff9f7SJohn Marino team->task_queue = task; 178*629ff9f7SJohn Marino } 179*629ff9f7SJohn Marino ++team->task_count; 180*629ff9f7SJohn Marino gomp_team_barrier_set_task_pending (&team->barrier); 181*629ff9f7SJohn Marino do_wake = team->task_running_count + !parent->in_tied_task 182*629ff9f7SJohn Marino < team->nthreads; 183*629ff9f7SJohn Marino gomp_mutex_unlock (&team->task_lock); 184*629ff9f7SJohn Marino if (do_wake) 185*629ff9f7SJohn Marino gomp_team_barrier_wake (&team->barrier, 1); 186*629ff9f7SJohn Marino } 187*629ff9f7SJohn Marino } 188*629ff9f7SJohn Marino 189*629ff9f7SJohn Marino void 190*629ff9f7SJohn Marino gomp_barrier_handle_tasks (gomp_barrier_state_t state) 191*629ff9f7SJohn Marino { 192*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 193*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 194*629ff9f7SJohn Marino struct gomp_task *task = thr->task; 195*629ff9f7SJohn Marino struct gomp_task *child_task = NULL; 196*629ff9f7SJohn Marino struct gomp_task *to_free = NULL; 197*629ff9f7SJohn Marino 198*629ff9f7SJohn Marino gomp_mutex_lock (&team->task_lock); 199*629ff9f7SJohn Marino if (gomp_barrier_last_thread (state)) 200*629ff9f7SJohn Marino { 201*629ff9f7SJohn Marino if (team->task_count == 0) 202*629ff9f7SJohn Marino { 203*629ff9f7SJohn Marino gomp_team_barrier_done (&team->barrier, state); 204*629ff9f7SJohn Marino gomp_mutex_unlock (&team->task_lock); 205*629ff9f7SJohn Marino gomp_team_barrier_wake (&team->barrier, 0); 206*629ff9f7SJohn Marino return; 207*629ff9f7SJohn Marino } 208*629ff9f7SJohn Marino gomp_team_barrier_set_waiting_for_tasks (&team->barrier); 209*629ff9f7SJohn Marino } 210*629ff9f7SJohn Marino 211*629ff9f7SJohn Marino while (1) 212*629ff9f7SJohn Marino { 213*629ff9f7SJohn Marino if (team->task_queue != NULL) 214*629ff9f7SJohn Marino { 215*629ff9f7SJohn Marino struct gomp_task *parent; 216*629ff9f7SJohn Marino 217*629ff9f7SJohn Marino child_task = team->task_queue; 218*629ff9f7SJohn Marino parent = child_task->parent; 219*629ff9f7SJohn Marino if (parent && parent->children == child_task) 220*629ff9f7SJohn Marino parent->children = child_task->next_child; 221*629ff9f7SJohn Marino child_task->prev_queue->next_queue = child_task->next_queue; 222*629ff9f7SJohn Marino child_task->next_queue->prev_queue = child_task->prev_queue; 223*629ff9f7SJohn Marino if (child_task->next_queue != child_task) 224*629ff9f7SJohn Marino team->task_queue = child_task->next_queue; 225*629ff9f7SJohn Marino else 226*629ff9f7SJohn Marino team->task_queue = NULL; 227*629ff9f7SJohn Marino child_task->kind = GOMP_TASK_TIED; 228*629ff9f7SJohn Marino team->task_running_count++; 229*629ff9f7SJohn Marino if (team->task_count == team->task_running_count) 230*629ff9f7SJohn Marino gomp_team_barrier_clear_task_pending (&team->barrier); 231*629ff9f7SJohn Marino } 232*629ff9f7SJohn Marino gomp_mutex_unlock (&team->task_lock); 233*629ff9f7SJohn Marino if (to_free) 234*629ff9f7SJohn Marino { 235*629ff9f7SJohn Marino gomp_finish_task (to_free); 236*629ff9f7SJohn Marino free (to_free); 237*629ff9f7SJohn Marino to_free = NULL; 238*629ff9f7SJohn Marino } 239*629ff9f7SJohn Marino if (child_task) 240*629ff9f7SJohn Marino { 241*629ff9f7SJohn Marino thr->task = child_task; 242*629ff9f7SJohn Marino child_task->fn (child_task->fn_data); 243*629ff9f7SJohn Marino thr->task = task; 244*629ff9f7SJohn Marino } 245*629ff9f7SJohn Marino else 246*629ff9f7SJohn Marino return; 247*629ff9f7SJohn Marino gomp_mutex_lock (&team->task_lock); 248*629ff9f7SJohn Marino if (child_task) 249*629ff9f7SJohn Marino { 250*629ff9f7SJohn Marino struct gomp_task *parent = child_task->parent; 251*629ff9f7SJohn Marino if (parent) 252*629ff9f7SJohn Marino { 253*629ff9f7SJohn Marino child_task->prev_child->next_child = child_task->next_child; 254*629ff9f7SJohn Marino child_task->next_child->prev_child = child_task->prev_child; 255*629ff9f7SJohn Marino if (parent->children == child_task) 256*629ff9f7SJohn Marino { 257*629ff9f7SJohn Marino if (child_task->next_child != child_task) 258*629ff9f7SJohn Marino parent->children = child_task->next_child; 259*629ff9f7SJohn Marino else 260*629ff9f7SJohn Marino { 261*629ff9f7SJohn Marino parent->children = NULL; 262*629ff9f7SJohn Marino if (parent->in_taskwait) 263*629ff9f7SJohn Marino gomp_sem_post (&parent->taskwait_sem); 264*629ff9f7SJohn Marino } 265*629ff9f7SJohn Marino } 266*629ff9f7SJohn Marino } 267*629ff9f7SJohn Marino gomp_clear_parent (child_task->children); 268*629ff9f7SJohn Marino to_free = child_task; 269*629ff9f7SJohn Marino child_task = NULL; 270*629ff9f7SJohn Marino team->task_running_count--; 271*629ff9f7SJohn Marino if (--team->task_count == 0 272*629ff9f7SJohn Marino && gomp_team_barrier_waiting_for_tasks (&team->barrier)) 273*629ff9f7SJohn Marino { 274*629ff9f7SJohn Marino gomp_team_barrier_done (&team->barrier, state); 275*629ff9f7SJohn Marino gomp_mutex_unlock (&team->task_lock); 276*629ff9f7SJohn Marino gomp_team_barrier_wake (&team->barrier, 0); 277*629ff9f7SJohn Marino gomp_mutex_lock (&team->task_lock); 278*629ff9f7SJohn Marino } 279*629ff9f7SJohn Marino } 280*629ff9f7SJohn Marino } 281*629ff9f7SJohn Marino } 282*629ff9f7SJohn Marino 283*629ff9f7SJohn Marino /* Called when encountering a taskwait directive. */ 284*629ff9f7SJohn Marino 285*629ff9f7SJohn Marino void 286*629ff9f7SJohn Marino GOMP_taskwait (void) 287*629ff9f7SJohn Marino { 288*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 289*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 290*629ff9f7SJohn Marino struct gomp_task *task = thr->task; 291*629ff9f7SJohn Marino struct gomp_task *child_task = NULL; 292*629ff9f7SJohn Marino struct gomp_task *to_free = NULL; 293*629ff9f7SJohn Marino 294*629ff9f7SJohn Marino if (task == NULL || team == NULL) 295*629ff9f7SJohn Marino return; 296*629ff9f7SJohn Marino 297*629ff9f7SJohn Marino gomp_mutex_lock (&team->task_lock); 298*629ff9f7SJohn Marino while (1) 299*629ff9f7SJohn Marino { 300*629ff9f7SJohn Marino if (task->children == NULL) 301*629ff9f7SJohn Marino { 302*629ff9f7SJohn Marino gomp_mutex_unlock (&team->task_lock); 303*629ff9f7SJohn Marino if (to_free) 304*629ff9f7SJohn Marino { 305*629ff9f7SJohn Marino gomp_finish_task (to_free); 306*629ff9f7SJohn Marino free (to_free); 307*629ff9f7SJohn Marino } 308*629ff9f7SJohn Marino return; 309*629ff9f7SJohn Marino } 310*629ff9f7SJohn Marino if (task->children->kind == GOMP_TASK_WAITING) 311*629ff9f7SJohn Marino { 312*629ff9f7SJohn Marino child_task = task->children; 313*629ff9f7SJohn Marino task->children = child_task->next_child; 314*629ff9f7SJohn Marino child_task->prev_queue->next_queue = child_task->next_queue; 315*629ff9f7SJohn Marino child_task->next_queue->prev_queue = child_task->prev_queue; 316*629ff9f7SJohn Marino if (team->task_queue == child_task) 317*629ff9f7SJohn Marino { 318*629ff9f7SJohn Marino if (child_task->next_queue != child_task) 319*629ff9f7SJohn Marino team->task_queue = child_task->next_queue; 320*629ff9f7SJohn Marino else 321*629ff9f7SJohn Marino team->task_queue = NULL; 322*629ff9f7SJohn Marino } 323*629ff9f7SJohn Marino child_task->kind = GOMP_TASK_TIED; 324*629ff9f7SJohn Marino team->task_running_count++; 325*629ff9f7SJohn Marino if (team->task_count == team->task_running_count) 326*629ff9f7SJohn Marino gomp_team_barrier_clear_task_pending (&team->barrier); 327*629ff9f7SJohn Marino } 328*629ff9f7SJohn Marino else 329*629ff9f7SJohn Marino /* All tasks we are waiting for are already running 330*629ff9f7SJohn Marino in other threads. Wait for them. */ 331*629ff9f7SJohn Marino task->in_taskwait = true; 332*629ff9f7SJohn Marino gomp_mutex_unlock (&team->task_lock); 333*629ff9f7SJohn Marino if (to_free) 334*629ff9f7SJohn Marino { 335*629ff9f7SJohn Marino gomp_finish_task (to_free); 336*629ff9f7SJohn Marino free (to_free); 337*629ff9f7SJohn Marino to_free = NULL; 338*629ff9f7SJohn Marino } 339*629ff9f7SJohn Marino if (child_task) 340*629ff9f7SJohn Marino { 341*629ff9f7SJohn Marino thr->task = child_task; 342*629ff9f7SJohn Marino child_task->fn (child_task->fn_data); 343*629ff9f7SJohn Marino thr->task = task; 344*629ff9f7SJohn Marino } 345*629ff9f7SJohn Marino else 346*629ff9f7SJohn Marino { 347*629ff9f7SJohn Marino gomp_sem_wait (&task->taskwait_sem); 348*629ff9f7SJohn Marino task->in_taskwait = false; 349*629ff9f7SJohn Marino return; 350*629ff9f7SJohn Marino } 351*629ff9f7SJohn Marino gomp_mutex_lock (&team->task_lock); 352*629ff9f7SJohn Marino if (child_task) 353*629ff9f7SJohn Marino { 354*629ff9f7SJohn Marino child_task->prev_child->next_child = child_task->next_child; 355*629ff9f7SJohn Marino child_task->next_child->prev_child = child_task->prev_child; 356*629ff9f7SJohn Marino if (task->children == child_task) 357*629ff9f7SJohn Marino { 358*629ff9f7SJohn Marino if (child_task->next_child != child_task) 359*629ff9f7SJohn Marino task->children = child_task->next_child; 360*629ff9f7SJohn Marino else 361*629ff9f7SJohn Marino task->children = NULL; 362*629ff9f7SJohn Marino } 363*629ff9f7SJohn Marino gomp_clear_parent (child_task->children); 364*629ff9f7SJohn Marino to_free = child_task; 365*629ff9f7SJohn Marino child_task = NULL; 366*629ff9f7SJohn Marino team->task_count--; 367*629ff9f7SJohn Marino team->task_running_count--; 368*629ff9f7SJohn Marino } 369*629ff9f7SJohn Marino } 370*629ff9f7SJohn Marino } 371*629ff9f7SJohn Marino 372*629ff9f7SJohn Marino /* Called when encountering a taskyield directive. */ 373*629ff9f7SJohn Marino 374*629ff9f7SJohn Marino void 375*629ff9f7SJohn Marino GOMP_taskyield (void) 376*629ff9f7SJohn Marino { 377*629ff9f7SJohn Marino /* Nothing at the moment. */ 378*629ff9f7SJohn Marino } 379*629ff9f7SJohn Marino 380*629ff9f7SJohn Marino int 381*629ff9f7SJohn Marino omp_in_final (void) 382*629ff9f7SJohn Marino { 383*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 384*629ff9f7SJohn Marino return thr->task && thr->task->final_task; 385*629ff9f7SJohn Marino } 386*629ff9f7SJohn Marino 387*629ff9f7SJohn Marino ialias (omp_in_final) 388