{-# OPTIONS -cpp #-}
{- Copyright (c) Happstack.com, 2009; (c) HAppS.org, 2005

   Using Happstack in GHCi

   Because there are many threads and reloading everything
   is slow here is a way to kill all threads from GHCi:
   add -DINTERACTIVE to the command line and use the
   function happsKill from the prompt.
-}
module Happstack.Util.Concurrent where

import Control.Concurrent
import Prelude hiding (catch)
import Control.Exception -- hiding (catch)
#ifdef INTERACTIVE
import System.IO.Unsafe
import System.Mem
#endif

--generic utils
-- | Equivalent to a composition of fork and foreverSt
forkEverSt :: (t -> IO t) -> t -> IO ThreadId
forkEverSt f = fork . foreverSt f

-- | Similar to forever but with an explicit state parameter threaded through
-- the computation.
foreverSt :: (Monad m) => (t -> m t) -> t -> m b
foreverSt f state= f state >>= foreverSt f

-- | Equivalent to a composition of fork and forever
forkEver :: IO a -> IO ThreadId
forkEver = fork . forever

-- | Lifts the argument with Right before writing it into the chan
writeChanRight :: Chan (Either a b) -> b -> IO ()
writeChanRight chan = writeChan chan . Right

-- | Lifts the argument with Left before writing it into the chan
writeChanLeft :: Chan (Either a b) -> a -> IO ()
writeChanLeft chan = writeChan chan . Left

-- | Fork that throws away the ThreadId
fork_ :: IO a -> IO ()
fork_ c = fork c >> return ()

-- | Fork a new thread.
fork :: IO a -> IO ThreadId

-- | Register an action to be run when ghci is restarted.
registerResetAction :: IO () -> IO ()
-- | Reset state
reset :: IO ()

-- | A version of forever that will gracefully catch IO exceptions and continue
-- executing the provided action.
forever :: IO a -> IO a

#ifndef INTERACTIVE
forever a = finally a (forever a)
fork c = forkIO (c >> return ())
registerResetAction _ = return ()
reset = return ()
#else
forever a = try a >>= w
    where w (Right _)                            = forever a
          w (Left (AsyncException ThreadKilled)) = return ()
          w (Left e)                             = print e >> forever a
registerResetAction x = modifyMVar_ happsThreadList (return . (x:))

fork c = do
    x <- forkIO (c >> return ())
    modifyMVar_ happsThreadList (\xs -> return (killThread x:xs))
    return x
reset = do xs <- swapMVar happsThreadList []
           sequence_ xs
           threadDelay 10000
           performGC
           logM "Happstack.Util.Concurrent" INFO "reset ok"
{-# NOINLINE happsThreadList #-}
happsThreadList = unsafePerformIO $ newMVar []
#endif

-- | Sleep N seconds
sleep :: Int -> IO ()
sleep n = threadDelay (n * second) where second = 1000000