1 
2 //===----------------------------------------------------------------------===//
3 //
4 //                     The LLVM Compiler Infrastructure
5 //
6 // This file is dual licensed under the MIT and the University of Illinois Open
7 // Source Licenses. See LICENSE.txt for details.
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #include "kmp.h"
12 #include "kmp_i18n.h"
13 #include "kmp_io.h"
14 #include "kmp_str.h"
15 
16 #if OMP_40_ENABLED
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 = KMP_COMPARE_AND_STORE_RET32(
52             &(this_team->t.t_cancel_request), cancel_noreq, cncl_kind);
53         if (old == cancel_noreq || old == cncl_kind) {
54           // printf("__kmpc_cancel: this_team->t.t_cancel_request=%d @ %p\n",
55           //       this_team->t.t_cancel_request,
56           //       &(this_team->t.t_cancel_request));
57           // we do not have a cancellation request in this team or we do have
58           // one that matches the current request -> cancel
59           return 1 /* true */;
60         }
61         break;
62       }
63     case cancel_taskgroup:
64       // cancellation requests for a task group
65       // are handled through the taskgroup structure
66       {
67         kmp_taskdata_t *task;
68         kmp_taskgroup_t *taskgroup;
69 
70         task = this_thr->th.th_current_task;
71         KMP_DEBUG_ASSERT(task);
72 
73         taskgroup = task->td_taskgroup;
74         if (taskgroup) {
75           kmp_int32 old = KMP_COMPARE_AND_STORE_RET32(
76               &(taskgroup->cancel_request), cancel_noreq, cncl_kind);
77           if (old == cancel_noreq || old == cncl_kind) {
78             // we do not have a cancellation request in this taskgroup or we do
79             // have one that matches the current request -> cancel
80             return 1 /* true */;
81           }
82         } else {
83           // TODO: what needs to happen here?
84           // the specification disallows cancellation w/o taskgroups
85           // so we might do anything here, let's abort for now
86           KMP_ASSERT(0 /* false */);
87         }
88       }
89       break;
90     default:
91       KMP_ASSERT(0 /* false */);
92     }
93   }
94 
95   // ICV OMP_CANCELLATION=false, so we ignored this cancel request
96   KMP_DEBUG_ASSERT(!__kmp_omp_cancellation);
97   return 0 /* false */;
98 }
99 
100 /*!
101 @ingroup CANCELLATION
102 @param loc_ref location of the original task directive
103 @param gtid Global thread ID of encountering thread
104 @param cncl_kind Cancellation kind (parallel, for, sections, taskgroup)
105 
106 @return returns true if a matching cancellation request has been flagged in the
107 RTL and the encountering thread has to cancel..
108 
109 Cancellation point for the encountering thread.
110 */
111 kmp_int32 __kmpc_cancellationpoint(ident_t *loc_ref, kmp_int32 gtid,
112                                    kmp_int32 cncl_kind) {
113   kmp_info_t *this_thr = __kmp_threads[gtid];
114 
115   KC_TRACE(10,
116            ("__kmpc_cancellationpoint: T#%d request %d OMP_CANCELLATION=%d\n",
117             gtid, cncl_kind, __kmp_omp_cancellation));
118 
119   KMP_DEBUG_ASSERT(cncl_kind != cancel_noreq);
120   KMP_DEBUG_ASSERT(cncl_kind == cancel_parallel || cncl_kind == cancel_loop ||
121                    cncl_kind == cancel_sections ||
122                    cncl_kind == cancel_taskgroup);
123   KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid);
124 
125   if (__kmp_omp_cancellation) {
126     switch (cncl_kind) {
127     case cancel_parallel:
128     case cancel_loop:
129     case cancel_sections:
130       // cancellation requests for parallel and worksharing constructs
131       // are handled through the team structure
132       {
133         kmp_team_t *this_team = this_thr->th.th_team;
134         KMP_DEBUG_ASSERT(this_team);
135         if (this_team->t.t_cancel_request) {
136           if (cncl_kind == this_team->t.t_cancel_request) {
137             // the request in the team structure matches the type of
138             // cancellation point so we can cancel
139             return 1 /* true */;
140           }
141           KMP_ASSERT(0 /* false */);
142         } else {
143           // we do not have a cancellation request pending, so we just
144           // ignore this cancellation point
145           return 0;
146         }
147         break;
148       }
149     case cancel_taskgroup:
150       // cancellation requests for a task group
151       // are handled through the taskgroup structure
152       {
153         kmp_taskdata_t *task;
154         kmp_taskgroup_t *taskgroup;
155 
156         task = this_thr->th.th_current_task;
157         KMP_DEBUG_ASSERT(task);
158 
159         taskgroup = task->td_taskgroup;
160         if (taskgroup) {
161           // return the current status of cancellation for the taskgroup
162           return !!taskgroup->cancel_request;
163         } else {
164           // if a cancellation point is encountered by a task that does not
165           // belong to a taskgroup, it is OK to ignore it
166           return 0 /* false */;
167         }
168       }
169     default:
170       KMP_ASSERT(0 /* false */);
171     }
172   }
173 
174   // ICV OMP_CANCELLATION=false, so we ignore the cancellation point
175   KMP_DEBUG_ASSERT(!__kmp_omp_cancellation);
176   return 0 /* false */;
177 }
178 
179 /*!
180 @ingroup CANCELLATION
181 @param loc_ref location of the original task directive
182 @param gtid Global thread ID of encountering thread
183 
184 @return returns true if a matching cancellation request has been flagged in the
185 RTL and the encountering thread has to cancel..
186 
187 Barrier with cancellation point to send threads from the barrier to the
188 end of the parallel region.  Needs a special code pattern as documented
189 in the design document for the cancellation feature.
190 */
191 kmp_int32 __kmpc_cancel_barrier(ident_t *loc, kmp_int32 gtid) {
192   int ret = 0 /* false */;
193   kmp_info_t *this_thr = __kmp_threads[gtid];
194   kmp_team_t *this_team = this_thr->th.th_team;
195 
196   KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid);
197 
198   // call into the standard barrier
199   __kmpc_barrier(loc, gtid);
200 
201   // if cancellation is active, check cancellation flag
202   if (__kmp_omp_cancellation) {
203     // depending on which construct to cancel, check the flag and
204     // reset the flag
205     switch (this_team->t.t_cancel_request) {
206     case cancel_parallel:
207       ret = 1;
208       // ensure that threads have checked the flag, when
209       // leaving the above barrier
210       __kmpc_barrier(loc, gtid);
211       this_team->t.t_cancel_request = cancel_noreq;
212       // the next barrier is the fork/join barrier, which
213       // synchronizes the threads leaving here
214       break;
215     case cancel_loop:
216     case cancel_sections:
217       ret = 1;
218       // ensure that threads have checked the flag, when
219       // leaving the above barrier
220       __kmpc_barrier(loc, gtid);
221       this_team->t.t_cancel_request = cancel_noreq;
222       // synchronize the threads again to make sure we do not have any run-away
223       // threads that cause a race on the cancellation flag
224       __kmpc_barrier(loc, gtid);
225       break;
226     case cancel_taskgroup:
227       // this case should not occur
228       KMP_ASSERT(0 /* false */);
229       break;
230     case cancel_noreq:
231       // do nothing
232       break;
233     default:
234       KMP_ASSERT(0 /* false */);
235     }
236   }
237 
238   return ret;
239 }
240 
241 /*!
242 @ingroup CANCELLATION
243 @param loc_ref location of the original task directive
244 @param gtid Global thread ID of encountering thread
245 
246 @return returns true if a matching cancellation request has been flagged in the
247 RTL and the encountering thread has to cancel..
248 
249 Query function to query the current status of cancellation requests.
250 Can be used to implement the following pattern:
251 
252 if (kmp_get_cancellation_status(kmp_cancel_parallel)) {
253     perform_cleanup();
254     #pragma omp cancellation point parallel
255 }
256 */
257 int __kmp_get_cancellation_status(int cancel_kind) {
258   if (__kmp_omp_cancellation) {
259     kmp_info_t *this_thr = __kmp_entry_thread();
260 
261     switch (cancel_kind) {
262     case cancel_parallel:
263     case cancel_loop:
264     case cancel_sections: {
265       kmp_team_t *this_team = this_thr->th.th_team;
266       return this_team->t.t_cancel_request == cancel_kind;
267     }
268     case cancel_taskgroup: {
269       kmp_taskdata_t *task;
270       kmp_taskgroup_t *taskgroup;
271       task = this_thr->th.th_current_task;
272       taskgroup = task->td_taskgroup;
273       return taskgroup && taskgroup->cancel_request;
274     }
275     }
276   }
277 
278   return 0 /* false */;
279 }
280 
281 #endif
282