1*629ff9f7SJohn Marino /* Copyright (C) 2005, 2009 Free Software Foundation, Inc. 2*629ff9f7SJohn Marino Contributed by Richard Henderson <rth@redhat.com>. 3*629ff9f7SJohn Marino 4*629ff9f7SJohn Marino This file is part of the GNU OpenMP Library (libgomp). 5*629ff9f7SJohn Marino 6*629ff9f7SJohn Marino Libgomp is free software; you can redistribute it and/or modify it 7*629ff9f7SJohn Marino under the terms of the GNU General Public License as published by 8*629ff9f7SJohn Marino the Free Software Foundation; either version 3, or (at your option) 9*629ff9f7SJohn Marino any later version. 10*629ff9f7SJohn Marino 11*629ff9f7SJohn Marino Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY 12*629ff9f7SJohn Marino WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13*629ff9f7SJohn Marino FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14*629ff9f7SJohn Marino more details. 15*629ff9f7SJohn Marino 16*629ff9f7SJohn Marino Under Section 7 of GPL version 3, you are granted additional 17*629ff9f7SJohn Marino permissions described in the GCC Runtime Library Exception, version 18*629ff9f7SJohn Marino 3.1, as published by the Free Software Foundation. 19*629ff9f7SJohn Marino 20*629ff9f7SJohn Marino You should have received a copy of the GNU General Public License and 21*629ff9f7SJohn Marino a copy of the GCC Runtime Library Exception along with this program; 22*629ff9f7SJohn Marino see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23*629ff9f7SJohn Marino <http://www.gnu.org/licenses/>. */ 24*629ff9f7SJohn Marino 25*629ff9f7SJohn Marino /* This file handles the ORDERED construct. */ 26*629ff9f7SJohn Marino 27*629ff9f7SJohn Marino #include "libgomp.h" 28*629ff9f7SJohn Marino 29*629ff9f7SJohn Marino 30*629ff9f7SJohn Marino /* This function is called when first allocating an iteration block. That 31*629ff9f7SJohn Marino is, the thread is not currently on the queue. The work-share lock must 32*629ff9f7SJohn Marino be held on entry. */ 33*629ff9f7SJohn Marino 34*629ff9f7SJohn Marino void 35*629ff9f7SJohn Marino gomp_ordered_first (void) 36*629ff9f7SJohn Marino { 37*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 38*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 39*629ff9f7SJohn Marino struct gomp_work_share *ws = thr->ts.work_share; 40*629ff9f7SJohn Marino unsigned index; 41*629ff9f7SJohn Marino 42*629ff9f7SJohn Marino /* Work share constructs can be orphaned. */ 43*629ff9f7SJohn Marino if (team == NULL || team->nthreads == 1) 44*629ff9f7SJohn Marino return; 45*629ff9f7SJohn Marino 46*629ff9f7SJohn Marino index = ws->ordered_cur + ws->ordered_num_used; 47*629ff9f7SJohn Marino if (index >= team->nthreads) 48*629ff9f7SJohn Marino index -= team->nthreads; 49*629ff9f7SJohn Marino ws->ordered_team_ids[index] = thr->ts.team_id; 50*629ff9f7SJohn Marino 51*629ff9f7SJohn Marino /* If this is the first and only thread in the queue, then there is 52*629ff9f7SJohn Marino no one to release us when we get to our ordered section. Post to 53*629ff9f7SJohn Marino our own release queue now so that we won't block later. */ 54*629ff9f7SJohn Marino if (ws->ordered_num_used++ == 0) 55*629ff9f7SJohn Marino gomp_sem_post (team->ordered_release[thr->ts.team_id]); 56*629ff9f7SJohn Marino } 57*629ff9f7SJohn Marino 58*629ff9f7SJohn Marino /* This function is called when completing the last iteration block. That 59*629ff9f7SJohn Marino is, there are no more iterations to perform and so the thread should be 60*629ff9f7SJohn Marino removed from the queue entirely. Because of the way ORDERED blocks are 61*629ff9f7SJohn Marino managed, it follows that we currently own access to the ORDERED block, 62*629ff9f7SJohn Marino and should now pass it on to the next thread. The work-share lock must 63*629ff9f7SJohn Marino be held on entry. */ 64*629ff9f7SJohn Marino 65*629ff9f7SJohn Marino void 66*629ff9f7SJohn Marino gomp_ordered_last (void) 67*629ff9f7SJohn Marino { 68*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 69*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 70*629ff9f7SJohn Marino struct gomp_work_share *ws = thr->ts.work_share; 71*629ff9f7SJohn Marino unsigned next_id; 72*629ff9f7SJohn Marino 73*629ff9f7SJohn Marino /* Work share constructs can be orphaned. */ 74*629ff9f7SJohn Marino if (team == NULL || team->nthreads == 1) 75*629ff9f7SJohn Marino return; 76*629ff9f7SJohn Marino 77*629ff9f7SJohn Marino /* We're no longer the owner. */ 78*629ff9f7SJohn Marino ws->ordered_owner = -1; 79*629ff9f7SJohn Marino 80*629ff9f7SJohn Marino /* If we're not the last thread in the queue, then wake the next. */ 81*629ff9f7SJohn Marino if (--ws->ordered_num_used > 0) 82*629ff9f7SJohn Marino { 83*629ff9f7SJohn Marino unsigned next = ws->ordered_cur + 1; 84*629ff9f7SJohn Marino if (next == team->nthreads) 85*629ff9f7SJohn Marino next = 0; 86*629ff9f7SJohn Marino ws->ordered_cur = next; 87*629ff9f7SJohn Marino 88*629ff9f7SJohn Marino next_id = ws->ordered_team_ids[next]; 89*629ff9f7SJohn Marino gomp_sem_post (team->ordered_release[next_id]); 90*629ff9f7SJohn Marino } 91*629ff9f7SJohn Marino } 92*629ff9f7SJohn Marino 93*629ff9f7SJohn Marino 94*629ff9f7SJohn Marino /* This function is called when allocating a subsequent allocation block. 95*629ff9f7SJohn Marino That is, we're done with the current iteration block and we're allocating 96*629ff9f7SJohn Marino another. This is the logical combination of a call to gomp_ordered_last 97*629ff9f7SJohn Marino followed by a call to gomp_ordered_first. The work-share lock must be 98*629ff9f7SJohn Marino held on entry. */ 99*629ff9f7SJohn Marino 100*629ff9f7SJohn Marino void 101*629ff9f7SJohn Marino gomp_ordered_next (void) 102*629ff9f7SJohn Marino { 103*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 104*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 105*629ff9f7SJohn Marino struct gomp_work_share *ws = thr->ts.work_share; 106*629ff9f7SJohn Marino unsigned index, next_id; 107*629ff9f7SJohn Marino 108*629ff9f7SJohn Marino /* Work share constructs can be orphaned. */ 109*629ff9f7SJohn Marino if (team == NULL || team->nthreads == 1) 110*629ff9f7SJohn Marino return; 111*629ff9f7SJohn Marino 112*629ff9f7SJohn Marino /* We're no longer the owner. */ 113*629ff9f7SJohn Marino ws->ordered_owner = -1; 114*629ff9f7SJohn Marino 115*629ff9f7SJohn Marino /* If there's only one thread in the queue, that must be us. */ 116*629ff9f7SJohn Marino if (ws->ordered_num_used == 1) 117*629ff9f7SJohn Marino { 118*629ff9f7SJohn Marino /* We have a similar situation as in gomp_ordered_first 119*629ff9f7SJohn Marino where we need to post to our own release semaphore. */ 120*629ff9f7SJohn Marino gomp_sem_post (team->ordered_release[thr->ts.team_id]); 121*629ff9f7SJohn Marino return; 122*629ff9f7SJohn Marino } 123*629ff9f7SJohn Marino 124*629ff9f7SJohn Marino /* If the queue is entirely full, then we move ourself to the end of 125*629ff9f7SJohn Marino the queue merely by incrementing ordered_cur. Only if it's not 126*629ff9f7SJohn Marino full do we have to write our id. */ 127*629ff9f7SJohn Marino if (ws->ordered_num_used < team->nthreads) 128*629ff9f7SJohn Marino { 129*629ff9f7SJohn Marino index = ws->ordered_cur + ws->ordered_num_used; 130*629ff9f7SJohn Marino if (index >= team->nthreads) 131*629ff9f7SJohn Marino index -= team->nthreads; 132*629ff9f7SJohn Marino ws->ordered_team_ids[index] = thr->ts.team_id; 133*629ff9f7SJohn Marino } 134*629ff9f7SJohn Marino 135*629ff9f7SJohn Marino index = ws->ordered_cur + 1; 136*629ff9f7SJohn Marino if (index == team->nthreads) 137*629ff9f7SJohn Marino index = 0; 138*629ff9f7SJohn Marino ws->ordered_cur = index; 139*629ff9f7SJohn Marino 140*629ff9f7SJohn Marino next_id = ws->ordered_team_ids[index]; 141*629ff9f7SJohn Marino gomp_sem_post (team->ordered_release[next_id]); 142*629ff9f7SJohn Marino } 143*629ff9f7SJohn Marino 144*629ff9f7SJohn Marino 145*629ff9f7SJohn Marino /* This function is called when a statically scheduled loop is first 146*629ff9f7SJohn Marino being created. */ 147*629ff9f7SJohn Marino 148*629ff9f7SJohn Marino void 149*629ff9f7SJohn Marino gomp_ordered_static_init (void) 150*629ff9f7SJohn Marino { 151*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 152*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 153*629ff9f7SJohn Marino 154*629ff9f7SJohn Marino if (team == NULL || team->nthreads == 1) 155*629ff9f7SJohn Marino return; 156*629ff9f7SJohn Marino 157*629ff9f7SJohn Marino gomp_sem_post (team->ordered_release[0]); 158*629ff9f7SJohn Marino } 159*629ff9f7SJohn Marino 160*629ff9f7SJohn Marino /* This function is called when a statically scheduled loop is moving to 161*629ff9f7SJohn Marino the next allocation block. Static schedules are not first come first 162*629ff9f7SJohn Marino served like the others, so we're to move to the numerically next thread, 163*629ff9f7SJohn Marino not the next thread on a list. The work-share lock should *not* be held 164*629ff9f7SJohn Marino on entry. */ 165*629ff9f7SJohn Marino 166*629ff9f7SJohn Marino void 167*629ff9f7SJohn Marino gomp_ordered_static_next (void) 168*629ff9f7SJohn Marino { 169*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 170*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 171*629ff9f7SJohn Marino struct gomp_work_share *ws = thr->ts.work_share; 172*629ff9f7SJohn Marino unsigned id = thr->ts.team_id; 173*629ff9f7SJohn Marino 174*629ff9f7SJohn Marino if (team == NULL || team->nthreads == 1) 175*629ff9f7SJohn Marino return; 176*629ff9f7SJohn Marino 177*629ff9f7SJohn Marino ws->ordered_owner = -1; 178*629ff9f7SJohn Marino 179*629ff9f7SJohn Marino /* This thread currently owns the lock. Increment the owner. */ 180*629ff9f7SJohn Marino if (++id == team->nthreads) 181*629ff9f7SJohn Marino id = 0; 182*629ff9f7SJohn Marino ws->ordered_team_ids[0] = id; 183*629ff9f7SJohn Marino gomp_sem_post (team->ordered_release[id]); 184*629ff9f7SJohn Marino } 185*629ff9f7SJohn Marino 186*629ff9f7SJohn Marino /* This function is called when we need to assert that the thread owns the 187*629ff9f7SJohn Marino ordered section. Due to the problem of posted-but-not-waited semaphores, 188*629ff9f7SJohn Marino this needs to happen before completing a loop iteration. */ 189*629ff9f7SJohn Marino 190*629ff9f7SJohn Marino void 191*629ff9f7SJohn Marino gomp_ordered_sync (void) 192*629ff9f7SJohn Marino { 193*629ff9f7SJohn Marino struct gomp_thread *thr = gomp_thread (); 194*629ff9f7SJohn Marino struct gomp_team *team = thr->ts.team; 195*629ff9f7SJohn Marino struct gomp_work_share *ws = thr->ts.work_share; 196*629ff9f7SJohn Marino 197*629ff9f7SJohn Marino /* Work share constructs can be orphaned. But this clearly means that 198*629ff9f7SJohn Marino we are the only thread, and so we automatically own the section. */ 199*629ff9f7SJohn Marino if (team == NULL || team->nthreads == 1) 200*629ff9f7SJohn Marino return; 201*629ff9f7SJohn Marino 202*629ff9f7SJohn Marino /* ??? I believe it to be safe to access this data without taking the 203*629ff9f7SJohn Marino ws->lock. The only presumed race condition is with the previous 204*629ff9f7SJohn Marino thread on the queue incrementing ordered_cur such that it points 205*629ff9f7SJohn Marino to us, concurrently with our check below. But our team_id is 206*629ff9f7SJohn Marino already present in the queue, and the other thread will always 207*629ff9f7SJohn Marino post to our release semaphore. So the two cases are that we will 208*629ff9f7SJohn Marino either win the race an momentarily block on the semaphore, or lose 209*629ff9f7SJohn Marino the race and find the semaphore already unlocked and so not block. 210*629ff9f7SJohn Marino Either way we get correct results. 211*629ff9f7SJohn Marino However, there is an implicit flush on entry to an ordered region, 212*629ff9f7SJohn Marino so we do need to have a barrier here. If we were taking a lock 213*629ff9f7SJohn Marino this could be MEMMODEL_RELEASE since the acquire would be coverd 214*629ff9f7SJohn Marino by the lock. */ 215*629ff9f7SJohn Marino 216*629ff9f7SJohn Marino __atomic_thread_fence (MEMMODEL_ACQ_REL); 217*629ff9f7SJohn Marino if (ws->ordered_owner != thr->ts.team_id) 218*629ff9f7SJohn Marino { 219*629ff9f7SJohn Marino gomp_sem_wait (team->ordered_release[thr->ts.team_id]); 220*629ff9f7SJohn Marino ws->ordered_owner = thr->ts.team_id; 221*629ff9f7SJohn Marino } 222*629ff9f7SJohn Marino } 223*629ff9f7SJohn Marino 224*629ff9f7SJohn Marino /* This function is called by user code when encountering the start of an 225*629ff9f7SJohn Marino ORDERED block. We must check to see if the current thread is at the 226*629ff9f7SJohn Marino head of the queue, and if not, block. */ 227*629ff9f7SJohn Marino 228*629ff9f7SJohn Marino #ifdef HAVE_ATTRIBUTE_ALIAS 229*629ff9f7SJohn Marino extern void GOMP_ordered_start (void) 230*629ff9f7SJohn Marino __attribute__((alias ("gomp_ordered_sync"))); 231*629ff9f7SJohn Marino #else 232*629ff9f7SJohn Marino void 233*629ff9f7SJohn Marino GOMP_ordered_start (void) 234*629ff9f7SJohn Marino { 235*629ff9f7SJohn Marino gomp_ordered_sync (); 236*629ff9f7SJohn Marino } 237*629ff9f7SJohn Marino #endif 238*629ff9f7SJohn Marino 239*629ff9f7SJohn Marino /* This function is called by user code when encountering the end of an 240*629ff9f7SJohn Marino ORDERED block. With the current ORDERED implementation there's nothing 241*629ff9f7SJohn Marino for us to do. 242*629ff9f7SJohn Marino 243*629ff9f7SJohn Marino However, the current implementation has a flaw in that it does not allow 244*629ff9f7SJohn Marino the next thread into the ORDERED section immediately after the current 245*629ff9f7SJohn Marino thread exits the ORDERED section in its last iteration. The existance 246*629ff9f7SJohn Marino of this function allows the implementation to change. */ 247*629ff9f7SJohn Marino 248*629ff9f7SJohn Marino void 249*629ff9f7SJohn Marino GOMP_ordered_end (void) 250*629ff9f7SJohn Marino { 251*629ff9f7SJohn Marino } 252