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