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(). For joined access to common resources we can use mutexes to lock critical sections.

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 freebsd

On Linux, run make linux instead. Import the module unix and link your Fortran application against libfortran-unix.a, and additionally against -lpthread to access POSIX Threads.

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 :: unix
    implicit none
    integer, parameter :: NTHREADS = 3

    integer           :: i, rc
    integer, target   :: routines(NTHREADS) = [ (i, i = 1, NTHREADS) ]
    type(c_pthread_t) :: threads(NTHREADS)
    type(c_ptr)       :: ptr

    print '("Starting threads ...")'

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

    print '("Waiting for threads to finish ...")'

    ! Join threads.
    do i = 1, NTHREADS
        rc = c_pthread_join(threads(i), ptr)
    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 !! Client data as C pointer.

        integer, pointer :: n ! Fortran pointer to client data.
        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. In Fortran 2018, the recursive attribute of subroutine hello() is not required anymore. Link the example against libfortran-unix.a and -lpthread, and then run the binary:

$ gfortran13 -o example example.f90 libfortran-unix.a -lpthread
$ ./example
Starting threads ...
--- Thread #1 - Loop iteration 1
--- Thread #2 - Loop iteration 1
Waiting for threads to finish ...
--- 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

Fortran Libraries

References