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