2009-07-18 Steven G. Kargl <kargl@gcc.gnu.org>
[official-gcc.git] / libgomp / task.c
blob95f163d53cd72c3c78026270d8442bceef81783d
1 /* Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
2 Contributed by Richard Henderson <rth@redhat.com>.
4 This file is part of the GNU OpenMP Library (libgomp).
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.
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.
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.
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/>. */
25 /* This file handles the maintainence of tasks in response to task
26 creation and termination. */
28 #include "libgomp.h"
29 #include <stdlib.h>
30 #include <string.h>
33 /* Create a new task data structure. */
35 void
36 gomp_init_task (struct gomp_task *task, struct gomp_task *parent_task,
37 struct gomp_task_icv *prev_icv)
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);
48 /* Clean up a task, after completing it. */
50 void
51 gomp_end_task (void)
53 struct gomp_thread *thr = gomp_thread ();
54 struct gomp_task *task = thr->task;
56 gomp_finish_task (task);
57 thr->task = task->parent;
60 static inline void
61 gomp_clear_parent (struct gomp_task *children)
63 struct gomp_task *task = children;
65 if (task)
68 task->parent = NULL;
69 task = task->next_child;
71 while (task != children);
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. */
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)))
83 struct gomp_thread *thr = gomp_thread ();
84 struct gomp_team *team = thr->ts.team;
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
97 if (!if_clause || team == NULL
98 || team->task_count > 64 * team->nthreads)
100 struct gomp_task task;
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))
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);
115 else
116 fn (data);
117 if (task.children)
119 gomp_mutex_lock (&team->task_lock);
120 gomp_clear_parent (task.children);
121 gomp_mutex_unlock (&team->task_lock);
123 gomp_end_task ();
125 else
127 struct gomp_task *task;
128 struct gomp_task *parent = thr->task;
129 char *arg;
130 bool do_wake;
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)
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;
156 else
158 task->next_child = task;
159 task->prev_child = task;
161 parent->children = task;
162 if (team->task_queue)
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;
169 else
171 task->next_queue = task;
172 task->prev_queue = task;
173 team->task_queue = task;
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 < team->nthreads;
179 gomp_mutex_unlock (&team->task_lock);
180 if (do_wake)
181 gomp_team_barrier_wake (&team->barrier, 1);
185 void
186 gomp_barrier_handle_tasks (gomp_barrier_state_t state)
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;
194 gomp_mutex_lock (&team->task_lock);
195 if (gomp_barrier_last_thread (state))
197 if (team->task_count == 0)
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;
204 gomp_team_barrier_set_waiting_for_tasks (&team->barrier);
207 while (1)
209 if (team->task_queue != NULL)
211 struct gomp_task *parent;
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);
228 gomp_mutex_unlock (&team->task_lock);
229 if (to_free)
231 gomp_finish_task (to_free);
232 free (to_free);
233 to_free = NULL;
235 if (child_task)
237 thr->task = child_task;
238 child_task->fn (child_task->fn_data);
239 thr->task = task;
241 else
242 return;
243 gomp_mutex_lock (&team->task_lock);
244 if (child_task)
246 struct gomp_task *parent = child_task->parent;
247 if (parent)
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)
253 if (child_task->next_child != child_task)
254 parent->children = child_task->next_child;
255 else
257 parent->children = NULL;
258 if (parent->in_taskwait)
259 gomp_sem_post (&parent->taskwait_sem);
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))
270 gomp_team_barrier_done (&team->barrier, state);
271 gomp_mutex_unlock (&team->task_lock);
272 gomp_team_barrier_wake (&team->barrier, 0);
278 /* Called when encountering a taskwait directive. */
280 void
281 GOMP_taskwait (void)
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;
289 if (task == NULL || task->children == NULL)
290 return;
291 gomp_mutex_lock (&team->task_lock);
292 while (1)
294 if (task->children == NULL)
296 gomp_mutex_unlock (&team->task_lock);
297 if (to_free)
299 gomp_finish_task (to_free);
300 free (to_free);
302 return;
304 if (task->children->kind == GOMP_TASK_WAITING)
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)
312 if (child_task->next_queue != child_task)
313 team->task_queue = child_task->next_queue;
314 else
315 team->task_queue = NULL;
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);
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)
329 gomp_finish_task (to_free);
330 free (to_free);
331 to_free = NULL;
333 if (child_task)
335 thr->task = child_task;
336 child_task->fn (child_task->fn_data);
337 thr->task = task;
339 else
341 gomp_sem_wait (&task->taskwait_sem);
342 task->in_taskwait = false;
343 return;
345 gomp_mutex_lock (&team->task_lock);
346 if (child_task)
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)
352 if (child_task->next_child != child_task)
353 task->children = child_task->next_child;
354 else
355 task->children = NULL;
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--;