packages feed

tsession-0.1: Control/TSession.hs

{-# LANGUAGE  RecordWildCards, DeriveDataTypeable, 
             ExistentialQuantification #-}

module Control.TSession
  ( atomicTransactionPart
  , createTransaction
  , readVal
  , safeLoadVal
  , writeVal
  , setStatus
  , getStatus
  , getReadSet
  , commit
  , finishSession
  , TouchedValue(..)
  , TouchedValues(..)
  , TransactionBase(..)
  , Trans(..)
  , TSessionId
  , TSessionIdGenerator(..)
  )
where

import Control.Monad.State
import Control.Exception        (catch, Exception, SomeException, throw)
import Prelude                  hiding (catch)
import Control.Concurrent       (forkIO, threadDelay)
import Control.Concurrent.MVar
import Control.Applicative      ((<$>))
import Data.Maybe
import Data.Typeable     (Typeable)
import Data.Foldable     (foldlM)
import Data.Time
import Data.IORef        (IORef, newIORef, readIORef, atomicModifyIORef)
import qualified Data.Map.Lazy as Map

type TSessionId = Integer

type Timeout = UTCTime

data TransactionBase k v status = TransactionBase
  { _trBaseName :: String
  , _trBaseLoadFun :: k -> IO (Maybe v)
  , _trBaseSaveFun :: k -> v -> IO ()
  , _trBaseTouchedValues :: MVar (Map.Map k [TouchedValue v status])
  , _tsessions :: TSessions k v status
  , _tsessionIdGenerator :: TSessionIdGenerator
  , _tsessioninitStatus :: status
  , _tsessionTimeout :: NominalDiffTime
  , _tsessionTimeoutIndex :: IORef (Map.Map TSessionId Timeout)
  }
  
type TSessions k v status = 
  MVar (Map.Map TSessionId (TSession k v status))

newtype TSession k v status = TSession 
  { _tsessionVar :: MVar (TSessionVar k v status)
  }

data TSessionVar k v status = TSessionVar 
  { _tsessionVarTimeout :: Timeout
  , _tsessionVarReadSet :: Map.Map k (TouchedValue v status)
  , _tsessionVarWriteSet :: Map.Map k (TouchedValue v status)
  , _tsessionVarStatus :: status
  }

data TSessionState k v status = TSessionState 
  { _tsessionBase :: TransactionBase k v status
  , _tsessionId :: TSessionId
  , _tsessionReadSet :: Map.Map k (TouchedValue v status)
  , _tsessionWriteSet :: Map.Map k (TouchedValue v status)
  , _tsessionCommit :: Bool
  , _tsessionFinish :: Bool
  , _tsessionStatus :: status
  , _trBaseTouchedValuesCopy :: Map.Map k [TouchedValue v status]
  }

data TSessionIdGenerator = forall g. TSessionIdGenerator 
  { _tsessionIdGeneratorRef :: IORef g
  , _tsessionIdGeneratorGenFun :: g -> (TSessionId, g)
  }

data TouchedValues v status = TouchedValues
  { _touchedValuesAll :: [TouchedValue v status]
  , _touchedValuesAfterLastLoad :: [TouchedValue v status]
  , _touchedValuesWithoutMe :: [TouchedValue v status]
  }
  deriving Show

data TouchedValue v status = 
  ValueCommitted TSessionId v | 
  ValueRead TSessionId v status | 
  ValueWritten TSessionId v status
  deriving (Show, Ord, Eq)

getVal :: TouchedValue v status -> v
getVal (ValueCommitted _ v) = v
getVal (ValueRead _ v _) = v
getVal (ValueWritten _ v _) = v

getId :: TouchedValue v status -> TSessionId
getId (ValueCommitted id _) = id
getId (ValueRead id _ _) = id
getId (ValueWritten id _ _) = id

manageTouchedValues 
  :: Ord k
    => Map.Map k [TouchedValue v status]
    -> TSessionId
    -> Map.Map k (TouchedValue v status) 
    -> Map.Map k [TouchedValue v status]
manageTouchedValues touchedVals tId touchedValuesToMerge = 
  Map.foldrWithKey f touchedVals touchedValuesToMerge
  where 
  f :: Ord k 
      => k 
      -> TouchedValue v status 
      -> Map.Map k [TouchedValue v status] 
      -> Map.Map k [TouchedValue v status]
  f k touchedValToUpdate touchedVals = Map.alter g k touchedVals
    where
    -- g :: Maybe [TouchedValue v status] -> Maybe [TouchedValue v status]
    g Nothing = Just [touchedValToUpdate]
    g (Just touchedVals) = 
      Just $ touchedValToUpdate:(filter touchedValWithId touchedVals)
    touchedValWithId :: TouchedValue v status -> Bool
    touchedValWithId (ValueRead tId' _ _) = 
      tId' /= tId
    touchedValWithId (ValueWritten tId' _ _) = 
      tId' /= tId
    touchedValWithId _ = True
  
createTransaction 
  :: Ord k
    => String                      -- ^ TransactionBase name
    -> (g -> (TSessionId, g), g)   -- ^ Tuple of ID generation function
                                   --   and generator state
    -> status                      -- ^ Initial status of new tsession
    -> (k -> IO (Maybe v))         -- ^ Function for loading from database
    -> (k -> v -> IO ())           -- ^ Function for storing to database
    -> NominalDiffTime             -- ^ Timeout in seconds
    -> IO (TransactionBase k v status)
createTransaction 
  name idFun_initransactionIdState iStatus loadFun saveFun timeout = do
  let (idFun, initransactionIdState) = idFun_initransactionIdState
  idRef <- newIORef initransactionIdState
  let idGenerator = TSessionIdGenerator idRef idFun
  statusRef <- newMVar Map.empty
  touchedValsRef <- newMVar Map.empty
  timeoutIndex <- newIORef Map.empty
  let tr = TransactionBase 
        { _trBaseName = name
        , _tsessionIdGenerator = idGenerator
        , _tsessions = statusRef
        , _tsessioninitStatus = iStatus
        , _trBaseLoadFun = loadFun
        , _trBaseSaveFun = saveFun
        , _tsessionTimeout = timeout
        , _trBaseTouchedValues = touchedValsRef
        , _tsessionTimeoutIndex = timeoutIndex
        }
  forkIO $ timeouthandler tr
  return tr
    
-- The STM monad itself
-- newtype  STM a = STM {runSTM :: StateT StmState IO (STMResult a)}
data TransactionResult a = Success a | Exception TransactionException

data TransactionException = TransactionException String
  deriving (Show, Eq, Typeable)
instance Exception TransactionException

newtype Trans k v status a = Trans 
  { runTransactionPart :: 
      StateT (TSessionState k v status) IO (TransactionResult a) 
  }

instance Monad (Trans k v status) where
  return = Trans . return . Success
  x >>= f = Trans $ do
    res <- runTransactionPart x
    case res of
      Success a ->
        runTransactionPart $ f a
      Exception e -> return $ Exception e

readVal 
  :: Ord k
    => k 
    -> Trans k v status (Maybe v, TouchedValues v status)
readVal k = Trans $ do
  state@TSessionState{..} <- get
  let lookup_touchedVals = 
        fromMaybe [] $ Map.lookup k _trBaseTouchedValuesCopy
  let tuchedVals = TouchedValues 
        { _touchedValuesAll = lookup_touchedVals
        , _touchedValuesAfterLastLoad = takeWhile filterTId lookup_touchedVals
        , _touchedValuesWithoutMe  = filter filterTId lookup_touchedVals
        }
        where 
        filterTId :: TouchedValue v status -> Bool
        filterTId tVal     = (getId tVal) /= _tsessionId
  -- first look in write set:
  case Map.lookup k _tsessionWriteSet of
    Just (ValueWritten _ v _) -> do
      put $ state 
        { _tsessionReadSet = 
            Map.insert 
              k 
              (ValueRead _tsessionId v _tsessionStatus) 
              _tsessionReadSet
        }
      return $ Success (Just v, tuchedVals)
    Nothing -> do
  -- then look in read set:
      case Map.lookup k _tsessionReadSet of
        Just (ValueRead _ v _) -> do
          return $ Success (Just v, tuchedVals)
        _ -> do
  -- then look in touched vals:
          case lookupValueCommitted lookup_touchedVals of
            Just v -> do
              put $ state 
                { _tsessionReadSet = 
                    Map.insert 
                      k 
                      (ValueRead _tsessionId v _tsessionStatus) 
                      _tsessionReadSet
                }
              return $ Success (Just v, tuchedVals)
            Nothing -> do
  -- finally load from DB:
              let tr@TransactionBase{..} = _tsessionBase
              vM <- liftIO $ _trBaseLoadFun k 
              case vM of
                Just v -> do
                  put $ state 
                    { _tsessionReadSet = 
                        Map.insert 
                          k 
                          (ValueRead _tsessionId v _tsessionStatus) 
                          _tsessionReadSet
                    }
                  return $ Success (Just v, tuchedVals)
                Nothing -> return $ 
                  Success (Nothing, tuchedVals)
                  
lookupValueCommitted :: [TouchedValue v status] -> Maybe v
lookupValueCommitted [] = Nothing
lookupValueCommitted ((ValueCommitted _ v):_) = Just v
lookupValueCommitted (_:xs) = lookupValueCommitted xs


safeLoadVal 
  :: Ord k
    => k 
    -> Trans k v status (Maybe v)
safeLoadVal k = Trans $ do
  state@TSessionState{..} <- get
  let lookup_touchedVals = 
        fromMaybe [] $ Map.lookup k _trBaseTouchedValuesCopy
  case lookupValueCommitted lookup_touchedVals of
    Just v -> do
      put $ state 
        { _tsessionReadSet = 
            Map.insert 
              k 
              (ValueRead _tsessionId v _tsessionStatus) 
              _tsessionReadSet
        }
      return $ Success $ Just v
    Nothing -> do
    -- finally load from DB:
      let tr@TransactionBase{..} = _tsessionBase
      vM <- liftIO $ _trBaseLoadFun k 
      case vM of
        Just v -> do
          put $ state 
            { _tsessionReadSet = 
                Map.insert 
                  k 
                  (ValueRead _tsessionId v _tsessionStatus) 
                  _tsessionReadSet
            }
          return $ Success $ Just v
        Nothing -> return $ 
          Success Nothing


writeVal 
  :: Ord k 
    => k 
    -> v 
    -> Trans k v status ()
writeVal k v = Trans $ do
  state@TSessionState{..} <- get
  put $ state 
    { _tsessionWriteSet = 
        Map.insert 
          k 
          (ValueWritten _tsessionId v _tsessionStatus) 
          _tsessionWriteSet 
    }
  return $ Success ()

setStatus 
  :: Eq status 
    => status 
    -> Trans k v status ()
setStatus status = Trans $ do
  state@TSessionState{..}  <- get
  when (_tsessionStatus /= status) $ do
    let transactionReadSet = 
          Map.map 
            (\(ValueRead id v _) -> (ValueRead id v status))
            _tsessionReadSet
    let transactionWriteSet = 
          Map.map 
            (\(ValueWritten id v _) -> (ValueWritten id v status)) 
            _tsessionWriteSet
    -- _tsessionReadSet :: Map.Map k (TouchedValue v status)
    put $ state 
      { _tsessionStatus = status
      , _tsessionWriteSet = transactionWriteSet
      , _tsessionReadSet = transactionReadSet 
      }
  return $ Success ()

getStatus :: Trans k v status status
getStatus = Trans $ do
  state@TSessionState{..}  <- get
  return $ Success _tsessionStatus

commit :: Trans k v status ()
commit = Trans $ do
  state <- get
--  liftIO $ putStrLn "Commit called"
  put $ state { _tsessionCommit = True }
  return $ Success ()

finishSession :: Trans k v status ()
finishSession = Trans $ do
  state <- get
  put $ state { _tsessionFinish = True }
  return $ Success ()

getReadSet 
  :: Ord k 
    => Trans k v status (Map.Map k [TouchedValue v status])
getReadSet = Trans $ do
  state@TSessionState{..} <- get
  let a = Map.foldrWithKey f Map.empty _tsessionReadSet
          where 
          -- f :: Ord k 
          --   => k 
          --   -> (TouchedValue v status) 
          --   -> Map.Map k [TouchedValue v status] 
          --   -> Map.Map k [TouchedValue v status]
          f k _ readSetMap = 
            let lookup_touchedVals = fromMaybe [] $ 
                  Map.lookup k _trBaseTouchedValuesCopy in
            let touchedReadValsAfterMyLastCommit = 
                  filter filterReadVal {- $ takeWhile filterTId -} lookup_touchedVals in
              Map.insert k touchedReadValsAfterMyLastCommit readSetMap
          filterTId :: TouchedValue v status -> Bool
          filterTId (ValueRead tId _ _)      = tId /= _tsessionId
          filterTId (ValueWritten tId _ _) = tId /= _tsessionId
          filterTId _ = True
          filterReadVal :: TouchedValue v status -> Bool
          filterReadVal (ValueRead _ _ _) = False
          filterReadVal _ = True
  return $ Success a

atomicTransactionPart
  :: (Ord k, Eq v, MonadIO m) 
    => (TransactionBase k v status -> m TSessionId)
    -> TransactionBase k v status 
    -> Trans k v status a -> m a
atomicTransactionPart cookieFun tr@TransactionBase{..} code = do
  tId <- cookieFun tr
  --liftIO $ print tId
  tSessions <- liftIO $ readMVar _tsessions
  let tsM = findSession tId tSessions 
  case tsM of
    Nothing -> do -- status was deleted after timeout or new transaction 
      tSessions <- liftIO $ takeMVar _tsessions
      -- search again with lock:
      let pageVarM = findSession tId tSessions 
      case pageVarM of
        Nothing -> do  -- create new transaction instance:
          trVar <- liftIO $ newEmptyMVar 
          let tSession = TSession trVar
          liftIO $ putMVar _tsessions $
            Map.insert tId tSession tSessions
          globalTouchedVals <- liftIO $ readMVar _trBaseTouchedValues
          
          let state =
                TSessionState 
                  tr 
                  tId 
                  Map.empty  
                  Map.empty
                  False
                  False
                  _tsessioninitStatus
                  globalTouchedVals 
          
          runcode tSession Map.empty Map.empty  
                  _tsessioninitStatus tId state 
          
        Just _ -> do -- fallback
          liftIO $ putMVar _tsessions tSessions
          atomicTransactionPart cookieFun tr code 
          
    Just tSession@TSession{..} -> do 
      
      _trVar@TSessionVar{..} <- liftIO $ takeMVar _tsessionVar
      globalTouchedVals <- liftIO $ readMVar _trBaseTouchedValues
      let state = 
            TSessionState 
              tr 
              tId
              _tsessionVarReadSet
              _tsessionVarWriteSet
              False
              False
              _tsessionVarStatus
              globalTouchedVals

      runcode  tSession _tsessionVarReadSet _tsessionVarWriteSet 
              _tsessionVarStatus tId state 
      
  where 
  -- _tsessionVar already taken !
  runcode _ts@TSession{..} oldReadSet 
          oldWriteSet oldStatus tId state = do
    (transactionResult, _state'@TSessionState{..}) <- liftIO $ 
      catch 
        (runStateT (runTransactionPart code) state)
        (\e -> return 
          (Exception 
            (TransactionException (show (e :: SomeException))), state))
            
    timeout <- liftIO $ addUTCTime _tsessionTimeout <$> getCurrentTime
    liftIO $ updateTimeout tr tId (Just timeout)
    
    case transactionResult of
      Exception e -> do
        liftIO $ putMVar _tsessionVar $
          TSessionVar 
            timeout 
            oldReadSet 
            oldWriteSet 
            oldStatus
        Control.Exception.throw e
        
      Success a -> do
      
        
        -- check if values from read-set are saved in the mean time:
        let newValsInReadSet = Map.difference _tsessionReadSet oldReadSet
        -- liftIO $ print newValsInReadSet
        -- lookupValueCommitted
        
        -- when ((not.Map.null) newValsInReadSet) $ 
        --  liftIO $ threadDelay $ 3 * 1000 * 1000
      
        globalTouchedVals <- liftIO $ takeMVar _trBaseTouchedValues
        
        -- liftIO $ putStrLn $ "newValsInReadSet: " ++ show newValsInReadSet
        
        -- check if values from read-set are saved in the mean time:
        let invalid = or $ map f (Map.toList newValsInReadSet)
              where
              -- f :: (k, TouchedValue v status) -> Bool
              f (k, touchedValRS) = case Map.lookup k globalTouchedVals of
                Nothing -> False
                Just touchedVal -> case lookupValueCommitted touchedVal of
                  Nothing -> False
                  Just v -> getVal touchedValRS /= v
          
        if invalid
          then do -- rollback
            liftIO $ putMVar _trBaseTouchedValues globalTouchedVals
            liftIO $ putMVar _tsessionVar $
              TSessionVar
                timeout 
                oldReadSet 
                oldWriteSet 
                oldStatus
            atomicTransactionPart cookieFun tr code
            
          else do -- not invalid
            if _tsessionCommit 
              then do
                -- write write-set to data base:
                liftIO $ 
                  Map.traverseWithKey 
                    (\k (ValueWritten _ v _) -> _trBaseSaveFun k v) 
                    _tsessionWriteSet
                
                let globalTouchedVals' = 
                      Map.foldrWithKey 
                        turnWrittenToComitted 
                          globalTouchedVals _tsessionWriteSet
                liftIO $ putMVar _trBaseTouchedValues globalTouchedVals'
                
              else do -- no commit
                let unionReadWriteSet = 
                -- if val in write and read set, use val from
                -- write set (argument order of union):
                      Map.union _tsessionWriteSet _tsessionReadSet                      
                let globalTouchedVals' = 
                      manageTouchedValues globalTouchedVals tId unionReadWriteSet
                liftIO $ putMVar _trBaseTouchedValues globalTouchedVals'
            
            liftIO $ putMVar _tsessionVar $
              TSessionVar
                timeout 
                _tsessionReadSet 
                _tsessionWriteSet 
                _tsessionStatus
            
            when _tsessionFinish $ 
              liftIO $ removeSessions tr Nothing [_tsessionId]
            return a

    where 
    filterValueWritten :: TouchedValue v status -> Bool
    filterValueWritten (ValueWritten _ _ _) = False
    filterValueWritten _ = True
    
    turnWrittenToComitted :: Ord k 
        => k 
        -> TouchedValue v status 
        -> Map.Map k [TouchedValue v status] 
        -> Map.Map k [TouchedValue v status]
    turnWrittenToComitted k touchedValToUpdate touchedVals = 
      Map.alter (g touchedValToUpdate) k touchedVals
      where 
      g _                           Nothing            = Nothing
      -- if tuchedVal == ValueWritten, insert a corresponding ValueCommitted to 
      -- list and filter out all vals with my id and the ValueCommitted
      g (ValueWritten id v _status) (Just touchedVals) = 
        Just $ (ValueCommitted id v) : filter touchedValWithIdAndCommitted touchedVals
      g _                           (Just touchedVals) = Just touchedVals
      touchedValWithIdAndCommitted :: TouchedValue v status -> Bool
      touchedValWithIdAndCommitted (ValueCommitted _ _) = False
      touchedValWithIdAndCommitted _                    = True
      

timeouthandler 
  :: Ord k
    => TransactionBase k v status 
    -> IO ()
timeouthandler tr@TransactionBase{..} = do
  threadDelay $ 1000 * 1000 * (round _tsessionTimeout ) `div` 10
  timeoutIndex <- readIORef _tsessionTimeoutIndex
  now <- getCurrentTime
  -- find all timedout IDs:
  let timedOutSessionIds = Map.foldlWithKey (f now) [] timeoutIndex  
        where
        -- foldlWithKey :: (a -> k -> b -> a) -> a -> Map k b -> a
        f :: Timeout 
            -> [TSessionId] 
            -> TSessionId 
            -> Timeout 
            -> [TSessionId]
        f now ids id timeout = 
          if round (diffUTCTime timeout now) <= 0 then id:ids else ids
              
  when ((not.null) timedOutSessionIds) $ do
    -- putStrLn $ "Timed out IDs: " ++ show timedOutSessionIds
    removeSessions tr (Just now) timedOutSessionIds
    
  timeouthandler tr
  
removeSessions
  :: Ord k 
    => TransactionBase k v status 
    -> Maybe UTCTime 
    -> [TSessionId] 
    -> IO ()
removeSessions tr@TransactionBase{..} nowM trIdsToRemove = do

  tSessions <- takeMVar _tsessions
  -- delete given Instances and collect TouchedValues that are to delete:
  (touchedValsToDelete, tSessions') <-
    foldlM (removeSessionAndFindTVtoDel nowM) ([], tSessions) trIdsToRemove 
  putMVar _tsessions tSessions'
  
  -- when ((not.null) touchedValsToDelete) $ 
  --   putStrLn $ "TouchedVals to delete: " ++ show touchedValsToDelete 
       -- :: Map.Map k (TouchedValue v status)
  
  touchedVals <- takeMVar _trBaseTouchedValues
  let touchedVals' = cleanupTouchedValues touchedVals touchedValsToDelete
  putMVar _trBaseTouchedValues touchedVals'
  
  -- when ((not.Map.null) touchedVals') $ 
  --   putStrLn $ "New touched vals: " ++ show touchedVals' 
       -- :: Map.Map k (TouchedValue v status)

  where
  removeSessionAndFindTVtoDel :: Ord k
      => Maybe UTCTime
      -> ([(k, TouchedValue v status)],
          Map.Map TSessionId (TSession k v status))
      -> TSessionId
      -> IO ([(k, TouchedValue v status)],
             Map.Map TSessionId (TSession k v status))
  removeSessionAndFindTVtoDel nowM (touchedValsToDelete, tSessions) tId = do
    let tSessionM = Map.lookup tId tSessions
    case tSessionM of
      Just tSession@TSession{..}-> do
        -- try to take MVar. If taken, don't delete. 
        -- if not taken, check timeout again and delete.
        tSessionVarM <- tryTakeMVar _tsessionVar 
        case tSessionVarM of
          Just tSessionVar@TSessionVar{..} -> do
            if nowM == Nothing || 
               round 
                 (diffUTCTime _tsessionVarTimeout 
                   (fromJust nowM)) 
                <= 0
              then do
                -- delete entry in Timeout Index:
                updateTimeout tr tId Nothing
                -- putStrLn $ "timeout: " ++ show tId
                -- look up and delete transaction instance:
                let (tSessionM, tSessions') = 
                      Map.updateLookupWithKey 
                        (\k v -> Nothing) 
                        tId 
                        tSessions
                let touchedValsToDelete' = 
                      case tSessionM of 
                        -- if instance available, collect read and write
                        -- set for later deletion in touched values:
                        Just tSession@TSession{..} -> 
                          Map.toList $ Map.union 
                            _tsessionVarReadSet
                            _tsessionVarWriteSet
                        Nothing -> []
                return 
                  ( touchedValsToDelete' ++ touchedValsToDelete
                  , tSessions'
                  ) 
              else return (touchedValsToDelete, tSessions)
          Nothing -> return (touchedValsToDelete, tSessions)
      Nothing -> return (touchedValsToDelete, tSessions)

  cleanupTouchedValues 
    :: Ord k
      => Map.Map k [TouchedValue v status] -- ^ global Touched Values (input)
      -> [(k, TouchedValue v status)]      -- ^ list of Touched Vals to remove
      -> Map.Map k [TouchedValue v status] -- ^ global Touched Values (output)
  cleanupTouchedValues touchedVals touchedValsToDelete = 
    foldl cleanupTouchedValue touchedVals touchedValsToDelete
    
  cleanupTouchedValue
    :: Ord k
      => Map.Map k [TouchedValue v status] -- ^ global Touched Values (input)
      -> (k, TouchedValue v status)        -- ^ Pair of key and TV to remove
      -> Map.Map k [TouchedValue v status] -- ^ global Touched Values (output)
  cleanupTouchedValue touchedVals (k, touchedVal) = 
    Map.alter (delIdInList (getId touchedVal)) k touchedVals
    
  delIdInList :: TSessionId                -- ^ ID to remove
      -> Maybe [TouchedValue v status]     -- ^ Touched Value List (input)
      -> Maybe [TouchedValue v status]     -- ^ Touched Value List (output)
  delIdInList _ Nothing = Nothing
  delIdInList tId (Just valList) = let newValList = filter filterTId valList in
    if isEmptyOrValueCommitted newValList
      then Nothing 
      else Just newValList
    where
    filterTId (ValueRead tId' _ _) = tId /= tId'
    filterTId (ValueWritten tId' _ _) = tId /= tId'
    filterTId _ = True
    isEmptyOrValueCommitted [] = True
    isEmptyOrValueCommitted [(ValueCommitted _ _)] = True
    isEmptyOrValueCommitted _ = False

    
updateTimeout 
  :: TransactionBase k v status 
    -> TSessionId 
    -> Maybe Timeout 
    -> IO ()
updateTimeout _tr@TransactionBase{..} tId timeout = do
  atomicModifyIORef 
    _tsessionTimeoutIndex  
    (\x -> (Map.alter (\_ -> timeout) tId  x, ()))




   -- [(TSessionId, Timeout, Messagebox k v, MVar ([(k, v)], status))]
findSession 
  :: TSessionId 
    -> Map.Map TSessionId (TSession k v status) 
    -> Maybe (TSession k v status)
findSession = Map.lookup