__kmp_free_task: Fix for serial explicit tasks producing proxy tasks

Consider the following code which may be executed by a serial team:

    int dep;
    #pragma omp target nowait depend(out: dep)
    {
        sleep(1);
    }
    #pragma omp task depend(in: dep)
    {
        #pragma omp target nowait
        {
            sleep(1);
        }
    }

Here the explicit task may not be freed until the nested proxy task has
finished. The current code hasn't considered this and called __kmp_free_task
anyway which triggered an assert because of remaining incomplete children:

    KMP_DEBUG_ASSERT( TCR_4(taskdata->td_incomplete_child_tasks) == 0 );

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

llvm-svn: 277991
This commit is contained in:
Jonas Hahnfeld 2016-08-08 10:08:07 +00:00
parent cae9aeed39
commit 69f8511f8f
2 changed files with 138 additions and 14 deletions

View File

@ -576,15 +576,13 @@ __kmp_free_task( kmp_int32 gtid, kmp_taskdata_t * taskdata, kmp_info_t * thread
static void
__kmp_free_task_and_ancestors( kmp_int32 gtid, kmp_taskdata_t * taskdata, kmp_info_t * thread )
{
kmp_int32 children = 0;
kmp_int32 team_or_tasking_serialized = taskdata -> td_flags.team_serial || taskdata -> td_flags.tasking_ser;
// Proxy tasks must always be allowed to free their parents
// because they can be run in background even in serial mode.
kmp_int32 task_serial = taskdata->td_flags.task_serial && !taskdata->td_flags.proxy;
KMP_DEBUG_ASSERT( taskdata -> td_flags.tasktype == TASK_EXPLICIT );
if ( !team_or_tasking_serialized ) {
children = KMP_TEST_THEN_DEC32( (kmp_int32 *)(& taskdata -> td_allocated_child_tasks) ) - 1;
KMP_DEBUG_ASSERT( children >= 0 );
}
kmp_int32 children = KMP_TEST_THEN_DEC32( (kmp_int32 *)(& taskdata -> td_allocated_child_tasks) ) - 1;
KMP_DEBUG_ASSERT( children >= 0 );
// Now, go up the ancestor tree to see if any ancestors can now be freed.
while ( children == 0 )
@ -599,16 +597,14 @@ __kmp_free_task_and_ancestors( kmp_int32 gtid, kmp_taskdata_t * taskdata, kmp_in
taskdata = parent_taskdata;
// Stop checking ancestors at implicit task or if tasking serialized
// Stop checking ancestors at implicit task
// instead of walking up ancestor tree to avoid premature deallocation of ancestors.
if ( team_or_tasking_serialized || taskdata -> td_flags.tasktype == TASK_IMPLICIT )
if ( task_serial || taskdata -> td_flags.tasktype == TASK_IMPLICIT )
return;
if ( !team_or_tasking_serialized ) {
// Predecrement simulated by "- 1" calculation
children = KMP_TEST_THEN_DEC32( (kmp_int32 *)(& taskdata -> td_allocated_child_tasks) ) - 1;
KMP_DEBUG_ASSERT( children >= 0 );
}
// Predecrement simulated by "- 1" calculation
children = KMP_TEST_THEN_DEC32( (kmp_int32 *)(& taskdata -> td_allocated_child_tasks) ) - 1;
KMP_DEBUG_ASSERT( children >= 0 );
}
KA_TRACE(20, ("__kmp_free_task_and_ancestors(exit): T#%d task %p has %d children; "

View File

@ -0,0 +1,128 @@
// RUN: %libomp-compile -lpthread && %libomp-run
#include <stdio.h>
#include <omp.h>
#include <pthread.h>
#include "omp_my_sleep.h"
/*
With task dependencies one can generate proxy tasks from an explicit task
being executed by a serial task team. The OpenMP runtime library didn't
expect that and tries to free the explicit task that is the parent of the
proxy task still working in background. It therefore has incomplete children
which triggers a debugging assertion.
*/
// Compiler-generated code (emulation)
typedef long kmp_intptr_t;
typedef int kmp_int32;
typedef char bool;
typedef struct ident {
kmp_int32 reserved_1; /**< might be used in Fortran; see above */
kmp_int32 flags; /**< also f.flags; KMP_IDENT_xxx flags; KMP_IDENT_KMPC identifies this union member */
kmp_int32 reserved_2; /**< not really used in Fortran any more; see above */
#if USE_ITT_BUILD
/* but currently used for storing region-specific ITT */
/* contextual information. */
#endif /* USE_ITT_BUILD */
kmp_int32 reserved_3; /**< source[4] in Fortran, do not use for C++ */
char const *psource; /**< String describing the source location.
The string is composed of semi-colon separated fields which describe the source file,
the function and a pair of line numbers that delimit the construct.
*/
} ident_t;
typedef struct kmp_depend_info {
kmp_intptr_t base_addr;
size_t len;
struct {
bool in:1;
bool out:1;
} flags;
} kmp_depend_info_t;
struct kmp_task;
typedef kmp_int32 (* kmp_routine_entry_t)( kmp_int32, struct kmp_task * );
typedef struct kmp_task { /* GEH: Shouldn't this be aligned somehow? */
void * shareds; /**< pointer to block of pointers to shared vars */
kmp_routine_entry_t routine; /**< pointer to routine to call for executing task */
kmp_int32 part_id; /**< part id for the task */
} kmp_task_t;
#ifdef __cplusplus
extern "C" {
#endif
kmp_int32 __kmpc_global_thread_num ( ident_t * );
kmp_task_t*
__kmpc_omp_task_alloc( ident_t *loc_ref, kmp_int32 gtid, kmp_int32 flags,
size_t sizeof_kmp_task_t, size_t sizeof_shareds,
kmp_routine_entry_t task_entry );
void __kmpc_proxy_task_completed_ooo ( kmp_task_t *ptask );
kmp_int32 __kmpc_omp_task_with_deps ( ident_t *loc_ref, kmp_int32 gtid, kmp_task_t * new_task,
kmp_int32 ndeps, kmp_depend_info_t *dep_list,
kmp_int32 ndeps_noalias, kmp_depend_info_t *noalias_dep_list );
kmp_int32
__kmpc_omp_task( ident_t *loc_ref, kmp_int32 gtid, kmp_task_t * new_task );
#ifdef __cplusplus
}
#endif
void *target(void *task)
{
my_sleep( 0.1 );
__kmpc_proxy_task_completed_ooo((kmp_task_t*) task);
return NULL;
}
pthread_t target_thread;
// User's code
int task_entry(kmp_int32 gtid, kmp_task_t *task)
{
pthread_create(&target_thread, NULL, &target, task);
return 0;
}
int main()
{
int dep;
#pragma omp taskgroup
{
/*
* Corresponds to:
#pragma omp target nowait depend(out: dep)
{
my_sleep( 0.1 );
}
*/
kmp_depend_info_t dep_info;
dep_info.base_addr = (long) &dep;
dep_info.len = sizeof(int);
// out = inout per spec and runtime expects this
dep_info.flags.in = 1;
dep_info.flags.out = 1;
kmp_int32 gtid = __kmpc_global_thread_num(NULL);
kmp_task_t *proxy_task = __kmpc_omp_task_alloc(NULL,gtid,17,sizeof(kmp_task_t),0,&task_entry);
__kmpc_omp_task_with_deps(NULL,gtid,proxy_task,1,&dep_info,0,NULL);
#pragma omp task depend(in: dep)
{
/*
* Corresponds to:
#pragma omp target nowait depend(out: dep)
{
my_sleep( 0.1 );
}
*/
kmp_task_t *nested_proxy_task = __kmpc_omp_task_alloc(NULL,gtid,17,sizeof(kmp_task_t),0,&task_entry);
__kmpc_omp_task(NULL,gtid,nested_proxy_task);
}
}
// only check that it didn't crash
return 0;
}