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