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