OSDN Git Service

d3d72663f35c0762a2f4c0557240350eb08c9dd5
[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               gomp_mutex_lock (&team->task_lock);
277             }
278         }
279     }
280 }
281
282 /* Called when encountering a taskwait directive.  */
283
284 void
285 GOMP_taskwait (void)
286 {
287   struct gomp_thread *thr = gomp_thread ();
288   struct gomp_team *team = thr->ts.team;
289   struct gomp_task *task = thr->task;
290   struct gomp_task *child_task = NULL;
291   struct gomp_task *to_free = NULL;
292
293   if (task == NULL || task->children == NULL)
294     return;
295   gomp_mutex_lock (&team->task_lock);
296   while (1)
297     {
298       if (task->children == NULL)
299         {
300           gomp_mutex_unlock (&team->task_lock);
301           if (to_free)
302             {
303               gomp_finish_task (to_free);
304               free (to_free);
305             }
306           return;
307         }
308       if (task->children->kind == GOMP_TASK_WAITING)
309         {
310           child_task = task->children;
311           task->children = child_task->next_child;
312           child_task->prev_queue->next_queue = child_task->next_queue;
313           child_task->next_queue->prev_queue = child_task->prev_queue;
314           if (team->task_queue == child_task)
315             {
316               if (child_task->next_queue != child_task)
317                 team->task_queue = child_task->next_queue;
318               else
319                 team->task_queue = NULL;
320             }
321           child_task->kind = GOMP_TASK_TIED;
322           team->task_running_count++;
323           if (team->task_count == team->task_running_count)
324             gomp_team_barrier_clear_task_pending (&team->barrier);
325         }
326       else
327         /* All tasks we are waiting for are already running
328            in other threads.  Wait for them.  */
329         task->in_taskwait = true;
330       gomp_mutex_unlock (&team->task_lock);
331       if (to_free)
332         {
333           gomp_finish_task (to_free);
334           free (to_free);
335           to_free = NULL;
336         }
337       if (child_task)
338         {
339           thr->task = child_task;
340           child_task->fn (child_task->fn_data);
341           thr->task = task;
342         }
343       else
344         {
345           gomp_sem_wait (&task->taskwait_sem);
346           task->in_taskwait = false;
347           return;
348         }
349       gomp_mutex_lock (&team->task_lock);
350       if (child_task)
351         {
352           child_task->prev_child->next_child = child_task->next_child;
353           child_task->next_child->prev_child = child_task->prev_child;
354           if (task->children == child_task)
355             {
356               if (child_task->next_child != child_task)
357                 task->children = child_task->next_child;
358               else
359                 task->children = NULL;
360             }
361           gomp_clear_parent (child_task->children);
362           to_free = child_task;
363           child_task = NULL;
364           team->task_count--;
365           team->task_running_count--;
366         }
367     }
368 }
369
370 /* Called when encountering a taskyield directive.  */
371
372 void
373 GOMP_taskyield (void)
374 {
375   /* Nothing at the moment.  */
376 }
377
378 int
379 omp_in_final (void)
380 {
381   struct gomp_thread *thr = gomp_thread ();
382   return thr->task && thr->task->final_task;
383 }
384
385 ialias (omp_in_final)