Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve async runtime scaling #946

Merged
merged 18 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion hakyll.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ Library
file-embed >= 0.0.10.1 && < 0.0.16,
filepath >= 1.0 && < 1.5,
hashable >= 1.0 && < 2,
lifted-async >= 0.10 && < 1,
lrucache >= 1.1.1 && < 1.3,
mtl >= 1 && < 2.3,
network-uri >= 2.6 && < 2.7,
Expand Down
78 changes: 41 additions & 37 deletions lib/Hakyll/Core/Logger.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
--------------------------------------------------------------------------------
-- | Produce pretty, thread-safe logs
{-# LANGUAGE Rank2Types #-}
module Hakyll.Core.Logger
( Verbosity (..)
, Logger
Expand All @@ -9,15 +10,19 @@ module Hakyll.Core.Logger
, header
, message
, debug

-- * Testing utilities
, newInMem
) where


--------------------------------------------------------------------------------
import Control.Concurrent (forkIO)
import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)
import Control.Monad (forever)
import Control.Concurrent.Chan (newChan, readChan, writeChan)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
import Control.Monad (forever, when)
import Control.Monad.Trans (MonadIO, liftIO)
import qualified Data.IORef as IORef
import Data.List (intercalate)
import Prelude hiding (error)

Expand All @@ -31,51 +36,34 @@ data Verbosity


--------------------------------------------------------------------------------
-- | Logger structure. Very complicated.
data Logger = Logger
{ loggerChan :: Chan (Maybe String) -- ^ Nothing marks the end
, loggerSync :: MVar () -- ^ Used for sync on quit
, loggerSink :: String -> IO () -- ^ Out sink
, loggerVerbosity :: Verbosity -- ^ Verbosity
{ -- | Flush the logger (blocks until flushed)
flush :: forall m. MonadIO m => m ()
, string :: forall m. MonadIO m => Verbosity -> String -> m ()
}


--------------------------------------------------------------------------------
-- | Create a new logger
new :: Verbosity -> IO Logger
new vbty = do
logger <- Logger <$>
newChan <*> newEmptyMVar <*> pure putStrLn <*> pure vbty
_ <- forkIO $ loggerThread logger
return logger
where
loggerThread logger = forever $ do
msg <- readChan $ loggerChan logger
chan <- newChan
sync <- newEmptyMVar
_ <- forkIO $ forever $ do
msg <- readChan chan
case msg of
-- Stop: sync
Nothing -> putMVar (loggerSync logger) ()
Nothing -> putMVar sync ()
-- Print and continue
Just m -> loggerSink logger m


--------------------------------------------------------------------------------
-- | Flush the logger (blocks until flushed)
flush :: Logger -> IO ()
flush logger = do
writeChan (loggerChan logger) Nothing
() <- takeMVar $ loggerSync logger
return ()


--------------------------------------------------------------------------------
string :: MonadIO m
=> Logger -- ^ Logger
-> Verbosity -- ^ Verbosity of the string
-> String -- ^ Section name
-> m () -- ^ No result
string l v m
| loggerVerbosity l >= v = liftIO $ writeChan (loggerChan l) (Just m)
| otherwise = return ()
Just m -> putStrLn m
return $ Logger
{ flush = liftIO $ do
writeChan chan Nothing
() <- takeMVar sync
return ()
, string = \v m -> when (vbty >= v) $
liftIO $ writeChan chan (Just m)
}


--------------------------------------------------------------------------------
Expand All @@ -101,3 +89,19 @@ debug l m = string l Debug $ " [DEBUG] " ++ indent m
--------------------------------------------------------------------------------
indent :: String -> String
indent = intercalate "\n " . lines


--------------------------------------------------------------------------------
-- | Create a new logger that just stores all the messages, useful for writing
-- tests.
newInMem :: IO (Logger, IO [(Verbosity, String)])
newInMem = do
ref <- IORef.newIORef []
pure
( Logger
{ string = \vbty msg -> liftIO $ IORef.atomicModifyIORef' ref $
\msgs -> ((vbty, msg) : msgs, ())
, flush = pure ()
}
, reverse <$> IORef.readIORef ref
)
Loading