OSDN Git Service

* trans-array.h (gfc_trans_create_temp_array): Replace info argument
[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 (task.children)
120         {
121           gomp_mutex_lock (&team->task_lock);
122           gomp_clear_parent (task.children);
123           gomp_mutex_unlock (&team->task_lock);
124         }
125       gomp_end_task ();
126     }
127   else
128     {
129       struct gomp_task *task;
130       struct gomp_task *parent = thr->task;
131       char *arg;
132       bool do_wake;
133
134       task = gomp_malloc (sizeof (*task) + arg_size + arg_align - 1);
135       arg = (char *) (((uintptr_t) (task + 1) + arg_align - 1)
136                       & ~(uintptr_t) (arg_align - 1));
137       gomp_init_task (task, parent, gomp_icv (false));
138       task->kind = GOMP_TASK_IFFALSE;
139       task->in_tied_task = parent->in_tied_task;
140       thr->task = task;
141       if (cpyfn)
142         cpyfn (arg, data);
143       else
144         memcpy (arg, data, arg_size);
145       thr->task = parent;
146       task->kind = GOMP_TASK_WAITING;
147       task->fn = fn;
148       task->fn_data = arg;
149       task->in_tied_task = true;
150       task->final_task = (flags & 2) >> 1;
151       gomp_mutex_lock (&team->task_lock);
152       if (parent->children)
153         {
154           task->next_child = parent->children;
155           task->prev_child = parent->children->prev_child;
156           task->next_child->prev_child = task;
157           task->prev_child->next_child = task;
158         }
159       else
160         {
161           task->next_child = task;
162           task->prev_child = task;
163         }
164       parent->children = task;
165       if (team->task_queue)
166         {
167           task->next_queue = team->task_queue;
168           task->prev_queue = team->task_queue->prev_queue;
169           task->next_queue->prev_queue = task;
170           task->prev_queue->next_queue = task;
171         }
172       else
173         {
174           task->next_queue = task;
175           task->prev_queue = task;
176           team->task_queue = task;
177         }
178       ++team->task_count;
179       gomp_team_barrier_set_task_pending (&team->barrier);
180       do_wake = team->task_running_count + !parent->in_tied_task
181                 < team->nthreads;
182       gomp_mutex_unlock (&team->task_lock);
183       if (do_wake)
184         gomp_team_barrier_wake (&team->barrier, 1);
185     }
186 }
187
188 void
189 gomp_barrier_handle_tasks (gomp_barrier_state_t state)
190 {
191   struct gomp_thread *thr = gomp_thread ();
192   struct gomp_team *team = thr->ts.team;
193   struct gomp_task *task = thr->task;
194   struct gomp_task *child_task = NULL;
195   struct gomp_task *to_free = NULL;
196
197   gomp_mutex_lock (&team->task_lock);
198   if (gomp_barrier_last_thread (state))
199     {
200       if (team->task_count == 0)
201         {
202           gomp_team_barrier_done (&team->barrier, state);
203           gomp_mutex_unlock (&team->task_lock);
204           gomp_team_barrier_wake (&team->barrier, 0);
205           return;
206         }
207       gomp_team_barrier_set_waiting_for_tasks (&team->barrier);
208     }
209
210   while (1)
211     {
212       if (team->task_queue != NULL)
213         {
214           struct gomp_task *parent;
215
216           child_task = team->task_queue;
217           parent = child_task->parent;
218           if (parent && parent->children == child_task)
219             parent->children = child_task->next_child;
220           child_task->prev_queue->next_queue = child_task->next_queue;
221           child_task->next_queue->prev_queue = child_task->prev_queue;
222           if (child_task->next_queue != child_task)
223             team->task_queue = child_task->next_queue;
224           else
225             team->task_queue = NULL;
226           child_task->kind = GOMP_TASK_TIED;
227           team->task_running_count++;
228           if (team->task_count == team->task_running_count)
229             gomp_team_barrier_clear_task_pending (&team->barrier);
230         }
231       gomp_mutex_unlock (&team->task_lock);
232       if (to_free)
233         {
234           gomp_finish_task (to_free);
235           free (to_free);
236           to_free = NULL;
237         }
238       if (child_task)
239         {
240           thr->task = child_task;
241           child_task->fn (child_task->fn_data);
242           thr->task = task;
243         }
244       else
245         return;
246       gomp_mutex_lock (&team->task_lock);
247       if (child_task)
248         {
249           struct gomp_task *parent = child_task->parent;
250           if (parent)
251             {
252               child_task->prev_child->next_child = child_task->next_child;
253               child_task->next_child->prev_child = child_task->prev_child;
254               if (parent->children == child_task)
255                 {
256                   if (child_task->next_child != child_task)
257                     parent->children = child_task->next_child;
258                   else
259                     {
260                       parent->children = NULL;
261                       if (parent->in_taskwait)
262                         gomp_sem_post (&parent->taskwait_sem);
263                     }
264                 }
265             }
266           gomp_clear_parent (child_task->children);
267           to_free = child_task;
268           child_task = NULL;
269           team->task_running_count--;
270           if (--team->task_count == 0
271               && gomp_team_barrier_waiting_for_tasks (&team->barrier))
272             {
273               gomp_team_barrier_done (&team->barrier, state);
274               gomp_mutex_unlock (&team->task_lock);
275               gomp_team_barrier_wake (&team->barrier, 0);
276             }
277         }
278     }
279 }
280
281 /* Called when encountering a taskwait directive.  */
282
283 void
284 GOMP_taskwait (void)
285 {
286   struct gomp_thread *thr = gomp_thread ();
287   struct gomp_team *team = thr->ts.team;
288   struct gomp_task *task = thr->task;
289   struct gomp_task *child_task = NULL;
290   struct gomp_task *to_free = NULL;
291
292   if (task == NULL || task->children == NULL)
293     return;
294   gomp_mutex_lock (&team->task_lock);
295   while (1)
296     {
297       if (task->children == NULL)
298         {
299           gomp_mutex_unlock (&team->task_lock);
300           if (to_free)
301             {
302               gomp_finish_task (to_free);
303               free (to_free);
304             }
305           return;
306         }
307       if (task->children->kind == GOMP_TASK_WAITING)
308         {
309           child_task = task->children;
310           task->children = child_task->next_child;
311           child_task->prev_queue->next_queue = child_task->next_queue;
312           child_task->next_queue->prev_queue = child_task->prev_queue;
313           if (team->task_queue == child_task)
314             {
315               if (child_task->next_queue != child_task)
316                 team->task_queue = child_task->next_queue;
317               else
318                 team->task_queue = NULL;
319             }
320           child_task->kind = GOMP_TASK_TIED;
321           team->task_running_count++;
322           if (team->task_count == team->task_running_count)
323             gomp_team_barrier_clear_task_pending (&team->barrier);
324         }
325       else
326         /* All tasks we are waiting for are already running
327            in other threads.  Wait for them.  */
328         task->in_taskwait = true;
329       gomp_mutex_unlock (&team->task_lock);
330       if (to_free)
331         {
332           gomp_finish_task (to_free);
333           free (to_free);
334           to_free = NULL;
335         }
336       if (child_task)
337         {
338           thr->task = child_task;
339           child_task->fn (child_task->fn_data);
340           thr->task = task;
341         }
342       else
343         {
344           gomp_sem_wait (&task->taskwait_sem);
345           task->in_taskwait = false;
346           return;
347         }
348       gomp_mutex_lock (&team->task_lock);
349       if (child_task)
350         {
351           child_task->prev_child->next_child = child_task->next_child;
352           child_task->next_child->prev_child = child_task->prev_child;
353           if (task->children == child_task)
354             {
355               if (child_task->next_child != child_task)
356                 task->children = child_task->next_child;
357               else
358                 task->children = NULL;
359             }
360           gomp_clear_parent (child_task->children);
361           to_free = child_task;
362           child_task = NULL;
363           team->task_count--;
364           team->task_running_count--;
365         }
366     }
367 }
368
369 /* Called when encountering a taskyield directive.  */
370
371 void
372 GOMP_taskyield (void)
373 {
374   /* Nothing at the moment.  */
375 }
376
377 int
378 omp_in_final (void)
379 {
380   struct gomp_thread *thr = gomp_thread ();
381   return thr->task && thr->task->final_task;
382 }
383
384 ialias (omp_in_final)