xref: /dflybsd-src/contrib/gcc-4.7/libgomp/ordered.c (revision 94b98a915ba84699b2a2adab9146fbc2cf617459)
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
gomp_ordered_first(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
gomp_ordered_last(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
gomp_ordered_next(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
gomp_ordered_static_init(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
gomp_ordered_static_next(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
gomp_ordered_sync(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
GOMP_ordered_start(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
GOMP_ordered_end(void)249*629ff9f7SJohn Marino GOMP_ordered_end (void)
250*629ff9f7SJohn Marino {
251*629ff9f7SJohn Marino }
252