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 contains routines to manage the work-share queue for a team
29169695Skan   of threads.  */
30169695Skan
31169695Skan#include "libgomp.h"
32169695Skan#include <stdlib.h>
33169695Skan#include <string.h>
34169695Skan
35169695Skan
36169695Skan/* Create a new work share structure.  */
37169695Skan
38169695Skanstruct gomp_work_share *
39169695Skangomp_new_work_share (bool ordered, unsigned nthreads)
40169695Skan{
41169695Skan  struct gomp_work_share *ws;
42169695Skan  size_t size;
43169695Skan
44169695Skan  size = sizeof (*ws);
45169695Skan  if (ordered)
46169695Skan    size += nthreads * sizeof (ws->ordered_team_ids[0]);
47169695Skan
48169695Skan  ws = gomp_malloc_cleared (size);
49169695Skan  gomp_mutex_init (&ws->lock);
50169695Skan  ws->ordered_owner = -1;
51169695Skan
52169695Skan  return ws;
53169695Skan}
54169695Skan
55169695Skan
56169695Skan/* Free a work share structure.  */
57169695Skan
58169695Skanstatic void
59169695Skanfree_work_share (struct gomp_work_share *ws)
60169695Skan{
61169695Skan  gomp_mutex_destroy (&ws->lock);
62169695Skan  free (ws);
63169695Skan}
64169695Skan
65169695Skan
66169695Skan/* The current thread is ready to begin the next work sharing construct.
67169695Skan   In all cases, thr->ts.work_share is updated to point to the new
68169695Skan   structure.  In all cases the work_share lock is locked.  Return true
69169695Skan   if this was the first thread to reach this point.  */
70169695Skan
71169695Skanbool
72169695Skangomp_work_share_start (bool ordered)
73169695Skan{
74169695Skan  struct gomp_thread *thr = gomp_thread ();
75169695Skan  struct gomp_team *team = thr->ts.team;
76169695Skan  struct gomp_work_share *ws;
77169695Skan  unsigned ws_index, ws_gen;
78169695Skan
79169695Skan  /* Work sharing constructs can be orphaned.  */
80169695Skan  if (team == NULL)
81169695Skan    {
82169695Skan      ws = gomp_new_work_share (ordered, 1);
83169695Skan      thr->ts.work_share = ws;
84169695Skan      thr->ts.static_trip = 0;
85169695Skan      gomp_mutex_lock (&ws->lock);
86169695Skan      return true;
87169695Skan    }
88169695Skan
89169695Skan  gomp_mutex_lock (&team->work_share_lock);
90169695Skan
91169695Skan  /* This thread is beginning its next generation.  */
92169695Skan  ws_gen = ++thr->ts.work_share_generation;
93169695Skan
94169695Skan  /* If this next generation is not newer than any other generation in
95169695Skan     the team, then simply reference the existing construct.  */
96169695Skan  if (ws_gen - team->oldest_live_gen < team->num_live_gen)
97169695Skan    {
98169695Skan      ws_index = ws_gen & team->generation_mask;
99169695Skan      ws = team->work_shares[ws_index];
100169695Skan      thr->ts.work_share = ws;
101169695Skan      thr->ts.static_trip = 0;
102169695Skan
103169695Skan      gomp_mutex_lock (&ws->lock);
104169695Skan      gomp_mutex_unlock (&team->work_share_lock);
105169695Skan
106169695Skan      return false;
107169695Skan    }
108169695Skan
109169695Skan  /* Resize the work shares queue if we've run out of space.  */
110169695Skan  if (team->num_live_gen++ == team->generation_mask)
111169695Skan    {
112169695Skan      team->work_shares = gomp_realloc (team->work_shares,
113169695Skan					2 * team->num_live_gen
114169695Skan					* sizeof (*team->work_shares));
115169695Skan
116169695Skan      /* Unless oldest_live_gen is zero, the sequence of live elements
117169695Skan	 wraps around the end of the array.  If we do nothing, we break
118169695Skan	 lookup of the existing elements.  Fix that by unwrapping the
119169695Skan	 data from the front to the end.  */
120169695Skan      if (team->oldest_live_gen > 0)
121169695Skan	memcpy (team->work_shares + team->num_live_gen,
122169695Skan		team->work_shares,
123169695Skan		(team->oldest_live_gen & team->generation_mask)
124169695Skan		* sizeof (*team->work_shares));
125169695Skan
126169695Skan      team->generation_mask = team->generation_mask * 2 + 1;
127169695Skan    }
128169695Skan
129169695Skan  ws_index = ws_gen & team->generation_mask;
130169695Skan  ws = gomp_new_work_share (ordered, team->nthreads);
131169695Skan  thr->ts.work_share = ws;
132169695Skan  thr->ts.static_trip = 0;
133169695Skan  team->work_shares[ws_index] = ws;
134169695Skan
135169695Skan  gomp_mutex_lock (&ws->lock);
136169695Skan  gomp_mutex_unlock (&team->work_share_lock);
137169695Skan
138169695Skan  return true;
139169695Skan}
140169695Skan
141169695Skan
142169695Skan/* The current thread is done with its current work sharing construct.
143169695Skan   This version does imply a barrier at the end of the work-share.  */
144169695Skan
145169695Skanvoid
146169695Skangomp_work_share_end (void)
147169695Skan{
148169695Skan  struct gomp_thread *thr = gomp_thread ();
149169695Skan  struct gomp_team *team = thr->ts.team;
150169695Skan  struct gomp_work_share *ws = thr->ts.work_share;
151169695Skan  bool last;
152169695Skan
153169695Skan  thr->ts.work_share = NULL;
154169695Skan
155169695Skan  /* Work sharing constructs can be orphaned.  */
156169695Skan  if (team == NULL)
157169695Skan    {
158169695Skan      free_work_share (ws);
159169695Skan      return;
160169695Skan    }
161169695Skan
162169695Skan  last = gomp_barrier_wait_start (&team->barrier);
163169695Skan
164169695Skan  if (last)
165169695Skan    {
166169695Skan      unsigned ws_index;
167169695Skan
168169695Skan      ws_index = thr->ts.work_share_generation & team->generation_mask;
169169695Skan      team->work_shares[ws_index] = NULL;
170169695Skan      team->oldest_live_gen++;
171169695Skan      team->num_live_gen = 0;
172169695Skan
173169695Skan      free_work_share (ws);
174169695Skan    }
175169695Skan
176169695Skan  gomp_barrier_wait_end (&team->barrier, last);
177169695Skan}
178169695Skan
179169695Skan
180169695Skan/* The current thread is done with its current work sharing construct.
181169695Skan   This version does NOT imply a barrier at the end of the work-share.  */
182169695Skan
183169695Skanvoid
184169695Skangomp_work_share_end_nowait (void)
185169695Skan{
186169695Skan  struct gomp_thread *thr = gomp_thread ();
187169695Skan  struct gomp_team *team = thr->ts.team;
188169695Skan  struct gomp_work_share *ws = thr->ts.work_share;
189169695Skan  unsigned completed;
190169695Skan
191169695Skan  thr->ts.work_share = NULL;
192169695Skan
193169695Skan  /* Work sharing constructs can be orphaned.  */
194169695Skan  if (team == NULL)
195169695Skan    {
196169695Skan      free_work_share (ws);
197169695Skan      return;
198169695Skan    }
199169695Skan
200169695Skan#ifdef HAVE_SYNC_BUILTINS
201169695Skan  completed = __sync_add_and_fetch (&ws->threads_completed, 1);
202169695Skan#else
203169695Skan  gomp_mutex_lock (&ws->lock);
204169695Skan  completed = ++ws->threads_completed;
205169695Skan  gomp_mutex_unlock (&ws->lock);
206169695Skan#endif
207169695Skan
208169695Skan  if (completed == team->nthreads)
209169695Skan    {
210169695Skan      unsigned ws_index;
211169695Skan
212169695Skan      gomp_mutex_lock (&team->work_share_lock);
213169695Skan
214169695Skan      ws_index = thr->ts.work_share_generation & team->generation_mask;
215169695Skan      team->work_shares[ws_index] = NULL;
216169695Skan      team->oldest_live_gen++;
217169695Skan      team->num_live_gen--;
218169695Skan
219169695Skan      gomp_mutex_unlock (&team->work_share_lock);
220169695Skan
221169695Skan      free_work_share (ws);
222169695Skan    }
223169695Skan}
224