diff options
Diffstat (limited to 'final/runtime/src/kmp_cancel.cpp')
-rw-r--r-- | final/runtime/src/kmp_cancel.cpp | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/final/runtime/src/kmp_cancel.cpp b/final/runtime/src/kmp_cancel.cpp new file mode 100644 index 0000000..3895f97 --- /dev/null +++ b/final/runtime/src/kmp_cancel.cpp @@ -0,0 +1,336 @@ + +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.txt for details. +// +//===----------------------------------------------------------------------===// + +#include "kmp.h" +#include "kmp_i18n.h" +#include "kmp_io.h" +#include "kmp_str.h" +#if OMPT_SUPPORT +#include "ompt-specific.h" +#endif + +#if OMP_40_ENABLED + +/*! +@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_do; + 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 + 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_do; + 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 */; +} + +#endif |