1 1.1.1.12 mrg /* Copyright (C) 2005-2024 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 is a Linux specific implementation of a barrier synchronization 27 1.1 mrg mechanism for libgomp. This type is private to the library. This 28 1.1 mrg implementation uses atomic instructions and the futex syscall. */ 29 1.1 mrg 30 1.1 mrg #include <limits.h> 31 1.1 mrg #include "wait.h" 32 1.1 mrg 33 1.1 mrg 34 1.1 mrg void 35 1.1 mrg gomp_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state) 36 1.1 mrg { 37 1.1.1.3 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 38 1.1 mrg { 39 1.1 mrg /* Next time we'll be awaiting TOTAL threads again. */ 40 1.1 mrg bar->awaited = bar->total; 41 1.1.1.3 mrg __atomic_store_n (&bar->generation, bar->generation + BAR_INCR, 42 1.1.1.2 mrg MEMMODEL_RELEASE); 43 1.1 mrg futex_wake ((int *) &bar->generation, INT_MAX); 44 1.1 mrg } 45 1.1 mrg else 46 1.1 mrg { 47 1.1 mrg do 48 1.1.1.2 mrg do_wait ((int *) &bar->generation, state); 49 1.1.1.2 mrg while (__atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE) == state); 50 1.1 mrg } 51 1.1 mrg } 52 1.1 mrg 53 1.1 mrg void 54 1.1 mrg gomp_barrier_wait (gomp_barrier_t *bar) 55 1.1 mrg { 56 1.1 mrg gomp_barrier_wait_end (bar, gomp_barrier_wait_start (bar)); 57 1.1 mrg } 58 1.1 mrg 59 1.1 mrg /* Like gomp_barrier_wait, except that if the encountering thread 60 1.1 mrg is not the last one to hit the barrier, it returns immediately. 61 1.1 mrg The intended usage is that a thread which intends to gomp_barrier_destroy 62 1.1 mrg this barrier calls gomp_barrier_wait, while all other threads 63 1.1 mrg call gomp_barrier_wait_last. When gomp_barrier_wait returns, 64 1.1 mrg the barrier can be safely destroyed. */ 65 1.1 mrg 66 1.1 mrg void 67 1.1 mrg gomp_barrier_wait_last (gomp_barrier_t *bar) 68 1.1 mrg { 69 1.1 mrg gomp_barrier_state_t state = gomp_barrier_wait_start (bar); 70 1.1.1.3 mrg if (state & BAR_WAS_LAST) 71 1.1 mrg gomp_barrier_wait_end (bar, state); 72 1.1 mrg } 73 1.1 mrg 74 1.1 mrg void 75 1.1 mrg gomp_team_barrier_wake (gomp_barrier_t *bar, int count) 76 1.1 mrg { 77 1.1 mrg futex_wake ((int *) &bar->generation, count == 0 ? INT_MAX : count); 78 1.1 mrg } 79 1.1 mrg 80 1.1 mrg void 81 1.1 mrg gomp_team_barrier_wait_end (gomp_barrier_t *bar, gomp_barrier_state_t state) 82 1.1 mrg { 83 1.1.1.2 mrg unsigned int generation, gen; 84 1.1 mrg 85 1.1.1.3 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 86 1.1 mrg { 87 1.1 mrg /* Next time we'll be awaiting TOTAL threads again. */ 88 1.1 mrg struct gomp_thread *thr = gomp_thread (); 89 1.1 mrg struct gomp_team *team = thr->ts.team; 90 1.1.1.2 mrg 91 1.1 mrg bar->awaited = bar->total; 92 1.1.1.3 mrg team->work_share_cancelled = 0; 93 1.1 mrg if (__builtin_expect (team->task_count, 0)) 94 1.1 mrg { 95 1.1 mrg gomp_barrier_handle_tasks (state); 96 1.1.1.3 mrg state &= ~BAR_WAS_LAST; 97 1.1 mrg } 98 1.1 mrg else 99 1.1 mrg { 100 1.1.1.3 mrg state &= ~BAR_CANCELLED; 101 1.1.1.3 mrg state += BAR_INCR - BAR_WAS_LAST; 102 1.1.1.3 mrg __atomic_store_n (&bar->generation, state, MEMMODEL_RELEASE); 103 1.1 mrg futex_wake ((int *) &bar->generation, INT_MAX); 104 1.1 mrg return; 105 1.1 mrg } 106 1.1 mrg } 107 1.1 mrg 108 1.1 mrg generation = state; 109 1.1.1.3 mrg state &= ~BAR_CANCELLED; 110 1.1 mrg do 111 1.1 mrg { 112 1.1 mrg do_wait ((int *) &bar->generation, generation); 113 1.1.1.2 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE); 114 1.1.1.3 mrg if (__builtin_expect (gen & BAR_TASK_PENDING, 0)) 115 1.1.1.2 mrg { 116 1.1.1.2 mrg gomp_barrier_handle_tasks (state); 117 1.1.1.2 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE); 118 1.1.1.2 mrg } 119 1.1.1.3 mrg generation |= gen & BAR_WAITING_FOR_TASK; 120 1.1 mrg } 121 1.1.1.3 mrg while (gen != state + BAR_INCR); 122 1.1 mrg } 123 1.1 mrg 124 1.1 mrg void 125 1.1 mrg gomp_team_barrier_wait (gomp_barrier_t *bar) 126 1.1 mrg { 127 1.1 mrg gomp_team_barrier_wait_end (bar, gomp_barrier_wait_start (bar)); 128 1.1 mrg } 129 1.1.1.3 mrg 130 1.1.1.3 mrg void 131 1.1.1.3 mrg gomp_team_barrier_wait_final (gomp_barrier_t *bar) 132 1.1.1.3 mrg { 133 1.1.1.3 mrg gomp_barrier_state_t state = gomp_barrier_wait_final_start (bar); 134 1.1.1.3 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 135 1.1.1.3 mrg bar->awaited_final = bar->total; 136 1.1.1.3 mrg gomp_team_barrier_wait_end (bar, state); 137 1.1.1.3 mrg } 138 1.1.1.3 mrg 139 1.1.1.3 mrg bool 140 1.1.1.3 mrg gomp_team_barrier_wait_cancel_end (gomp_barrier_t *bar, 141 1.1.1.3 mrg gomp_barrier_state_t state) 142 1.1.1.3 mrg { 143 1.1.1.3 mrg unsigned int generation, gen; 144 1.1.1.3 mrg 145 1.1.1.3 mrg if (__builtin_expect (state & BAR_WAS_LAST, 0)) 146 1.1.1.3 mrg { 147 1.1.1.3 mrg /* Next time we'll be awaiting TOTAL threads again. */ 148 1.1.1.3 mrg /* BAR_CANCELLED should never be set in state here, because 149 1.1.1.3 mrg cancellation means that at least one of the threads has been 150 1.1.1.3 mrg cancelled, thus on a cancellable barrier we should never see 151 1.1.1.3 mrg all threads to arrive. */ 152 1.1.1.3 mrg struct gomp_thread *thr = gomp_thread (); 153 1.1.1.3 mrg struct gomp_team *team = thr->ts.team; 154 1.1.1.3 mrg 155 1.1.1.3 mrg bar->awaited = bar->total; 156 1.1.1.3 mrg team->work_share_cancelled = 0; 157 1.1.1.3 mrg if (__builtin_expect (team->task_count, 0)) 158 1.1.1.3 mrg { 159 1.1.1.3 mrg gomp_barrier_handle_tasks (state); 160 1.1.1.3 mrg state &= ~BAR_WAS_LAST; 161 1.1.1.3 mrg } 162 1.1.1.3 mrg else 163 1.1.1.3 mrg { 164 1.1.1.3 mrg state += BAR_INCR - BAR_WAS_LAST; 165 1.1.1.3 mrg __atomic_store_n (&bar->generation, state, MEMMODEL_RELEASE); 166 1.1.1.3 mrg futex_wake ((int *) &bar->generation, INT_MAX); 167 1.1.1.3 mrg return false; 168 1.1.1.3 mrg } 169 1.1.1.3 mrg } 170 1.1.1.3 mrg 171 1.1.1.3 mrg if (__builtin_expect (state & BAR_CANCELLED, 0)) 172 1.1.1.3 mrg return true; 173 1.1.1.3 mrg 174 1.1.1.3 mrg generation = state; 175 1.1.1.3 mrg do 176 1.1.1.3 mrg { 177 1.1.1.3 mrg do_wait ((int *) &bar->generation, generation); 178 1.1.1.3 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE); 179 1.1.1.3 mrg if (__builtin_expect (gen & BAR_CANCELLED, 0)) 180 1.1.1.3 mrg return true; 181 1.1.1.3 mrg if (__builtin_expect (gen & BAR_TASK_PENDING, 0)) 182 1.1.1.3 mrg { 183 1.1.1.3 mrg gomp_barrier_handle_tasks (state); 184 1.1.1.3 mrg gen = __atomic_load_n (&bar->generation, MEMMODEL_ACQUIRE); 185 1.1.1.3 mrg } 186 1.1.1.3 mrg generation |= gen & BAR_WAITING_FOR_TASK; 187 1.1.1.3 mrg } 188 1.1.1.3 mrg while (gen != state + BAR_INCR); 189 1.1.1.3 mrg 190 1.1.1.3 mrg return false; 191 1.1.1.3 mrg } 192 1.1.1.3 mrg 193 1.1.1.3 mrg bool 194 1.1.1.3 mrg gomp_team_barrier_wait_cancel (gomp_barrier_t *bar) 195 1.1.1.3 mrg { 196 1.1.1.3 mrg return gomp_team_barrier_wait_cancel_end (bar, gomp_barrier_wait_start (bar)); 197 1.1.1.3 mrg } 198 1.1.1.3 mrg 199 1.1.1.3 mrg void 200 1.1.1.3 mrg gomp_team_barrier_cancel (struct gomp_team *team) 201 1.1.1.3 mrg { 202 1.1.1.3 mrg gomp_mutex_lock (&team->task_lock); 203 1.1.1.3 mrg if (team->barrier.generation & BAR_CANCELLED) 204 1.1.1.3 mrg { 205 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 206 1.1.1.3 mrg return; 207 1.1.1.3 mrg } 208 1.1.1.3 mrg team->barrier.generation |= BAR_CANCELLED; 209 1.1.1.3 mrg gomp_mutex_unlock (&team->task_lock); 210 1.1.1.3 mrg futex_wake ((int *) &team->barrier.generation, INT_MAX); 211 1.1.1.3 mrg } 212