]>
Commit | Line | Data |
---|---|---|
748086b7 | 1 | /* Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. |
a68ab351 JJ |
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 | |
748086b7 JJ |
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. | |
a68ab351 JJ |
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 | |
748086b7 | 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
a68ab351 JJ |
14 | more details. |
15 | ||
748086b7 JJ |
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/>. */ | |
a68ab351 JJ |
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; | |
5f836cbb | 43 | task->in_tied_task = false; |
a68ab351 JJ |
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; | |
5f836cbb JJ |
104 | if (thr->task) |
105 | task.in_tied_task = thr->task->in_tied_task; | |
a68ab351 JJ |
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; | |
5f836cbb | 137 | task->in_tied_task = parent->in_tied_task; |
a68ab351 JJ |
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; | |
5f836cbb | 147 | task->in_tied_task = true; |
a68ab351 JJ |
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 | } | |
5f836cbb JJ |
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; | |
a68ab351 JJ |
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 | } |