packages feed

shared-resource-cache 0.1.0.2 → 0.1.0.3

raw patch · 4 files changed

+18/−5 lines, 4 files

Files

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
shared-resource-cache.cabal view
@@ -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>>. 
src/Data/SharedResourceCache.hs view
@@ -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
src/Data/SharedResourceCache/Internal/ExpiringSharedResourceCache.hs view
@@ -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