Yesod.MonadSite module gets some nice upgrades

- Fork and async are no longer class methods, which simplifies things a lot and
  allows for many more trivial instances, much like with MonadHandler. Fork and
  async are still available, but instead of unnecessarily being class methods,
  they are now provided as follows: You can fork and async a worker (no more
  fork/async for handler, because I never actually need that, and not sure
  there's ever a need for that in general), and you can do that from any
  MonadSite. So, you can fork or async a worker from a Handler, from a Worker,
  from a ReaderT on top of them e.g. inside runDB, and so on.
- Following the simplification, new MonadSite instances are provided, so far
  just the ones in actual use in the code. ReaderT, ExceptT and lazy RWST. More
  can be added easily. Oh, and WidgetFor got an instance too.

In particular, this change means there's no usage of `forkHandler` anymore, at
all. I wonder if it ever makes a difference to `forkWorker` versus
`forkHandler`. Like, does it cause memory leaks or anything. I guess could
check why `forkResource` etc. is good for in `forkHandler` implementation. I
suppose if needed, I could fix possible memory leaks in `forkWorker`.
This commit is contained in:
fr33domlover 2019-06-14 17:10:12 +00:00
parent 42febca91f
commit 6df2200f47
2 changed files with 40 additions and 3 deletions

View file

@ -1903,7 +1903,7 @@ retryOutboxDelivery = do
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd) = map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
. groupWithExtractBy ((==) `on` fst) fst snd . groupWithExtractBy ((==) `on` fst) fst snd
fork action = do fork action = do
wait <- asyncSite action wait <- asyncWorker action
return $ do return $ do
result <- wait result <- wait
case result of case result of

View file

@ -26,6 +26,7 @@ module Yesod.MonadSite
, WorkerFor , WorkerFor
, runWorker , runWorker
, forkWorker , forkWorker
, asyncWorker
) )
where where
@ -37,6 +38,7 @@ import Control.Monad.IO.Class
import Control.Monad.IO.Unlift import Control.Monad.IO.Unlift
import Control.Monad.Logger.CallStack import Control.Monad.Logger.CallStack
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.Reader import Control.Monad.Trans.Reader
import Data.Functor import Data.Functor
import Data.Text (Text) import Data.Text (Text)
@ -47,6 +49,7 @@ import Yesod.Core hiding (logError)
import Yesod.Core.Types import Yesod.Core.Types
import Yesod.Persist.Core import Yesod.Persist.Core
import qualified Control.Monad.Trans.RWS.Lazy as RWSL
import qualified Data.Text as T import qualified Data.Text as T
class PersistConfig (SitePersistConfig site) => Site site where class PersistConfig (SitePersistConfig site) => Site site where
@ -56,18 +59,35 @@ class PersistConfig (SitePersistConfig site) => Site site where
sitePersistPool :: site -> PersistConfigPool (SitePersistConfig site) sitePersistPool :: site -> PersistConfigPool (SitePersistConfig site)
siteLogger :: site -> Logger siteLogger :: site -> Logger
class (MonadUnliftIO m, MonadLogger m) => MonadSite m where class (MonadIO m, MonadLogger m) => MonadSite m where
type SiteEnv m type SiteEnv m
askSite :: m (SiteEnv m) askSite :: m (SiteEnv m)
askUrlRender :: m (Route (SiteEnv m) -> Text) askUrlRender :: m (Route (SiteEnv m) -> Text)
{-
forkSite :: (SomeException -> m ()) -> m () -> m () forkSite :: (SomeException -> m ()) -> m () -> m ()
asyncSite :: m a -> m (m (Either SomeException a)) asyncSite :: m a -> m (m (Either SomeException a))
-}
instance MonadSite m => MonadSite (ReaderT r m) where
type SiteEnv (ReaderT r m) = SiteEnv m
askSite = lift askSite
askUrlRender = lift askUrlRender
instance MonadSite m => MonadSite (ExceptT e m) where
type SiteEnv (ExceptT e m) = SiteEnv m
askSite = lift askSite
askUrlRender = lift askUrlRender
instance (Monoid w, MonadSite m) => MonadSite (RWSL.RWST r w s m) where
type SiteEnv (RWSL.RWST r w s m) = SiteEnv m
askSite = lift askSite
askUrlRender = lift askUrlRender
asksSite :: MonadSite m => (SiteEnv m -> a) -> m a asksSite :: MonadSite m => (SiteEnv m -> a) -> m a
asksSite f = f <$> askSite asksSite f = f <$> askSite
runSiteDB runSiteDB
:: (MonadSite m, Site (SiteEnv m)) :: (MonadUnliftIO m, MonadSite m, Site (SiteEnv m))
=> PersistConfigBackend (SitePersistConfig (SiteEnv m)) m a => PersistConfigBackend (SitePersistConfig (SiteEnv m)) m a
-> m a -> m a
runSiteDB action = do runSiteDB action = do
@ -78,6 +98,7 @@ instance MonadSite (HandlerFor site) where
type SiteEnv (HandlerFor site) = site type SiteEnv (HandlerFor site) = site
askSite = getYesod askSite = getYesod
askUrlRender = getUrlRender askUrlRender = getUrlRender
{-
forkSite = forkHandler forkSite = forkHandler
asyncSite action = do asyncSite action = do
mvar <- newEmptyMVar mvar <- newEmptyMVar
@ -86,6 +107,12 @@ instance MonadSite (HandlerFor site) where
result <- action result <- action
putMVar mvar $ Right result putMVar mvar $ Right result
return $ liftIO $ readMVar mvar return $ liftIO $ readMVar mvar
-}
instance MonadSite (WidgetFor site) where
type SiteEnv (WidgetFor site) = site
askSite = getYesod
askUrlRender = getUrlRender
newtype WorkerT site m a = WorkerT newtype WorkerT site m a = WorkerT
{ unWorkerT :: LoggingT (ReaderT site m) a { unWorkerT :: LoggingT (ReaderT site m) a
@ -111,11 +138,13 @@ instance (MonadUnliftIO m, Yesod site, Site site) => MonadSite (WorkerT site m)
askUrlRender = do askUrlRender = do
site <- askSite site <- askSite
return $ \ route -> yesodRender site (siteApproot site) route [] return $ \ route -> yesodRender site (siteApproot site) route []
{-
forkSite handler action = void $ forkFinally action handler' forkSite handler action = void $ forkFinally action handler'
where where
handler' (Left e) = handler e handler' (Left e) = handler e
handler' (Right _) = pure () handler' (Right _) = pure ()
asyncSite action = waitCatch <$> async action asyncSite action = waitCatch <$> async action
-}
runWorkerT :: (Yesod site, Site site) => WorkerT site m a -> site -> m a runWorkerT :: (Yesod site, Site site) => WorkerT site m a -> site -> m a
runWorkerT (WorkerT action) site = runReaderT (runLoggingT action logFunc) site runWorkerT (WorkerT action) site = runReaderT (runLoggingT action logFunc) site
@ -143,3 +172,11 @@ forkWorker err worker = do
"Worker thread threw exception: " <> err <> ": " <> "Worker thread threw exception: " <> err <> ": " <>
T.pack (displayException e) T.pack (displayException e)
Right _ -> return () Right _ -> return ()
asyncWorker
:: (MonadSite m, SiteEnv m ~ site, Yesod site, Site site)
=> WorkerFor site a
-> m (m (Either SomeException a))
asyncWorker worker = do
site <- askSite
liftIO $ waitCatch <$> async (runWorker worker site)