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