OSDN Git Service

2014-04-01 Richard Biener <rguenther@suse.de>
[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       /* Access to "children" is normally done inside a task_lock
120          mutex region, but the only way this particular task.children
121          can be set is if this thread's task work function (fn)
122          creates children.  So since the setter is *this* thread, we
123          need no barriers here when testing for non-NULL.  We can have
124          task.children set by the current thread then changed by a
125          child thread, but seeing a stale non-NULL value is not a
126          problem.  Once past the task_lock acquisition, this thread
127          will see the real value of task.children.  */
128       if (task.children != NULL)
129         {
130           gomp_mutex_lock (&team->task_lock);
131           gomp_clear_parent (task.children);
132           gomp_mutex_unlock (&team->task_lock);
133         }
134       gomp_end_task ();
135     }
136   else
137     {
138       struct gomp_task *task;
139       struct gomp_task *parent = thr->task;
140       char *arg;
141       bool do_wake;
142
143       task = gomp_malloc (sizeof (*task) + arg_size + arg_align - 1);
144       arg = (char *) (((uintptr_t) (task + 1) + arg_align - 1)
145                       & ~(uintptr_t) (arg_align - 1));
146       gomp_init_task (task, parent, gomp_icv (false));
147       task->kind = GOMP_TASK_IFFALSE;
148       task->in_tied_task = parent->in_tied_task;
149       thr->task = task;
150       if (cpyfn)
151         cpyfn (arg, data);
152       else
153         memcpy (arg, data, arg_size);
154       thr->task = parent;
155       task->kind = GOMP_TASK_WAITING;
156       task->fn = fn;
157       task->fn_data = arg;
158       task->in_tied_task = true;
159       task->final_task = (flags & 2) >> 1;
160       gomp_mutex_lock (&team->task_lock);
161       if (parent->children)
162         {
163           task->next_child = parent->children;
164           task->prev_child = parent->children->prev_child;
165           task->next_child->prev_child = task;
166           task->prev_child->next_child = task;
167         }
168       else
169         {
170           task->next_child = task;
171           task->prev_child = task;
172         }
173       parent->children = task;
174       if (team->task_queue)
175         {
176           task->next_queue = team->task_queue;
177           task->prev_queue = team->task_queue->prev_queue;
178           task->next_queue->prev_queue = task;
179           task->prev_queue->next_queue = task;
180         }
181       else
182         {
183           task->next_queue = task;
184           task->prev_queue = task;
185           team->task_queue = task;
186         }
187       ++team->task_count;
188       gomp_team_barrier_set_task_pending (&team->barrier);
189       do_wake = team->task_running_count + !parent->in_tied_task
190                 < team->nthreads;
191       gomp_mutex_unlock (&team->task_lock);
192       if (do_wake)
193         gomp_team_barrier_wake (&team->barrier, 1);
194     }
195 }
196
197 void
198 gomp_barrier_handle_tasks (gomp_barrier_state_t state)
199 {
200   struct gomp_thread *thr = gomp_thread ();
201   struct gomp_team *team = thr->ts.team;
202   struct gomp_task *task = thr->task;
203   struct gomp_task *child_task = NULL;
204   struct gomp_task *to_free = NULL;
205
206   gomp_mutex_lock (&team->task_lock);
207   if (gomp_barrier_last_thread (state))
208     {
209       if (team->task_count == 0)
210         {
211           gomp_team_barrier_done (&team->barrier, state);
212           gomp_mutex_unlock (&team->task_lock);
213           gomp_team_barrier_wake (&team->barrier, 0);
214           return;
215         }
216       gomp_team_barrier_set_waiting_for_tasks (&team->barrier);
217     }
218
219   while (1)
220     {
221       if (team->task_queue != NULL)
222         {
223           struct gomp_task *parent;
224
225           child_task = team->task_queue;
226           parent = child_task->parent;
227           if (parent && parent->children == child_task)
228             parent->children = child_task->next_child;
229           child_task->prev_queue->next_queue = child_task->next_queue;
230           child_task->next_queue->prev_queue = child_task->prev_queue;
231           if (child_task->next_queue != child_task)
232             team->task_queue = child_task->next_queue;
233           else
234             team->task_queue = NULL;
235           child_task->kind = GOMP_TASK_TIED;
236           team->task_running_count++;
237           if (team->task_count == team->task_running_count)
238             gomp_team_barrier_clear_task_pending (&team->barrier);
239         }
240       gomp_mutex_unlock (&team->task_lock);
241       if (to_free)
242         {
243           gomp_finish_task (to_free);
244           free (to_free);
245           to_free = NULL;
246         }
247       if (child_task)
248         {
249           thr->task = child_task;
250           child_task->fn (child_task->fn_data);
251           thr->task = task;
252         }
253       else
254         return;
255       gomp_mutex_lock (&team->task_lock);
256       if (child_task)
257         {
258           struct gomp_task *parent = child_task->parent;
259           if (parent)
260             {
261               child_task->prev_child->next_child = child_task->next_child;
262               child_task->next_child->prev_child = child_task->prev_child;
263               if (parent->children == child_task)
264                 {
265                   if (child_task->next_child != child_task)
266                     parent->children = child_task->next_child;
267                   else
268                     {
269                       /* We access task->children in GOMP_taskwait
270                          outside of the task lock mutex region, so
271                          need a release barrier here to ensure memory
272                          written by child_task->fn above is flushed
273                          before the NULL is written.  */
274                       __atomic_store_n (&parent->children, NULL,
275                                         MEMMODEL_RELEASE);
276                       if (parent->in_taskwait)
277                         gomp_sem_post (&parent->taskwait_sem);
278                     }
279                 }
280             }
281           gomp_clear_parent (child_task->children);
282           to_free = child_task;
283           child_task = NULL;
284           team->task_running_count--;
285           if (--team->task_count == 0
286               && gomp_team_barrier_waiting_for_tasks (&team->barrier))
287             {
288               gomp_team_barrier_done (&team->barrier, state);
289               gomp_mutex_unlock (&team->task_lock);
290               gomp_team_barrier_wake (&team->barrier, 0);
291               gomp_mutex_lock (&team->task_lock);
292             }
293         }
294     }
295 }
296
297 /* Called when encountering a taskwait directive.  */
298
299 void
300 GOMP_taskwait (void)
301 {
302   struct gomp_thread *thr = gomp_thread ();
303   struct gomp_team *team = thr->ts.team;
304   struct gomp_task *task = thr->task;
305   struct gomp_task *child_task = NULL;
306   struct gomp_task *to_free = NULL;
307
308   /* The acquire barrier on load of task->children here synchronizes
309      with the write of a NULL in gomp_barrier_handle_tasks.  It is
310      not necessary that we synchronize with other non-NULL writes at
311      this point, but we must ensure that all writes to memory by a
312      child thread task work function are seen before we exit from
313      GOMP_taskwait.  */
314   if (task == NULL
315       || __atomic_load_n (&task->children, MEMMODEL_ACQUIRE) == NULL)
316     return;
317
318   gomp_mutex_lock (&team->task_lock);
319   while (1)
320     {
321       if (task->children == NULL)
322         {
323           gomp_mutex_unlock (&team->task_lock);
324           if (to_free)
325             {
326               gomp_finish_task (to_free);
327               free (to_free);
328             }
329           return;
330         }
331       if (task->children->kind == GOMP_TASK_WAITING)
332         {
333           child_task = task->children;
334           task->children = child_task->next_child;
335           child_task->prev_queue->next_queue = child_task->next_queue;
336           child_task->next_queue->prev_queue = child_task->prev_queue;
337           if (team->task_queue == child_task)
338             {
339               if (child_task->next_queue != child_task)
340                 team->task_queue = child_task->next_queue;
341               else
342                 team->task_queue = NULL;
343             }
344           child_task->kind = GOMP_TASK_TIED;
345           team->task_running_count++;
346           if (team->task_count == team->task_running_count)
347             gomp_team_barrier_clear_task_pending (&team->barrier);
348         }
349       else
350         /* All tasks we are waiting for are already running
351            in other threads.  Wait for them.  */
352         task->in_taskwait = true;
353       gomp_mutex_unlock (&team->task_lock);
354       if (to_free)
355         {
356           gomp_finish_task (to_free);
357           free (to_free);
358           to_free = NULL;
359         }
360       if (child_task)
361         {
362           thr->task = child_task;
363           child_task->fn (child_task->fn_data);
364           thr->task = task;
365         }
366       else
367         {
368           gomp_sem_wait (&task->taskwait_sem);
369           task->in_taskwait = false;
370           return;
371         }
372       gomp_mutex_lock (&team->task_lock);
373       if (child_task)
374         {
375           child_task->prev_child->next_child = child_task->next_child;
376           child_task->next_child->prev_child = child_task->prev_child;
377           if (task->children == child_task)
378             {
379               if (child_task->next_child != child_task)
380                 task->children = child_task->next_child;
381               else
382                 task->children = NULL;
383             }
384           gomp_clear_parent (child_task->children);
385           to_free = child_task;
386           child_task = NULL;
387           team->task_count--;
388           team->task_running_count--;
389         }
390     }
391 }
392
393 /* Called when encountering a taskyield directive.  */
394
395 void
396 GOMP_taskyield (void)
397 {
398   /* Nothing at the moment.  */
399 }
400
401 int
402 omp_in_final (void)
403 {
404   struct gomp_thread *thr = gomp_thread ();
405   return thr->task && thr->task->final_task;
406 }
407
408 ialias (omp_in_final)