POSIX Threads

In modern Fortran, multi-processing may be achieved by accessing POSIX Threads (pthreads) on Unix-like operating systems. The fortran-unix library provides interface bindings to the C structure pthread_t, as well as the C functions pthread_create() and pthread_join().

fortran-unix

Clone the fortran-unix repository and build the static library libfortran-unix.a:

$ git clone https://github.com/interkosmos/fortran-unix
$ cd fortran-unix/
$ make

Import the module unix and link your Fortran application with libfortran-unix.a.

Example

Fortran subroutines which will run inside a thread must have the recursive and the bind(c) attribute. The routines are given an argument that contains a pointer to arbitrary client data passed by the c_pthread_create() function. The pointer has to be converted to the appropriate Fortran type first to be accessible. Due to the bind(c) attribute, it is required to specify whether an argument is passed by reference (default) or by value (dummy argument attribute value) to the routine.

! example.f90
program main
    use, intrinsic :: iso_c_binding
    use :: unix
    integer, parameter :: NTHREADS = 3
    type(c_pthread_t)  :: threads(NTHREADS)
    integer, target    :: routines(NTHREADS) = [ (i, i = 1, NTHREADS) ]
    integer            :: i, rc

    print '(a)', 'Starting threads ...'

    do i = 1, NTHREADS
        ! Create new thread.
        rc = c_pthread_create(threads(i), c_null_ptr, c_funloc(hello), c_loc(routines(i)))
    end do

    print '(a)', 'Joining threads ...'

    do i = 1, NTHREADS
        ! Join thread.
        rc = c_pthread_join(threads(i), c_loc(routines(i)))
    end do
contains
    recursive subroutine hello(arg) bind(c)
        !! Runs inside a thread and prints out current thread id and loop
        !! iteration.
        type(c_ptr), intent(in), value :: arg
        integer, pointer               :: n
        integer                        :: i, rc

        if (.not. c_associated(arg)) return ! Check whether argument has been passed.
        call c_f_pointer(arg, n)            ! Convert C pointer to Fortran pointer.

        do i = 1, 5
            print '("--- Thread #", i0, " - Loop iteration ", i0)', n, i
            rc = c_usleep(10**6)            ! Sleep 1 sec.
        end do
    end subroutine hello
end program main

In Fortran 2003, the subroutine hello() has to be delared inside a module. Starting with Fortran 2008, program routines are allowed to have the bind(c) attribute as well. Link the example against libfortran-unix.a and -lpthread, and then run the binary:

$ gfortran10 -o example example.f90 libfortran-unix.a -lpthread
$ ./example
Starting threads ...
--- Thread #1 - Loop iteration 1
--- Thread #2 - Loop iteration 1
Joining threads ...
--- Thread #3 - Loop iteration 1
--- Thread #1 - Loop iteration 2
--- Thread #2 - Loop iteration 2
--- Thread #3 - Loop iteration 2
--- Thread #1 - Loop iteration 3
--- Thread #3 - Loop iteration 3
--- Thread #2 - Loop iteration 3
--- Thread #1 - Loop iteration 4
--- Thread #3 - Loop iteration 4
--- Thread #2 - Loop iteration 4
--- Thread #1 - Loop iteration 5
--- Thread #3 - Loop iteration 5
--- Thread #2 - Loop iteration 5

References