OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / libgomp / task.c
1 /* Copyright (C) 2007, 2008, 2009, 2011 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 "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->final_task = false;
45   task->children = NULL;
46   gomp_sem_init (&task->taskwait_sem, 0);
47 }
48
49 /* Clean up a task, after completing it.  */
50
51 void
52 gomp_end_task (void)
53 {
54   struct gomp_thread *thr = gomp_thread ();
55   struct gomp_task *task = thr->task;
56
57   gomp_finish_task (task);
58   thr->task = task->parent;
59 }
60
61 static inline void
62 gomp_clear_parent (struct gomp_task *children)
63 {
64   struct gomp_task *task = children;
65
66   if (task)
67     do
68       {
69         task->parent = NULL;
70         task = task->next_child;
71       }
72     while (task != children);
73 }
74
75 /* Called when encountering an explicit task directive.  If IF_CLAUSE is
76    false, then we must not delay in executing the task.  If UNTIED is true,
77    then the task may be executed by any member of the team.  */
78
79 void
80 GOMP_task (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *),
81            long arg_size, long arg_align, bool if_clause, unsigned flags)
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       || (thr->task && thr->task->final_task)
99       || team->task_count > 64 * team->nthreads)
100     {
101       struct gomp_task task;
102
103       gomp_init_task (&task, thr->task, gomp_icv (false));
104       task.kind = GOMP_TASK_IFFALSE;
105       task.final_task = (thr->task && thr->task->final_task) || (flags & 2);
106       if (thr->task)
107         task.in_tied_task = thr->task->in_tied_task;
108       thr->task = &task;
109       if (__builtin_expect (cpyfn != NULL, 0))
110         {
111           char buf[arg_size + arg_align - 1];
112           char *arg = (char *) (((uintptr_t) buf + arg_align - 1)
113                                 & ~(uintptr_t) (arg_align - 1));
114           cpyfn (arg, data);
115           fn (arg);
116         }
117       else
118         fn (data);
119       if (team != NULL)
120         {
121           gomp_mutex_lock (&team->task_lock);
122           if (task.children != NULL)
123             gomp_clear_parent (task.children);
124           gomp_mutex_unlock (&team->task_lock);
125         }
126       gomp_end_task ();
127     }
128   else
129     {
130       struct gomp_task *task;
131       struct gomp_task *parent = thr->task;
132       char *arg;
133       bool do_wake;
134
135       task = gomp_malloc (sizeof (*task) + arg_size + arg_align - 1);
136       arg = (char *) (((uintptr_t) (task + 1) + arg_align - 1)
137                       & ~(uintptr_t) (arg_align - 1));
138       gomp_init_task (task, parent, gomp_icv (false));
139       task->kind = GOMP_TASK_IFFALSE;
140       task->in_tied_task = parent->in_tied_task;
141       thr->task = task;
142       if (cpyfn)
143         cpyfn (arg, data);
144       else
145         memcpy (arg, data, arg_size);
146       thr->task = parent;
147       task->kind = GOMP_TASK_WAITING;
148       task->fn = fn;
149       task->fn_data = arg;
150       task->in_tied_task = true;
151       task->final_task = (flags & 2) >> 1;
152       gomp_mutex_lock (&team->task_lock);
153       if (parent->children)
154         {
155           task->next_child = parent->children;
156           task->prev_child = parent->children->prev_child;
157           task->next_child->prev_child = task;
158           task->prev_child->next_child = task;
159         }
160       else
161         {
162           task->next_child = task;
163           task->prev_child = task;
164         }
165       parent->children = task;
166       if (team->task_queue)
167         {
168           task->next_queue = team->task_queue;
169           task->prev_queue = team->task_queue->prev_queue;
170           task->next_queue->prev_queue = task;
171           task->prev_queue->next_queue = task;
172         }
173       else
174         {
175           task->next_queue = task;
176           task->prev_queue = task;
177           team->task_queue = task;
178         }
179       ++team->task_count;
180       gomp_team_barrier_set_task_pending (&team->barrier);
181       do_wake = team->task_running_count + !parent->in_tied_task
182                 < team->nthreads;
183       gomp_mutex_unlock (&team->task_lock);
184       if (do_wake)
185         gomp_team_barrier_wake (&team->barrier, 1);
186     }
187 }
188
189 void
190 gomp_barrier_handle_tasks (gomp_barrier_state_t state)
191 {
192   struct gomp_thread *thr = gomp_thread ();
193   struct gomp_team *team = thr->ts.team;
194   struct gomp_task *task = thr->task;
195   struct gomp_task *child_task = NULL;
196   struct gomp_task *to_free = NULL;
197
198   gomp_mutex_lock (&team->task_lock);
199   if (gomp_barrier_last_thread (state))
200     {
201       if (team->task_count == 0)
202         {
203           gomp_team_barrier_done (&team->barrier, state);
204           gomp_mutex_unlock (&team->task_lock);
205           gomp_team_barrier_wake (&team->barrier, 0);
206           return;
207         }
208       gomp_team_barrier_set_waiting_for_tasks (&team->barrier);
209     }
210
211   while (1)
212     {
213       if (team->task_queue != NULL)
214         {
215           struct gomp_task *parent;
216
217           child_task = team->task_queue;
218           parent = child_task->parent;
219           if (parent && parent->children == child_task)
220             parent->children = child_task->next_child;
221           child_task->prev_queue->next_queue = child_task->next_queue;
222           child_task->next_queue->prev_queue = child_task->prev_queue;
223           if (child_task->next_queue != child_task)
224             team->task_queue = child_task->next_queue;
225           else
226             team->task_queue = NULL;
227           child_task->kind = GOMP_TASK_TIED;
228           team->task_running_count++;
229           if (team->task_count == team->task_running_count)
230             gomp_team_barrier_clear_task_pending (&team->barrier);
231         }
232       gomp_mutex_unlock (&team->task_lock);
233       if (to_free)
234         {
235           gomp_finish_task (to_free);
236           free (to_free);
237           to_free = NULL;
238         }
239       if (child_task)
240         {
241           thr->task = child_task;
242           child_task->fn (child_task->fn_data);
243           thr->task = task;
244         }
245       else
246         return;
247       gomp_mutex_lock (&team->task_lock);
248       if (child_task)
249         {
250           struct gomp_task *parent = child_task->parent;
251           if (parent)
252             {
253               child_task->prev_child->next_child = child_task->next_child;
254               child_task->next_child->prev_child = child_task->prev_child;
255               if (parent->children == child_task)
256                 {
257                   if (child_task->next_child != child_task)
258                     parent->children = child_task->next_child;
259                   else
260                     {
261                       parent->children = NULL;
262                       if (parent->in_taskwait)
263                         gomp_sem_post (&parent->taskwait_sem);
264                     }
265                 }
266             }
267           gomp_clear_parent (child_task->children);
268           to_free = child_task;
269           child_task = NULL;
270           team->task_running_count--;
271           if (--team->task_count == 0
272               && gomp_team_barrier_waiting_for_tasks (&team->barrier))
273             {
274               gomp_team_barrier_done (&team->barrier, state);
275               gomp_mutex_unlock (&team->task_lock);
276               gomp_team_barrier_wake (&team->barrier, 0);
277               gomp_mutex_lock (&team->task_lock);
278             }
279         }
280     }
281 }
282
283 /* Called when encountering a taskwait directive.  */
284
285 void
286 GOMP_taskwait (void)
287 {
288   struct gomp_thread *thr = gomp_thread ();
289   struct gomp_team *team = thr->ts.team;
290   struct gomp_task *task = thr->task;
291   struct gomp_task *child_task = NULL;
292   struct gomp_task *to_free = NULL;
293
294   if (task == NULL || team == NULL)
295     return;
296
297   gomp_mutex_lock (&team->task_lock);
298   while (1)
299     {
300       if (task->children == NULL)
301         {
302           gomp_mutex_unlock (&team->task_lock);
303           if (to_free)
304             {
305               gomp_finish_task (to_free);
306               free (to_free);
307             }
308           return;
309         }
310       if (task->children->kind == GOMP_TASK_WAITING)
311         {
312           child_task = task->children;
313           task->children = child_task->next_child;
314           child_task->prev_queue->next_queue = child_task->next_queue;
315           child_task->next_queue->prev_queue = child_task->prev_queue;
316           if (team->task_queue == child_task)
317             {
318               if (child_task->next_queue != child_task)
319                 team->task_queue = child_task->next_queue;
320               else
321                 team->task_queue = NULL;
322             }
323           child_task->kind = GOMP_TASK_TIED;
324           team->task_running_count++;
325           if (team->task_count == team->task_running_count)
326             gomp_team_barrier_clear_task_pending (&team->barrier);
327         }
328       else
329         /* All tasks we are waiting for are already running
330            in other threads.  Wait for them.  */
331         task->in_taskwait = true;
332       gomp_mutex_unlock (&team->task_lock);
333       if (to_free)
334         {
335           gomp_finish_task (to_free);
336           free (to_free);
337           to_free = NULL;
338         }
339       if (child_task)
340         {
341           thr->task = child_task;
342           child_task->fn (child_task->fn_data);
343           thr->task = task;
344         }
345       else
346         {
347           gomp_sem_wait (&task->taskwait_sem);
348           task->in_taskwait = false;
349           return;
350         }
351       gomp_mutex_lock (&team->task_lock);
352       if (child_task)
353         {
354           child_task->prev_child->next_child = child_task->next_child;
355           child_task->next_child->prev_child = child_task->prev_child;
356           if (task->children == child_task)
357             {
358               if (child_task->next_child != child_task)
359                 task->children = child_task->next_child;
360               else
361                 task->children = NULL;
362             }
363           gomp_clear_parent (child_task->children);
364           to_free = child_task;
365           child_task = NULL;
366           team->task_count--;
367           team->task_running_count--;
368         }
369     }
370 }
371
372 /* Called when encountering a taskyield directive.  */
373
374 void
375 GOMP_taskyield (void)
376 {
377   /* Nothing at the moment.  */
378 }
379
380 int
381 omp_in_final (void)
382 {
383   struct gomp_thread *thr = gomp_thread ();
384   return thr->task && thr->task->final_task;
385 }
386
387 ialias (omp_in_final)