Search code examples
multithreadingfortranblocking

Calling a subroutine in FORTRAN without blocking the main program


Assuming an over-simplified FORTRAN code compiled with mpif90 as:

program main
!
   use mpi
   implicit none
   integer:: j, numtasks, taskid, ierr
   integer:: master = 0
!
   call mpi_init(ierr)
   call mpi_comm_rank(mpi_comm_world, taskid, ierr)   
!
   if (taskid .eq. master) then
      j = 5
      call child (j)
  !   do stuff
   end if
   call mpi_finalize(ierr)
!
end program main

subroutine child(j)
!
   implicit none
   integer, intent(in):: j
!  do some stuff with j
end subroutine child

By default, the master CPU from the main waits until the child is done with its calculations. However, I want it to continue its tasks after calling the child, while the child is also doing its tasks. I would like the child to be a subroutine for the main since I need to pass some data from the main to the child (but not vice versa). I was wondering to know if this is possible in FORTRAN (maybe by using some kind of non-blocking subroutine call or multi-threading such as mpi_comm_spawn).


Solution

  • I would use a POSIX thread for this. Maybe also an OpenMP task, but my experience with them is limited. I will assume you do not call any MPI procedures in the child.

    With a simple interface in C

    #include <pthread.h>
    
    void pthread_create_opaque(pthread_t *threadptr, void *procptr, void *dataptr, int *err){
    //   creates a new thread using an opaque pointer to the pthread_t structure
      *err = pthread_create(threadptr, NULL, procptr, dataptr);
    }
    
    void pthread_join_opaque(pthread_t *threadptr, int *err) {
    //  joines a thread using an opaque pointer to the pthread_t structure
     *err = pthread_join(*threadptr, NULL);
    }
    

    and in Fortran

    module Pthreads
      implicit none
    
      interface
        subroutine pthread_create_opaque(threadptr, procptr, dataptr, err) bind(C,name="pthread_create_opaque")
          use iso_c_binding
          type(c_ptr) :: threadptr
          type(c_funptr),value :: procptr
          type(c_ptr),value :: dataptr
          integer(c_int),intent(out) :: err
        end subroutine
    
        subroutine pthread_join_opaque(thread, err) bind(C,name="pthread_join_opaque")
          use iso_c_binding
          type(c_ptr),value :: thread
          integer(c_int),intent(out) :: err
        end subroutine
      end interface
    end module Pthreads
    

    you can call a child if it is C interoperable

    subroutine child(j) bind(C)
    !
       implicit none
       integer, intent(in):: j
    !  do some stuff with j
    end subroutine child
    

    simply as

    type(c_ptr) :: thread
    integer :: err
    
    call pthread_create_opaque(thread, c_funloc(join), loc(j), err)
    

    and later at some convenient place (before program end or wherever) wait for it to finish its work

    call pthread_join_opaque(thread, err)
    

    I use this successfully in an MPI parallel program for asynchronous output of time-step data.