OSDN Git Service

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