OSDN Git Service

95f163d53cd72c3c78026270d8442bceef81783d
[pf3gnuchains/gcc-fork.git] / libgomp / task.c
1 /* Copyright (C) 2007, 2008, 2009 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->children = NULL;
45   gomp_sem_init (&task->taskwait_sem, 0);
46 }
47
48 /* Clean up a task, after completing it.  */
49
50 void
51 gomp_end_task (void)
52 {
53   struct gomp_thread *thr = gomp_thread ();
54   struct gomp_task *task = thr->task;
55
56   gomp_finish_task (task);
57   thr->task = task->parent;
58 }
59
60 static inline void
61 gomp_clear_parent (struct gomp_task *children)
62 {
63   struct gomp_task *task = children;
64
65   if (task)
66     do
67       {
68         task->parent = NULL;
69         task = task->next_child;
70       }
71     while (task != children);
72 }
73
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.  */
77
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)))
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       || team->task_count > 64 * team->nthreads)
99     {
100       struct gomp_task task;
101
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))
108         {
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);
114         }
115       else
116         fn (data);
117       if (task.children)
118         {
119           gomp_mutex_lock (&team->task_lock);
120           gomp_clear_parent (task.children);
121           gomp_mutex_unlock (&team->task_lock);
122         }
123       gomp_end_task ();
124     }
125   else
126     {
127       struct gomp_task *task;
128       struct gomp_task *parent = thr->task;
129       char *arg;
130       bool do_wake;
131
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)
150         {
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;
155         }
156       else
157         {
158           task->next_child = task;
159           task->prev_child = task;
160         }
161       parent->children = task;
162       if (team->task_queue)
163         {
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;
168         }
169       else
170         {
171           task->next_queue = task;
172           task->prev_queue = task;
173           team->task_queue = task;
174         }
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);
182     }
183 }
184
185 void
186 gomp_barrier_handle_tasks (gomp_barrier_state_t state)
187 {
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;
193
194   gomp_mutex_lock (&team->task_lock);
195   if (gomp_barrier_last_thread (state))
196     {
197       if (team->task_count == 0)
198         {
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;
203         }
204       gomp_team_barrier_set_waiting_for_tasks (&team->barrier);
205     }
206
207   while (1)
208     {
209       if (team->task_queue != NULL)
210         {
211           struct gomp_task *parent;
212
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);
227         }
228       gomp_mutex_unlock (&team->task_lock);
229       if (to_free)
230         {
231           gomp_finish_task (to_free);
232           free (to_free);
233           to_free = NULL;
234         }
235       if (child_task)
236         {
237           thr->task = child_task;
238           child_task->fn (child_task->fn_data);
239           thr->task = task;
240         }
241       else
242         return;
243       gomp_mutex_lock (&team->task_lock);
244       if (child_task)
245         {
246           struct gomp_task *parent = child_task->parent;
247           if (parent)
248             {
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)
252                 {
253                   if (child_task->next_child != child_task)
254                     parent->children = child_task->next_child;
255                   else
256                     {
257                       parent->children = NULL;
258                       if (parent->in_taskwait)
259                         gomp_sem_post (&parent->taskwait_sem);
260                     }
261                 }
262             }
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))
269             {
270               gomp_team_barrier_done (&team->barrier, state);
271               gomp_mutex_unlock (&team->task_lock);
272               gomp_team_barrier_wake (&team->barrier, 0);
273             }
274         }
275     }
276 }
277
278 /* Called when encountering a taskwait directive.  */
279
280 void
281 GOMP_taskwait (void)
282 {
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;
288
289   if (task == NULL || task->children == NULL)
290     return;
291   gomp_mutex_lock (&team->task_lock);
292   while (1)
293     {
294       if (task->children == NULL)
295         {
296           gomp_mutex_unlock (&team->task_lock);
297           if (to_free)
298             {
299               gomp_finish_task (to_free);
300               free (to_free);
301             }
302           return;
303         }
304       if (task->children->kind == GOMP_TASK_WAITING)
305         {
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)
311             {
312               if (child_task->next_queue != child_task)
313                 team->task_queue = child_task->next_queue;
314               else
315                 team->task_queue = NULL;
316             }
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);
321         }
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)
328         {
329           gomp_finish_task (to_free);
330           free (to_free);
331           to_free = NULL;
332         }
333       if (child_task)
334         {
335           thr->task = child_task;
336           child_task->fn (child_task->fn_data);
337           thr->task = task;
338         }
339       else
340         {
341           gomp_sem_wait (&task->taskwait_sem);
342           task->in_taskwait = false;
343           return;
344         }
345       gomp_mutex_lock (&team->task_lock);
346       if (child_task)
347         {
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)
351             {
352               if (child_task->next_child != child_task)
353                 task->children = child_task->next_child;
354               else
355                 task->children = NULL;
356             }
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--;
362         }
363     }
364 }