xref: /netbsd-src/external/gpl3/gcc.old/dist/libgomp/task.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
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 }
365