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
25execution thread needs to proceed to the end of the canceled region.
26
27Request cancellation of the binding OpenMP region.
28*/
29kmp_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
131RTL and the encountering thread has to cancel..
132
133Cancellation point for the encountering thread.
134*/
135kmp_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
237RTL and the encountering thread has to cancel..
238
239Barrier with cancellation point to send threads from the barrier to the
240end of the parallel region.  Needs a special code pattern as documented
241in the design document for the cancellation feature.
242*/
243kmp_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
299RTL and the encountering thread has to cancel..
300
301Query function to query the current status of cancellation requests.
302Can be used to implement the following pattern:
303
304if (kmp_get_cancellation_status(kmp_cancel_parallel)) {
305    perform_cleanup();
306    #pragma omp cancellation point parallel
307}
308*/
309int __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