shared-resource-cache 0.1.0.2 → 0.1.0.3
raw patch · 4 files changed
+18/−5 lines, 4 files
Files
- CHANGELOG.md +9/−0
- shared-resource-cache.cabal +1/−1
- src/Data/SharedResourceCache.hs +2/−1
- src/Data/SharedResourceCache/Internal/ExpiringSharedResourceCache.hs +6/−3
CHANGELOG.md view
@@ -8,6 +8,15 @@ ## Unreleased +## 0.1.0.3 - 2026-03-23++### Fixed++- Fixed a resource leak where an async exception delivered during `handleSharerLeave`+ (between `getCurrentTime` and the `atomically` call, or during the `atomically` if it+ blocks) could leave a cache entry's sharer count permanently elevated, preventing+ eviction. `handleSharerLeave` is now wrapped in `uninterruptibleMask_`.+ ## 0.1.0.2 - 2026-03-23 ### Fixed
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: shared-resource-cache-version: 0.1.0.2+version: 0.1.0.3 synopsis: A thread-safe cache for sharing resources across threads with automatic expiry description: A cache designed for guaranteeing that different threads can share the same reference to a resource. For example, it allows threads to communicate via a shared <<https://hackage.haskell.org/package/stm-2.5.3.1/docs/Control-Concurrent-STM-TChan.html TChan broadcast channel>>.
@@ -30,6 +30,7 @@ import Data.SharedResourceCache.Internal.ExpiringSharedResourceCache (CacheEntry(..), SharedResourceCache (..), CacheExpiryConfig, loadCacheableResource, handleSharerLeave, handlerSharerJoin, handleSharerLeaveSTM) import Data.SharedResourceCache.Internal.Broom (startBroomLoop) import Data.SharedResourceCache.Internal.Model (CacheExpiryConfig(..))+import Control.Exception (uninterruptibleMask_) -- | Constructs a resource cache that is expected to be used for the lifetime of the program. Internally, it forks a thread to -- manage periodically removing cache entries that have expired (as per the cache expiry configuration.)@@ -56,7 +57,7 @@ deallocateResource :: SharedResourceCache err a -> IO () deallocateResource cache = do let threadId = cacheCleanupThreadId cache- killThread threadId+ uninterruptibleMask_ (killThread threadId) -- | Executes the given action using the resource with the given ID. This function is a wrapper around the 'getCacheableResource' function, -- where the resource is freed at the end of the supplied action
@@ -35,9 +35,12 @@ removeScheduledCleanup (cleanUpMap cache) resourceId handleSharerLeave :: SharedResourceCache err a -> CacheItem a -> Text -> IO ()- handleSharerLeave cache cacheItem resourceId = do- now <- getCurrentTime- atomically (handleSharerLeaveSTM cache cacheItem resourceId now)+ handleSharerLeave cache cacheItem resourceId = + -- We wrap this in an uninterruptibleMask_ so that we don't end up with items in the cache with phantom+ -- connections if the thread is killed during a blocking operation+ uninterruptibleMask_ $ do+ now <- getCurrentTime+ atomically (handleSharerLeaveSTM cache cacheItem resourceId now) handleSharerLeaveSTM :: SharedResourceCache err a -> CacheItem a -> Text -> UTCTime -> STM () handleSharerLeaveSTM (SharedResourceCache cache cleanUpMap _ _ _ config) cacheItem resourceId now = do