team.c revision 283010
1/* Copyright (C) 2005, 2006, 2007 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 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 35/* This array manages threads spawned from the top level, which will 36 return to the idle loop once the current PARALLEL construct ends. */ 37static struct gomp_thread **gomp_threads; 38static unsigned gomp_threads_size; 39static unsigned gomp_threads_used; 40 41/* This attribute contains PTHREAD_CREATE_DETACHED. */ 42pthread_attr_t gomp_thread_attr; 43 44/* This barrier holds and releases threads waiting in gomp_threads. */ 45static gomp_barrier_t gomp_threads_dock; 46 47/* This is the libgomp per-thread data structure. */ 48#ifdef HAVE_TLS 49__thread struct gomp_thread gomp_tls_data; 50#else 51pthread_key_t gomp_tls_key; 52#endif 53 54 55/* This structure is used to communicate across pthread_create. */ 56 57struct gomp_thread_start_data 58{ 59 struct gomp_team_state ts; 60 void (*fn) (void *); 61 void *fn_data; 62 bool nested; 63}; 64 65 66/* This function is a pthread_create entry point. This contains the idle 67 loop in which a thread waits to be called up to become part of a team. */ 68 69static void * 70gomp_thread_start (void *xdata) 71{ 72 struct gomp_thread_start_data *data = xdata; 73 struct gomp_thread *thr; 74 void (*local_fn) (void *); 75 void *local_data; 76 77#ifdef HAVE_TLS 78 thr = &gomp_tls_data; 79#else 80 struct gomp_thread local_thr; 81 thr = &local_thr; 82 pthread_setspecific (gomp_tls_key, thr); 83#endif 84 gomp_sem_init (&thr->release, 0); 85 86 /* Extract what we need from data. */ 87 local_fn = data->fn; 88 local_data = data->fn_data; 89 thr->ts = data->ts; 90 91 thr->ts.team->ordered_release[thr->ts.team_id] = &thr->release; 92 93 if (data->nested) 94 { 95 gomp_barrier_wait (&thr->ts.team->barrier); 96 local_fn (local_data); 97 gomp_barrier_wait (&thr->ts.team->barrier); 98 } 99 else 100 { 101 gomp_threads[thr->ts.team_id] = thr; 102 103 gomp_barrier_wait (&gomp_threads_dock); 104 do 105 { 106 struct gomp_team *team; 107 108 local_fn (local_data); 109 110 /* Clear out the team and function data. This is a debugging 111 signal that we're in fact back in the dock. */ 112 team = thr->ts.team; 113 thr->fn = NULL; 114 thr->data = NULL; 115 thr->ts.team = NULL; 116 thr->ts.work_share = NULL; 117 thr->ts.team_id = 0; 118 thr->ts.work_share_generation = 0; 119 thr->ts.static_trip = 0; 120 121 gomp_barrier_wait (&team->barrier); 122 gomp_barrier_wait (&gomp_threads_dock); 123 124 local_fn = thr->fn; 125 local_data = thr->data; 126 } 127 while (local_fn); 128 } 129 130 return NULL; 131} 132 133 134/* Create a new team data structure. */ 135 136static struct gomp_team * 137new_team (unsigned nthreads, struct gomp_work_share *work_share) 138{ 139 struct gomp_team *team; 140 size_t size; 141 142 size = sizeof (*team) + nthreads * sizeof (team->ordered_release[0]); 143 team = gomp_malloc (size); 144 gomp_mutex_init (&team->work_share_lock); 145 146 team->work_shares = gomp_malloc (4 * sizeof (struct gomp_work_share *)); 147 team->generation_mask = 3; 148 team->oldest_live_gen = work_share == NULL; 149 team->num_live_gen = work_share != NULL; 150 team->work_shares[0] = work_share; 151 152 team->nthreads = nthreads; 153 gomp_barrier_init (&team->barrier, nthreads); 154 155 gomp_sem_init (&team->master_release, 0); 156 team->ordered_release[0] = &team->master_release; 157 158 return team; 159} 160 161 162/* Free a team data structure. */ 163 164static void 165free_team (struct gomp_team *team) 166{ 167 free (team->work_shares); 168 gomp_mutex_destroy (&team->work_share_lock); 169 gomp_barrier_destroy (&team->barrier); 170 gomp_sem_destroy (&team->master_release); 171 free (team); 172} 173 174 175/* Launch a team. */ 176 177void 178gomp_team_start (void (*fn) (void *), void *data, unsigned nthreads, 179 struct gomp_work_share *work_share) 180{ 181 struct gomp_thread_start_data *start_data; 182 struct gomp_thread *thr, *nthr; 183 struct gomp_team *team; 184 bool nested; 185 unsigned i, n, old_threads_used = 0; 186 pthread_attr_t thread_attr, *attr; 187 188 thr = gomp_thread (); 189 nested = thr->ts.team != NULL; 190 191 team = new_team (nthreads, work_share); 192 193 /* Always save the previous state, even if this isn't a nested team. 194 In particular, we should save any work share state from an outer 195 orphaned work share construct. */ 196 team->prev_ts = thr->ts; 197 198 thr->ts.team = team; 199 thr->ts.work_share = work_share; 200 thr->ts.team_id = 0; 201 thr->ts.work_share_generation = 0; 202 thr->ts.static_trip = 0; 203 204 if (nthreads == 1) 205 return; 206 207 i = 1; 208 209 /* We only allow the reuse of idle threads for non-nested PARALLEL 210 regions. This appears to be implied by the semantics of 211 threadprivate variables, but perhaps that's reading too much into 212 things. Certainly it does prevent any locking problems, since 213 only the initial program thread will modify gomp_threads. */ 214 if (!nested) 215 { 216 old_threads_used = gomp_threads_used; 217 218 if (nthreads <= old_threads_used) 219 n = nthreads; 220 else if (old_threads_used == 0) 221 { 222 n = 0; 223 gomp_barrier_init (&gomp_threads_dock, nthreads); 224 } 225 else 226 { 227 n = old_threads_used; 228 229 /* Increase the barrier threshold to make sure all new 230 threads arrive before the team is released. */ 231 gomp_barrier_reinit (&gomp_threads_dock, nthreads); 232 } 233 234 /* Not true yet, but soon will be. We're going to release all 235 threads from the dock, and those that aren't part of the 236 team will exit. */ 237 gomp_threads_used = nthreads; 238 239 /* Release existing idle threads. */ 240 for (; i < n; ++i) 241 { 242 nthr = gomp_threads[i]; 243 nthr->ts.team = team; 244 nthr->ts.work_share = work_share; 245 nthr->ts.team_id = i; 246 nthr->ts.work_share_generation = 0; 247 nthr->ts.static_trip = 0; 248 nthr->fn = fn; 249 nthr->data = data; 250 team->ordered_release[i] = &nthr->release; 251 } 252 253 if (i == nthreads) 254 goto do_release; 255 256 /* If necessary, expand the size of the gomp_threads array. It is 257 expected that changes in the number of threads is rare, thus we 258 make no effort to expand gomp_threads_size geometrically. */ 259 if (nthreads >= gomp_threads_size) 260 { 261 gomp_threads_size = nthreads + 1; 262 gomp_threads 263 = gomp_realloc (gomp_threads, 264 gomp_threads_size 265 * sizeof (struct gomp_thread_data *)); 266 } 267 } 268 269 attr = &gomp_thread_attr; 270 if (gomp_cpu_affinity != NULL) 271 { 272 size_t stacksize; 273 pthread_attr_init (&thread_attr); 274 pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED); 275 if (! pthread_attr_getstacksize (&gomp_thread_attr, &stacksize)) 276 pthread_attr_setstacksize (&thread_attr, stacksize); 277 attr = &thread_attr; 278 } 279 280 start_data = gomp_alloca (sizeof (struct gomp_thread_start_data) 281 * (nthreads-i)); 282 283 /* Launch new threads. */ 284 for (; i < nthreads; ++i, ++start_data) 285 { 286 pthread_t pt; 287 int err; 288 289 start_data->ts.team = team; 290 start_data->ts.work_share = work_share; 291 start_data->ts.team_id = i; 292 start_data->ts.work_share_generation = 0; 293 start_data->ts.static_trip = 0; 294 start_data->fn = fn; 295 start_data->fn_data = data; 296 start_data->nested = nested; 297 298 if (gomp_cpu_affinity != NULL) 299 gomp_init_thread_affinity (attr); 300 301 err = pthread_create (&pt, attr, gomp_thread_start, start_data); 302 if (err != 0) 303 gomp_fatal ("Thread creation failed: %s", strerror (err)); 304 } 305 306 if (gomp_cpu_affinity != NULL) 307 pthread_attr_destroy (&thread_attr); 308 309 do_release: 310 gomp_barrier_wait (nested ? &team->barrier : &gomp_threads_dock); 311 312 /* Decrease the barrier threshold to match the number of threads 313 that should arrive back at the end of this team. The extra 314 threads should be exiting. Note that we arrange for this test 315 to never be true for nested teams. */ 316 if (nthreads < old_threads_used) 317 gomp_barrier_reinit (&gomp_threads_dock, nthreads); 318} 319 320 321/* Terminate the current team. This is only to be called by the master 322 thread. We assume that we must wait for the other threads. */ 323 324void 325gomp_team_end (void) 326{ 327 struct gomp_thread *thr = gomp_thread (); 328 struct gomp_team *team = thr->ts.team; 329 330 gomp_barrier_wait (&team->barrier); 331 332 thr->ts = team->prev_ts; 333 334 free_team (team); 335} 336 337 338/* Constructors for this file. */ 339 340static void __attribute__((constructor)) 341initialize_team (void) 342{ 343 struct gomp_thread *thr; 344 345#ifndef HAVE_TLS 346 static struct gomp_thread initial_thread_tls_data; 347 348 pthread_key_create (&gomp_tls_key, NULL); 349 pthread_setspecific (gomp_tls_key, &initial_thread_tls_data); 350#endif 351 352#ifdef HAVE_TLS 353 thr = &gomp_tls_data; 354#else 355 thr = &initial_thread_tls_data; 356#endif 357 gomp_sem_init (&thr->release, 0); 358} 359