work.c revision 285830
1250661Sdavidcs/* Copyright (C) 2005 Free Software Foundation, Inc.
2250661Sdavidcs   Contributed by Richard Henderson <rth@redhat.com>.
3250661Sdavidcs
4250661Sdavidcs   This file is part of the GNU OpenMP Library (libgomp).
5250661Sdavidcs
6250661Sdavidcs   Libgomp is free software; you can redistribute it and/or modify it
7250661Sdavidcs   under the terms of the GNU Lesser General Public License as published by
8250661Sdavidcs   the Free Software Foundation; either version 2.1 of the License, or
9250661Sdavidcs   (at your option) any later version.
10250661Sdavidcs
11250661Sdavidcs   Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
12250661Sdavidcs   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13250661Sdavidcs   FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
14250661Sdavidcs   more details.
15250661Sdavidcs
16250661Sdavidcs   You should have received a copy of the GNU Lesser General Public License
17250661Sdavidcs   along with libgomp; see the file COPYING.LIB.  If not, write to the
18250661Sdavidcs   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19250661Sdavidcs   MA 02110-1301, USA.  */
20250661Sdavidcs
21250661Sdavidcs/* As a special exception, if you link this library with other files, some
22250661Sdavidcs   of which are compiled with GCC, to produce an executable, this library
23250661Sdavidcs   does not by itself cause the resulting executable to be covered by the
24250661Sdavidcs   GNU General Public License.  This exception does not however invalidate
25250661Sdavidcs   any other reasons why the executable file might be covered by the GNU
26250661Sdavidcs   General Public License.  */
27250661Sdavidcs
28250661Sdavidcs/* This file contains routines to manage the work-share queue for a team
29250661Sdavidcs   of threads.  */
30250661Sdavidcs
31250661Sdavidcs#include "libgomp.h"
32250661Sdavidcs#include <stdlib.h>
33250661Sdavidcs#include <string.h>
34250661Sdavidcs
35250661Sdavidcs
36250661Sdavidcs/* Create a new work share structure.  */
37250661Sdavidcs
38250661Sdavidcsstruct gomp_work_share *
39250661Sdavidcsgomp_new_work_share (bool ordered, unsigned nthreads)
40250661Sdavidcs{
41250661Sdavidcs  struct gomp_work_share *ws;
42250661Sdavidcs  size_t size;
43250661Sdavidcs
44250661Sdavidcs  size = sizeof (*ws);
45250661Sdavidcs  if (ordered)
46250661Sdavidcs    size += nthreads * sizeof (ws->ordered_team_ids[0]);
47250661Sdavidcs
48250661Sdavidcs  ws = gomp_malloc_cleared (size);
49250661Sdavidcs  gomp_mutex_init (&ws->lock);
50250661Sdavidcs  ws->ordered_owner = -1;
51250661Sdavidcs
52250661Sdavidcs  return ws;
53250661Sdavidcs}
54250661Sdavidcs
55250661Sdavidcs
56250661Sdavidcs/* Free a work share structure.  */
57250661Sdavidcs
58250661Sdavidcsstatic void
59250661Sdavidcsfree_work_share (struct gomp_work_share *ws)
60250661Sdavidcs{
61250661Sdavidcs  gomp_mutex_destroy (&ws->lock);
62250661Sdavidcs  free (ws);
63250661Sdavidcs}
64250661Sdavidcs
65250661Sdavidcs
66250661Sdavidcs/* The current thread is ready to begin the next work sharing construct.
67250661Sdavidcs   In all cases, thr->ts.work_share is updated to point to the new
68250661Sdavidcs   structure.  In all cases the work_share lock is locked.  Return true
69250661Sdavidcs   if this was the first thread to reach this point.  */
70250661Sdavidcs
71250661Sdavidcsbool
72250661Sdavidcsgomp_work_share_start (bool ordered)
73250661Sdavidcs{
74250661Sdavidcs  struct gomp_thread *thr = gomp_thread ();
75250661Sdavidcs  struct gomp_team *team = thr->ts.team;
76250661Sdavidcs  struct gomp_work_share *ws;
77250661Sdavidcs  unsigned ws_index, ws_gen;
78250661Sdavidcs
79250661Sdavidcs  /* Work sharing constructs can be orphaned.  */
80250661Sdavidcs  if (team == NULL)
81250661Sdavidcs    {
82250661Sdavidcs      ws = gomp_new_work_share (ordered, 1);
83250661Sdavidcs      thr->ts.work_share = ws;
84250661Sdavidcs      thr->ts.static_trip = 0;
85250661Sdavidcs      gomp_mutex_lock (&ws->lock);
86250661Sdavidcs      return true;
87250661Sdavidcs    }
88250661Sdavidcs
89250661Sdavidcs  gomp_mutex_lock (&team->work_share_lock);
90250661Sdavidcs
91250661Sdavidcs  /* This thread is beginning its next generation.  */
92250661Sdavidcs  ws_gen = ++thr->ts.work_share_generation;
93250661Sdavidcs
94250661Sdavidcs  /* If this next generation is not newer than any other generation in
95250661Sdavidcs     the team, then simply reference the existing construct.  */
96250661Sdavidcs  if (ws_gen - team->oldest_live_gen < team->num_live_gen)
97250661Sdavidcs    {
98250661Sdavidcs      ws_index = ws_gen & team->generation_mask;
99250661Sdavidcs      ws = team->work_shares[ws_index];
100250661Sdavidcs      thr->ts.work_share = ws;
101250661Sdavidcs      thr->ts.static_trip = 0;
102250661Sdavidcs
103250661Sdavidcs      gomp_mutex_lock (&ws->lock);
104250661Sdavidcs      gomp_mutex_unlock (&team->work_share_lock);
105250661Sdavidcs
106250661Sdavidcs      return false;
107250661Sdavidcs    }
108250661Sdavidcs
109250661Sdavidcs  /* Resize the work shares queue if we've run out of space.  */
110250661Sdavidcs  if (team->num_live_gen++ == team->generation_mask)
111250661Sdavidcs    {
112250661Sdavidcs      team->work_shares = gomp_realloc (team->work_shares,
113250661Sdavidcs					2 * team->num_live_gen
114250661Sdavidcs					* sizeof (*team->work_shares));
115250661Sdavidcs
116250661Sdavidcs      /* Unless oldest_live_gen is zero, the sequence of live elements
117250661Sdavidcs	 wraps around the end of the array.  If we do nothing, we break
118250661Sdavidcs	 lookup of the existing elements.  Fix that by unwrapping the
119250661Sdavidcs	 data from the front to the end.  */
120250661Sdavidcs      if (team->oldest_live_gen > 0)
121250661Sdavidcs	memcpy (team->work_shares + team->num_live_gen,
122250661Sdavidcs		team->work_shares,
123250661Sdavidcs		(team->oldest_live_gen & team->generation_mask)
124250661Sdavidcs		* sizeof (*team->work_shares));
125250661Sdavidcs
126250661Sdavidcs      team->generation_mask = team->generation_mask * 2 + 1;
127250661Sdavidcs    }
128250661Sdavidcs
129250661Sdavidcs  ws_index = ws_gen & team->generation_mask;
130250661Sdavidcs  ws = gomp_new_work_share (ordered, team->nthreads);
131250661Sdavidcs  thr->ts.work_share = ws;
132250661Sdavidcs  thr->ts.static_trip = 0;
133250661Sdavidcs  team->work_shares[ws_index] = ws;
134250661Sdavidcs
135250661Sdavidcs  gomp_mutex_lock (&ws->lock);
136250661Sdavidcs  gomp_mutex_unlock (&team->work_share_lock);
137250661Sdavidcs
138250661Sdavidcs  return true;
139250661Sdavidcs}
140250661Sdavidcs
141250661Sdavidcs
142250661Sdavidcs/* The current thread is done with its current work sharing construct.
143250661Sdavidcs   This version does imply a barrier at the end of the work-share.  */
144250661Sdavidcs
145250661Sdavidcsvoid
146250661Sdavidcsgomp_work_share_end (void)
147250661Sdavidcs{
148250661Sdavidcs  struct gomp_thread *thr = gomp_thread ();
149250661Sdavidcs  struct gomp_team *team = thr->ts.team;
150250661Sdavidcs  struct gomp_work_share *ws = thr->ts.work_share;
151250661Sdavidcs  bool last;
152250661Sdavidcs
153250661Sdavidcs  thr->ts.work_share = NULL;
154250661Sdavidcs
155250661Sdavidcs  /* Work sharing constructs can be orphaned.  */
156250661Sdavidcs  if (team == NULL)
157250661Sdavidcs    {
158250661Sdavidcs      free_work_share (ws);
159250661Sdavidcs      return;
160250661Sdavidcs    }
161250661Sdavidcs
162250661Sdavidcs  last = gomp_barrier_wait_start (&team->barrier);
163273736Shselasky
164250661Sdavidcs  if (last)
165250661Sdavidcs    {
166250661Sdavidcs      unsigned ws_index;
167250661Sdavidcs
168250661Sdavidcs      ws_index = thr->ts.work_share_generation & team->generation_mask;
169250661Sdavidcs      team->work_shares[ws_index] = NULL;
170250661Sdavidcs      team->oldest_live_gen++;
171250661Sdavidcs      team->num_live_gen = 0;
172250661Sdavidcs
173250661Sdavidcs      free_work_share (ws);
174250661Sdavidcs    }
175250661Sdavidcs
176250661Sdavidcs  gomp_barrier_wait_end (&team->barrier, last);
177250661Sdavidcs}
178250661Sdavidcs
179250661Sdavidcs
180250661Sdavidcs/* The current thread is done with its current work sharing construct.
181250661Sdavidcs   This version does NOT imply a barrier at the end of the work-share.  */
182250661Sdavidcs
183250661Sdavidcsvoid
184250661Sdavidcsgomp_work_share_end_nowait (void)
185250661Sdavidcs{
186250661Sdavidcs  struct gomp_thread *thr = gomp_thread ();
187250661Sdavidcs  struct gomp_team *team = thr->ts.team;
188250661Sdavidcs  struct gomp_work_share *ws = thr->ts.work_share;
189250661Sdavidcs  unsigned completed;
190250661Sdavidcs
191250661Sdavidcs  thr->ts.work_share = NULL;
192250661Sdavidcs
193250661Sdavidcs  /* Work sharing constructs can be orphaned.  */
194250661Sdavidcs  if (team == NULL)
195250661Sdavidcs    {
196250661Sdavidcs      free_work_share (ws);
197250661Sdavidcs      return;
198250661Sdavidcs    }
199250661Sdavidcs
200250661Sdavidcs#ifdef HAVE_SYNC_BUILTINS
201250661Sdavidcs  completed = __sync_add_and_fetch (&ws->threads_completed, 1);
202250661Sdavidcs#else
203250661Sdavidcs  gomp_mutex_lock (&ws->lock);
204250661Sdavidcs  completed = ++ws->threads_completed;
205250661Sdavidcs  gomp_mutex_unlock (&ws->lock);
206250661Sdavidcs#endif
207250661Sdavidcs
208250661Sdavidcs  if (completed == team->nthreads)
209250661Sdavidcs    {
210250661Sdavidcs      unsigned ws_index;
211250661Sdavidcs
212250661Sdavidcs      gomp_mutex_lock (&team->work_share_lock);
213250661Sdavidcs
214250661Sdavidcs      ws_index = thr->ts.work_share_generation & team->generation_mask;
215250661Sdavidcs      team->work_shares[ws_index] = NULL;
216250661Sdavidcs      team->oldest_live_gen++;
217250661Sdavidcs      team->num_live_gen--;
218250661Sdavidcs
219250661Sdavidcs      gomp_mutex_unlock (&team->work_share_lock);
220250661Sdavidcs
221250661Sdavidcs      free_work_share (ws);
222250661Sdavidcs    }
223250661Sdavidcs}
224250661Sdavidcs