xref: /netbsd-src/external/gpl3/gcc/dist/libgomp/config/rtems/bar.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /* Copyright (C) 2005-2016 Free Software Foundation, Inc.
2    Contributed by Sebastian Huber <sebastian.huber@embedded-brains.de>.
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 General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    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 General Public License for
14    more details.
15 
16    Under Section 7 of GPL version 3, you are granted additional
17    permissions described in the GCC Runtime Library Exception, version
18    3.1, as published by the Free Software Foundation.
19 
20    You should have received a copy of the GNU General Public License and
21    a copy of the GCC Runtime Library Exception along with this program;
22    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23    <http://www.gnu.org/licenses/>.  */
24 
25 /* This is the RTEMS implementation of a barrier synchronization
26    mechanism for libgomp.  It is identical to the Linux implementation, except
27    that the futex API is slightly different.  This type is private to the
28    library.  */
29 
30 #include "libgomp.h"
31 #include "bar.h"
32 #include <limits.h>
33 
34 static gomp_barrier_t *
35 generation_to_barrier (int *addr)
36 {
37   return (gomp_barrier_t *)
38 	 ((char *) addr - __builtin_offsetof (gomp_barrier_t, generation));
39 }
40 
41 static void
42 futex_wait (int *addr, int val)
43 {
44   gomp_barrier_t *bar = generation_to_barrier (addr);
45   _Futex_Wait (&bar->futex, addr, val);
46 }
47 
48 static void
49 futex_wake (int *addr, int count)
50 {
51   gomp_barrier_t *bar = generation_to_barrier (addr);
52   _Futex_Wake (&bar->futex, count);
53 }
54 
55 static int
56 do_spin (int *addr, int val)
57 {
58   unsigned long long i, count = gomp_spin_count_var;
59 
60   if (__builtin_expect (gomp_managed_threads > gomp_available_cpus, 0))
61     count = gomp_throttled_spin_count_var;
62   for (i = 0; i < count; i++)
63     if (__builtin_expect (__atomic_load_n (addr, MEMMODEL_RELAXED) != val, 0))
64       return 0;
65   return 1;
66 }
67 
68 static void
69 do_wait (int *addr, int val)
70 {
71   if (do_spin (addr, val))
72     futex_wait (addr, val);
73 }
74 
75 /* Everything below this point should be identical to the Linux
76    implementation.  */
77 
78 void
79 gomp_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state)
80 {
81   if (__builtin_expect (state & BAR_WAS_LAST, 0))
82     {
83       /* Next time we'll be awaiting TOTAL threads again.  */
84       bar->awaited = bar->total;
85       __atomic_store_n (&bar->generation, bar->generation + BAR_INCR,
86 			MEMMODEL_RELEASE);
87       futex_wake ((int *) &bar->generation, INT_MAX);
88     }
89   else
90     {
91       do
92 	do_wait ((int *) &bar->generation, state);
93       while (__atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE) == state);
94     }
95 }
96 
97 void
98 gomp_barrier_wait (gomp_barrier_t *bar)
99 {
100   gomp_barrier_wait_end (bar, gomp_barrier_wait_start (bar));
101 }
102 
103 /* Like gomp_barrier_wait, except that if the encountering thread
104    is not the last one to hit the barrier, it returns immediately.
105    The intended usage is that a thread which intends to gomp_barrier_destroy
106    this barrier calls gomp_barrier_wait, while all other threads
107    call gomp_barrier_wait_last.  When gomp_barrier_wait returns,
108    the barrier can be safely destroyed.  */
109 
110 void
111 gomp_barrier_wait_last (gomp_barrier_t *bar)
112 {
113   gomp_barrier_state_t state = gomp_barrier_wait_start (bar);
114   if (state & BAR_WAS_LAST)
115     gomp_barrier_wait_end (bar, state);
116 }
117 
118 void
119 gomp_team_barrier_wake (gomp_barrier_t *bar, int count)
120 {
121   futex_wake ((int *) &bar->generation, count == 0 ? INT_MAX : count);
122 }
123 
124 void
125 gomp_team_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state)
126 {
127   unsigned int generation, gen;
128 
129   if (__builtin_expect (state & BAR_WAS_LAST, 0))
130     {
131       /* Next time we'll be awaiting TOTAL threads again.  */
132       struct gomp_thread *thr = gomp_thread ();
133       struct gomp_team *team = thr->ts.team;
134 
135       bar->awaited = bar->total;
136       team->work_share_cancelled = 0;
137       if (__builtin_expect (team->task_count, 0))
138 	{
139 	  gomp_barrier_handle_tasks (state);
140 	  state &= ~BAR_WAS_LAST;
141 	}
142       else
143 	{
144 	  state &= ~BAR_CANCELLED;
145 	  state += BAR_INCR - BAR_WAS_LAST;
146 	  __atomic_store_n (&bar->generation, state, MEMMODEL_RELEASE);
147 	  futex_wake ((int *) &bar->generation, INT_MAX);
148 	  return;
149 	}
150     }
151 
152   generation = state;
153   state &= ~BAR_CANCELLED;
154   do
155     {
156       do_wait ((int *) &bar->generation, generation);
157       gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
158       if (__builtin_expect (gen & BAR_TASK_PENDING, 0))
159 	{
160 	  gomp_barrier_handle_tasks (state);
161 	  gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
162 	}
163       generation |= gen & BAR_WAITING_FOR_TASK;
164     }
165   while (gen != state + BAR_INCR);
166 }
167 
168 void
169 gomp_team_barrier_wait (gomp_barrier_t *bar)
170 {
171   gomp_team_barrier_wait_end (bar, gomp_barrier_wait_start (bar));
172 }
173 
174 void
175 gomp_team_barrier_wait_final (gomp_barrier_t *bar)
176 {
177   gomp_barrier_state_t state = gomp_barrier_wait_final_start (bar);
178   if (__builtin_expect (state & BAR_WAS_LAST, 0))
179     bar->awaited_final = bar->total;
180   gomp_team_barrier_wait_end (bar, state);
181 }
182 
183 bool
184 gomp_team_barrier_wait_cancel_end (gomp_barrier_t *bar,
185 				   gomp_barrier_state_t state)
186 {
187   unsigned int generation, gen;
188 
189   if (__builtin_expect (state & BAR_WAS_LAST, 0))
190     {
191       /* Next time we'll be awaiting TOTAL threads again.  */
192       /* BAR_CANCELLED should never be set in state here, because
193 	 cancellation means that at least one of the threads has been
194 	 cancelled, thus on a cancellable barrier we should never see
195 	 all threads to arrive.  */
196       struct gomp_thread *thr = gomp_thread ();
197       struct gomp_team *team = thr->ts.team;
198 
199       bar->awaited = bar->total;
200       team->work_share_cancelled = 0;
201       if (__builtin_expect (team->task_count, 0))
202 	{
203 	  gomp_barrier_handle_tasks (state);
204 	  state &= ~BAR_WAS_LAST;
205 	}
206       else
207 	{
208 	  state += BAR_INCR - BAR_WAS_LAST;
209 	  __atomic_store_n (&bar->generation, state, MEMMODEL_RELEASE);
210 	  futex_wake ((int *) &bar->generation, INT_MAX);
211 	  return false;
212 	}
213     }
214 
215   if (__builtin_expect (state & BAR_CANCELLED, 0))
216     return true;
217 
218   generation = state;
219   do
220     {
221       do_wait ((int *) &bar->generation, generation);
222       gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
223       if (__builtin_expect (gen & BAR_CANCELLED, 0))
224 	return true;
225       if (__builtin_expect (gen & BAR_TASK_PENDING, 0))
226 	{
227 	  gomp_barrier_handle_tasks (state);
228 	  gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE);
229 	}
230       generation |= gen & BAR_WAITING_FOR_TASK;
231     }
232   while (gen != state + BAR_INCR);
233 
234   return false;
235 }
236 
237 bool
238 gomp_team_barrier_wait_cancel (gomp_barrier_t *bar)
239 {
240   return gomp_team_barrier_wait_cancel_end (bar, gomp_barrier_wait_start (bar));
241 }
242 
243 void
244 gomp_team_barrier_cancel (struct gomp_team *team)
245 {
246   gomp_mutex_lock (&team->task_lock);
247   if (team->barrier.generation & BAR_CANCELLED)
248     {
249       gomp_mutex_unlock (&team->task_lock);
250       return;
251     }
252   team->barrier.generation |= BAR_CANCELLED;
253   gomp_mutex_unlock (&team->task_lock);
254   futex_wake ((int *) &team->barrier.generation, INT_MAX);
255 }
256