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