withImageCache :: Manager
               -> Int
               -> Int
               -> String
               -> (ImageCache -> IO ())
               -> IO ()
withImageCache manager memCacheEntryLimit numConcReq cacheFolder f = do
    -- Make sure our cache folder exists
    createDirectoryIfMissing True cacheFolder
    -- Build record
    ic <- do
        icOutstandingReq <- newTVarIO $ LBM.empty $ memCacheEntryLimit `div` 2
        icCacheEntries   <- newTVarIO $ LBM.empty memCacheEntryLimit
        [icBytesTrans, icMisses, icDiskHits, icMemHits] <- replicateM 4 $ newIORef 0
        return $ ImageCache { icCacheFolder = B8.pack $ addTrailingPathSeparator cacheFolder
                            , ..
                            }
    bracket -- Fetch thread launch and cleanup
        --
        -- Note the asyncWithUnmask. Otherwise all fetch threads would be
        -- created with a MaskedInterruptible masking state (bracket does that
        -- for its 'before' operations) and we would have hangs during cleanup
        --
        (forM [1..numConcReq] $ \_ -> asyncWithUnmask $ fetchThread ic manager)
        (\threads -> do
            -- Error checking, statistics
            req   <- LBM.valid <$> (atomically . readTVar $ icOutstandingReq ic)
            cache <- LBM.valid <$> (atomically . readTVar $ icCacheEntries   ic)
            case req   of Just err -> traceS TLError $ "LRUBoundedMap: icOutstandingReq:\n" ++ err
                          Nothing  -> return ()
            case cache of Just err -> traceS TLError $ "LRUBoundedMap: icCacheEntries:\n"   ++ err
                          Nothing  -> return ()
            -- Shutdown
            traceT TLInfo "Shutting down image fetch threads"
            forM_ threads cancel
            forM_ threads $ \thread -> do
                r <- waitCatch thread
                case r of
                    Left ex -> traceS TLError $ printf "Exception from fetch thread '%s': %s"
                                                       (show $ asyncThreadId thread)
                                                       (show ex)
                    _       -> return ()
        )
        (\_ -> f ic)
withImageCache :: Manager
               -> Int
               -> Int
               -> String
               -> (ImageCache -> IO ())
               -> IO ()
withImageCache manager memCacheEntryLimit numConcReq cacheFolder f = do
    -- Make sure our cache folder exists
    createDirectoryIfMissing True cacheFolder
    -- Build record
    ic <- do
        icOutstandingReq <- newTVarIO $ LBM.empty $ memCacheEntryLimit `div` 2
        icCacheEntries   <- newTVarIO $ LBM.empty memCacheEntryLimit
        [icBytesTrans, icMisses, icDiskHits, icMemHits] <- replicateM 4 $ newIORef 0
        return $ ImageCache { icCacheFolder = B8.pack $ addTrailingPathSeparator cacheFolder
                            , ..
                            }
    bracket -- Fetch thread launch and cleanup
        --
        -- Note the asyncWithUnmask. Otherwise all fetch threads would be
        -- created with a MaskedInterruptible masking state (bracket does that
        -- for its 'before' operations) and we would have hangs during cleanup
        --
        (forM [1..numConcReq] $ \_ -> asyncWithUnmask $ fetchThread ic manager)
        (\threads -> do
            -- Error checking, statistics
            req   <- LBM.valid <$> (atomically . readTVar $ icOutstandingReq ic)
            cache <- LBM.valid <$> (atomically . readTVar $ icCacheEntries   ic)
            case req   of Just err -> traceS TLError $ "LRUBoundedMap: icOutstandingReq:\n" ++ err
                          Nothing  -> return ()
            case cache of Just err -> traceS TLError $ "LRUBoundedMap: icCacheEntries:\n"   ++ err
                          Nothing  -> return ()
            -- Shutdown
            traceT TLInfo "Shutting down image fetch threads"
            forM_ threads cancel
            forM_ threads $ \thread -> do
                r <- waitCatch thread
                case r of
                    Left ex -> traceS TLError $ printf "Exception from fetch thread '%s': %s"
                                                       (show $ asyncThreadId thread)
                                                       (show ex)
                    _       -> return ()
        )
        (\_ -> f ic)
withImageCache manager memCacheEntryLimit numConcReq cacheFolder f = do
    -- Make sure our cache folder exists
    createDirectoryIfMissing True cacheFolder
    -- Build record
    ic <- do
        icOutstandingReq <- newTVarIO $ LBM.empty $ memCacheEntryLimit `div` 2
        icCacheEntries   <- newTVarIO $ LBM.empty memCacheEntryLimit
        [icBytesTrans, icMisses, icDiskHits, icMemHits] <- replicateM 4 $ newIORef 0
        return $ ImageCache { icCacheFolder = B8.pack $ addTrailingPathSeparator cacheFolder
                            , ..
                            }
    bracket -- Fetch thread launch and cleanup
        --
        -- Note the asyncWithUnmask. Otherwise all fetch threads would be
        -- created with a MaskedInterruptible masking state (bracket does that
        -- for its 'before' operations) and we would have hangs during cleanup
        --
        (forM [1..numConcReq] $ \_ -> asyncWithUnmask $ fetchThread ic manager)
        (\threads -> do
            -- Error checking, statistics
            req   <- LBM.valid <$> (atomically . readTVar $ icOutstandingReq ic)
            cache <- LBM.valid <$> (atomically . readTVar $ icCacheEntries   ic)
            case req   of Just err -> traceS TLError $ "LRUBoundedMap: icOutstandingReq:\n" ++ err
                          Nothing  -> return ()
            case cache of Just err -> traceS TLError $ "LRUBoundedMap: icCacheEntries:\n"   ++ err
                          Nothing  -> return ()
            -- Shutdown
            traceT TLInfo "Shutting down image fetch threads"
            forM_ threads cancel
            forM_ threads $ \thread -> do
                r <- waitCatch thread
                case r of
                    Left ex -> traceS TLError $ printf "Exception from fetch thread '%s': %s"
                                                       (show $ asyncThreadId thread)
                                                       (show ex)
                    _       -> return ()
        )
        (\_ -> f ic)