xref: /dragonfly/contrib/gcc-4.7/libgomp/task.c (revision 07a2f99c)
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)
388