packages feed

hs-bindgen-1.0.0.0: src-internal/HsBindgen/Cache.hs

module HsBindgen.Cache (
    Cached(..)
  , getCached
  , cacheWith
  , CacheMsg(..)
  ) where

import Control.Concurrent (MVar, modifyMVar, newMVar)
import Control.Monad.IO.Class
import GHC.Generics (Generic)
import Text.SimplePrettyPrint (CtxDoc, (<+>), (><))
import Text.SimplePrettyPrint qualified as PP

import HsBindgen.Util.Tracer

newtype Cached a = Cached (IO a)
  deriving newtype (Functor, Applicative, Monad, MonadIO)

getCached :: Cached a -> IO a
getCached (Cached ma) = ma

-- | Cache a computation with a name.
--
-- Names only serve for debug messages. They need not be unique.
cacheWith :: Tracer CacheMsg -> Maybe String -> Cached a -> IO (Cached a)
cacheWith tracerCache name action = do
  cacheVar <- newMVar Nothing
  pure $ getWithCache tracerCache name cacheVar action

getWithCache ::
     Tracer CacheMsg
  -> Maybe String
  -> MVar (Maybe a)
  -> Cached a
  -> Cached a
getWithCache tracerCache name cacheVar action = Cached $
    modifyMVar cacheVar $ \case
      Nothing -> do
        traceWith tracerCache $ withCallStack $ CacheMiss name
        !newRes <- getCached action
        pure (Just newRes, newRes)
      Just cachedRes -> do
        traceWith tracerCache $ withCallStack $ CacheHit name
        pure (Just cachedRes, cachedRes)

{-------------------------------------------------------------------------------
  Traces
-------------------------------------------------------------------------------}

data CacheMsg =
    CacheMiss (Maybe String)
  | CacheHit  (Maybe String)
  deriving (Show, Eq, Generic)

prettyForTraceName :: Maybe String -> CtxDoc
prettyForTraceName Nothing = "anonymous computation"
prettyForTraceName (Just name) = PP.string name

instance PrettyForTrace CacheMsg where
  prettyForTrace = \case
    CacheMiss mName -> "Cache miss:" <+> prettyForTraceName mName >< "; computing value"
    CacheHit  mName -> "Cache hit: " <+> prettyForTraceName mName

instance IsTrace SafeLevel CacheMsg where
  getDefaultLogLevel = const SafeDebug
  getSource          = const HsBindgen
  getTraceId         = const "cache"