xref: /freebsd/contrib/llvm-project/openmp/runtime/src/kmp_cancel.cpp (revision 7877fdebeeb35fad1cbbafce22598b1bdf97c786)
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(old, 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(0, NULL, &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(old, 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(0, NULL, &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(0, NULL, &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(0, NULL, &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, 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, 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, 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, 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