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