Compare commits

..

4 commits

Author SHA1 Message Date
Dennis Frieberg
1816bd9bb5 smaller changes not changing semantics 2022-01-02 23:58:22 +01:00
Dennis Frieberg
70ee16d202 added a comment to formatMessage 2021-04-14 16:21:25 +02:00
Dennis Frieberg
f45297d86b added some reasoning about deadlocks and removed unneeded code 2021-04-14 16:19:39 +02:00
Dennis Frieberg
b98e5e27d0 forced evaluation of logging message before blocking 2021-04-14 10:48:59 +02:00
8 changed files with 33 additions and 31 deletions

2
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "tickLeiste"] [submodule "tickLeiste"]
path = tickLeiste path = tickLeiste
url = https://git.nerfingen.de/Splittertech/tickLeiste.git url = ssh://gitolite@nerfingen.de:39999/tickLeiste

View file

@ -21,9 +21,7 @@ import qualified Backend.Https as HTTPS
-- list of MVar is there to communicate the termination of the backend. (The main -- list of MVar is there to communicate the termination of the backend. (The main
-- threat will wait till all MVar are present (not neccesarry at once)). -- threat will wait till all MVar are present (not neccesarry at once)).
-- The action takes two parameter, the application the backend should run and -- The action takes two parameter, the application the backend should run and
-- the path of the config File. If the Bool is False there are no guarantees on the FilePath -- the path of the config File.
-- and the action should not try to evaluate the FilePath.
-- type Backend = (Wai.Application -> FilePath -> IO [MVar ()],T.Text,Bool)
data Backend = data Backend =
BackendWithConfig (Wai.Application -> FilePath -> EnvM [MVar ()]) String String BackendWithConfig (Wai.Application -> FilePath -> EnvM [MVar ()]) String String

View file

@ -67,9 +67,9 @@ httpDefaultSettings = [httpDefaultSetting]
httpConfigCodec :: TomlCodec HttpConfiguration httpConfigCodec :: TomlCodec HttpConfiguration
httpConfigCodec = httpConfigCodec =
HttpConfiguration HttpConfiguration
<$> Toml.configM Toml.text "instanceName" .= instanceName <$> Toml.configM Toml.text "InstanceName" .= instanceName
<*> Toml.configM Toml.int "port" .= port <*> Toml.configM Toml.int "Port" .= port
<*> Toml.configM bindPreference "bind" .= bindPref <*> Toml.configM bindPreference "Bind" .= bindPref
httpConfigsCodec :: TomlCodec [HttpConfiguration] httpConfigsCodec :: TomlCodec [HttpConfiguration]
httpConfigsCodec = Toml.list httpConfigCodec "http" httpConfigsCodec = Toml.list httpConfigCodec "http"

View file

@ -55,9 +55,9 @@ httpsDefaultSettings = [httpsDefaultSetting]
httpsConfigCodec' :: TomlCodec TLSConfiguration httpsConfigCodec' :: TomlCodec TLSConfiguration
httpsConfigCodec' = httpsConfigCodec' =
TLSConfiguration TLSConfiguration
<$> Toml.configM Toml.string "certificate" .= certFile <$> Toml.configM Toml.string "Certificate" .= certFile
<*> Toml.configM (Toml.arrayOf Toml._String) "certChain" .= certChain <*> Toml.configM (Toml.arrayOf Toml._String) "CertChain" .= certChain
<*> Toml.configM Toml.string "keyFile" .= keyFile <*> Toml.configM Toml.string "KeyFile" .= keyFile
httpsConfigCodec :: TomlCodec HttpsConfiguration httpsConfigCodec :: TomlCodec HttpsConfiguration
httpsConfigCodec = Toml.pair httpConfigCodec httpsConfigCodec' httpsConfigCodec = Toml.pair httpConfigCodec httpsConfigCodec'

View file

