{-# LANGUAGE BlockArguments, DeriveAnyClass, DerivingStrategies, FlexibleInstances, MultiParamTypeClasses #-} import Control.Concurrent import Control.Exception import Control.Monad import Data.IORef import Data.Maybe (isJust) import Data.TimerWheel import System.Mem (performGC) import System.Mem.Weak (deRefWeak) main :: IO () main = do do putStrLn "Timer wheel runs scheduled actions" with Config { spokes = 4, resolution = 0.05 } \wheel -> do var <- newEmptyMVar let n = 1000 replicateM_ n (register_ wheel 0 (putMVar var ())) replicateM_ n (takeMVar var) do putStrLn "Timers can be canceled" var <- newEmptyMVar with Config { spokes = 4, resolution = 0.05 } \wheel -> do let n = 1000 cancels <- replicateM n (register wheel 0 (putMVar var ())) successes <- sequence (take (n `div` 2) cancels) replicateM_ (n - length (filter id successes)) (takeMVar var) do putStrLn "Successful `cancel` returns True (then False)" with Config { spokes = 4, resolution = 0.05 } \wheel -> do cancel <- register wheel 1 (pure ()) cancel `is` True cancel `is` False do putStrLn "Unsuccessful `cancel` returns False" with Config { spokes = 4, resolution = 0.05 } \wheel -> do var <- newEmptyMVar cancel <- register wheel 0 (putMVar var ()) takeMVar var cancel `is` False do putStrLn "Recurring timers work" with Config { spokes = 4, resolution = 0.05 } \wheel -> do canary <- newIORef () -- kept alive only by timer weakCanary <- mkWeakIORef canary (pure ()) var <- newEmptyMVar cancel <- recurring wheel 0 (readIORef canary >> putMVar var ()) replicateM_ 2 (takeMVar var) cancel -- should drop reference canary after a GC performGC (isJust <$> deRefWeak weakCanary) `is` False do putStrLn "`with` re-throws exception from background thread" catch (with Config { spokes = 4, resolution = 0.05 } \wheel -> do var <- newEmptyMVar register_ wheel 0 (throwIO Bye >> putMVar var ()) takeMVar var throwIO (userError "fail")) (\ex -> case fromException ex of Just Bye -> pure () _ -> throwIO ex) data Bye = Bye deriving stock (Show) deriving anyclass (Exception) class Assert a b where is :: a -> b -> IO () instance (Eq a, Show a) => Assert (IO a) a where is mx y = do x <- mx unless (x == y) (throwIO (userError (show x ++ " /= " ++ show y)))