import Std.Internal.Async
import Std.Sync.Mutex

open Std

open Std.Internal.IO.Async

def wait (ms : Nat) (ref : Std.Mutex Nat) (val : Nat) : Async Unit := do
  ref.atomically (·.modify (· * val))
  IO.sleep ms
  ref.atomically (·.modify (· + val))

-- Tests

def sequential : Async Unit := do
  let ref ← Std.Mutex.new 0
  wait 200 ref 1
  wait 400 ref 2
  ref.atomically (·.modify (· * 10))
  assert! (← ref.atomically (·.get)) == 40

#eval do (← sequential.toEIO).block

def conc : Async Unit := do
  let ref ← Std.Mutex.new 0
  discard <| concurrently (wait 200 ref 1) (wait 400 ref 2)
  ref.atomically (·.modify (· * 10))
  assert! (← ref.atomically (·.get)) == 30

#eval do (← conc.toEIO).block

def racer : Async Unit := do
  let ref ← Std.Mutex.new 0
  race (wait 200 ref 1) (wait 400 ref 2)
  ref.atomically (·.modify (· * 10))
  assert! (← ref.atomically (·.get)) == 10

#eval do (← racer.toEIO).block

def concAll : Async Unit := do
  let ref ← Std.Mutex.new 0
  discard <| concurrentlyAll #[(wait 200 ref 1), (wait 400 ref 2)]
  ref.atomically (·.modify (· * 10))
  assert! (← ref.atomically (·.get)) == 30

#eval do (← concAll.toEIO).block

def racerAll : Async Unit := do
  let ref ← Std.Mutex.new 0
  raceAll #[(wait 200 ref 1), (wait 400 ref 2)]
  ref.atomically (·.modify (· * 10))
  assert! (← ref.atomically (·.get)) == 10

#eval do (← racerAll.toEIO).block
