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