1 /* Copyright (C) 2005-2018 Free Software Foundation, Inc.
2    Contributed by Richard Henderson <rth@redhat.com>.
3 
4    This file is part of the GNU Offloading and Multi Processing Library
5    (libgomp).
6 
7    Libgomp is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3, or (at your option)
10    any later version.
11 
12    Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15    more details.
16 
17    Under Section 7 of GPL version 3, you are granted additional
18    permissions described in the GCC Runtime Library Exception, version
19    3.1, as published by the Free Software Foundation.
20 
21    You should have received a copy of the GNU General Public License and
22    a copy of the GCC Runtime Library Exception along with this program;
23    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24    <http://www.gnu.org/licenses/>.  */
25 
26 /* This file handles the SECTIONS construct.  */
27 
28 #include "libgomp.h"
29 
30 
31 /* Initialize the given work share construct from the given arguments.  */
32 
33 static inline void
34 gomp_sections_init (struct gomp_work_share *ws, unsigned count)
35 {
36   ws->sched = GFS_DYNAMIC;
37   ws->chunk_size = 1;
38   ws->end = count + 1L;
39   ws->incr = 1;
40   ws->next = 1;
41 #ifdef HAVE_SYNC_BUILTINS
42   /* Prepare things to make each iteration faster.  */
43   if (sizeof (long) > sizeof (unsigned))
44     ws->mode = 1;
45   else
46     {
47       struct gomp_thread *thr = gomp_thread ();
48       struct gomp_team *team = thr->ts.team;
49       long nthreads = team ? team->nthreads : 1;
50 
51       ws->mode = ((nthreads | ws->end)
52 		  < 1UL << (sizeof (long) * __CHAR_BIT__ / 2 - 1));
53     }
54 #else
55   ws->mode = 0;
56 #endif
57 }
58 
59 /* This routine is called when first encountering a sections construct
60    that is not bound directly to a parallel construct.  The first thread
61    that arrives will create the work-share construct; subsequent threads
62    will see the construct exists and allocate work from it.
63 
64    COUNT is the number of sections in this construct.
65 
66    Returns the 1-based section number for this thread to perform, or 0 if
67    all work was assigned to other threads prior to this thread's arrival.  */
68 
69 unsigned
70 GOMP_sections_start (unsigned count)
71 {
72   struct gomp_thread *thr = gomp_thread ();
73   long s, e, ret;
74 
75   if (gomp_work_share_start (false))
76     {
77       gomp_sections_init (thr->ts.work_share, count);
78       gomp_work_share_init_done ();
79     }
80 
81 #ifdef HAVE_SYNC_BUILTINS
82   if (gomp_iter_dynamic_next (&s, &e))
83     ret = s;
84   else
85     ret = 0;
86 #else
87   gomp_mutex_lock (&thr->ts.work_share->lock);
88   if (gomp_iter_dynamic_next_locked (&s, &e))
89     ret = s;
90   else
91     ret = 0;
92   gomp_mutex_unlock (&thr->ts.work_share->lock);
93 #endif
94 
95   return ret;
96 }
97 
98 /* This routine is called when the thread completes processing of the
99    section currently assigned to it.  If the work-share construct is
100    bound directly to a parallel construct, then the construct may have
101    been set up before the parallel.  In which case, this may be the
102    first iteration for the thread.
103 
104    Returns the 1-based section number for this thread to perform, or 0 if
105    all work was assigned to other threads prior to this thread's arrival.  */
106 
107 unsigned
108 GOMP_sections_next (void)
109 {
110   long s, e, ret;
111 
112 #ifdef HAVE_SYNC_BUILTINS
113   if (gomp_iter_dynamic_next (&s, &e))
114     ret = s;
115   else
116     ret = 0;
117 #else
118   struct gomp_thread *thr = gomp_thread ();
119 
120   gomp_mutex_lock (&thr->ts.work_share->lock);
121   if (gomp_iter_dynamic_next_locked (&s, &e))
122     ret = s;
123   else
124     ret = 0;
125   gomp_mutex_unlock (&thr->ts.work_share->lock);
126 #endif
127 
128   return ret;
129 }
130 
131 /* This routine pre-initializes a work-share construct to avoid one
132    synchronization once we get into the loop.  */
133 
134 void
135 GOMP_parallel_sections_start (void (*fn) (void *), void *data,
136 			      unsigned num_threads, unsigned count)
137 {
138   struct gomp_team *team;
139 
140   num_threads = gomp_resolve_num_threads (num_threads, count);
141   team = gomp_new_team (num_threads);
142   gomp_sections_init (&team->work_shares[0], count);
143   gomp_team_start (fn, data, num_threads, 0, team);
144 }
145 
146 ialias_redirect (GOMP_parallel_end)
147 
148 void
149 GOMP_parallel_sections (void (*fn) (void *), void *data,
150 			unsigned num_threads, unsigned count, unsigned flags)
151 {
152   struct gomp_team *team;
153 
154   num_threads = gomp_resolve_num_threads (num_threads, count);
155   team = gomp_new_team (num_threads);
156   gomp_sections_init (&team->work_shares[0], count);
157   gomp_team_start (fn, data, num_threads, flags, team);
158   fn (data);
159   GOMP_parallel_end ();
160 }
161 
162 /* The GOMP_section_end* routines are called after the thread is told
163    that all sections are complete.  The first two versions synchronize
164    all threads; the nowait version does not.  */
165 
166 void
167 GOMP_sections_end (void)
168 {
169   gomp_work_share_end ();
170 }
171 
172 bool
173 GOMP_sections_end_cancel (void)
174 {
175   return gomp_work_share_end_cancel ();
176 }
177 
178 void
179 GOMP_sections_end_nowait (void)
180 {
181   gomp_work_share_end_nowait ();
182 }
183