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