content: module Halive.Concurrent (
    killThreads,
    registerThread,
    forkIO',
    forkOS'
    ) where
  
  import Control.Concurrent
  import System.IO.Unsafe
  import           Data.Set (Set)
  import qualified Data.Set as Set
  import Prelude hiding (mapM_)
  import Data.Foldable
  
  -- | A small collection of helper functions for creating threads that can be killed each time Halive re-runs your main function.
  -- This is helpful for programs where you control the threads, but doesn't solve the problem of libraries that use threads 
  -- (unless you unpack them and replace all forkIO/forkOS with forkIO'/forkOS')
  -- It would be good to ask GHC devs about this; 
  -- perhaps a GHC flag that registers threads similar to this module, for development use only?
  
  -- An internal global variable to hold threads that should be killed
  {-# NOINLINE registeredThreads #-}
  registeredThreads :: MVar (Set ThreadId)
  registeredThreads = unsafePerformIO (newMVar Set.empty)
  
  -- | Kill all threads registered to be killed.
  -- Meant to be called at the beginning of your program to clean up threads from the last execution before continuing
  killThreads :: IO ()
  killThreads = modifyMVar_ registeredThreads $ \threadIDs -> do
    mapM_ killThread threadIDs
    return Set.empty
  
  -- | Register a thread to be killed when killThreads is called
  registerThread :: ThreadId -> IO ()
  registerThread threadID = modifyMVar_ registeredThreads (return . Set.insert threadID)
  
  -- | Fork a thread and register it to be killed when killThreads is called
  forkIO' :: IO () -> IO ThreadId
  forkIO' action = do
    threadID <- forkIO action
    registerThread threadID
    return threadID
  
  -- | Fork an OS thread and register it to be killed when killThreads is called
  forkOS' :: IO () -> IO ThreadId
  forkOS' action = do
    threadID <- forkOS action
    registerThread threadID
    return threadID