1169695Skan/* Copyright (C) 2005 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; 186169695Skan 187169695Skan thr = gomp_thread (); 188169695Skan nested = thr->ts.team != NULL; 189169695Skan 190169695Skan team = new_team (nthreads, work_share); 191169695Skan 192169695Skan /* Always save the previous state, even if this isn't a nested team. 193169695Skan In particular, we should save any work share state from an outer 194169695Skan orphaned work share construct. */ 195169695Skan team->prev_ts = thr->ts; 196169695Skan 197169695Skan thr->ts.team = team; 198169695Skan thr->ts.work_share = work_share; 199169695Skan thr->ts.team_id = 0; 200169695Skan thr->ts.work_share_generation = 0; 201169695Skan thr->ts.static_trip = 0; 202169695Skan 203169695Skan if (nthreads == 1) 204169695Skan return; 205169695Skan 206169695Skan i = 1; 207169695Skan 208169695Skan /* We only allow the reuse of idle threads for non-nested PARALLEL 209169695Skan regions. This appears to be implied by the semantics of 210169695Skan threadprivate variables, but perhaps that's reading too much into 211169695Skan things. Certainly it does prevent any locking problems, since 212169695Skan only the initial program thread will modify gomp_threads. */ 213169695Skan if (!nested) 214169695Skan { 215169695Skan old_threads_used = gomp_threads_used; 216169695Skan 217169695Skan if (nthreads <= old_threads_used) 218169695Skan n = nthreads; 219169695Skan else if (old_threads_used == 0) 220169695Skan { 221169695Skan n = 0; 222169695Skan gomp_barrier_init (&gomp_threads_dock, nthreads); 223169695Skan } 224169695Skan else 225169695Skan { 226169695Skan n = old_threads_used; 227169695Skan 228169695Skan /* Increase the barrier threshold to make sure all new 229169695Skan threads arrive before the team is released. */ 230169695Skan gomp_barrier_reinit (&gomp_threads_dock, nthreads); 231169695Skan } 232169695Skan 233169695Skan /* Not true yet, but soon will be. We're going to release all 234169695Skan threads from the dock, and those that aren't part of the 235169695Skan team will exit. */ 236169695Skan gomp_threads_used = nthreads; 237169695Skan 238169695Skan /* Release existing idle threads. */ 239169695Skan for (; i < n; ++i) 240169695Skan { 241169695Skan nthr = gomp_threads[i]; 242169695Skan nthr->ts.team = team; 243169695Skan nthr->ts.work_share = work_share; 244169695Skan nthr->ts.team_id = i; 245169695Skan nthr->ts.work_share_generation = 0; 246169695Skan nthr->ts.static_trip = 0; 247169695Skan nthr->fn = fn; 248169695Skan nthr->data = data; 249169695Skan team->ordered_release[i] = &nthr->release; 250169695Skan } 251169695Skan 252169695Skan if (i == nthreads) 253169695Skan goto do_release; 254169695Skan 255169695Skan /* If necessary, expand the size of the gomp_threads array. It is 256169695Skan expected that changes in the number of threads is rare, thus we 257169695Skan make no effort to expand gomp_threads_size geometrically. */ 258169695Skan if (nthreads >= gomp_threads_size) 259169695Skan { 260169695Skan gomp_threads_size = nthreads + 1; 261169695Skan gomp_threads 262169695Skan = gomp_realloc (gomp_threads, 263169695Skan gomp_threads_size 264169695Skan * sizeof (struct gomp_thread_data *)); 265169695Skan } 266169695Skan } 267169695Skan 268169695Skan start_data = gomp_alloca (sizeof (struct gomp_thread_start_data) 269169695Skan * (nthreads-i)); 270169695Skan 271169695Skan /* Launch new threads. */ 272169695Skan for (; i < nthreads; ++i, ++start_data) 273169695Skan { 274169695Skan pthread_t pt; 275169695Skan int err; 276169695Skan 277169695Skan start_data->ts.team = team; 278169695Skan start_data->ts.work_share = work_share; 279169695Skan start_data->ts.team_id = i; 280169695Skan start_data->ts.work_share_generation = 0; 281169695Skan start_data->ts.static_trip = 0; 282169695Skan start_data->fn = fn; 283169695Skan start_data->fn_data = data; 284169695Skan start_data->nested = nested; 285169695Skan 286169695Skan err = pthread_create (&pt, &gomp_thread_attr, 287169695Skan gomp_thread_start, start_data); 288169695Skan if (err != 0) 289169695Skan gomp_fatal ("Thread creation failed: %s", strerror (err)); 290169695Skan } 291169695Skan 292169695Skan do_release: 293169695Skan gomp_barrier_wait (nested ? &team->barrier : &gomp_threads_dock); 294169695Skan 295169695Skan /* Decrease the barrier threshold to match the number of threads 296169695Skan that should arrive back at the end of this team. The extra 297169695Skan threads should be exiting. Note that we arrange for this test 298169695Skan to never be true for nested teams. */ 299169695Skan if (nthreads < old_threads_used) 300169695Skan gomp_barrier_reinit (&gomp_threads_dock, nthreads); 301169695Skan} 302169695Skan 303169695Skan 304169695Skan/* Terminate the current team. This is only to be called by the master 305169695Skan thread. We assume that we must wait for the other threads. */ 306169695Skan 307169695Skanvoid 308169695Skangomp_team_end (void) 309169695Skan{ 310169695Skan struct gomp_thread *thr = gomp_thread (); 311169695Skan struct gomp_team *team = thr->ts.team; 312169695Skan 313169695Skan gomp_barrier_wait (&team->barrier); 314169695Skan 315169695Skan thr->ts = team->prev_ts; 316169695Skan 317169695Skan free_team (team); 318169695Skan} 319169695Skan 320169695Skan 321169695Skan/* Constructors for this file. */ 322169695Skan 323169695Skanstatic void __attribute__((constructor)) 324169695Skaninitialize_team (void) 325169695Skan{ 326169695Skan struct gomp_thread *thr; 327169695Skan 328169695Skan#ifndef HAVE_TLS 329169695Skan static struct gomp_thread initial_thread_tls_data; 330169695Skan 331169695Skan pthread_key_create (&gomp_tls_key, NULL); 332169695Skan pthread_setspecific (gomp_tls_key, &initial_thread_tls_data); 333169695Skan#endif 334169695Skan 335169695Skan#ifdef HAVE_TLS 336169695Skan thr = &gomp_tls_data; 337169695Skan#else 338169695Skan thr = &initial_thread_tls_data; 339169695Skan#endif 340169695Skan gomp_sem_init (&thr->release, 0); 341169695Skan} 342