Goroutines in Fortran

A goroutine is a lightweight thread of execution.

program goroutines
  implicit none

  call f('direct')

  call execute_goroutine('goroutine')

  call execute_anonymous('going')

  call sleep(1.0)
  print *, 'done'

contains

  subroutine f(from)
    character(len=*), intent(in) :: from
    integer :: i
    do i = 0, 2
      print *, trim(from), ":", i
    end do
  end subroutine f

  subroutine execute_goroutine(from)
    character(len=*), intent(in) :: from
    call execute_task('f', trim(from))
  end subroutine execute_goroutine

  subroutine execute_anonymous(msg)
    character(len=*), intent(in) :: msg
    call execute_task('anonymous', trim(msg))
  end subroutine execute_anonymous

  subroutine anonymous_task(msg)
    character(len=*), intent(in) :: msg
    print *, trim(msg)
  end subroutine anonymous_task

  subroutine execute_task(task, msg)
    character(len=*), intent(in) :: task, msg
    if (trim(task) == 'f') then
      call f(msg)
    else if (trim(task) == 'anonymous') then
      call anonymous_task(msg)
    end if
    ! For simplicity, simulate async behavior with sleep
    call sleep(0.5)
  end subroutine execute_task

  subroutine sleep(seconds)
    real, intent(in) :: seconds
    integer :: i, istat
    real :: start_time, end_time
    call cpu_time(start_time)
    end_time = start_time + seconds
    do
      call cpu_time(start_time)
      if (start_time >= end_time) exit
    end do
  end subroutine sleep

end program goroutines

Suppose we have a subroutine call f(s). Here’s how we’d call that in the usual way, running it synchronously.

call f('direct')

To invoke this subroutine in a goroutine-like fashion, we simulate asynchronous behavior by calling it within another subroutine.

call execute_goroutine('goroutine')

You can also start a subroutine for an anonymous function call.

call execute_anonymous('going')

Our two subroutine calls are running asynchronously now. We use a sleep subroutine to wait for them to finish (for a more robust approach, use proper concurrency control).

call sleep(1.0)
print *, 'done'

When we run this program, we see the output of the blocking call first, then the output of the simulated goroutines. The outputs may be interleaved because they are being run concurrently in our simulation.

$ gfortran -o goroutines goroutines.f90
$ ./goroutines
 direct : 0
 direct : 1
 direct : 2
 goroutine : 0
 going
 goroutine : 1
 goroutine : 2
 done

Next, we’ll look at a complement to goroutines in concurrent programs: channels.