1282115Spfg/* Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. 2169695Skan Contributed by Richard Henderson <rth@redhat.com>. 3169695Skan 4169695Skan This file is part of the GNU OpenMP Library (libgomp). 5169695Skan 6169695Skan Libgomp is free software; you can redistribute it and/or modify it 7169695Skan under the terms of the GNU Lesser General Public License as published by 8169695Skan the Free Software Foundation; either version 2.1 of the License, or 9169695Skan (at your option) any later version. 10169695Skan 11169695Skan Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY 12169695Skan WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13169695Skan FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 14169695Skan more details. 15169695Skan 16169695Skan You should have received a copy of the GNU Lesser General Public License 17169695Skan along with libgomp; see the file COPYING.LIB. If not, write to the 18169695Skan Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19169695Skan MA 02110-1301, USA. */ 20169695Skan 21169695Skan/* As a special exception, if you link this library with other files, some 22169695Skan of which are compiled with GCC, to produce an executable, this library 23169695Skan does not by itself cause the resulting executable to be covered by the 24169695Skan GNU General Public License. This exception does not however invalidate 25169695Skan any other reasons why the executable file might be covered by the GNU 26169695Skan General Public License. */ 27169695Skan 28169695Skan/* This file handles the maintainence of threads in response to team 29169695Skan creation and termination. */ 30169695Skan 31169695Skan#include "libgomp.h" 32169695Skan#include <stdlib.h> 33169695Skan#include <string.h> 34169695Skan 35169695Skan/* This array manages threads spawned from the top level, which will 36169695Skan return to the idle loop once the current PARALLEL construct ends. */ 37169695Skanstatic struct gomp_thread **gomp_threads; 38169695Skanstatic unsigned gomp_threads_size; 39169695Skanstatic unsigned gomp_threads_used; 40169695Skan 41169695Skan/* This attribute contains PTHREAD_CREATE_DETACHED. */ 42169695Skanpthread_attr_t gomp_thread_attr; 43169695Skan 44169695Skan/* This barrier holds and releases threads waiting in gomp_threads. */ 45169695Skanstatic gomp_barrier_t gomp_threads_dock; 46169695Skan 47169695Skan/* This is the libgomp per-thread data structure. */ 48169695Skan#ifdef HAVE_TLS 49169695Skan__thread struct gomp_thread gomp_tls_data; 50169695Skan#else 51169695Skanpthread_key_t gomp_tls_key; 52169695Skan#endif 53169695Skan 54169695Skan 55169695Skan/* This structure is used to communicate across pthread_create. */ 56169695Skan 57169695Skanstruct gomp_thread_start_data 58169695Skan{ 59169695Skan struct gomp_team_state ts; 60169695Skan void (*fn) (void *); 61169695Skan void *fn_data; 62169695Skan bool nested; 63169695Skan}; 64169695Skan 65169695Skan 66169695Skan/* This function is a pthread_create entry point. This contains the idle 67169695Skan loop in which a thread waits to be called up to become part of a team. */ 68169695Skan 69169695Skanstatic void * 70169695Skangomp_thread_start (void *xdata) 71169695Skan{ 72169695Skan struct gomp_thread_start_data *data = xdata; 73169695Skan struct gomp_thread *thr; 74169695Skan void (*local_fn) (void *); 75169695Skan void *local_data; 76169695Skan 77169695Skan#ifdef HAVE_TLS 78169695Skan thr = &gomp_tls_data; 79169695Skan#else 80169695Skan struct gomp_thread local_thr; 81169695Skan thr = &local_thr; 82169695Skan pthread_setspecific (gomp_tls_key, thr); 83169695Skan#endif 84169695Skan gomp_sem_init (&thr->release, 0); 85169695Skan 86169695Skan /* Extract what we need from data. */ 87169695Skan local_fn = data->fn; 88169695Skan local_data = data->fn_data; 89169695Skan thr->ts = data->ts; 90169695Skan 91169695Skan thr->ts.team->ordered_release[thr->ts.team_id] = &thr->release; 92169695Skan 93169695Skan if (data->nested) 94169695Skan { 95169695Skan gomp_barrier_wait (&thr->ts.team->barrier); 96169695Skan local_fn (local_data); 97169695Skan gomp_barrier_wait (&thr->ts.team->barrier); 98169695Skan } 99169695Skan else 100169695Skan { 101169695Skan gomp_threads[thr->ts.team_id] = thr; 102169695Skan 103169695Skan gomp_barrier_wait (&gomp_threads_dock); 104169695Skan do 105169695Skan { 106169695Skan struct gomp_team *team; 107169695Skan 108169695Skan local_fn (local_data); 109169695Skan 110169695Skan /* Clear out the team and function data. This is a debugging 111169695Skan signal that we're in fact back in the dock. */ 112169695Skan team = thr->ts.team; 113169695Skan thr->fn = NULL; 114169695Skan thr->data = NULL; 115169695Skan thr->ts.team = NULL; 116169695Skan thr->ts.work_share = NULL; 117169695Skan thr->ts.team_id = 0; 118169695Skan thr->ts.work_share_generation = 0; 119169695Skan thr->ts.static_trip = 0; 120169695Skan 121169695Skan gomp_barrier_wait (&team->barrier); 122169695Skan gomp_barrier_wait (&gomp_threads_dock); 123169695Skan 124169695Skan local_fn = thr->fn; 125169695Skan local_data = thr->data; 126169695Skan } 127169695Skan while (local_fn); 128169695Skan } 129169695Skan 130169695Skan return NULL; 131169695Skan} 132169695Skan 133169695Skan 134169695Skan/* Create a new team data structure. */ 135169695Skan 136169695Skanstatic struct gomp_team * 137169695Skannew_team (unsigned nthreads, struct gomp_work_share *work_share) 138169695Skan{ 139169695Skan struct gomp_team *team; 140169695Skan size_t size; 141169695Skan 142169695Skan size = sizeof (*team) + nthreads * sizeof (team->ordered_release[0]); 143169695Skan team = gomp_malloc (size); 144169695Skan gomp_mutex_init (&team->work_share_lock); 145169695Skan 146169695Skan team->work_shares = gomp_malloc (4 * sizeof (struct gomp_work_share *)); 147169695Skan team->generation_mask = 3; 148169695Skan team->oldest_live_gen = work_share == NULL; 149169695Skan team->num_live_gen = work_share != NULL; 150169695Skan team->work_shares[0] = work_share; 151169695Skan 152169695Skan team->nthreads = nthreads; 153169695Skan gomp_barrier_init (&team->barrier, nthreads); 154169695Skan 155169695Skan gomp_sem_init (&team->master_release, 0); 156169695Skan team->ordered_release[0] = &team->master_release; 157169695Skan 158169695Skan return team; 159169695Skan} 160169695Skan 161169695Skan 162169695Skan/* Free a team data structure. */ 163169695Skan 164169695Skanstatic void 165169695Skanfree_team (struct gomp_team *team) 166169695Skan{ 167169695Skan free (team->work_shares); 168169695Skan gomp_mutex_destroy (&team->work_share_lock); 169169695Skan gomp_barrier_destroy (&team->barrier); 170169695Skan gomp_sem_destroy (&team->master_release); 171169695Skan free (team); 172169695Skan} 173169695Skan 174169695Skan 175169695Skan/* Launch a team. */ 176169695Skan 177169695Skanvoid 178169695Skangomp_team_start (void (*fn) (void *), void *data, unsigned nthreads, 179169695Skan struct gomp_work_share *work_share) 180169695Skan{ 181169695Skan struct gomp_thread_start_data *start_data; 182169695Skan struct gomp_thread *thr, *nthr; 183169695Skan struct gomp_team *team; 184169695Skan bool nested; 185169695Skan unsigned i, n, old_threads_used = 0; 186282115Spfg pthread_attr_t thread_attr, *attr; 187169695Skan 188169695Skan thr = gomp_thread (); 189169695Skan nested = thr->ts.team != NULL; 190169695Skan 191169695Skan team = new_team (nthreads, work_share); 192169695Skan 193169695Skan /* Always save the previous state, even if this isn't a nested team. 194169695Skan In particular, we should save any work share state from an outer 195169695Skan orphaned work share construct. */ 196169695Skan team->prev_ts = thr->ts; 197169695Skan 198169695Skan thr->ts.team = team; 199169695Skan thr->ts.work_share = work_share; 200169695Skan thr->ts.team_id = 0; 201169695Skan thr->ts.work_share_generation = 0; 202169695Skan thr->ts.static_trip = 0; 203169695Skan 204169695Skan if (nthreads == 1) 205169695Skan return; 206169695Skan 207169695Skan i = 1; 208169695Skan 209169695Skan /* We only allow the reuse of idle threads for non-nested PARALLEL 210169695Skan regions. This appears to be implied by the semantics of 211169695Skan threadprivate variables, but perhaps that's reading too much into 212169695Skan things. Certainly it does prevent any locking problems, since 213169695Skan only the initial program thread will modify gomp_threads. */ 214169695Skan if (!nested) 215169695Skan { 216169695Skan old_threads_used = gomp_threads_used; 217169695Skan 218169695Skan if (nthreads <= old_threads_used) 219169695Skan n = nthreads; 220169695Skan else if (old_threads_used == 0) 221169695Skan { 222169695Skan n = 0; 223169695Skan gomp_barrier_init (&gomp_threads_dock, nthreads); 224169695Skan } 225169695Skan else 226169695Skan { 227169695Skan n = old_threads_used; 228169695Skan 229169695Skan /* Increase the barrier threshold to make sure all new 230169695Skan threads arrive before the team is released. */ 231169695Skan gomp_barrier_reinit (&gomp_threads_dock, nthreads); 232169695Skan } 233169695Skan 234169695Skan /* Not true yet, but soon will be. We're going to release all 235169695Skan threads from the dock, and those that aren't part of the 236169695Skan team will exit. */ 237169695Skan gomp_threads_used = nthreads; 238169695Skan 239169695Skan /* Release existing idle threads. */ 240169695Skan for (; i < n; ++i) 241169695Skan { 242169695Skan nthr = gomp_threads[i]; 243169695Skan nthr->ts.team = team; 244169695Skan nthr->ts.work_share = work_share; 245169695Skan nthr->ts.team_id = i; 246169695Skan nthr->ts.work_share_generation = 0; 247169695Skan nthr->ts.static_trip = 0; 248169695Skan nthr->fn = fn; 249169695Skan nthr->data = data; 250169695Skan team->ordered_release[i] = &nthr->release; 251169695Skan } 252169695Skan 253169695Skan if (i == nthreads) 254169695Skan goto do_release; 255169695Skan 256169695Skan /* If necessary, expand the size of the gomp_threads array. It is 257169695Skan expected that changes in the number of threads is rare, thus we 258169695Skan make no effort to expand gomp_threads_size geometrically. */ 259169695Skan if (nthreads >= gomp_threads_size) 260169695Skan { 261169695Skan gomp_threads_size = nthreads + 1; 262169695Skan gomp_threads 263169695Skan = gomp_realloc (gomp_threads, 264169695Skan gomp_threads_size 265169695Skan * sizeof (struct gomp_thread_data *)); 266169695Skan } 267169695Skan } 268169695Skan 269282115Spfg attr = &gomp_thread_attr; 270282115Spfg if (gomp_cpu_affinity != NULL) 271282115Spfg { 272282115Spfg size_t stacksize; 273282115Spfg pthread_attr_init (&thread_attr); 274282115Spfg pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED); 275282115Spfg if (! pthread_attr_getstacksize (&gomp_thread_attr, &stacksize)) 276282115Spfg pthread_attr_setstacksize (&thread_attr, stacksize); 277282115Spfg attr = &thread_attr; 278282115Spfg } 279282115Spfg 280169695Skan start_data = gomp_alloca (sizeof (struct gomp_thread_start_data) 281169695Skan * (nthreads-i)); 282169695Skan 283169695Skan /* Launch new threads. */ 284169695Skan for (; i < nthreads; ++i, ++start_data) 285169695Skan { 286169695Skan pthread_t pt; 287169695Skan int err; 288169695Skan 289169695Skan start_data->ts.team = team; 290169695Skan start_data->ts.work_share = work_share; 291169695Skan start_data->ts.team_id = i; 292169695Skan start_data->ts.work_share_generation = 0; 293169695Skan start_data->ts.static_trip = 0; 294169695Skan start_data->fn = fn; 295169695Skan start_data->fn_data = data; 296169695Skan start_data->nested = nested; 297169695Skan 298282152Spfg if (gomp_cpu_affinity != NULL) 299282115Spfg gomp_init_thread_affinity (attr); 300282115Spfg 301282115Spfg err = pthread_create (&pt, attr, gomp_thread_start, start_data); 302169695Skan if (err != 0) 303169695Skan gomp_fatal ("Thread creation failed: %s", strerror (err)); 304169695Skan } 305169695Skan 306282115Spfg if (gomp_cpu_affinity != NULL) 307282115Spfg pthread_attr_destroy (&thread_attr); 308282115Spfg 309169695Skan do_release: 310169695Skan gomp_barrier_wait (nested ? &team->barrier : &gomp_threads_dock); 311169695Skan 312169695Skan /* Decrease the barrier threshold to match the number of threads 313169695Skan that should arrive back at the end of this team. The extra 314169695Skan threads should be exiting. Note that we arrange for this test 315169695Skan to never be true for nested teams. */ 316169695Skan if (nthreads < old_threads_used) 317169695Skan gomp_barrier_reinit (&gomp_threads_dock, nthreads); 318169695Skan} 319169695Skan 320169695Skan 321169695Skan/* Terminate the current team. This is only to be called by the master 322169695Skan thread. We assume that we must wait for the other threads. */ 323169695Skan 324169695Skanvoid 325169695Skangomp_team_end (void) 326169695Skan{ 327169695Skan struct gomp_thread *thr = gomp_thread (); 328169695Skan struct gomp_team *team = thr->ts.team; 329169695Skan 330169695Skan gomp_barrier_wait (&team->barrier); 331169695Skan 332169695Skan thr->ts = team->prev_ts; 333169695Skan 334169695Skan free_team (team); 335169695Skan} 336169695Skan 337169695Skan 338169695Skan/* Constructors for this file. */ 339169695Skan 340169695Skanstatic void __attribute__((constructor)) 341169695Skaninitialize_team (void) 342169695Skan{ 343169695Skan struct gomp_thread *thr; 344169695Skan 345169695Skan#ifndef HAVE_TLS 346169695Skan static struct gomp_thread initial_thread_tls_data; 347169695Skan 348169695Skan pthread_key_create (&gomp_tls_key, NULL); 349169695Skan pthread_setspecific (gomp_tls_key, &initial_thread_tls_data); 350169695Skan#endif 351169695Skan 352169695Skan#ifdef HAVE_TLS 353169695Skan thr = &gomp_tls_data; 354169695Skan#else 355169695Skan thr = &initial_thread_tls_data; 356169695Skan#endif 357169695Skan gomp_sem_init (&thr->release, 0); 358169695Skan} 359