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