transient-0.7.0.0: src/Transient/Mailboxes.hs
{-# LANGUAGE ExistentialQuantification #-}
module Transient.Mailboxes where
import Transient.Internals
import Transient.EVars
import qualified Data.Map as M
import Data.IORef
import Data.Typeable
import System.IO.Unsafe
import Unsafe.Coerce
import Control.Monad.IO.Class
mailboxes :: IORef (M.Map MailboxId (EVar SData))
mailboxes= unsafePerformIO $ newIORef M.empty
data MailboxId = forall a .(Typeable a, Ord a) => MailboxId a TypeRep
--type SData= ()
instance Eq MailboxId where
id1 == id2 = id1 `compare` id2== EQ
instance Ord MailboxId where
MailboxId n t `compare` MailboxId n' t'=
case typeOf n `compare` typeOf n' of
EQ -> case n `compare` unsafeCoerce n' of
EQ -> t `compare` t'
LT -> LT
GT -> GT
other -> other
instance Show MailboxId where
show ( MailboxId _ t) = show t
-- | write to the mailbox
-- Mailboxes are node-wide, for all processes that share the same connection data, that is, are under the
-- same `listen` or `connect`
-- while EVars are only visible by the process that initialized it and his children.
-- Internally, the mailbox is in a well known EVar stored by `listen` in the `Connection` state.
putMailbox :: Typeable val => val -> TransIO ()
putMailbox = putMailbox' (0::Int)
-- | write to a mailbox identified by an identifier besides the type
putMailbox' :: (Typeable key, Ord key, Typeable val) => key -> val -> TransIO ()
putMailbox' idbox dat= do
let name= MailboxId idbox $ typeOf dat
mbs <- liftIO $ readIORef mailboxes
let mev = M.lookup name mbs
case mev of
Nothing -> newMailbox name >> putMailbox' idbox dat
Just ev -> writeEVar ev $ unsafeCoerce dat
newMailbox :: MailboxId -> TransIO ()
newMailbox name= do
-- return () -- !> "newMailBox"
ev <- newEVar
liftIO $ atomicModifyIORef mailboxes $ \mv -> (M.insert name ev mv,())
-- | get messages from the mailbox that matches with the type expected.
-- The order of reading is defined by `readTChan`
-- This is reactive. it means that each new message trigger the execution of the continuation
-- each message wake up all the `getMailbox` computations waiting for it.
getMailbox :: Typeable val => TransIO val
getMailbox = getMailbox' (0 :: Int)
-- | read from a mailbox identified by an identifier besides the type
getMailbox' :: (Typeable key, Ord key, Typeable val) => key -> TransIO val
getMailbox' mboxid = x where
x = do
let name= MailboxId mboxid $ typeOf $ typeOfM x
mbs <- liftIO $ readIORef mailboxes
let mev = M.lookup name mbs
case mev of
Nothing ->newMailbox name >> getMailbox' mboxid
Just ev ->unsafeCoerce $ readEVar ev
typeOfM :: TransIO a -> a
typeOfM = undefined
-- | delete all subscriptions for that mailbox expecting this kind of data
deleteMailbox :: Typeable a => a -> TransIO ()
deleteMailbox = deleteMailbox' (0 ::Int)
-- | clean a mailbox identified by an Int and the type
deleteMailbox' :: (Typeable key, Ord key, Typeable a) => key -> a -> TransIO ()
deleteMailbox' mboxid witness= do
let name= MailboxId mboxid $ typeOf witness
mbs <- liftIO $ readIORef mailboxes
let mev = M.lookup name mbs
case mev of
Nothing -> return()
Just ev -> do cleanEVar ev
liftIO $ atomicModifyIORef mailboxes $ \bs -> (M.delete name bs,())