Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

unlimited polymorphic variables and multi-threading results in crash

Tobias_Loew
Novice
1,460 Views

Hi,

the following code has a (very trivial) implementation of an auto-growing array (called "list"), where each instance of it can hold a different type. It works well, as long you don't use it multi-threaded:

If you compile the code with /Qopenmp it will simply crash!

The reason is (and I couldn't believe when I saw it in the assembler code) that inside of the append-subroutine at run-time the currently used type is switched and written to GLOBAL STATIC memory. This is simply insane, and I cannot find any reason, why this information is not written onto the stack or at least in thread-local storage when /threads is specified.

Tobias

    module test_mod
    implicit none
    
    type list
        class(*), allocatable :: c(:)
        integer :: num_nodes
    contains
    procedure :: append
    end type 

    contains
    
    subroutine append(this,element)
    class(list) :: this
    class(*), intent(in) :: element
    class(*), allocatable :: tmp(:)
    integer :: l

    if (.not.allocated(this%c)) then
        allocate(this%c(1),source=element)
        this%c(1) = element
        this%num_nodes = 1
        return
    else
        l = size(this%c,1)
        allocate(tmp(l+1),source=element)
        tmp(1:l) = this%c(1:l)
        tmp(l+1) = element
        deallocate(this%c)
        call move_alloc(tmp,this%c)
        this%num_nodes = this%num_nodes + 1
    endif
    end subroutine

    
    
    
    subroutine test()
    type(list) :: lines
    type(list) :: ilines
    character(256):: line
    integer:: err,cnt,i,file_unit

    !Init:
    cnt = 1
 
    DO i=1,100
        line = 'hello'
            call append(lines,line)
            call append(ilines,i)

    end do

    end subroutine
    end module
    
    program Console1
    use test_mod
    implicit none
    integer loop_t, nr_temps

    nr_temps = 20000
    
    !$omp parallel do
    do loop_t = 1,nr_temps
        call test()
    end do
    !$omp end parallel do


    end program Console1

 

0 Kudos
21 Replies
Tobias_Loew
Novice
143 Views

Hello Jim,

IMHO the problems with the code in #1 are:

- the code looks unsuspicious: it only uses basic runtime functions, which usually can be used in a thread-safe manner without the need for threading primitives

- searching the web for "unlimited polymorphism multi-threaded" or "unlimited polymorphism openmp" won't find the OpenMP document refered to by Steve. Furthermore, the term "polymorphic assignment" which is used in the OpenMP documentation doesn't seem to be an term in the intel-fortran docs. There, this is just an aspect of "unlimited polymorphism". So, it's quite hard to make the connection between "unlimited polymorphism" and the OpenMP-documention.

 

So, here is my recommendation for other users sharing the same problem (it is quite technical, but I tried to nail it down as exact as I could)

First, check if your version of the compiler addressed that problem (IF XE 2019 Update 5 (19.0.0052.16) is buggy, earlier version probably too) - if this is not the case:

If you cannot prove, that in all modules that share the same image of the IF-runtime, there is no concurrent access to unlimited polymorphic variables, then do not use unlimited polymorphic variables.

Sloppy stated the other way around: if you use unlimited polymorphic variables and you have concurrent access to them, your program may crash (or whatever you like to call the outcome of Console1.exe in #15).

Additional notes: 
- The condition is not just about concurrent access to the SAME unlimited polymorphic variable, but concurrent access to ANY two (maybe distinct) unlimited polymorphic variables.

- You may also get away, if you always use the same type with unlimited polymorphism, but
   - there are still writes to global static memory (and results in a race-condition in a multi-threaded environment)
   - there is very few use of polymorphism, if you use it for just one type

- The prove can be e.g.
    - my code is never used by two or more concurrent threads simultaneously, or a bit more fine-grained
    - every access to unlimited polymorphic variable is guarded by a critical-section as supposed by Jim in #12 (but this will only work for OpenMP based multi-threading)

Tobias

0 Kudos
Reply