module Control.Concurrent.Worker (
	Worker(..),
	worker_, worker,
	stopWorker,
	ignoreException
	) where

import Control.Exception (SomeException)
import Control.Monad (void)
import Control.Monad.IO.Class
import Control.Monad.CatchIO

import Control.Concurrent (forkIO)
import Control.Concurrent.FiniteChan

data Worker a = Worker {
	sendWork :: a -> IO (),
	workerChan :: Chan a }

worker_ :: MonadIO m => (m () -> IO ()) -> (m () -> m ()) -> (a -> m b) -> IO (Worker a)
worker_ run initialize work = worker run initialize' work' where
	initialize' f = initialize $ f ()
	work' _ = work

worker :: MonadIO m => (m () -> IO ()) -> ((s -> m ()) -> m ()) -> (s -> a -> m b) -> IO (Worker a)
worker run initialize work = do
	ch <- newChan
	void $ forkIO $ run $ initialize $ \s ->
		liftIO (readChan ch) >>= mapM_ (work s)
	return $ Worker (putChan ch) ch

stopWorker :: Worker a -> IO ()
stopWorker = closeChan . workerChan

ignoreException :: MonadCatchIO m => m () -> m ()
ignoreException act = catch act onError where
	onError :: MonadCatchIO m => SomeException -> m ()
	onError _ = return ()