1 | |
2 | //===----------------------------------------------------------------------===// |
3 | // |
4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
5 | // See https://llvm.org/LICENSE.txt for license information. |
6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #include "kmp.h" |
11 | #include "kmp_i18n.h" |
12 | #include "kmp_io.h" |
13 | #include "kmp_str.h" |
14 | #if OMPT_SUPPORT |
15 | #include "ompt-specific.h" |
16 | #endif |
17 | |
18 | /*! |
19 | @ingroup CANCELLATION |
20 | @param loc_ref location of the original task directive |
21 | @param gtid Global thread ID of encountering thread |
22 | @param cncl_kind Cancellation kind (parallel, for, sections, taskgroup) |
23 | |
24 | @return returns true if the cancellation request has been activated and the |
25 | execution thread needs to proceed to the end of the canceled region. |
26 | |
27 | Request cancellation of the binding OpenMP region. |
28 | */ |
29 | kmp_int32 __kmpc_cancel(ident_t *loc_ref, kmp_int32 gtid, kmp_int32 cncl_kind) { |
30 | kmp_info_t *this_thr = __kmp_threads[gtid]; |
31 | |
32 | KC_TRACE(10, ("__kmpc_cancel: T#%d request %d OMP_CANCELLATION=%d\n" , gtid, |
33 | cncl_kind, __kmp_omp_cancellation)); |
34 | |
35 | KMP_DEBUG_ASSERT(cncl_kind != cancel_noreq); |
36 | KMP_DEBUG_ASSERT(cncl_kind == cancel_parallel || cncl_kind == cancel_loop || |
37 | cncl_kind == cancel_sections || |
38 | cncl_kind == cancel_taskgroup); |
39 | KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid); |
40 | |
41 | if (__kmp_omp_cancellation) { |
42 | switch (cncl_kind) { |
43 | case cancel_parallel: |
44 | case cancel_loop: |
45 | case cancel_sections: |
46 | // cancellation requests for parallel and worksharing constructs |
47 | // are handled through the team structure |
48 | { |
49 | kmp_team_t *this_team = this_thr->th.th_team; |
50 | KMP_DEBUG_ASSERT(this_team); |
51 | kmp_int32 old = cancel_noreq; |
52 | this_team->t.t_cancel_request.compare_exchange_strong(i1&: old, i2: cncl_kind); |
53 | if (old == cancel_noreq || old == cncl_kind) { |
54 | // we do not have a cancellation request in this team or we do have |
55 | // one that matches the current request -> cancel |
56 | #if OMPT_SUPPORT && OMPT_OPTIONAL |
57 | if (ompt_enabled.ompt_callback_cancel) { |
58 | ompt_data_t *task_data; |
59 | __ompt_get_task_info_internal(ancestor_level: 0, NULL, task_data: &task_data, NULL, NULL, |
60 | NULL); |
61 | ompt_cancel_flag_t type = ompt_cancel_parallel; |
62 | if (cncl_kind == cancel_parallel) |
63 | type = ompt_cancel_parallel; |
64 | else if (cncl_kind == cancel_loop) |
65 | type = ompt_cancel_loop; |
66 | else if (cncl_kind == cancel_sections) |
67 | type = ompt_cancel_sections; |
68 | ompt_callbacks.ompt_callback(ompt_callback_cancel)( |
69 | task_data, type | ompt_cancel_activated, |
70 | OMPT_GET_RETURN_ADDRESS(0)); |
71 | } |
72 | #endif // OMPT_SUPPORT && OMPT_OPTIONAL |
73 | return 1 /* true */; |
74 | } |
75 | break; |
76 | } |
77 | case cancel_taskgroup: |
78 | // cancellation requests for a task group |
79 | // are handled through the taskgroup structure |
80 | { |
81 | kmp_taskdata_t *task; |
82 | kmp_taskgroup_t *taskgroup; |
83 | |
84 | task = this_thr->th.th_current_task; |
85 | KMP_DEBUG_ASSERT(task); |
86 | |
87 | taskgroup = task->td_taskgroup; |
88 | if (taskgroup) { |
89 | kmp_int32 old = cancel_noreq; |
90 | taskgroup->cancel_request.compare_exchange_strong(i1&: old, i2: cncl_kind); |
91 | if (old == cancel_noreq || old == cncl_kind) { |
92 | // we do not have a cancellation request in this taskgroup or we do |
93 | // have one that matches the current request -> cancel |
94 | #if OMPT_SUPPORT && OMPT_OPTIONAL |
95 | if (ompt_enabled.ompt_callback_cancel) { |
96 | ompt_data_t *task_data; |
97 | __ompt_get_task_info_internal(ancestor_level: 0, NULL, task_data: &task_data, NULL, NULL, |
98 | NULL); |
99 | ompt_callbacks.ompt_callback(ompt_callback_cancel)( |
100 | task_data, ompt_cancel_taskgroup | ompt_cancel_activated, |
101 | OMPT_GET_RETURN_ADDRESS(0)); |
102 | } |
103 | #endif |
104 | return 1 /* true */; |
105 | } |
106 | } else { |
107 | // TODO: what needs to happen here? |
108 | // the specification disallows cancellation w/o taskgroups |
109 | // so we might do anything here, let's abort for now |
110 | KMP_ASSERT(0 /* false */); |
111 | } |
112 | } |
113 | break; |
114 | default: |
115 | KMP_ASSERT(0 /* false */); |
116 | } |
117 | } |
118 | |
119 | // ICV OMP_CANCELLATION=false, so we ignored this cancel request |
120 | KMP_DEBUG_ASSERT(!__kmp_omp_cancellation); |
121 | return 0 /* false */; |
122 | } |
123 | |
124 | /*! |
125 | @ingroup CANCELLATION |
126 | @param loc_ref location of the original task directive |
127 | @param gtid Global thread ID of encountering thread |
128 | @param cncl_kind Cancellation kind (parallel, for, sections, taskgroup) |
129 | |
130 | @return returns true if a matching cancellation request has been flagged in the |
131 | RTL and the encountering thread has to cancel.. |
132 | |
133 | Cancellation point for the encountering thread. |
134 | */ |
135 | kmp_int32 __kmpc_cancellationpoint(ident_t *loc_ref, kmp_int32 gtid, |
136 | kmp_int32 cncl_kind) { |
137 | kmp_info_t *this_thr = __kmp_threads[gtid]; |
138 | |
139 | KC_TRACE(10, |
140 | ("__kmpc_cancellationpoint: T#%d request %d OMP_CANCELLATION=%d\n" , |
141 | gtid, cncl_kind, __kmp_omp_cancellation)); |
142 | |
143 | KMP_DEBUG_ASSERT(cncl_kind != cancel_noreq); |
144 | KMP_DEBUG_ASSERT(cncl_kind == cancel_parallel || cncl_kind == cancel_loop || |
145 | cncl_kind == cancel_sections || |
146 | cncl_kind == cancel_taskgroup); |
147 | KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid); |
148 | |
149 | if (__kmp_omp_cancellation) { |
150 | switch (cncl_kind) { |
151 | case cancel_parallel: |
152 | case cancel_loop: |
153 | case cancel_sections: |
154 | // cancellation requests for parallel and worksharing constructs |
155 | // are handled through the team structure |
156 | { |
157 | kmp_team_t *this_team = this_thr->th.th_team; |
158 | KMP_DEBUG_ASSERT(this_team); |
159 | if (this_team->t.t_cancel_request) { |
160 | if (cncl_kind == this_team->t.t_cancel_request) { |
161 | // the request in the team structure matches the type of |
162 | // cancellation point so we can cancel |
163 | #if OMPT_SUPPORT && OMPT_OPTIONAL |
164 | if (ompt_enabled.ompt_callback_cancel) { |
165 | ompt_data_t *task_data; |
166 | __ompt_get_task_info_internal(ancestor_level: 0, NULL, task_data: &task_data, NULL, NULL, |
167 | NULL); |
168 | ompt_cancel_flag_t type = ompt_cancel_parallel; |
169 | if (cncl_kind == cancel_parallel) |
170 | type = ompt_cancel_parallel; |
171 | else if (cncl_kind == cancel_loop) |
172 | type = ompt_cancel_loop; |
173 | else if (cncl_kind == cancel_sections) |
174 | type = ompt_cancel_sections; |
175 | ompt_callbacks.ompt_callback(ompt_callback_cancel)( |
176 | task_data, type | ompt_cancel_detected, |
177 | OMPT_GET_RETURN_ADDRESS(0)); |
178 | } |
179 | #endif |
180 | return 1 /* true */; |
181 | } |
182 | KMP_ASSERT(0 /* false */); |
183 | } else { |
184 | // we do not have a cancellation request pending, so we just |
185 | // ignore this cancellation point |
186 | return 0; |
187 | } |
188 | break; |
189 | } |
190 | case cancel_taskgroup: |
191 | // cancellation requests for a task group |
192 | // are handled through the taskgroup structure |
193 | { |
194 | kmp_taskdata_t *task; |
195 | kmp_taskgroup_t *taskgroup; |
196 | |
197 | task = this_thr->th.th_current_task; |
198 | KMP_DEBUG_ASSERT(task); |
199 | |
200 | taskgroup = task->td_taskgroup; |
201 | if (taskgroup) { |
202 | // return the current status of cancellation for the taskgroup |
203 | #if OMPT_SUPPORT && OMPT_OPTIONAL |
204 | if (ompt_enabled.ompt_callback_cancel && |
205 | !!taskgroup->cancel_request) { |
206 | ompt_data_t *task_data; |
207 | __ompt_get_task_info_internal(ancestor_level: 0, NULL, task_data: &task_data, NULL, NULL, |
208 | NULL); |
209 | ompt_callbacks.ompt_callback(ompt_callback_cancel)( |
210 | task_data, ompt_cancel_taskgroup | ompt_cancel_detected, |
211 | OMPT_GET_RETURN_ADDRESS(0)); |
212 | } |
213 | #endif |
214 | return !!taskgroup->cancel_request; |
215 | } else { |
216 | // if a cancellation point is encountered by a task that does not |
217 | // belong to a taskgroup, it is OK to ignore it |
218 | return 0 /* false */; |
219 | } |
220 | } |
221 | default: |
222 | KMP_ASSERT(0 /* false */); |
223 | } |
224 | } |
225 | |
226 | // ICV OMP_CANCELLATION=false, so we ignore the cancellation point |
227 | KMP_DEBUG_ASSERT(!__kmp_omp_cancellation); |
228 | return 0 /* false */; |
229 | } |
230 | |
231 | /*! |
232 | @ingroup CANCELLATION |
233 | @param loc_ref location of the original task directive |
234 | @param gtid Global thread ID of encountering thread |
235 | |
236 | @return returns true if a matching cancellation request has been flagged in the |
237 | RTL and the encountering thread has to cancel.. |
238 | |
239 | Barrier with cancellation point to send threads from the barrier to the |
240 | end of the parallel region. Needs a special code pattern as documented |
241 | in the design document for the cancellation feature. |
242 | */ |
243 | kmp_int32 __kmpc_cancel_barrier(ident_t *loc, kmp_int32 gtid) { |
244 | int ret = 0 /* false */; |
245 | kmp_info_t *this_thr = __kmp_threads[gtid]; |
246 | kmp_team_t *this_team = this_thr->th.th_team; |
247 | |
248 | KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid); |
249 | |
250 | // call into the standard barrier |
251 | __kmpc_barrier(loc, global_tid: gtid); |
252 | |
253 | // if cancellation is active, check cancellation flag |
254 | if (__kmp_omp_cancellation) { |
255 | // depending on which construct to cancel, check the flag and |
256 | // reset the flag |
257 | switch (KMP_ATOMIC_LD_RLX(&(this_team->t.t_cancel_request))) { |
258 | case cancel_parallel: |
259 | ret = 1; |
260 | // ensure that threads have checked the flag, when |
261 | // leaving the above barrier |
262 | __kmpc_barrier(loc, global_tid: gtid); |
263 | this_team->t.t_cancel_request = cancel_noreq; |
264 | // the next barrier is the fork/join barrier, which |
265 | // synchronizes the threads leaving here |
266 | break; |
267 | case cancel_loop: |
268 | case cancel_sections: |
269 | ret = 1; |
270 | // ensure that threads have checked the flag, when |
271 | // leaving the above barrier |
272 | __kmpc_barrier(loc, global_tid: gtid); |
273 | this_team->t.t_cancel_request = cancel_noreq; |
274 | // synchronize the threads again to make sure we do not have any run-away |
275 | // threads that cause a race on the cancellation flag |
276 | __kmpc_barrier(loc, global_tid: gtid); |
277 | break; |
278 | case cancel_taskgroup: |
279 | // this case should not occur |
280 | KMP_ASSERT(0 /* false */); |
281 | break; |
282 | case cancel_noreq: |
283 | // do nothing |
284 | break; |
285 | default: |
286 | KMP_ASSERT(0 /* false */); |
287 | } |
288 | } |
289 | |
290 | return ret; |
291 | } |
292 | |
293 | /*! |
294 | @ingroup CANCELLATION |
295 | @param loc_ref location of the original task directive |
296 | @param gtid Global thread ID of encountering thread |
297 | |
298 | @return returns true if a matching cancellation request has been flagged in the |
299 | RTL and the encountering thread has to cancel.. |
300 | |
301 | Query function to query the current status of cancellation requests. |
302 | Can be used to implement the following pattern: |
303 | |
304 | if (kmp_get_cancellation_status(kmp_cancel_parallel)) { |
305 | perform_cleanup(); |
306 | #pragma omp cancellation point parallel |
307 | } |
308 | */ |
309 | int __kmp_get_cancellation_status(int cancel_kind) { |
310 | if (__kmp_omp_cancellation) { |
311 | kmp_info_t *this_thr = __kmp_entry_thread(); |
312 | |
313 | switch (cancel_kind) { |
314 | case cancel_parallel: |
315 | case cancel_loop: |
316 | case cancel_sections: { |
317 | kmp_team_t *this_team = this_thr->th.th_team; |
318 | return this_team->t.t_cancel_request == cancel_kind; |
319 | } |
320 | case cancel_taskgroup: { |
321 | kmp_taskdata_t *task; |
322 | kmp_taskgroup_t *taskgroup; |
323 | task = this_thr->th.th_current_task; |
324 | taskgroup = task->td_taskgroup; |
325 | return taskgroup && taskgroup->cancel_request; |
326 | } |
327 | } |
328 | } |
329 | |
330 | return 0 /* false */; |
331 | } |
332 | |