mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
332 lines
11 KiB
C++
332 lines
11 KiB
C++
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
//
|
||
|
// 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 */;
|
||
|
}
|