Mutexes in PureScript

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

module Main where

import Prelude

import Effect (Effect)
import Effect.Console (log)
import Effect.Ref as Ref
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Control.Parallel (parallel, sequential)

We’ll create a Container type that holds a map of counters. Since we want to update it concurrently from multiple fibers, we’ll use a Ref to synchronize access.

type Container = Ref.Ref (Map.Map String Int)

-- Increment the counter for a given name
inc :: Container -> String -> Effect Unit
inc container name = Ref.modify_ (Map.alter increment name) container
  where
    increment Nothing = Just 1
    increment (Just n) = Just (n + 1)

The inc function uses Ref.modify_ to safely update the counter. This ensures that updates are atomic and thread-safe.

main :: Effect Unit
main = do
  -- Create a new container with initial counters
  c <- Ref.new $ Map.fromFoldable [Tuple "a" 0, Tuple "b" 0]

  -- This function increments a named counter in a loop
  let doIncrement name n = 
        sequence_ $ replicate n (inc c name)

  -- Run several fibers concurrently
  _ <- sequential $ parallel (doIncrement "a" 10000) 
                 *> parallel (doIncrement "a" 10000)
                 *> parallel (doIncrement "b" 10000)

  -- Print the final state of the counters
  result <- Ref.read c
  log $ show result

In the main function, we create a new Container and define a doIncrement function that increments a named counter multiple times. We then run three fibers concurrently using parallel, two of which access the same counter.

Running the program shows that the counters are updated as expected:

$ spago run
Map (fromFoldable [Tuple "a" 20000,Tuple "b" 10000])

This example demonstrates how to use Ref in PureScript to manage shared state across multiple fibers. While PureScript doesn’t have built-in mutexes like some other languages, the Ref type provides a safe way to handle concurrent access to mutable state.

Next, we’ll look at implementing this same state management task using only fibers and channels.