Waitgroups in Fortran

Our program demonstrates how to use a simple synchronization mechanism in Fortran. Here’s the full source code:

program waitgroups
    use, intrinsic :: iso_fortran_env, only: int64
    implicit none
    
    integer :: i
    
    ! Start 5 worker tasks
    do i = 1, 5
        call execute_command_line("./worker " // trim(adjustl(str(i))) // " &")
    end do
    
    ! Wait for all workers to finish
    call system("wait")
    
contains
    function str(n)
        integer, intent(in) :: n
        character(len=20) :: str
        write (str, '(I0)') n
    end function str
end program waitgroups

This is the worker program that will be executed by each task:

program worker
    use, intrinsic :: iso_fortran_env, only: int64
    implicit none
    
    integer :: id
    character(len=10) :: arg
    
    ! Get the worker ID from command line argument
    call get_command_argument(1, arg)
    read(arg, *) id
    
    print '(A,I0,A)', 'Worker ', id, ' starting'
    
    ! Sleep to simulate an expensive task
    call sleep(1)
    
    print '(A,I0,A)', 'Worker ', id, ' done'
end program worker

To run the program, compile both files and execute the main program:

$ gfortran -o waitgroups waitgroups.f90
$ gfortran -o worker worker.f90
$ ./waitgroups
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

The order of workers starting up and finishing is likely to be different for each invocation.

In this Fortran implementation, we use the execute_command_line subroutine to start separate processes for each worker. The & at the end of the command makes each worker run in the background. We then use the system subroutine to call the shell’s wait command, which waits for all background processes to complete.

This approach differs from the original example in that it uses separate processes instead of threads or coroutines. Fortran doesn’t have built-in support for lightweight concurrency primitives like goroutines, so we use the operating system’s process management capabilities instead.

Note that this approach has limitations compared to the original example:

  1. It’s less efficient due to the overhead of creating separate processes.
  2. It relies on features of the operating system and shell, making it less portable.
  3. There’s no straightforward way to propagate errors from workers back to the main program.

For more advanced use cases in Fortran, you might consider using libraries that provide threading capabilities, or using MPI (Message Passing Interface) for distributed computing tasks.