Mutexes in Fortran

In the previous example, we saw how to manage simple counter state using atomic operations. For more complex state, we can use a mutex to safely access data across multiple threads.

module mutexes
  use, intrinsic :: iso_fortran_env
  use omp_lib
  implicit none

  type container
    integer :: mutex
    integer, allocatable :: counters(:)
  end type container

contains

  subroutine inc(c, name)
    type(container), intent(inout) :: c
    integer, intent(in) :: name
    
    call omp_set_lock(c%mutex)
    c%counters(name) = c%counters(name) + 1
    call omp_unset_lock(c%mutex)
  end subroutine inc

  subroutine do_increment(c, name, n)
    type(container), intent(inout) :: c
    integer, intent(in) :: name, n
    integer :: i

    do i = 1, n
      call inc(c, name)
    end do
  end subroutine do_increment

end module mutexes

program main
  use mutexes
  implicit none

  type(container) :: c
  integer :: i

  allocate(c%counters(2))
  c%counters = 0
  call omp_init_lock(c%mutex)

  !$omp parallel sections
    !$omp section
      call do_increment(c, 1, 10000)
    !$omp section
      call do_increment(c, 1, 10000)
    !$omp section
      call do_increment(c, 2, 10000)
  !$omp end parallel sections

  call omp_destroy_lock(c%mutex)

  print *, "Counters:", c%counters

  deallocate(c%counters)
end program main

In this Fortran implementation:

  1. We define a container type that holds a mutex (implemented as an integer for use with OpenMP) and an array of counters.

  2. The inc subroutine increments a specific counter, using OpenMP’s lock routines to ensure thread safety.

  3. The do_increment subroutine calls inc in a loop, similar to the original example.

  4. In the main program, we initialize the container, create parallel sections to run the increment operations concurrently, and then print the results.

  5. We use OpenMP for parallelism, which is a common approach in Fortran for shared-memory parallelism.

  6. Instead of a map, we use a simple integer array for counters. The index of the array represents the “name” of the counter.

To compile and run this program, you would typically use a command like:

$ gfortran -fopenmp mutexes.f90 -o mutexes
$ ./mutexes
Counters:  20000  10000

This output shows that the counters were updated as expected, with the first counter (index 1) incremented to 20000 and the second counter (index 2) to 10000.

Note that Fortran doesn’t have built-in support for maps or hash tables, so we’ve simplified the example to use array indices instead of string keys. Also, Fortran doesn’t have a direct equivalent to goroutines, so we’ve used OpenMP parallel sections to achieve similar concurrency.

In the next example, we’ll look at implementing this same state management task using only threading and message passing.