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