@ -11,13 +11,13 @@ module Environment(Env (..), EnvM, runEnvM, myLogAction,defaultEnv) where
import Prelude hiding (log) import Prelude hiding (log)
import Colog import Colog
import Control.Monad.Reader (ReaderT (..), MonadReader) import Control.Monad.Reader (ReaderT (..), MonadReader)
import Control.Monad.Trans import Control.Monad.Trans(lift)
import Control.Monad.IO.Unlift import Control.Monad.IO.Unlift
import Control.Concurrent.MVar (MVar,modifyMVar_,newMVar) import Control.Concurrent.MVar (MVar,modifyMVar_,newMVar)
import System.IO.Unsafe(unsafePerformIO) import System.IO.Unsafe(unsafePerformIO)
import qualified Control.Concurrent.MVar as M
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import Control.Parallel(pseq)
newtype Env m = Env {logAction :: LogAction m Message} newtype Env m = Env {logAction :: LogAction m Message}
@ -42,20 +42,21 @@ instance MonadUnliftIO EnvM where
withRunInIO inner = EnvM $ withRunInIO (\fromReader -> inner (fromReader . unEnvM)) withRunInIO inner = EnvM $ withRunInIO (\fromReader -> inner (fromReader . unEnvM))
{-# INLINE withRunInIO #-} {-# INLINE withRunInIO #-}
modifyMVar :: M.MVar a -> (a -> EnvM (a,b)) -> EnvM b
modifyMVar mvar action = withRunInIO $ \run -> M.modifyMVar mvar (run . action)
runEnvM :: Env EnvM -> EnvM a -> IO a runEnvM :: Env EnvM -> EnvM a -> IO a
runEnvM env envm = runReaderT (unEnvM envm) env runEnvM env envm = runReaderT (unEnvM envm) env
-- we do not export this MVar (which makes it easier to reason about deadlocks
syncMVar :: MVar () syncMVar :: MVar ()
syncMVar = unsafePerformIO $ newMVar () syncMVar = unsafePerformIO $ newMVar ()
{-# NOINLINE syncMVar #-} {-# NOINLINE syncMVar #-}
-- Every time you do something like this you should ask yourself: Is this a potential deadlock? And the answer
-- is in this case is NO. Even if m diverges it won't be able to lock up other threads as it will be forced to
-- evaluate before we take the MVar. So as long printing to stdout terminates we will always put back the mvar.
myLogAction :: LogAction EnvM Message myLogAction :: LogAction EnvM Message
myLogAction = LogAction $ \x -> EnvM . lift $ modifyMVar_ syncMVar (const (T.putStrLn . formatMessage $ x)) myLogAction = LogAction $ \x -> let m = formatMessage x in EnvM . lift $ m `pseq` modifyMVar_ syncMVar (const (T.putStrLn m))
-- my format Message without fancy colors, but with syslog numbers
formatMessage :: Message -> T.Text formatMessage :: Message -> T.Text
formatMessage Msg{..} = formatMessage Msg{..} =
showMyServerity msgSeverity showMyServerity msgSeverity

View file

@ -41,6 +41,13 @@ defaultLogLevel = Info
blockBackends :: [MVar ()] -> EnvM () blockBackends :: [MVar ()] -> EnvM ()
blockBackends = mapM_ takeMVar blockBackends = mapM_ takeMVar
runBackend :: Wai.Application -> Either (Maybe String) Bool -> Backend -> EnvM [MVar ()]
runBackend app (Left (Just conf)) (BackendWithConfig b _ _) = b app conf
runBackend _ (Left Nothing) (BackendWithConfig _ _ _) = return []
runBackend app (Right True) (BackendWithoutConfig b _ _) = b app
runBackend _ (Right False) (BackendWithoutConfig _ _ _) = return []
runBackend _ _ _ = logError "Backend and parser type don't match! THIS IS A BUG" >> return []
data CommandLineOptions = CommandLineOptions { data CommandLineOptions = CommandLineOptions {
backendOptions :: [Either (Maybe String) Bool], backendOptions :: [Either (Maybe String) Bool],
logSeverity :: Maybe Severity logSeverity :: Maybe Severity
@ -57,14 +64,6 @@ parser = CommandLineOptions
<$> backendsParser <$> backendsParser
<*> severityParser <*> severityParser
runBackend :: Wai.Application -> Either (Maybe String) Bool -> Backend -> EnvM [MVar ()]
runBackend app (Left (Just conf)) (BackendWithConfig b _ _) = b app conf
runBackend _ (Left Nothing) (BackendWithConfig _ _ _) = return []
runBackend app (Right True) (BackendWithoutConfig b _ _) = b app
runBackend _ (Right False) (BackendWithoutConfig _ _ _) = return []
runBackend _ _ _ = logError "Backend and parser type don't match! THIS IS A BUG" >> return []
commandLineParser :: ParserInfo CommandLineOptions commandLineParser :: ParserInfo CommandLineOptions
commandLineParser = info (parser <**> helper) commandLineParser = info (parser <**> helper)
( fullDesc ( fullDesc

View file

@ -41,17 +41,16 @@ dependencies:
- containers - containers
- wai - wai
- wai-websockets - wai-websockets
# only used for the error message on missing webclient
- http-types - http-types
- tomland - tomland
# if we want more interaction with tomland errors this might - validation-selective
# be practical (not used atm, there are functions to Either)
#- validation-selective
- optparse-applicative - optparse-applicative
- co-log - co-log
- unliftio-core - unliftio-core
- unliftio - unliftio
- mtl - mtl
# for pseq in logging
- parallel
when: when:
- condition: flag(http) - condition: flag(http)
@ -86,5 +85,5 @@ tests:
- -threaded - -threaded
- -rtsopts - -rtsopts
- -with-rtsopts=-N - -with-rtsopts=-N
# dependencies: dependencies:
# - tickLeisteServer - tickLeisteServer

View file

@ -56,6 +56,7 @@ executable tickLeisteServer
, http-types , http-types
, mtl , mtl
, optparse-applicative , optparse-applicative
, parallel
, text , text
, tickLeiste , tickLeiste
, tickLeiste-aeson , tickLeiste-aeson
@ -63,6 +64,7 @@ executable tickLeisteServer
, unliftio , unliftio
, unliftio-core , unliftio-core
, uuid , uuid
, validation-selective
, wai , wai
, wai-websockets , wai-websockets
, websockets , websockets
@ -96,13 +98,16 @@ test-suite tickLeisteServer-test
, http-types , http-types
, mtl , mtl
, optparse-applicative , optparse-applicative
, parallel
, text , text
, tickLeiste , tickLeiste
, tickLeiste-aeson , tickLeiste-aeson
, tickLeisteServer
, tomland , tomland
, unliftio , unliftio
, unliftio-core , unliftio-core
, uuid , uuid
, validation-selective
, wai , wai
, wai-websockets , wai-websockets
, websockets , websockets