| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "kmp.h" | |
| #include "kmp_i18n.h" | |
| #include "kmp_io.h" | |
| #include "kmp_str.h" | |
| #if OMPT_SUPPORT | |
| #include "ompt-specific.h" | |
| #endif | |
| /*! | |
| @ingroup CANCELLATION | |
| @param loc_ref location of the original task directive | |
| @param gtid Global thread ID of encountering thread | |
| @param cncl_kind Cancellation kind (parallel, for, sections, taskgroup) | |
| @return returns true if the cancellation request has been activated and the | |
| execution thread needs to proceed to the end of the canceled region. | |
| Request cancellation of the binding OpenMP region. | |
| */ | |
| kmp_int32 __kmpc_cancel(ident_t *loc_ref, kmp_int32 gtid, kmp_int32 cncl_kind) { | |
| kmp_info_t *this_thr = __kmp_threads[gtid]; | |
| KC_TRACE(10, ("__kmpc_cancel: T#%d request %d OMP_CANCELLATION=%d\n", gtid, | |
| cncl_kind, __kmp_omp_cancellation)); | |
| KMP_DEBUG_ASSERT(cncl_kind != cancel_noreq); | |
| KMP_DEBUG_ASSERT(cncl_kind == cancel_parallel || cncl_kind == cancel_loop || | |
| cncl_kind == cancel_sections || | |
| cncl_kind == cancel_taskgroup); | |
| KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid); | |
| if (__kmp_omp_cancellation) { | |
| switch (cncl_kind) { | |
| case cancel_parallel: | |
| case cancel_loop: | |
| case cancel_sections: | |
| // cancellation requests for parallel and worksharing constructs | |
| // are handled through the team structure | |
| { | |
| kmp_team_t *this_team = this_thr->th.th_team; | |
| KMP_DEBUG_ASSERT(this_team); | |
| kmp_int32 old = cancel_noreq; | |
| this_team->t.t_cancel_request.compare_exchange_strong(old, cncl_kind); | |
| if (old == cancel_noreq || old == cncl_kind) { | |
| // we do not have a cancellation request in this team or we do have | |
| // one that matches the current request -> cancel | |
| #if OMPT_SUPPORT && OMPT_OPTIONAL | |
| if (ompt_enabled.ompt_callback_cancel) { | |
| ompt_data_t *task_data; | |
| __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL, | |
| NULL); | |
| ompt_cancel_flag_t type = ompt_cancel_parallel; | |
| if (cncl_kind == cancel_parallel) | |
| type = ompt_cancel_parallel; | |
| else if (cncl_kind == cancel_loop) | |
| type = ompt_cancel_loop; | |
| else if (cncl_kind == cancel_sections) | |
| type = ompt_cancel_sections; | |
| ompt_callbacks.ompt_callback(ompt_callback_cancel)( | |
| task_data, type | ompt_cancel_activated, | |
| OMPT_GET_RETURN_ADDRESS(0)); | |
| } | |
| #endif // OMPT_SUPPORT && OMPT_OPTIONAL | |
| return 1 /* true */; | |
| } | |
| break; | |
| } | |
| case cancel_taskgroup: | |
| // cancellation requests for a task group | |
| // are handled through the taskgroup structure | |
| { | |
| kmp_taskdata_t *task; | |
| kmp_taskgroup_t *taskgroup; | |
| task = this_thr->th.th_current_task; | |
| KMP_DEBUG_ASSERT(task); | |
| taskgroup = task->td_taskgroup; | |
| if (taskgroup) { | |
| kmp_int32 old = cancel_noreq; | |
| taskgroup->cancel_request.compare_exchange_strong(old, cncl_kind); | |
| if (old == cancel_noreq || old == cncl_kind) { | |
| // we do not have a cancellation request in this taskgroup or we do | |
| // have one that matches the current request -> cancel | |
| #if OMPT_SUPPORT && OMPT_OPTIONAL | |
| if (ompt_enabled.ompt_callback_cancel) { | |
| ompt_data_t *task_data; | |
| __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL, | |
| NULL); | |
| ompt_callbacks.ompt_callback(ompt_callback_cancel)( | |
| task_data, ompt_cancel_taskgroup | ompt_cancel_activated, | |
| OMPT_GET_RETURN_ADDRESS(0)); | |
| } | |
| #endif | |
| return 1 /* true */; | |
| } | |
| } else { | |
| // TODO: what needs to happen here? | |
| // the specification disallows cancellation w/o taskgroups | |
| // so we might do anything here, let's abort for now | |
| KMP_ASSERT(0 /* false */); | |
| } | |
| } | |
| break; | |
| default: | |
| KMP_ASSERT(0 /* false */); | |
| } | |
| } | |
| // ICV OMP_CANCELLATION=false, so we ignored this cancel request | |
| KMP_DEBUG_ASSERT(!__kmp_omp_cancellation); | |
| return 0 /* false */; | |
| } | |
| /*! | |
| @ingroup CANCELLATION | |
| @param loc_ref location of the original task directive | |
| @param gtid Global thread ID of encountering thread | |
| @param cncl_kind Cancellation kind (parallel, for, sections, taskgroup) | |
| @return returns true if a matching cancellation request has been flagged in the | |
| RTL and the encountering thread has to cancel.. | |
| Cancellation point for the encountering thread. | |
| */ | |
| kmp_int32 __kmpc_cancellationpoint(ident_t *loc_ref, kmp_int32 gtid, | |
| kmp_int32 cncl_kind) { | |
| kmp_info_t *this_thr = __kmp_threads[gtid]; | |
| KC_TRACE(10, | |
| ("__kmpc_cancellationpoint: T#%d request %d OMP_CANCELLATION=%d\n", | |
| gtid, cncl_kind, __kmp_omp_cancellation)); | |
| KMP_DEBUG_ASSERT(cncl_kind != cancel_noreq); | |
| KMP_DEBUG_ASSERT(cncl_kind == cancel_parallel || cncl_kind == cancel_loop || | |
| cncl_kind == cancel_sections || | |
| cncl_kind == cancel_taskgroup); | |
| KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid); | |
| if (__kmp_omp_cancellation) { | |
| switch (cncl_kind) { | |
| case cancel_parallel: | |
| case cancel_loop: | |
| case cancel_sections: | |
| // cancellation requests for parallel and worksharing constructs | |
| // are handled through the team structure | |
| { | |
| kmp_team_t *this_team = this_thr->th.th_team; | |
| KMP_DEBUG_ASSERT(this_team); | |
| if (this_team->t.t_cancel_request) { | |
| if (cncl_kind == this_team->t.t_cancel_request) { | |
| // the request in the team structure matches the type of | |
| // cancellation point so we can cancel | |
| #if OMPT_SUPPORT && OMPT_OPTIONAL | |
| if (ompt_enabled.ompt_callback_cancel) { | |
| ompt_data_t *task_data; | |
| __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL, | |
| NULL); | |
| ompt_cancel_flag_t type = ompt_cancel_parallel; | |
| if (cncl_kind == cancel_parallel) | |
| type = ompt_cancel_parallel; | |
| else if (cncl_kind == cancel_loop) | |
| type = ompt_cancel_loop; | |
| else if (cncl_kind == cancel_sections) | |
| type = ompt_cancel_sections; | |
| ompt_callbacks.ompt_callback(ompt_callback_cancel)( | |
| task_data, type | ompt_cancel_detected, | |
| OMPT_GET_RETURN_ADDRESS(0)); | |
| } | |
| #endif | |
| return 1 /* true */; | |
| } | |
| KMP_ASSERT(0 /* false */); | |
| } else { | |
| // we do not have a cancellation request pending, so we just | |
| // ignore this cancellation point | |
| return 0; | |
| } | |
| break; | |
| } | |
| case cancel_taskgroup: | |
| // cancellation requests for a task group | |
| // are handled through the taskgroup structure | |
| { | |
| kmp_taskdata_t *task; | |
| kmp_taskgroup_t *taskgroup; | |
| task = this_thr->th.th_current_task; | |
| KMP_DEBUG_ASSERT(task); | |
| taskgroup = task->td_taskgroup; | |
| if (taskgroup) { | |
| // return the current status of cancellation for the taskgroup | |
| #if OMPT_SUPPORT && OMPT_OPTIONAL | |
| if (ompt_enabled.ompt_callback_cancel && | |
| !!taskgroup->cancel_request) { | |
| ompt_data_t *task_data; | |
| __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL, | |
| NULL); | |
| ompt_callbacks.ompt_callback(ompt_callback_cancel)( | |
| task_data, ompt_cancel_taskgroup | ompt_cancel_detected, | |
| OMPT_GET_RETURN_ADDRESS(0)); | |
| } | |
| #endif | |
| return !!taskgroup->cancel_request; | |
| } else { | |
| // if a cancellation point is encountered by a task that does not | |
| // belong to a taskgroup, it is OK to ignore it | |
| return 0 /* false */; | |
| } | |
| } | |
| default: | |
| KMP_ASSERT(0 /* false */); | |
| } | |
| } | |
| // ICV OMP_CANCELLATION=false, so we ignore the cancellation point | |
| KMP_DEBUG_ASSERT(!__kmp_omp_cancellation); | |
| return 0 /* false */; | |
| } | |
| /*! | |
| @ingroup CANCELLATION | |
| @param loc_ref location of the original task directive | |
| @param gtid Global thread ID of encountering thread | |
| @return returns true if a matching cancellation request has been flagged in the | |
| RTL and the encountering thread has to cancel.. | |
| Barrier with cancellation point to send threads from the barrier to the | |
| end of the parallel region. Needs a special code pattern as documented | |
| in the design document for the cancellation feature. | |
| */ | |
| kmp_int32 __kmpc_cancel_barrier(ident_t *loc, kmp_int32 gtid) { | |
| int ret = 0 /* false */; | |
| kmp_info_t *this_thr = __kmp_threads[gtid]; | |
| kmp_team_t *this_team = this_thr->th.th_team; | |
| KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid); | |
| // call into the standard barrier | |
| __kmpc_barrier(loc, gtid); | |
| // if cancellation is active, check cancellation flag | |
| if (__kmp_omp_cancellation) { | |
| // depending on which construct to cancel, check the flag and | |
| // reset the flag | |
| switch (KMP_ATOMIC_LD_RLX(&(this_team->t.t_cancel_request))) { | |
| case cancel_parallel: | |
| ret = 1; | |
| // ensure that threads have checked the flag, when | |
| // leaving the above barrier | |
| __kmpc_barrier(loc, gtid); | |
| this_team->t.t_cancel_request = cancel_noreq; | |
| // the next barrier is the fork/join barrier, which | |
| // synchronizes the threads leaving here | |
| break; | |
| case cancel_loop: | |
| case cancel_sections: | |
| ret = 1; | |
| // ensure that threads have checked the flag, when | |
| // leaving the above barrier | |
| __kmpc_barrier(loc, gtid); | |
| this_team->t.t_cancel_request = cancel_noreq; | |
| // synchronize the threads again to make sure we do not have any run-away | |
| // threads that cause a race on the cancellation flag | |
| __kmpc_barrier(loc, gtid); | |
| break; | |
| case cancel_taskgroup: | |
| // this case should not occur | |
| KMP_ASSERT(0 /* false */); | |
| break; | |
| case cancel_noreq: | |
| // do nothing | |
| break; | |
| default: | |
| KMP_ASSERT(0 /* false */); | |
| } | |
| } | |
| return ret; | |
| } | |
| /*! | |
| @ingroup CANCELLATION | |
| @param loc_ref location of the original task directive | |
| @param gtid Global thread ID of encountering thread | |
| @return returns true if a matching cancellation request has been flagged in the | |
| RTL and the encountering thread has to cancel.. | |
| Query function to query the current status of cancellation requests. | |
| Can be used to implement the following pattern: | |
| if (kmp_get_cancellation_status(kmp_cancel_parallel)) { | |
| perform_cleanup(); | |
| #pragma omp cancellation point parallel | |
| } | |
| */ | |
| int __kmp_get_cancellation_status(int cancel_kind) { | |
| if (__kmp_omp_cancellation) { | |
| kmp_info_t *this_thr = __kmp_entry_thread(); | |
| switch (cancel_kind) { | |
| case cancel_parallel: | |
| case cancel_loop: | |
| case cancel_sections: { | |
| kmp_team_t *this_team = this_thr->th.th_team; | |
| return this_team->t.t_cancel_request == cancel_kind; | |
| } | |
| case cancel_taskgroup: { | |
| kmp_taskdata_t *task; | |
| kmp_taskgroup_t *taskgroup; | |
| task = this_thr->th.th_current_task; | |
| taskgroup = task->td_taskgroup; | |
| return taskgroup && taskgroup->cancel_request; | |
| } | |
| } | |
| } | |
| return 0 /* false */; | |
| } |