[OpenMP] Adding GOMP compatible cancellation

Remove fatal error messages from the cancellation API for GOMP
Add __kmp_barrier_gomp_cancel() to implement cancellation of parallel regions.
This new function uses the linear barrier algorithm with a cancellable
nonsleepable wait loop.

Differential Revision: https://reviews.llvm.org/D57969

llvm-svn: 354367
This commit is contained in:
Jonathan Peyton 2019-02-19 18:47:57 +00:00
parent 6bde702ac9
commit 4fe5271fa0
5 changed files with 248 additions and 115 deletions

View File

@ -3560,6 +3560,7 @@ extern int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
size_t reduce_size, void *reduce_data,
void (*reduce)(void *, void *));
extern void __kmp_end_split_barrier(enum barrier_type bt, int gtid);
extern int __kmp_barrier_gomp_cancel(int gtid);
/*!
* Tell the fork call which compiler generated the fork call, and therefore how

View File

@ -44,7 +44,8 @@ void __kmp_print_structure(void); // Forward declaration
// ---------------------------- Barrier Algorithms ----------------------------
// Linear Barrier
static void __kmp_linear_barrier_gather(
template <bool cancellable = false>
static bool __kmp_linear_barrier_gather_template(
enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
void (*reduce)(void *, void *) USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(KMP_linear_gather);
@ -104,7 +105,14 @@ static void __kmp_linear_barrier_gather(
// Wait for worker thread to arrive
kmp_flag_64 flag(&other_threads[i]->th.th_bar[bt].bb.b_arrived,
new_state);
if (cancellable) {
bool cancelled = flag.wait_cancellable_nosleep(
this_thr, FALSE USE_ITT_BUILD_ARG(itt_sync_obj));
if (cancelled)
return true;
} else {
flag.wait(this_thr, FALSE USE_ITT_BUILD_ARG(itt_sync_obj));
}
ANNOTATE_BARRIER_END(other_threads[i]);
#if USE_ITT_BUILD && USE_ITT_NOTIFY
// Barrier imbalance - write min of the thread time and the other thread
@ -137,9 +145,11 @@ static void __kmp_linear_barrier_gather(
20,
("__kmp_linear_barrier_gather: T#%d(%d:%d) exit for barrier type %d\n",
gtid, team->t.t_id, tid, bt));
return false;
}
static void __kmp_linear_barrier_release(
template <bool cancellable = false>
static bool __kmp_linear_barrier_release_template(
enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
int propagate_icvs USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(KMP_linear_release);
@ -201,7 +211,15 @@ static void __kmp_linear_barrier_release(
KA_TRACE(20, ("__kmp_linear_barrier_release: T#%d wait go(%p) == %u\n",
gtid, &thr_bar->b_go, KMP_BARRIER_STATE_BUMP));
kmp_flag_64 flag(&thr_bar->b_go, KMP_BARRIER_STATE_BUMP);
if (cancellable) {
bool cancelled = flag.wait_cancellable_nosleep(
this_thr, TRUE USE_ITT_BUILD_ARG(itt_sync_obj));
if (cancelled) {
return true;
}
} else {
flag.wait(this_thr, TRUE USE_ITT_BUILD_ARG(itt_sync_obj));
}
ANNOTATE_BARRIER_END(this_thr);
#if USE_ITT_BUILD && USE_ITT_NOTIFY
if ((__itt_sync_create_ptr && itt_sync_obj == NULL) || KMP_ITT_DEBUG) {
@ -212,7 +230,7 @@ static void __kmp_linear_barrier_release(
__kmp_itt_task_starting(itt_sync_obj);
if (bt == bs_forkjoin_barrier && TCR_4(__kmp_global.g.g_done))
return;
return false;
itt_sync_obj = __kmp_itt_barrier_object(gtid, bs_forkjoin_barrier);
if (itt_sync_obj != NULL)
@ -222,7 +240,7 @@ static void __kmp_linear_barrier_release(
#endif /* USE_ITT_BUILD && USE_ITT_NOTIFY */
// Early exit for reaping threads releasing forkjoin barrier
if (bt == bs_forkjoin_barrier && TCR_4(__kmp_global.g.g_done))
return;
return false;
// The worker thread may now assume that the team is valid.
#ifdef KMP_DEBUG
tid = __kmp_tid_from_gtid(gtid);
@ -239,6 +257,35 @@ static void __kmp_linear_barrier_release(
20,
("__kmp_linear_barrier_release: T#%d(%d:%d) exit for barrier type %d\n",
gtid, team->t.t_id, tid, bt));
return false;
}
static void __kmp_linear_barrier_gather(
enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
void (*reduce)(void *, void *) USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
__kmp_linear_barrier_gather_template<false>(
bt, this_thr, gtid, tid, reduce USE_ITT_BUILD_ARG(itt_sync_obj));
}
static bool __kmp_linear_barrier_gather_cancellable(
enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
void (*reduce)(void *, void *) USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
return __kmp_linear_barrier_gather_template<true>(
bt, this_thr, gtid, tid, reduce USE_ITT_BUILD_ARG(itt_sync_obj));
}
static void __kmp_linear_barrier_release(
enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
int propagate_icvs USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
__kmp_linear_barrier_release_template<false>(
bt, this_thr, gtid, tid, propagate_icvs USE_ITT_BUILD_ARG(itt_sync_obj));
}
static bool __kmp_linear_barrier_release_cancellable(
enum barrier_type bt, kmp_info_t *this_thr, int gtid, int tid,
int propagate_icvs USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
return __kmp_linear_barrier_release_template<true>(
bt, this_thr, gtid, tid, propagate_icvs USE_ITT_BUILD_ARG(itt_sync_obj));
}
// Tree barrier
@ -1208,12 +1255,35 @@ static void __kmp_hierarchical_barrier_release(
// End of Barrier Algorithms
// type traits for cancellable value
// if cancellable is true, then is_cancellable is a normal boolean variable
// if cancellable is false, then is_cancellable is a compile time constant
template <bool cancellable> struct is_cancellable {};
template <> struct is_cancellable<true> {
bool value;
is_cancellable() : value(false) {}
is_cancellable(bool b) : value(b) {}
is_cancellable &operator=(bool b) {
value = b;
return *this;
}
operator bool() const { return value; }
};
template <> struct is_cancellable<false> {
is_cancellable &operator=(bool b) { return *this; }
constexpr operator bool() const { return false; }
};
// Internal function to do a barrier.
/* If is_split is true, do a split barrier, otherwise, do a plain barrier
If reduce is non-NULL, do a split reduction barrier, otherwise, do a split
barrier
Returns 0 if master thread, 1 if worker thread. */
int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
When cancellable = false,
Returns 0 if master thread, 1 if worker thread.
When cancellable = true
Returns 0 if not cancelled, 1 if cancelled. */
template <bool cancellable = false>
static int __kmp_barrier_template(enum barrier_type bt, int gtid, int is_split,
size_t reduce_size, void *reduce_data,
void (*reduce)(void *, void *)) {
KMP_TIME_PARTITIONED_BLOCK(OMP_plain_barrier);
@ -1222,6 +1292,7 @@ int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
kmp_info_t *this_thr = __kmp_threads[gtid];
kmp_team_t *team = this_thr->th.th_team;
int status = 0;
is_cancellable<cancellable> cancelled;
#if OMPT_SUPPORT && OMPT_OPTIONAL
ompt_data_t *my_task_data;
ompt_data_t *my_parallel_data;
@ -1305,26 +1376,29 @@ int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
}
if (KMP_MASTER_TID(tid) && __kmp_tasking_mode != tskm_immediate_exec)
__kmp_task_team_setup(
this_thr, team,
0); // use 0 to only setup the current team if nthreads > 1
// use 0 to only setup the current team if nthreads > 1
__kmp_task_team_setup(this_thr, team, 0);
if (cancellable) {
cancelled = __kmp_linear_barrier_gather_cancellable(
bt, this_thr, gtid, tid, reduce USE_ITT_BUILD_ARG(itt_sync_obj));
} else {
switch (__kmp_barrier_gather_pattern[bt]) {
case bp_hyper_bar: {
KMP_ASSERT(__kmp_barrier_gather_branch_bits[bt]); // don't set branch bits
// to 0; use linear
// don't set branch bits to 0; use linear
KMP_ASSERT(__kmp_barrier_gather_branch_bits[bt]);
__kmp_hyper_barrier_gather(bt, this_thr, gtid, tid,
reduce USE_ITT_BUILD_ARG(itt_sync_obj));
break;
}
case bp_hierarchical_bar: {
__kmp_hierarchical_barrier_gather(bt, this_thr, gtid, tid,
reduce USE_ITT_BUILD_ARG(itt_sync_obj));
__kmp_hierarchical_barrier_gather(
bt, this_thr, gtid, tid, reduce USE_ITT_BUILD_ARG(itt_sync_obj));
break;
}
case bp_tree_bar: {
KMP_ASSERT(__kmp_barrier_gather_branch_bits[bt]); // don't set branch bits
// to 0; use linear
// don't set branch bits to 0; use linear
KMP_ASSERT(__kmp_barrier_gather_branch_bits[bt]);
__kmp_tree_barrier_gather(bt, this_thr, gtid, tid,
reduce USE_ITT_BUILD_ARG(itt_sync_obj));
break;
@ -1334,12 +1408,13 @@ int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
reduce USE_ITT_BUILD_ARG(itt_sync_obj));
}
}
}
KMP_MB();
if (KMP_MASTER_TID(tid)) {
status = 0;
if (__kmp_tasking_mode != tskm_immediate_exec) {
if (__kmp_tasking_mode != tskm_immediate_exec && !cancelled) {
__kmp_task_team_wait(this_thr, team USE_ITT_BUILD_ARG(itt_sync_obj));
}
#if USE_DEBUGGER
@ -1349,11 +1424,14 @@ int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
#endif
#if OMP_40_ENABLED
if (__kmp_omp_cancellation) {
kmp_int32 cancel_request = KMP_ATOMIC_LD_RLX(&team->t.t_cancel_request);
// Reset cancellation flag for worksharing constructs
if (cancel_request == cancel_loop || cancel_request == cancel_sections) {
if (cancel_request == cancel_loop ||
cancel_request == cancel_sections) {
KMP_ATOMIC_ST_RLX(&team->t.t_cancel_request, cancel_noreq);
}
}
#endif
#if USE_ITT_BUILD
/* TODO: In case of split reduction barrier, master thread may send
@ -1416,7 +1494,11 @@ int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
__kmp_itt_barrier_middle(gtid, itt_sync_obj);
#endif /* USE_ITT_BUILD */
}
if (status == 1 || !is_split) {
if ((status == 1 || !is_split) && !cancelled) {
if (cancellable) {
cancelled = __kmp_linear_barrier_release_cancellable(
bt, this_thr, gtid, tid, FALSE USE_ITT_BUILD_ARG(itt_sync_obj));
} else {
switch (__kmp_barrier_release_pattern[bt]) {
case bp_hyper_bar: {
KMP_ASSERT(__kmp_barrier_release_branch_bits[bt]);
@ -1440,7 +1522,8 @@ int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
FALSE USE_ITT_BUILD_ARG(itt_sync_obj));
}
}
if (__kmp_tasking_mode != tskm_immediate_exec) {
}
if (__kmp_tasking_mode != tskm_immediate_exec && !cancelled) {
__kmp_task_team_sync(this_thr, team);
}
}
@ -1506,9 +1589,43 @@ int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
#endif
ANNOTATE_BARRIER_END(&team->t.t_bar);
if (cancellable)
return (int)cancelled;
return status;
}
// Returns 0 if master thread, 1 if worker thread.
int __kmp_barrier(enum barrier_type bt, int gtid, int is_split,
size_t reduce_size, void *reduce_data,
void (*reduce)(void *, void *)) {
return __kmp_barrier_template<>(bt, gtid, is_split, reduce_size, reduce_data,
reduce);
}
#if defined(KMP_GOMP_COMPAT)
// Returns 1 if cancelled, 0 otherwise
int __kmp_barrier_gomp_cancel(int gtid) {
if (__kmp_omp_cancellation) {
int cancelled = __kmp_barrier_template<true>(bs_plain_barrier, gtid, FALSE,
0, NULL, NULL);
if (cancelled) {
int tid = __kmp_tid_from_gtid(gtid);
kmp_info_t *this_thr = __kmp_threads[gtid];
if (KMP_MASTER_TID(tid)) {
// Master does not need to revert anything
} else {
// Workers need to revert their private b_arrived flag
this_thr->th.th_bar[bs_plain_barrier].bb.b_arrived -=
KMP_BARRIER_STATE_BUMP;
}
}
return cancelled;
}
__kmp_barrier(bs_plain_barrier, gtid, FALSE, 0, NULL, NULL);
return FALSE;
}
#endif
void __kmp_end_split_barrier(enum barrier_type bt, int gtid) {
KMP_TIME_DEVELOPER_PARTITIONED_BLOCK(KMP_end_split_barrier);
KMP_SET_THREAD_STATE_BLOCK(PLAIN_BARRIER);

View File

@ -626,8 +626,6 @@
#define KMP_API_NAME_GOMP_TASKYIELD GOMP_taskyield
// All GOMP_4.0 symbols
// TODO: As of 2013-10-14, none of the GOMP_4.0 functions are implemented in
// libomp
#define KMP_API_NAME_GOMP_BARRIER_CANCEL GOMP_barrier_cancel
#define KMP_API_NAME_GOMP_CANCEL GOMP_cancel
#define KMP_API_NAME_GOMP_CANCELLATION_POINT GOMP_cancellation_point

View File

@ -1544,11 +1544,7 @@ void KMP_EXPAND_NAME(KMP_API_NAME_GOMP_TASKGROUP_END)(void) {
return;
}
#ifndef KMP_DEBUG
static
#endif /* KMP_DEBUG */
kmp_int32
__kmp_gomp_to_omp_cancellation_kind(int gomp_kind) {
static kmp_int32 __kmp_gomp_to_omp_cancellation_kind(int gomp_kind) {
kmp_int32 cncl_kind = 0;
switch (gomp_kind) {
case 1:
@ -1567,71 +1563,49 @@ static
return cncl_kind;
}
// Return true if cancellation should take place, false otherwise
bool KMP_EXPAND_NAME(KMP_API_NAME_GOMP_CANCELLATION_POINT)(int which) {
if (__kmp_omp_cancellation) {
KMP_FATAL(NoGompCancellation);
}
int gtid = __kmp_get_gtid();
MKLOC(loc, "GOMP_cancellation_point");
KA_TRACE(20, ("GOMP_cancellation_point: T#%d\n", gtid));
KA_TRACE(20, ("GOMP_cancellation_point: T#%d which:%d\n", gtid, which));
kmp_int32 cncl_kind = __kmp_gomp_to_omp_cancellation_kind(which);
return __kmpc_cancellationpoint(&loc, gtid, cncl_kind);
}
bool KMP_EXPAND_NAME(KMP_API_NAME_GOMP_BARRIER_CANCEL)(void) {
if (__kmp_omp_cancellation) {
KMP_FATAL(NoGompCancellation);
}
KMP_FATAL(NoGompCancellation);
int gtid = __kmp_get_gtid();
MKLOC(loc, "GOMP_barrier_cancel");
KA_TRACE(20, ("GOMP_barrier_cancel: T#%d\n", gtid));
return __kmpc_cancel_barrier(&loc, gtid);
}
// Return true if cancellation should take place, false otherwise
bool KMP_EXPAND_NAME(KMP_API_NAME_GOMP_CANCEL)(int which, bool do_cancel) {
if (__kmp_omp_cancellation) {
KMP_FATAL(NoGompCancellation);
} else {
return FALSE;
}
int gtid = __kmp_get_gtid();
MKLOC(loc, "GOMP_cancel");
KA_TRACE(20, ("GOMP_cancel: T#%d\n", gtid));
KA_TRACE(20, ("GOMP_cancel: T#%d which:%d do_cancel:%d\n", gtid, which,
(int)do_cancel));
kmp_int32 cncl_kind = __kmp_gomp_to_omp_cancellation_kind(which);
if (do_cancel == FALSE) {
return KMP_EXPAND_NAME(KMP_API_NAME_GOMP_CANCELLATION_POINT)(which);
return __kmpc_cancellationpoint(&loc, gtid, cncl_kind);
} else {
return __kmpc_cancel(&loc, gtid, cncl_kind);
}
}
// Return true if cancellation should take place, false otherwise
bool KMP_EXPAND_NAME(KMP_API_NAME_GOMP_BARRIER_CANCEL)(void) {
int gtid = __kmp_get_gtid();
KA_TRACE(20, ("GOMP_barrier_cancel: T#%d\n", gtid));
return __kmp_barrier_gomp_cancel(gtid);
}
// Return true if cancellation should take place, false otherwise
bool KMP_EXPAND_NAME(KMP_API_NAME_GOMP_SECTIONS_END_CANCEL)(void) {
if (__kmp_omp_cancellation) {
KMP_FATAL(NoGompCancellation);
}
int gtid = __kmp_get_gtid();
MKLOC(loc, "GOMP_sections_end_cancel");
KA_TRACE(20, ("GOMP_sections_end_cancel: T#%d\n", gtid));
return __kmpc_cancel_barrier(&loc, gtid);
return __kmp_barrier_gomp_cancel(gtid);
}
// Return true if cancellation should take place, false otherwise
bool KMP_EXPAND_NAME(KMP_API_NAME_GOMP_LOOP_END_CANCEL)(void) {
if (__kmp_omp_cancellation) {
KMP_FATAL(NoGompCancellation);
}
int gtid = __kmp_get_gtid();
MKLOC(loc, "GOMP_loop_end_cancel");
KA_TRACE(20, ("GOMP_loop_end_cancel: T#%d\n", gtid));
return __kmpc_cancel_barrier(&loc, gtid);
return __kmp_barrier_gomp_cancel(gtid);
}
// All target functions are empty as of 2014-05-29

View File

@ -155,8 +155,9 @@ static void __ompt_implicit_task_end(kmp_info_t *this_thr,
to wake it back up to prevent deadlocks!
NOTE: We may not belong to a team at this point. */
template <class C, int final_spin>
static inline void
template <class C, int final_spin, bool cancellable = false,
bool sleepable = true>
static inline bool
__kmp_wait_template(kmp_info_t *this_thr,
C *flag USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
#if USE_ITT_BUILD && USE_ITT_NOTIFY
@ -176,9 +177,14 @@ __kmp_wait_template(kmp_info_t *this_thr,
KMP_FSYNC_SPIN_INIT(spin, NULL);
if (flag->done_check()) {
KMP_FSYNC_SPIN_ACQUIRED(CCAST(void *, spin));
return;
return false;
}
th_gtid = this_thr->th.th_info.ds.ds_gtid;
if (cancellable) {
kmp_team_t *team = this_thr->th.th_team;
if (team && team->t.t_cancel_request == cancel_parallel)
return true;
}
#if KMP_OS_UNIX
if (final_spin)
KMP_ATOMIC_ST_REL(&this_thr->th.th_blocking, true);
@ -400,6 +406,12 @@ final_spin=FALSE)
KMP_PUSH_PARTITIONED_TIMER(OMP_idle);
}
#endif
// Check if the barrier surrounding this wait loop has been cancelled
if (cancellable) {
kmp_team_t *team = this_thr->th.th_team;
if (team && team->t.t_cancel_request == cancel_parallel)
break;
}
// Don't suspend if KMP_BLOCKTIME is set to "infinite"
if (__kmp_dflt_blocktime == KMP_MAX_BLOCKTIME
@ -421,6 +433,10 @@ final_spin=FALSE)
if (KMP_BLOCKING(hibernate_goal, poll_count++))
continue;
#endif
// Don't suspend if wait loop designated non-sleepable
// in template parameters
if (!sleepable)
continue;
#if OMP_50_ENABLED
if (__kmp_dflt_blocktime == KMP_MAX_BLOCKTIME &&
@ -479,6 +495,21 @@ final_spin=FALSE)
KMP_ATOMIC_ST_REL(&this_thr->th.th_blocking, false);
#endif
KMP_FSYNC_SPIN_ACQUIRED(CCAST(void *, spin));
if (cancellable) {
kmp_team_t *team = this_thr->th.th_team;
if (team && team->t.t_cancel_request == cancel_parallel) {
if (tasks_completed) {
// undo the previous decrement of unfinished_threads so that the
// thread can decrement at the join barrier with no problem
kmp_task_team_t *task_team = this_thr->th.th_task_team;
std::atomic<kmp_int32> *unfinished_threads =
&(task_team->tt.tt_unfinished_threads);
KMP_ATOMIC_INC(unfinished_threads);
}
return true;
}
}
return false;
}
/* Release any threads specified as waiting on the flag by releasing the flag
@ -796,6 +827,18 @@ public:
__kmp_wait_template<kmp_flag_64, FALSE>(
this_thr, this USE_ITT_BUILD_ARG(itt_sync_obj));
}
bool wait_cancellable_nosleep(kmp_info_t *this_thr,
int final_spin
USE_ITT_BUILD_ARG(void *itt_sync_obj)) {
bool retval = false;
if (final_spin)
retval = __kmp_wait_template<kmp_flag_64, TRUE, true, false>(
this_thr, this USE_ITT_BUILD_ARG(itt_sync_obj));
else
retval = __kmp_wait_template<kmp_flag_64, FALSE, true, false>(
this_thr, this USE_ITT_BUILD_ARG(itt_sync_obj));
return retval;
}
void release() { __kmp_release_template(this); }
flag_type get_ptr_type() { return flag64; }
};