| |
| //===----------------------------------------------------------------------===// |
| // |
| // 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 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 = KMP_COMPARE_AND_STORE_RET32(&(this_team->t.t_cancel_request), cancel_noreq, cncl_kind); |
| if (old == cancel_noreq || old == cncl_kind) { |
| //printf("__kmpc_cancel: this_team->t.t_cancel_request=%d @ %p\n", |
| // this_team->t.t_cancel_request, &(this_team->t.t_cancel_request)); |
| // we do not have a cancellation request in this team or we do have one |
| // that matches the current request -> cancel |
| 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 = KMP_COMPARE_AND_STORE_RET32(&(taskgroup->cancel_request), cancel_noreq, 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 |
| 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 |
| 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 |
| 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 (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 |