packages feed

job-0.1: test/Main.hs

{-# OPTIONS_GHC -Wno-incomplete-patterns #-}

module Main (main) where

import Control.Concurrent
import Control.Exception qualified as Ex
import Control.Monad
import Data.Acquire qualified as A
import Data.Function
import Data.IORef
import Data.List qualified as List
import Data.Time qualified as Time
import Job
import Job.Memory qualified
import System.Random.Stateful qualified as R

--------------------------------------------------------------------------------

infix 4 ==.
(==.) :: (Eq a, Show a) => a -> a -> IO ()
(==.) l r =
   if l == r
      then mempty
      else fail $ (showsPrec 11 l . showString " /= " . showsPrec 11 r) ""

infix 4 >.
(>.) :: (Ord a, Show a) => a -> a -> IO ()
(>.) l r =
   if l > r
      then mempty
      else fail $ (showsPrec 11 l . showString " <= " . showsPrec 11 r) ""

main :: IO ()
main = A.withAcquire (Job.Memory.queue @String) \q -> do
   t0 <- Time.getCurrentTime

   putStrLn "a"
   [] <- prune q \i m j -> (False, [(i, m, j)])

   putStrLn "b"
   i0 <- push q nice0 t0 "j0"
   [(x0i, x0m, x0j)] <- prune q \i m j -> (False, [(i, m, j)])
   x0i ==. i0
   x0j ==. "j0"
   x0m.nice ==. nice0
   x0m.wait ==. t0
   x0m.try ==. 0
   x0m.alive ==. Nothing
   [] <- prune q \i m j -> (False, [(i, m, j)])

   putStrLn "c"
   i1 <- push q nice0 t0 "j1"
   [(x1i, x1m, x1j)] <- prune q \i m j -> (True, [(i, m, j)])
   x1i ==. i1
   x1j ==. "j1"
   x1m.nice ==. nice0
   x1m.wait ==. t0
   x1m.try ==. 0
   x1m.alive ==. Nothing

   putStrLn "d"
   [(x2i, x2m, x2j)] <- prune q \i m j -> (True, [(i, m, j)])
   x2i ==. i1
   x2j ==. "j1"
   x2m.nice ==. nice0
   x2m.wait ==. t0
   x2m.try ==. 0
   x2m.alive ==. Nothing

   putStrLn "e"
   t1 <- Time.getCurrentTime
   A.withAcquire q.pull $ mapM_ \w -> do
      putStrLn "e'"
      w.id ==. i1
      w.job ==. "j1"
      w.meta.nice ==. nice0
      w.meta.wait ==. t0
      w.meta.try ==. 0
      retry w (Nice 1) t1

   putStrLn "f"
   [(x3i, x3m, x3j)] <- prune q \i m j -> (True, [(i, m, j)])
   x3i ==. i1
   x3j ==. "j1"
   x3m.nice ==. Nice 1
   x3m.wait ==. t1
   x3m.try ==. 1
   x3m.alive ==. Nothing

   putStrLn "g"
   A.withAcquire q.pull $ mapM_ \w -> do
      putStrLn "g'"
      w.id ==. i1
      w.job ==. "j1"
      w.meta.nice ==. Nice 1
      w.meta.wait ==. t1
      w.meta.try ==. 1
      retry w (Nice 1) t1

   putStrLn "h"
   [(x4i, x4m, x4j)] <- prune q \i m j -> (True, [(i, m, j)])
   x4i ==. i1
   x4j ==. "j1"
   x4m.nice ==. Nice 1
   x4m.wait ==. t1
   x4m.try ==. 2
   x4m.alive ==. Nothing

   putStrLn "i"
   A.withAcquire q.pull $ mapM_ \w -> do
      putStrLn "i'"
      w.id ==. i1
      w.job ==. "j1"
      w.meta.nice ==. Nice 1
      w.meta.wait ==. t1
      w.meta.try ==. 2
      pure ()

   putStrLn "j"
   [] <- prune q \i m j -> (True, [(i, m, j)])
   t2 <- Time.addUTCTime 0.3 <$> Time.getCurrentTime
   i2 <- push q nice0 t2 "j2"
   [(x5i, x5m, x5j)] <- prune q \i m j -> (True, [(i, m, j)])
   x5i ==. i2
   x5j ==. "j2"
   x5m.nice ==. nice0
   x5m.wait ==. t2
   x5m.try ==. 0
   x5m.alive ==. Nothing

   putStrLn "k"
   Ex.handle (\case Err 1 -> pure ()) do
      A.withAcquire q.pull $ mapM_ \w -> do
         putStrLn "k'"
         w.id ==. i2
         w.job ==. "j2"
         w.meta.nice ==. nice0
         w.meta.wait ==. t2
         w.meta.try ==. 0
         void $ Ex.throwIO $ Err 1

   putStrLn "l"
   [(x6i, x6m, x6j)] <- prune q \i m j -> (True, [(i, m, j)])
   x6i ==. i2
   x6j ==. "j2"
   x6m.nice ==. nice0
   x6m.wait >. t2
   x6m.try ==. 1
   x6m.alive ==. Nothing

   putStrLn "m"
   i3 <- push q nice0 t0 "j3"
   i4 <- push q (pred nice0) t1 "j4"
   i5 <- push q nice0 t0 "j5"
   jIds0 <- prune q \i _ _ -> (True, [i])
   jIds0 ==. [i4, i3, i5, i2]

   putStrLn "n"
   forM_ (zip jIds0 (List.drop 1 (List.tails jIds0))) \(jId, tl) ->
      A.withAcquire q.pull $ mapM_ \w -> do
         w.id ==. jId
         jIds1 <- prune q \i _ _ -> (True, [i])
         jIds1 ==. tl <> [jId]
   [] <- prune q \i m j -> (True, [(i, m, j)])

   putStrLn "o"
   do
      t3 <- Time.getCurrentTime
      rwout <- newIORef ([] :: [String])
      let wins :: [String] = fmap show [0 .. 1000 :: Word]
      forM_ wins \win -> do
         wrd :: Word <- R.uniformM R.globalStdGen
         let pct = toRational wrd / toRational (maxBound :: Word)
             ndt = fromRational (pct * 0.5)
         push q nice0 (Time.addUTCTime ndt t3) win
      putStrLn "o'"
      replicateM_ 4 $ forkIO $ fix \again -> do
         y <- A.withAcquire q.pull $ mapM \w ->
            atomicModifyIORef' rwout \xs -> (w.job : xs, ())
         forM_ y \_ -> again
      threadDelay 3_100_000
      [] <- prune q \i m j -> (True, [(i, m, j)])
      wout <- readIORef rwout
      length wout ==. length wins
      List.sort wout ==. List.sort wins

   putStrLn "done"

data Err = Err Int
   deriving stock (Eq, Show)
   deriving anyclass (Ex.Exception)