work.c revision 1.1 1 1.1 mrg /* Copyright (C) 2005, 2008, 2009 Free Software Foundation, Inc.
2 1.1 mrg Contributed by Richard Henderson <rth (at) redhat.com>.
3 1.1 mrg
4 1.1 mrg This file is part of the GNU OpenMP Library (libgomp).
5 1.1 mrg
6 1.1 mrg Libgomp is free software; you can redistribute it and/or modify it
7 1.1 mrg under the terms of the GNU General Public License as published by
8 1.1 mrg the Free Software Foundation; either version 3, or (at your option)
9 1.1 mrg any later version.
10 1.1 mrg
11 1.1 mrg Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
12 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 1.1 mrg FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 1.1 mrg more details.
15 1.1 mrg
16 1.1 mrg Under Section 7 of GPL version 3, you are granted additional
17 1.1 mrg permissions described in the GCC Runtime Library Exception, version
18 1.1 mrg 3.1, as published by the Free Software Foundation.
19 1.1 mrg
20 1.1 mrg You should have received a copy of the GNU General Public License and
21 1.1 mrg a copy of the GCC Runtime Library Exception along with this program;
22 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 1.1 mrg <http://www.gnu.org/licenses/>. */
24 1.1 mrg
25 1.1 mrg /* This file contains routines to manage the work-share queue for a team
26 1.1 mrg of threads. */
27 1.1 mrg
28 1.1 mrg #include "libgomp.h"
29 1.1 mrg #include <stddef.h>
30 1.1 mrg #include <stdlib.h>
31 1.1 mrg #include <string.h>
32 1.1 mrg
33 1.1 mrg
34 1.1 mrg /* Allocate a new work share structure, preferably from current team's
35 1.1 mrg free gomp_work_share cache. */
36 1.1 mrg
37 1.1 mrg static struct gomp_work_share *
38 1.1 mrg alloc_work_share (struct gomp_team *team)
39 1.1 mrg {
40 1.1 mrg struct gomp_work_share *ws;
41 1.1 mrg unsigned int i;
42 1.1 mrg
43 1.1 mrg /* This is called in a critical section. */
44 1.1 mrg if (team->work_share_list_alloc != NULL)
45 1.1 mrg {
46 1.1 mrg ws = team->work_share_list_alloc;
47 1.1 mrg team->work_share_list_alloc = ws->next_free;
48 1.1 mrg return ws;
49 1.1 mrg }
50 1.1 mrg
51 1.1 mrg #ifdef HAVE_SYNC_BUILTINS
52 1.1 mrg ws = team->work_share_list_free;
53 1.1 mrg /* We need atomic read from work_share_list_free,
54 1.1 mrg as free_work_share can be called concurrently. */
55 1.1 mrg __asm ("" : "+r" (ws));
56 1.1 mrg
57 1.1 mrg if (ws && ws->next_free)
58 1.1 mrg {
59 1.1 mrg struct gomp_work_share *next = ws->next_free;
60 1.1 mrg ws->next_free = NULL;
61 1.1 mrg team->work_share_list_alloc = next->next_free;
62 1.1 mrg return next;
63 1.1 mrg }
64 1.1 mrg #else
65 1.1 mrg gomp_mutex_lock (&team->work_share_list_free_lock);
66 1.1 mrg ws = team->work_share_list_free;
67 1.1 mrg if (ws)
68 1.1 mrg {
69 1.1 mrg team->work_share_list_alloc = ws->next_free;
70 1.1 mrg team->work_share_list_free = NULL;
71 1.1 mrg gomp_mutex_unlock (&team->work_share_list_free_lock);
72 1.1 mrg return ws;
73 1.1 mrg }
74 1.1 mrg gomp_mutex_unlock (&team->work_share_list_free_lock);
75 1.1 mrg #endif
76 1.1 mrg
77 1.1 mrg team->work_share_chunk *= 2;
78 1.1 mrg ws = gomp_malloc (team->work_share_chunk * sizeof (struct gomp_work_share));
79 1.1 mrg ws->next_alloc = team->work_shares[0].next_alloc;
80 1.1 mrg team->work_shares[0].next_alloc = ws;
81 1.1 mrg team->work_share_list_alloc = &ws[1];
82 1.1 mrg for (i = 1; i < team->work_share_chunk - 1; i++)
83 1.1 mrg ws[i].next_free = &ws[i + 1];
84 1.1 mrg ws[i].next_free = NULL;
85 1.1 mrg return ws;
86 1.1 mrg }
87 1.1 mrg
88 1.1 mrg /* Initialize an already allocated struct gomp_work_share.
89 1.1 mrg This shouldn't touch the next_alloc field. */
90 1.1 mrg
91 1.1 mrg void
92 1.1 mrg gomp_init_work_share (struct gomp_work_share *ws, bool ordered,
93 1.1 mrg unsigned nthreads)
94 1.1 mrg {
95 1.1 mrg gomp_mutex_init (&ws->lock);
96 1.1 mrg if (__builtin_expect (ordered, 0))
97 1.1 mrg {
98 1.1 mrg #define INLINE_ORDERED_TEAM_IDS_CNT \
99 1.1 mrg ((sizeof (struct gomp_work_share) \
100 1.1 mrg - offsetof (struct gomp_work_share, inline_ordered_team_ids)) \
101 1.1 mrg / sizeof (((struct gomp_work_share *) 0)->inline_ordered_team_ids[0]))
102 1.1 mrg
103 1.1 mrg if (nthreads > INLINE_ORDERED_TEAM_IDS_CNT)
104 1.1 mrg ws->ordered_team_ids
105 1.1 mrg = gomp_malloc (nthreads * sizeof (*ws->ordered_team_ids));
106 1.1 mrg else
107 1.1 mrg ws->ordered_team_ids = ws->inline_ordered_team_ids;
108 1.1 mrg memset (ws->ordered_team_ids, '\0',
109 1.1 mrg nthreads * sizeof (*ws->ordered_team_ids));
110 1.1 mrg ws->ordered_num_used = 0;
111 1.1 mrg ws->ordered_owner = -1;
112 1.1 mrg ws->ordered_cur = 0;
113 1.1 mrg }
114 1.1 mrg else
115 1.1 mrg ws->ordered_team_ids = NULL;
116 1.1 mrg gomp_ptrlock_init (&ws->next_ws, NULL);
117 1.1 mrg ws->threads_completed = 0;
118 1.1 mrg }
119 1.1 mrg
120 1.1 mrg /* Do any needed destruction of gomp_work_share fields before it
121 1.1 mrg is put back into free gomp_work_share cache or freed. */
122 1.1 mrg
123 1.1 mrg void
124 1.1 mrg gomp_fini_work_share (struct gomp_work_share *ws)
125 1.1 mrg {
126 1.1 mrg gomp_mutex_destroy (&ws->lock);
127 1.1 mrg if (ws->ordered_team_ids != ws->inline_ordered_team_ids)
128 1.1 mrg free (ws->ordered_team_ids);
129 1.1 mrg gomp_ptrlock_destroy (&ws->next_ws);
130 1.1 mrg }
131 1.1 mrg
132 1.1 mrg /* Free a work share struct, if not orphaned, put it into current
133 1.1 mrg team's free gomp_work_share cache. */
134 1.1 mrg
135 1.1 mrg static inline void
136 1.1 mrg free_work_share (struct gomp_team *team, struct gomp_work_share *ws)
137 1.1 mrg {
138 1.1 mrg gomp_fini_work_share (ws);
139 1.1 mrg if (__builtin_expect (team == NULL, 0))
140 1.1 mrg free (ws);
141 1.1 mrg else
142 1.1 mrg {
143 1.1 mrg struct gomp_work_share *next_ws;
144 1.1 mrg #ifdef HAVE_SYNC_BUILTINS
145 1.1 mrg do
146 1.1 mrg {
147 1.1 mrg next_ws = team->work_share_list_free;
148 1.1 mrg ws->next_free = next_ws;
149 1.1 mrg }
150 1.1 mrg while (!__sync_bool_compare_and_swap (&team->work_share_list_free,
151 1.1 mrg next_ws, ws));
152 1.1 mrg #else
153 1.1 mrg gomp_mutex_lock (&team->work_share_list_free_lock);
154 1.1 mrg next_ws = team->work_share_list_free;
155 1.1 mrg ws->next_free = next_ws;
156 1.1 mrg team->work_share_list_free = ws;
157 1.1 mrg gomp_mutex_unlock (&team->work_share_list_free_lock);
158 1.1 mrg #endif
159 1.1 mrg }
160 1.1 mrg }
161 1.1 mrg
162 1.1 mrg /* The current thread is ready to begin the next work sharing construct.
163 1.1 mrg In all cases, thr->ts.work_share is updated to point to the new
164 1.1 mrg structure. In all cases the work_share lock is locked. Return true
165 1.1 mrg if this was the first thread to reach this point. */
166 1.1 mrg
167 1.1 mrg bool
168 1.1 mrg gomp_work_share_start (bool ordered)
169 1.1 mrg {
170 1.1 mrg struct gomp_thread *thr = gomp_thread ();
171 1.1 mrg struct gomp_team *team = thr->ts.team;
172 1.1 mrg struct gomp_work_share *ws;
173 1.1 mrg
174 1.1 mrg /* Work sharing constructs can be orphaned. */
175 1.1 mrg if (team == NULL)
176 1.1 mrg {
177 1.1 mrg ws = gomp_malloc (sizeof (*ws));
178 1.1 mrg gomp_init_work_share (ws, ordered, 1);
179 1.1 mrg thr->ts.work_share = ws;
180 1.1 mrg return ws;
181 1.1 mrg }
182 1.1 mrg
183 1.1 mrg ws = thr->ts.work_share;
184 1.1 mrg thr->ts.last_work_share = ws;
185 1.1 mrg ws = gomp_ptrlock_get (&ws->next_ws);
186 1.1 mrg if (ws == NULL)
187 1.1 mrg {
188 1.1 mrg /* This thread encountered a new ws first. */
189 1.1 mrg struct gomp_work_share *ws = alloc_work_share (team);
190 1.1 mrg gomp_init_work_share (ws, ordered, team->nthreads);
191 1.1 mrg thr->ts.work_share = ws;
192 1.1 mrg return true;
193 1.1 mrg }
194 1.1 mrg else
195 1.1 mrg {
196 1.1 mrg thr->ts.work_share = ws;
197 1.1 mrg return false;
198 1.1 mrg }
199 1.1 mrg }
200 1.1 mrg
201 1.1 mrg /* The current thread is done with its current work sharing construct.
202 1.1 mrg This version does imply a barrier at the end of the work-share. */
203 1.1 mrg
204 1.1 mrg void
205 1.1 mrg gomp_work_share_end (void)
206 1.1 mrg {
207 1.1 mrg struct gomp_thread *thr = gomp_thread ();
208 1.1 mrg struct gomp_team *team = thr->ts.team;
209 1.1 mrg gomp_barrier_state_t bstate;
210 1.1 mrg
211 1.1 mrg /* Work sharing constructs can be orphaned. */
212 1.1 mrg if (team == NULL)
213 1.1 mrg {
214 1.1 mrg free_work_share (NULL, thr->ts.work_share);
215 1.1 mrg thr->ts.work_share = NULL;
216 1.1 mrg return;
217 1.1 mrg }
218 1.1 mrg
219 1.1 mrg bstate = gomp_barrier_wait_start (&team->barrier);
220 1.1 mrg
221 1.1 mrg if (gomp_barrier_last_thread (bstate))
222 1.1 mrg {
223 1.1 mrg if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
224 1.1 mrg free_work_share (team, thr->ts.last_work_share);
225 1.1 mrg }
226 1.1 mrg
227 1.1 mrg gomp_team_barrier_wait_end (&team->barrier, bstate);
228 1.1 mrg thr->ts.last_work_share = NULL;
229 1.1 mrg }
230 1.1 mrg
231 1.1 mrg /* The current thread is done with its current work sharing construct.
232 1.1 mrg This version does NOT imply a barrier at the end of the work-share. */
233 1.1 mrg
234 1.1 mrg void
235 1.1 mrg gomp_work_share_end_nowait (void)
236 1.1 mrg {
237 1.1 mrg struct gomp_thread *thr = gomp_thread ();
238 1.1 mrg struct gomp_team *team = thr->ts.team;
239 1.1 mrg struct gomp_work_share *ws = thr->ts.work_share;
240 1.1 mrg unsigned completed;
241 1.1 mrg
242 1.1 mrg /* Work sharing constructs can be orphaned. */
243 1.1 mrg if (team == NULL)
244 1.1 mrg {
245 1.1 mrg free_work_share (NULL, ws);
246 1.1 mrg thr->ts.work_share = NULL;
247 1.1 mrg return;
248 1.1 mrg }
249 1.1 mrg
250 1.1 mrg if (__builtin_expect (thr->ts.last_work_share == NULL, 0))
251 1.1 mrg return;
252 1.1 mrg
253 1.1 mrg #ifdef HAVE_SYNC_BUILTINS
254 1.1 mrg completed = __sync_add_and_fetch (&ws->threads_completed, 1);
255 1.1 mrg #else
256 1.1 mrg gomp_mutex_lock (&ws->lock);
257 1.1 mrg completed = ++ws->threads_completed;
258 1.1 mrg gomp_mutex_unlock (&ws->lock);
259 1.1 mrg #endif
260 1.1 mrg
261 1.1 mrg if (completed == team->nthreads)
262 1.1 mrg free_work_share (team, thr->ts.last_work_share);
263 1.1 mrg thr->ts.last_work_share = NULL;
264 1.1 mrg }
265