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