{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE CPP #-}

-- | This is the simplest implementation of thread-local storage using
-- GHC's built-in ThreadId capabilities.
--
-- While the interface below is designed to match the other
-- implementations of TLS in this package, the GHC implementation can
-- generaly be relied upon to keep a stable copy of the TLS variable
-- for each `ThreadId` that calls `getTLS`.  This may change in the
-- future, however!

module Data.TLS.GHC
    ( TLS
    , mkTLS
    , getTLS
    , allTLS
    , forEachTLS_
    , freeAllTLS
    -- * Deprecated, backwards compatibility
    , freeTLS
    ) where

import Control.Monad
import Control.Concurrent
import Data.Map.Strict as M
import Data.IORef

-- Module signature:
--------------------------------------------------------------------------------

#include "TLS_Sig.hs"

--------------------------------------------------------------------------------

-- | A thread-local variable of type `a`.
data TLS a = TLS { mkNew     ::  !(IO a)
                 , allCopies :: {-# UNPACK #-} !(IORef (Map ThreadId a)) }

mkTLS new = do
  v <- newIORef $! M.empty
  return $! TLS new v

getTLS TLS{mkNew,allCopies} = do
    tid  <- myThreadId
    peek <- readIORef allCopies
    case M.lookup tid peek of
      Just a  -> return a
      Nothing -> do
        a <- mkNew
        atomicModifyIORef' allCopies (\ mp -> (M.insert tid a mp, ()))
        return $! a

allTLS TLS{allCopies} = do
  mp <- readIORef allCopies
  return $! M.elems mp

forEachTLS_ tls fn = do
  ls <- allTLS tls
  forM_ ls fn

-- Nothing to do here... we haven't pinned anything.  Normal GC is fine.
freeAllTLS _ =
  do return ()