packages feed

glue-core 0.6 → 0.6.1

raw patch · 3 files changed

+44/−24 lines, 3 files

Files

glue-core.cabal view
@@ -1,5 +1,5 @@ name:                   glue-core-version:                0.6+version:                0.6.1 synopsis:               Make better services and clients. description:            Combinator library to enhance the general functionality of services and clients. license:                BSD3
src/Glue/DogpileProtection.hs view
@@ -6,32 +6,52 @@   dogpileProtect ) where -import Control.Concurrent.Lifted-import Control.Exception.Lifted-import Control.Monad.Trans.Control+import Control.Concurrent+import Control.Exception import Data.Hashable import qualified Data.HashMap.Strict as M-import Data.IORef.Lifted+import Data.IORef import Glue.Types --- TODO: Should make this return just BasicService, hiding the HashMap.+data DogpileResult b = CachedValue (Either SomeException b)+                     | RequestInProgress (IO b)++type ResultMap a b = M.HashMap a (DogpileResult b)++type ResultRef a b = IORef (ResultMap a b)++getDogpileResult :: DogpileResult b -> IO b+getDogpileResult (RequestInProgress requestWait) = requestWait+getDogpileResult (CachedValue result) = either throw return result++waitForMVar :: MVar (Either SomeException b) -> IO b+waitForMVar mvar = do+  result <- readMVar mvar+  either throw return result++getProtectedResult :: (Eq a, Hashable a)+                   => a -> BasicService IO a b -> MVar (Either SomeException b) -> ResultRef a b -> ResultMap a b -> (ResultMap a b, IO b)+getProtectedResult request service mvar mapRef resultMap =+  let mvarWait = waitForMVar mvar+      invokeService = do+        result <- try $ service request+        putMVar mvar result+        atomicModifyIORef' mapRef (\mapToUpdate -> (M.insert request (CachedValue result) mapToUpdate, ()))+        forkIO $ do+          threadDelay (5 * 1000 * 1000)+          atomicModifyIORef' mapRef (\mapToUpdate -> (M.delete request mapToUpdate, ()))+      inCache = M.lookup request resultMap+  in  maybe (M.insert request (RequestInProgress mvarWait) resultMap, invokeService >> mvarWait) (\r -> (resultMap, getDogpileResult r)) inCache+ -- | Dogpile protection of a service, to prevent multiple calls for the same value being submitted. -- | Loses the values held within m.-dogpileProtect :: (MonadBaseControl IO m, MonadBaseControl IO n, Eq a, Hashable a) -               => BasicService m a b   -- ^ The service to protect.-               -> n (IORef (M.HashMap a (ResultVar b)), BasicService m a b)+dogpileProtect :: (Eq a, Hashable a)+               => BasicService IO a b   -- ^ The service to protect.+               -> IO (BasicService IO a b) dogpileProtect service = do   mapRef <- newIORef M.empty   let protectedService request = do-                                    firstRequestMVar <- newEmptyMVar -                                    resultAction <- atomicModifyIORef' mapRef (\refMap -> -                                        let removeFromMap           = atomicModifyIORef' mapRef (\m -> (M.delete request m, ()))-                                            invokeService           = do -                                                                        result <- bracketOnError (return ()) (\_ -> removeFromMap) (\_ -> service request)-                                                                        putMVar firstRequestMVar $ Right result-                                                                        return result-                                            updateMap mvar          = (M.insert request mvar refMap, getResult mvar)-                                            addToMap                = (M.insert request firstRequestMVar refMap, invokeService)-                                        in  maybe addToMap updateMap $ M.lookup request refMap)-                                    resultAction-  return (mapRef, protectedService)+        mvar <- newEmptyMVar+        resultAction <- atomicModifyIORef' mapRef $ getProtectedResult request service mvar mapRef+        resultAction+  return protectedService
test/Glue/RetrySpec.hs view
@@ -26,7 +26,7 @@ spec = do   describe "retryingService" $ do     it "Attempts a service call multiple times" $ do-      property $ \(request, (SmallInt failures), (SmallInt retries)) -> +      property $ \(request, (SmallInt failures), (SmallInt retries)) ->         do           ref <- liftIO $ newIORef 0           let service req   = do@@ -40,11 +40,11 @@     it "Asynchronous exceptions are rethrown" $ do       property $ \(request, retries) ->          do-          ref <- liftIO $ newIORef 0+          ref <- liftIO $ newIORef (0 :: Int)           let service req   = do                                 atomicModifyIORef' ref (\c -> (c + 1, ()))                                 threadId <- myThreadId-                                forkIO (threadDelay 10000 >> throwTo threadId UserInterrupt)+                                _ <- forkIO (threadDelay 10000 >> throwTo threadId UserInterrupt)                                 threadDelay 1000000                                 return (req * 2 :: Int)           let options       = defaultRetryOptions { maximumRetries = retries }