refactor(ldap): use async

This commit is contained in:
Gregor Kleen 2019-07-29 15:23:26 +02:00
parent e6c3be4f7b
commit 9f0a91f0dd
2 changed files with 31 additions and 16 deletions

View File

@ -1,15 +1,17 @@
module Control.Concurrent.Async.Lifted.Safe.Utils module Control.Concurrent.Async.Lifted.Safe.Utils
( allocateLinkedAsync ( allocateAsync, allocateLinkedAsync
) where ) where
import ClassyPrelude hiding (cancel) import ClassyPrelude hiding (cancel)
import Control.Lens
import Control.Concurrent.Async.Lifted.Safe import Control.Concurrent.Async.Lifted.Safe
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
allocateLinkedAsync :: forall m a. allocateLinkedAsync, allocateAsync :: forall m a.
MonadResource m MonadResource m
=> IO a -> m (Async a) => IO a -> m (Async a)
allocateLinkedAsync act = allocate (async act) cancel >>= (\(_k, a) -> a <$ link a) allocateAsync = fmap (view _2) . flip allocate cancel . async
allocateLinkedAsync = uncurry (<$) . (id &&& link) <=< allocateAsync

View File

@ -10,6 +10,8 @@ module Ldap.Client.Pool
import ClassyPrelude import ClassyPrelude
import Control.Lens
import Ldap.Client (Ldap, LdapError) import Ldap.Client (Ldap, LdapError)
import qualified Ldap.Client as Ldap import qualified Ldap.Client as Ldap
@ -22,11 +24,17 @@ import Data.Dynamic
import System.Timeout.Lifted import System.Timeout.Lifted
import Control.Concurrent.Async.Lifted.Safe
import Control.Concurrent.Async.Lifted.Safe.Utils
import Control.Monad.Trans.Resource (MonadResource)
import qualified Control.Monad.Trans.Resource as Resource
type LdapPool = Pool LdapExecutor type LdapPool = Pool LdapExecutor
data LdapExecutor = LdapExecutor data LdapExecutor = LdapExecutor
{ ldapExec :: forall a. Typeable a => (Ldap -> IO a) -> IO (Either LdapPoolError a) { ldapExec :: forall a. Typeable a => (Ldap -> IO a) -> IO (Either LdapPoolError a)
, ldapDestroy :: TMVar () , ldapDestroy :: TMVar ()
, ldapAsync :: Async ()
} }
instance Exception LdapError instance Exception LdapError
@ -41,7 +49,7 @@ withLdap :: (MonadBaseControl IO m, MonadIO m, Typeable a) => LdapPool -> (Ldap
withLdap pool act = withResource pool $ \LdapExecutor{..} -> liftIO $ ldapExec act withLdap pool act = withResource pool $ \LdapExecutor{..} -> liftIO $ ldapExec act
createLdapPool :: ( MonadLoggerIO m, MonadIO m ) createLdapPool :: ( MonadLoggerIO m, MonadResource m )
=> Ldap.Host => Ldap.Host
-> Ldap.PortNumber -> Ldap.PortNumber
-> Int -- ^ Stripes -> Int -- ^ Stripes
@ -53,15 +61,15 @@ createLdapPool host port stripes timeoutConn (round . (* 1e6) -> timeoutAct) lim
logFunc <- askLoggerIO logFunc <- askLoggerIO
let let
mkExecutor :: IO LdapExecutor mkExecutor :: Resource.InternalState -> IO LdapExecutor
mkExecutor = do mkExecutor rSt = Resource.runInternalState ?? rSt $ do
ldapDestroy <- newEmptyTMVarIO ldapDestroy <- liftIO newEmptyTMVarIO
ldapAct <- newEmptyTMVarIO ldapAct <- liftIO newEmptyTMVarIO
let let
ldapExec :: forall a. Typeable a => (Ldap -> IO a) -> IO (Either LdapPoolError a) ldapExec :: forall a. Typeable a => (Ldap -> IO a) -> IO (Either LdapPoolError a)
ldapExec act = do ldapExec act = do
ldapAnswer <- newEmptyTMVarIO :: IO (TMVar (Either SomeException Dynamic)) ldapAnswer <- liftIO newEmptyTMVarIO :: IO (TMVar (Either SomeException Dynamic))
atomically $ putTMVar ldapAct (fmap toDyn . act, ldapAnswer) atomically $ putTMVar ldapAct (fmap toDyn . act, ldapAnswer)
either throwIO (return . Right . flip fromDyn (error "Could not cast dynamic")) =<< atomically (takeTMVar ldapAnswer) either throwIO (return . Right . flip fromDyn (error "Could not cast dynamic")) =<< atomically (takeTMVar ldapAnswer)
`catches` `catches`
@ -91,10 +99,10 @@ createLdapPool host port stripes timeoutConn (round . (* 1e6) -> timeoutAct) lim
] ]
go Nothing ldap go Nothing ldap
withTimeout $ do ldapAsync <- withTimeout $ do
setup <- newEmptyTMVarIO setup <- liftIO newEmptyTMVarIO
void . fork . flip runLoggingT logFunc $ do ldapAsync <- allocateAsync . flip runLoggingT logFunc $ do
$logInfoS "LdapExecutor" "Starting" $logInfoS "LdapExecutor" "Starting"
res <- liftIO . Ldap.with host port $ flip runLoggingT logFunc . go (Just setup) res <- liftIO . Ldap.with host port $ flip runLoggingT logFunc . go (Just setup)
case res of case res of
@ -105,11 +113,16 @@ createLdapPool host port stripes timeoutConn (round . (* 1e6) -> timeoutAct) lim
maybe (return ()) throwM =<< atomically (takeTMVar setup) maybe (return ()) throwM =<< atomically (takeTMVar setup)
return ldapAsync
return LdapExecutor{..} return LdapExecutor{..}
delExecutor :: LdapExecutor -> IO () delExecutor :: LdapExecutor -> IO ()
delExecutor LdapExecutor{..} = atomically . void $ tryPutTMVar ldapDestroy () delExecutor LdapExecutor{..} = do
liftIO $ createPool mkExecutor delExecutor stripes timeoutConn limit atomically . void $ tryPutTMVar ldapDestroy ()
wait ldapAsync
rSt <- view _2 <$> Resource.allocate Resource.createInternalState Resource.closeInternalState
liftIO $ createPool (mkExecutor rSt) delExecutor stripes timeoutConn limit
where where
withTimeout :: forall m a. (MonadBaseControl IO m, MonadThrow m) => m a -> m a withTimeout :: forall m a. (MonadBaseControl IO m, MonadThrow m) => m a -> m a
withTimeout = maybe (throwM LdapPoolTimeout) return <=< timeout timeoutAct withTimeout = maybe (throwM LdapPoolTimeout) return <=< timeout timeoutAct