Mercurial > hg > CbC > CbC_gcc
annotate libgomp/team.c @ 19:58ad6c70ea60
update gcc from 4.4.0 to 4.4.1.
author | kent@firefly.cr.ie.u-ryukyu.ac.jp |
---|---|
date | Thu, 24 Sep 2009 13:21:57 +0900 |
parents | a06113de4d67 |
children | 04ced10e8804 |
rev | line source |
---|---|
0 | 1 /* Copyright (C) 2005, 2006, 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 threads in response to team | |
26 creation and termination. */ | |
27 | |
28 #include "libgomp.h" | |
29 #include <stdlib.h> | |
30 #include <string.h> | |
31 | |
32 /* This attribute contains PTHREAD_CREATE_DETACHED. */ | |
33 pthread_attr_t gomp_thread_attr; | |
34 | |
35 /* This key is for the thread destructor. */ | |
36 pthread_key_t gomp_thread_destructor; | |
37 | |
38 | |
39 /* This is the libgomp per-thread data structure. */ | |
40 #ifdef HAVE_TLS | |
41 __thread struct gomp_thread gomp_tls_data; | |
42 #else | |
43 pthread_key_t gomp_tls_key; | |
44 #endif | |
45 | |
46 | |
47 /* This structure is used to communicate across pthread_create. */ | |
48 | |
49 struct gomp_thread_start_data | |
50 { | |
51 void (*fn) (void *); | |
52 void *fn_data; | |
53 struct gomp_team_state ts; | |
54 struct gomp_task *task; | |
55 struct gomp_thread_pool *thread_pool; | |
56 bool nested; | |
57 }; | |
58 | |
59 | |
60 /* This function is a pthread_create entry point. This contains the idle | |
61 loop in which a thread waits to be called up to become part of a team. */ | |
62 | |
63 static void * | |
64 gomp_thread_start (void *xdata) | |
65 { | |
66 struct gomp_thread_start_data *data = xdata; | |
67 struct gomp_thread *thr; | |
68 struct gomp_thread_pool *pool; | |
69 void (*local_fn) (void *); | |
70 void *local_data; | |
71 | |
72 #ifdef HAVE_TLS | |
73 thr = &gomp_tls_data; | |
74 #else | |
75 struct gomp_thread local_thr; | |
76 thr = &local_thr; | |
77 pthread_setspecific (gomp_tls_key, thr); | |
78 #endif | |
79 gomp_sem_init (&thr->release, 0); | |
80 | |
81 /* Extract what we need from data. */ | |
82 local_fn = data->fn; | |
83 local_data = data->fn_data; | |
84 thr->thread_pool = data->thread_pool; | |
85 thr->ts = data->ts; | |
86 thr->task = data->task; | |
87 | |
88 thr->ts.team->ordered_release[thr->ts.team_id] = &thr->release; | |
89 | |
90 /* Make thread pool local. */ | |
91 pool = thr->thread_pool; | |
92 | |
93 if (data->nested) | |
94 { | |
95 struct gomp_team *team = thr->ts.team; | |
96 struct gomp_task *task = thr->task; | |
97 | |
98 gomp_barrier_wait (&team->barrier); | |
99 | |
100 local_fn (local_data); | |
101 gomp_team_barrier_wait (&team->barrier); | |
102 gomp_finish_task (task); | |
103 gomp_barrier_wait_last (&team->barrier); | |
104 } | |
105 else | |
106 { | |
107 pool->threads[thr->ts.team_id] = thr; | |
108 | |
109 gomp_barrier_wait (&pool->threads_dock); | |
110 do | |
111 { | |
112 struct gomp_team *team = thr->ts.team; | |
113 struct gomp_task *task = thr->task; | |
114 | |
115 local_fn (local_data); | |
116 gomp_team_barrier_wait (&team->barrier); | |
117 gomp_finish_task (task); | |
118 | |
119 gomp_barrier_wait (&pool->threads_dock); | |
120 | |
121 local_fn = thr->fn; | |
122 local_data = thr->data; | |
123 thr->fn = NULL; | |
124 } | |
125 while (local_fn); | |
126 } | |
127 | |
19
58ad6c70ea60
update gcc from 4.4.0 to 4.4.1.
kent@firefly.cr.ie.u-ryukyu.ac.jp
parents:
0
diff
changeset
|
128 gomp_sem_destroy (&thr->release); |
0 | 129 return NULL; |
130 } | |
131 | |
132 | |
133 /* Create a new team data structure. */ | |
134 | |
135 struct gomp_team * | |
136 gomp_new_team (unsigned nthreads) | |
137 { | |
138 struct gomp_team *team; | |
139 size_t size; | |
140 int i; | |
141 | |
142 size = sizeof (*team) + nthreads * (sizeof (team->ordered_release[0]) | |
143 + sizeof (team->implicit_task[0])); | |
144 team = gomp_malloc (size); | |
145 | |
146 team->work_share_chunk = 8; | |
147 #ifdef HAVE_SYNC_BUILTINS | |
148 team->single_count = 0; | |
149 #else | |
150 gomp_mutex_init (&team->work_share_list_free_lock); | |
151 #endif | |
152 gomp_init_work_share (&team->work_shares[0], false, nthreads); | |
153 team->work_shares[0].next_alloc = NULL; | |
154 team->work_share_list_free = NULL; | |
155 team->work_share_list_alloc = &team->work_shares[1]; | |
156 for (i = 1; i < 7; i++) | |
157 team->work_shares[i].next_free = &team->work_shares[i + 1]; | |
158 team->work_shares[i].next_free = NULL; | |
159 | |
160 team->nthreads = nthreads; | |
161 gomp_barrier_init (&team->barrier, nthreads); | |
162 | |
163 gomp_sem_init (&team->master_release, 0); | |
164 team->ordered_release = (void *) &team->implicit_task[nthreads]; | |
165 team->ordered_release[0] = &team->master_release; | |
166 | |
167 gomp_mutex_init (&team->task_lock); | |
168 team->task_queue = NULL; | |
169 team->task_count = 0; | |
170 team->task_running_count = 0; | |
171 | |
172 return team; | |
173 } | |
174 | |
175 | |
176 /* Free a team data structure. */ | |
177 | |
178 static void | |
179 free_team (struct gomp_team *team) | |
180 { | |
181 gomp_barrier_destroy (&team->barrier); | |
182 gomp_mutex_destroy (&team->task_lock); | |
183 free (team); | |
184 } | |
185 | |
186 /* Allocate and initialize a thread pool. */ | |
187 | |
188 static struct gomp_thread_pool *gomp_new_thread_pool (void) | |
189 { | |
190 struct gomp_thread_pool *pool | |
191 = gomp_malloc (sizeof(struct gomp_thread_pool)); | |
192 pool->threads = NULL; | |
193 pool->threads_size = 0; | |
194 pool->threads_used = 0; | |
195 pool->last_team = NULL; | |
196 return pool; | |
197 } | |
198 | |
199 static void | |
200 gomp_free_pool_helper (void *thread_pool) | |
201 { | |
202 struct gomp_thread_pool *pool | |
203 = (struct gomp_thread_pool *) thread_pool; | |
204 gomp_barrier_wait_last (&pool->threads_dock); | |
19
58ad6c70ea60
update gcc from 4.4.0 to 4.4.1.
kent@firefly.cr.ie.u-ryukyu.ac.jp
parents:
0
diff
changeset
|
205 gomp_sem_destroy (&gomp_thread ()->release); |
0 | 206 pthread_exit (NULL); |
207 } | |
208 | |
209 /* Free a thread pool and release its threads. */ | |
210 | |
211 static void | |
212 gomp_free_thread (void *arg __attribute__((unused))) | |
213 { | |
214 struct gomp_thread *thr = gomp_thread (); | |
215 struct gomp_thread_pool *pool = thr->thread_pool; | |
216 if (pool) | |
217 { | |
218 if (pool->threads_used > 0) | |
219 { | |
220 int i; | |
221 for (i = 1; i < pool->threads_used; i++) | |
222 { | |
223 struct gomp_thread *nthr = pool->threads[i]; | |
224 nthr->fn = gomp_free_pool_helper; | |
225 nthr->data = pool; | |
226 } | |
227 /* This barrier undocks threads docked on pool->threads_dock. */ | |
228 gomp_barrier_wait (&pool->threads_dock); | |
229 /* And this waits till all threads have called gomp_barrier_wait_last | |
230 in gomp_free_pool_helper. */ | |
231 gomp_barrier_wait (&pool->threads_dock); | |
232 /* Now it is safe to destroy the barrier and free the pool. */ | |
233 gomp_barrier_destroy (&pool->threads_dock); | |
234 } | |
235 free (pool->threads); | |
236 if (pool->last_team) | |
237 free_team (pool->last_team); | |
238 free (pool); | |
239 thr->thread_pool = NULL; | |
240 } | |
241 if (thr->task != NULL) | |
242 { | |
243 struct gomp_task *task = thr->task; | |
244 gomp_end_task (); | |
245 free (task); | |
246 } | |
247 } | |
248 | |
249 /* Launch a team. */ | |
250 | |
251 void | |
252 gomp_team_start (void (*fn) (void *), void *data, unsigned nthreads, | |
253 struct gomp_team *team) | |
254 { | |
255 struct gomp_thread_start_data *start_data; | |
256 struct gomp_thread *thr, *nthr; | |
257 struct gomp_task *task; | |
258 struct gomp_task_icv *icv; | |
259 bool nested; | |
260 struct gomp_thread_pool *pool; | |
261 unsigned i, n, old_threads_used = 0; | |
262 pthread_attr_t thread_attr, *attr; | |
263 | |
264 thr = gomp_thread (); | |
265 nested = thr->ts.team != NULL; | |
266 if (__builtin_expect (thr->thread_pool == NULL, 0)) | |
267 { | |
268 thr->thread_pool = gomp_new_thread_pool (); | |
269 pthread_setspecific (gomp_thread_destructor, thr); | |
270 } | |
271 pool = thr->thread_pool; | |
272 task = thr->task; | |
273 icv = task ? &task->icv : &gomp_global_icv; | |
274 | |
275 /* Always save the previous state, even if this isn't a nested team. | |
276 In particular, we should save any work share state from an outer | |
277 orphaned work share construct. */ | |
278 team->prev_ts = thr->ts; | |
279 | |
280 thr->ts.team = team; | |
281 thr->ts.team_id = 0; | |
282 ++thr->ts.level; | |
283 if (nthreads > 1) | |
284 ++thr->ts.active_level; | |
285 thr->ts.work_share = &team->work_shares[0]; | |
286 thr->ts.last_work_share = NULL; | |
287 #ifdef HAVE_SYNC_BUILTINS | |
288 thr->ts.single_count = 0; | |
289 #endif | |
290 thr->ts.static_trip = 0; | |
291 thr->task = &team->implicit_task[0]; | |
292 gomp_init_task (thr->task, task, icv); | |
293 | |
294 if (nthreads == 1) | |
295 return; | |
296 | |
297 i = 1; | |
298 | |
299 /* We only allow the reuse of idle threads for non-nested PARALLEL | |
300 regions. This appears to be implied by the semantics of | |
301 threadprivate variables, but perhaps that's reading too much into | |
302 things. Certainly it does prevent any locking problems, since | |
303 only the initial program thread will modify gomp_threads. */ | |
304 if (!nested) | |
305 { | |
306 old_threads_used = pool->threads_used; | |
307 | |
308 if (nthreads <= old_threads_used) | |
309 n = nthreads; | |
310 else if (old_threads_used == 0) | |
311 { | |
312 n = 0; | |
313 gomp_barrier_init (&pool->threads_dock, nthreads); | |
314 } | |
315 else | |
316 { | |
317 n = old_threads_used; | |
318 | |
319 /* Increase the barrier threshold to make sure all new | |
320 threads arrive before the team is released. */ | |
321 gomp_barrier_reinit (&pool->threads_dock, nthreads); | |
322 } | |
323 | |
324 /* Not true yet, but soon will be. We're going to release all | |
325 threads from the dock, and those that aren't part of the | |
326 team will exit. */ | |
327 pool->threads_used = nthreads; | |
328 | |
329 /* Release existing idle threads. */ | |
330 for (; i < n; ++i) | |
331 { | |
332 nthr = pool->threads[i]; | |
333 nthr->ts.team = team; | |
334 nthr->ts.work_share = &team->work_shares[0]; | |
335 nthr->ts.last_work_share = NULL; | |
336 nthr->ts.team_id = i; | |
337 nthr->ts.level = team->prev_ts.level + 1; | |
338 nthr->ts.active_level = thr->ts.active_level; | |
339 #ifdef HAVE_SYNC_BUILTINS | |
340 nthr->ts.single_count = 0; | |
341 #endif | |
342 nthr->ts.static_trip = 0; | |
343 nthr->task = &team->implicit_task[i]; | |
344 gomp_init_task (nthr->task, task, icv); | |
345 nthr->fn = fn; | |
346 nthr->data = data; | |
347 team->ordered_release[i] = &nthr->release; | |
348 } | |
349 | |
350 if (i == nthreads) | |
351 goto do_release; | |
352 | |
353 /* If necessary, expand the size of the gomp_threads array. It is | |
354 expected that changes in the number of threads are rare, thus we | |
355 make no effort to expand gomp_threads_size geometrically. */ | |
356 if (nthreads >= pool->threads_size) | |
357 { | |
358 pool->threads_size = nthreads + 1; | |
359 pool->threads | |
360 = gomp_realloc (pool->threads, | |
361 pool->threads_size | |
362 * sizeof (struct gomp_thread_data *)); | |
363 } | |
364 } | |
365 | |
366 if (__builtin_expect (nthreads > old_threads_used, 0)) | |
367 { | |
368 long diff = (long) nthreads - (long) old_threads_used; | |
369 | |
370 if (old_threads_used == 0) | |
371 --diff; | |
372 | |
373 #ifdef HAVE_SYNC_BUILTINS | |
374 __sync_fetch_and_add (&gomp_managed_threads, diff); | |
375 #else | |
376 gomp_mutex_lock (&gomp_remaining_threads_lock); | |
377 gomp_managed_threads += diff; | |
378 gomp_mutex_unlock (&gomp_remaining_threads_lock); | |
379 #endif | |
380 } | |
381 | |
382 attr = &gomp_thread_attr; | |
383 if (__builtin_expect (gomp_cpu_affinity != NULL, 0)) | |
384 { | |
385 size_t stacksize; | |
386 pthread_attr_init (&thread_attr); | |
387 pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED); | |
388 if (! pthread_attr_getstacksize (&gomp_thread_attr, &stacksize)) | |
389 pthread_attr_setstacksize (&thread_attr, stacksize); | |
390 attr = &thread_attr; | |
391 } | |
392 | |
393 start_data = gomp_alloca (sizeof (struct gomp_thread_start_data) | |
394 * (nthreads-i)); | |
395 | |
396 /* Launch new threads. */ | |
397 for (; i < nthreads; ++i, ++start_data) | |
398 { | |
399 pthread_t pt; | |
400 int err; | |
401 | |
402 start_data->fn = fn; | |
403 start_data->fn_data = data; | |
404 start_data->ts.team = team; | |
405 start_data->ts.work_share = &team->work_shares[0]; | |
406 start_data->ts.last_work_share = NULL; | |
407 start_data->ts.team_id = i; | |
408 start_data->ts.level = team->prev_ts.level + 1; | |
409 start_data->ts.active_level = thr->ts.active_level; | |
410 #ifdef HAVE_SYNC_BUILTINS | |
411 start_data->ts.single_count = 0; | |
412 #endif | |
413 start_data->ts.static_trip = 0; | |
414 start_data->task = &team->implicit_task[i]; | |
415 gomp_init_task (start_data->task, task, icv); | |
416 start_data->thread_pool = pool; | |
417 start_data->nested = nested; | |
418 | |
419 if (gomp_cpu_affinity != NULL) | |
420 gomp_init_thread_affinity (attr); | |
421 | |
422 err = pthread_create (&pt, attr, gomp_thread_start, start_data); | |
423 if (err != 0) | |
424 gomp_fatal ("Thread creation failed: %s", strerror (err)); | |
425 } | |
426 | |
427 if (__builtin_expect (gomp_cpu_affinity != NULL, 0)) | |
428 pthread_attr_destroy (&thread_attr); | |
429 | |
430 do_release: | |
431 gomp_barrier_wait (nested ? &team->barrier : &pool->threads_dock); | |
432 | |
433 /* Decrease the barrier threshold to match the number of threads | |
434 that should arrive back at the end of this team. The extra | |
435 threads should be exiting. Note that we arrange for this test | |
436 to never be true for nested teams. */ | |
437 if (__builtin_expect (nthreads < old_threads_used, 0)) | |
438 { | |
439 long diff = (long) nthreads - (long) old_threads_used; | |
440 | |
441 gomp_barrier_reinit (&pool->threads_dock, nthreads); | |
442 | |
443 #ifdef HAVE_SYNC_BUILTINS | |
444 __sync_fetch_and_add (&gomp_managed_threads, diff); | |
445 #else | |
446 gomp_mutex_lock (&gomp_remaining_threads_lock); | |
447 gomp_managed_threads += diff; | |
448 gomp_mutex_unlock (&gomp_remaining_threads_lock); | |
449 #endif | |
450 } | |
451 } | |
452 | |
453 | |
454 /* Terminate the current team. This is only to be called by the master | |
455 thread. We assume that we must wait for the other threads. */ | |
456 | |
457 void | |
458 gomp_team_end (void) | |
459 { | |
460 struct gomp_thread *thr = gomp_thread (); | |
461 struct gomp_team *team = thr->ts.team; | |
462 | |
463 /* This barrier handles all pending explicit threads. */ | |
464 gomp_team_barrier_wait (&team->barrier); | |
465 gomp_fini_work_share (thr->ts.work_share); | |
466 | |
467 gomp_end_task (); | |
468 thr->ts = team->prev_ts; | |
469 | |
470 if (__builtin_expect (thr->ts.team != NULL, 0)) | |
471 { | |
472 #ifdef HAVE_SYNC_BUILTINS | |
473 __sync_fetch_and_add (&gomp_managed_threads, 1L - team->nthreads); | |
474 #else | |
475 gomp_mutex_lock (&gomp_remaining_threads_lock); | |
476 gomp_managed_threads -= team->nthreads - 1L; | |
477 gomp_mutex_unlock (&gomp_remaining_threads_lock); | |
478 #endif | |
479 /* This barrier has gomp_barrier_wait_last counterparts | |
480 and ensures the team can be safely destroyed. */ | |
481 gomp_barrier_wait (&team->barrier); | |
482 } | |
483 | |
484 if (__builtin_expect (team->work_shares[0].next_alloc != NULL, 0)) | |
485 { | |
486 struct gomp_work_share *ws = team->work_shares[0].next_alloc; | |
487 do | |
488 { | |
489 struct gomp_work_share *next_ws = ws->next_alloc; | |
490 free (ws); | |
491 ws = next_ws; | |
492 } | |
493 while (ws != NULL); | |
494 } | |
495 gomp_sem_destroy (&team->master_release); | |
496 #ifndef HAVE_SYNC_BUILTINS | |
497 gomp_mutex_destroy (&team->work_share_list_free_lock); | |
498 #endif | |
499 | |
500 if (__builtin_expect (thr->ts.team != NULL, 0) | |
501 || __builtin_expect (team->nthreads == 1, 0)) | |
502 free_team (team); | |
503 else | |
504 { | |
505 struct gomp_thread_pool *pool = thr->thread_pool; | |
506 if (pool->last_team) | |
507 free_team (pool->last_team); | |
508 pool->last_team = team; | |
509 } | |
510 } | |
511 | |
512 | |
513 /* Constructors for this file. */ | |
514 | |
515 static void __attribute__((constructor)) | |
516 initialize_team (void) | |
517 { | |
518 struct gomp_thread *thr; | |
519 | |
520 #ifndef HAVE_TLS | |
521 static struct gomp_thread initial_thread_tls_data; | |
522 | |
523 pthread_key_create (&gomp_tls_key, NULL); | |
524 pthread_setspecific (gomp_tls_key, &initial_thread_tls_data); | |
525 #endif | |
526 | |
527 if (pthread_key_create (&gomp_thread_destructor, gomp_free_thread) != 0) | |
528 gomp_fatal ("could not create thread pool destructor."); | |
529 | |
530 #ifdef HAVE_TLS | |
531 thr = &gomp_tls_data; | |
532 #else | |
533 thr = &initial_thread_tls_data; | |
534 #endif | |
535 gomp_sem_init (&thr->release, 0); | |
536 } | |
537 | |
538 static void __attribute__((destructor)) | |
539 team_destructor (void) | |
540 { | |
541 /* Without this dlclose on libgomp could lead to subsequent | |
542 crashes. */ | |
543 pthread_key_delete (gomp_thread_destructor); | |
544 } | |
545 | |
546 struct gomp_task_icv * | |
547 gomp_new_icv (void) | |
548 { | |
549 struct gomp_thread *thr = gomp_thread (); | |
550 struct gomp_task *task = gomp_malloc (sizeof (struct gomp_task)); | |
551 gomp_init_task (task, NULL, &gomp_global_icv); | |
552 thr->task = task; | |
553 pthread_setspecific (gomp_thread_destructor, thr); | |
554 return &task->icv; | |
555 } |