{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE UnboxedTuples #-}
module Control.Concurrent.Thread.Storage
(
ThreadStorageMap
, newThreadStorageMap
, lookup
, lookupOnThread
, attach
, attachOnThread
, detach
, detachFromThread
, adjust
, adjustOnThread
, storedItems
) where
import Control.Concurrent
import Control.Concurrent.Thread.Finalizers
import Control.Monad ( void )
import Control.Monad.IO.Class
import Data.IORef
import GHC.IO (IO(..))
import GHC.Int
import GHC.Conc.Sync ( ThreadId(..) )
import GHC.Prim
import qualified Data.IntMap.Strict as I
import Foreign.C.Types
import Prelude hiding (lookup)
foreign import ccall unsafe "rts_getThreadId" c_getThreadId :: ThreadId# -> CInt
getThreadId :: ThreadId -> Int
getThreadId :: ThreadId -> Int
getThreadId (ThreadId ThreadId#
tid#) = CInt -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ThreadId# -> CInt
c_getThreadId ThreadId#
tid#)
{-# INLINE getThreadId #-}
newtype ThreadStorageMap a = ThreadStorageMap (IORef (I.IntMap a))
newThreadStorageMap
:: MonadIO m => m (ThreadStorageMap a)
newThreadStorageMap :: m (ThreadStorageMap a)
newThreadStorageMap = IO (ThreadStorageMap a) -> m (ThreadStorageMap a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IORef (IntMap a) -> ThreadStorageMap a
forall a. IORef (IntMap a) -> ThreadStorageMap a
ThreadStorageMap (IORef (IntMap a) -> ThreadStorageMap a)
-> IO (IORef (IntMap a)) -> IO (ThreadStorageMap a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IntMap a -> IO (IORef (IntMap a))
forall a. a -> IO (IORef a)
newIORef IntMap a
forall a. Monoid a => a
mempty)
readStripe :: ThreadStorageMap a -> IO (I.IntMap a)
readStripe :: ThreadStorageMap a -> IO (IntMap a)
readStripe (ThreadStorageMap IORef (IntMap a)
tsm) = IORef (IntMap a) -> IO (IntMap a)
forall a. IORef a -> IO a
readIORef IORef (IntMap a)
tsm
{-# INLINABLE readStripe #-}
atomicModifyStripe :: ThreadStorageMap a -> (I.IntMap a -> (I.IntMap a, b)) -> IO b
atomicModifyStripe :: ThreadStorageMap a -> (IntMap a -> (IntMap a, b)) -> IO b
atomicModifyStripe (ThreadStorageMap IORef (IntMap a)
tsm) IntMap a -> (IntMap a, b)
f = IORef (IntMap a) -> (IntMap a -> (IntMap a, b)) -> IO b
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (IntMap a)
tsm IntMap a -> (IntMap a, b)
f
{-# INLINABLE atomicModifyStripe #-}
lookup :: MonadIO m => ThreadStorageMap a -> m (Maybe a)
lookup :: ThreadStorageMap a -> m (Maybe a)
lookup ThreadStorageMap a
tsm = IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a)) -> IO (Maybe a) -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ do
ThreadId
tid <- IO ThreadId
myThreadId
ThreadStorageMap a -> ThreadId -> IO (Maybe a)
forall (m :: * -> *) a.
MonadIO m =>
ThreadStorageMap a -> ThreadId -> m (Maybe a)
lookupOnThread ThreadStorageMap a
tsm ThreadId
tid
{-# INLINABLE lookup #-}
lookupOnThread :: MonadIO m => ThreadStorageMap a -> ThreadId -> m (Maybe a)
lookupOnThread :: ThreadStorageMap a -> ThreadId -> m (Maybe a)
lookupOnThread ThreadStorageMap a
tsm ThreadId
tid = IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a)) -> IO (Maybe a) -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ do
let threadAsInt :: Int
threadAsInt = ThreadId -> Int
getThreadId ThreadId
tid
IntMap a
m <- ThreadStorageMap a -> IO (IntMap a)
forall a. ThreadStorageMap a -> IO (IntMap a)
readStripe ThreadStorageMap a
tsm
Maybe a -> IO (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe a -> IO (Maybe a)) -> Maybe a -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ Int -> IntMap a -> Maybe a
forall a. Int -> IntMap a -> Maybe a
I.lookup Int
threadAsInt IntMap a
m
{-# INLINABLE lookupOnThread #-}
attach :: MonadIO m => ThreadStorageMap a -> a -> m (Maybe a)
attach :: ThreadStorageMap a -> a -> m (Maybe a)
attach ThreadStorageMap a
tsm a
x = IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a)) -> IO (Maybe a) -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ do
ThreadId
tid <- IO ThreadId
myThreadId
ThreadStorageMap a -> ThreadId -> a -> IO (Maybe a)
forall (m :: * -> *) a.
MonadIO m =>
ThreadStorageMap a -> ThreadId -> a -> m (Maybe a)
attachOnThread ThreadStorageMap a
tsm ThreadId
tid a
x
{-# INLINABLE attach #-}
attachOnThread :: MonadIO m => ThreadStorageMap a -> ThreadId -> a -> m (Maybe a)
attachOnThread :: ThreadStorageMap a -> ThreadId -> a -> m (Maybe a)
attachOnThread ThreadStorageMap a
tsm ThreadId
tid a
ctxt = IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a)) -> IO (Maybe a) -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ do
let threadAsInt :: Int
threadAsInt = ThreadId -> Int
getThreadId ThreadId
tid
Maybe a
old <- ThreadStorageMap a
-> (IntMap a -> (IntMap a, Maybe a)) -> IO (Maybe a)
forall a b.
ThreadStorageMap a -> (IntMap a -> (IntMap a, b)) -> IO b
atomicModifyStripe ThreadStorageMap a
tsm ((IntMap a -> (IntMap a, Maybe a)) -> IO (Maybe a))
-> (IntMap a -> (IntMap a, Maybe a)) -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ \IntMap a
m ->
let (Maybe a
old, IntMap a
updated) = (Int -> a -> a -> a) -> Int -> a -> IntMap a -> (Maybe a, IntMap a)
forall a.
(Int -> a -> a -> a) -> Int -> a -> IntMap a -> (Maybe a, IntMap a)
I.insertLookupWithKey (\Int
_ a
n a
_ -> a
n) Int
threadAsInt a
ctxt IntMap a
m
in (IntMap a
updated, Maybe a
old)
case Maybe a
old of
Maybe a
Nothing -> ThreadId -> IO () -> IO ()
addThreadFinalizer ThreadId
tid (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ ThreadStorageMap a -> Int -> IO ()
forall a. ThreadStorageMap a -> Int -> IO ()
cleanUp ThreadStorageMap a
tsm Int
threadAsInt
Just a
_ -> () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Maybe a -> IO (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
old
{-# INLINABLE attachOnThread #-}
detach :: MonadIO m => ThreadStorageMap a -> m (Maybe a)
detach :: ThreadStorageMap a -> m (Maybe a)
detach ThreadStorageMap a
tsm = IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a)) -> IO (Maybe a) -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ do
ThreadId
tid <- IO ThreadId
myThreadId
ThreadStorageMap a -> ThreadId -> IO (Maybe a)
forall (m :: * -> *) a.
MonadIO m =>
ThreadStorageMap a -> ThreadId -> m (Maybe a)
detachFromThread ThreadStorageMap a
tsm ThreadId
tid
{-# INLINABLE detach #-}
detachFromThread :: MonadIO m => ThreadStorageMap a -> ThreadId -> m (Maybe a)
detachFromThread :: ThreadStorageMap a -> ThreadId -> m (Maybe a)
detachFromThread ThreadStorageMap a
tsm ThreadId
tid = IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a)) -> IO (Maybe a) -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ do
let threadAsInt :: Int
threadAsInt = ThreadId -> Int
getThreadId ThreadId
tid
ThreadStorageMap a
-> (IntMap a -> (IntMap a, Maybe a)) -> IO (Maybe a)
forall a b.
ThreadStorageMap a -> (IntMap a -> (IntMap a, b)) -> IO b
atomicModifyStripe ThreadStorageMap a
tsm ((IntMap a -> (IntMap a, Maybe a)) -> IO (Maybe a))
-> (IntMap a -> (IntMap a, Maybe a)) -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ \IntMap a
m -> (Int -> IntMap a -> IntMap a
forall a. Int -> IntMap a -> IntMap a
I.delete Int
threadAsInt IntMap a
m, Int -> IntMap a -> Maybe a
forall a. Int -> IntMap a -> Maybe a
I.lookup Int
threadAsInt IntMap a
m)
{-# INLINABLE detachFromThread #-}
adjust :: MonadIO m => ThreadStorageMap a -> (a -> a) -> m ()
adjust :: ThreadStorageMap a -> (a -> a) -> m ()
adjust ThreadStorageMap a
tsm a -> a
f = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ do
ThreadId
tid <- IO ThreadId
myThreadId
ThreadStorageMap a -> ThreadId -> (a -> a) -> IO ()
forall (m :: * -> *) a.
MonadIO m =>
ThreadStorageMap a -> ThreadId -> (a -> a) -> m ()
adjustOnThread ThreadStorageMap a
tsm ThreadId
tid a -> a
f
{-# INLINABLE adjust #-}
adjustOnThread :: MonadIO m => ThreadStorageMap a -> ThreadId -> (a -> a) -> m ()
adjustOnThread :: ThreadStorageMap a -> ThreadId -> (a -> a) -> m ()
adjustOnThread ThreadStorageMap a
tsm ThreadId
tid a -> a
f = IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ do
let threadAsInt :: Int
threadAsInt = ThreadId -> Int
getThreadId ThreadId
tid
ThreadStorageMap a -> (IntMap a -> (IntMap a, ())) -> IO ()
forall a b.
ThreadStorageMap a -> (IntMap a -> (IntMap a, b)) -> IO b
atomicModifyStripe ThreadStorageMap a
tsm ((IntMap a -> (IntMap a, ())) -> IO ())
-> (IntMap a -> (IntMap a, ())) -> IO ()
forall a b. (a -> b) -> a -> b
$ \IntMap a
m -> ((a -> a) -> Int -> IntMap a -> IntMap a
forall a. (a -> a) -> Int -> IntMap a -> IntMap a
I.adjust a -> a
f Int
threadAsInt IntMap a
m, ())
{-# INLINABLE adjustOnThread #-}
cleanUp :: ThreadStorageMap a -> Int -> IO ()
cleanUp :: ThreadStorageMap a -> Int -> IO ()
cleanUp (ThreadStorageMap IORef (IntMap a)
tsm) Int
tid = IORef (IntMap a) -> (IntMap a -> (IntMap a, ())) -> IO ()
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef IORef (IntMap a)
tsm ((IntMap a -> (IntMap a, ())) -> IO ())
-> (IntMap a -> (IntMap a, ())) -> IO ()
forall a b. (a -> b) -> a -> b
$ \IntMap a
m ->
(Int -> IntMap a -> IntMap a
forall a. Int -> IntMap a -> IntMap a
I.delete Int
tid IntMap a
m, ())
storedItems :: ThreadStorageMap a -> IO [(Int, a)]
storedItems :: ThreadStorageMap a -> IO [(Int, a)]
storedItems (ThreadStorageMap IORef (IntMap a)
tsm) = IntMap a -> [(Int, a)]
forall a. IntMap a -> [(Int, a)]
I.toList (IntMap a -> [(Int, a)]) -> IO (IntMap a) -> IO [(Int, a)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef (IntMap a) -> IO (IntMap a)
forall a. IORef a -> IO a
readIORef IORef (IntMap a)
tsm