packages feed

quickcheck-dynamic-3.2.0: test/Spec/DynamicLogic/Registry.hs

-- A simple local name service for threads... behaves like the Erlang
-- process registry.
module Spec.DynamicLogic.Registry where

import Control.Concurrent.STM
import Control.Monad
import GHC.Conc

type Registry = TVar [(String, ThreadId)]

isAlive :: ThreadId -> IO Bool
isAlive tid = do
  s <- threadStatus tid
  return $ s /= ThreadFinished && s /= ThreadDied

setupRegistry :: IO Registry
setupRegistry = atomically $ newTVar []

whereis :: Registry -> String -> IO (Maybe ThreadId)
whereis registry name = do
  reg <- readRegistry registry
  return $ lookup name reg

register :: Registry -> String -> ThreadId -> IO ()
register registry name tid = do
  ok <- isAlive tid
  reg <- readRegistry registry
  if ok && name `notElem` map fst reg && tid `notElem` map snd reg
    then atomically $ do
      reg' <- readTVar registry
      if name `notElem` map fst reg' && tid `notElem` map snd reg'
        then writeTVar registry ((name, tid) : reg')
        else error "badarg"
    else error "badarg"

unregister :: Registry -> String -> IO ()
unregister registry name = do
  reg <- readRegistry registry
  when (name `elem` map fst reg) $ do
    atomically $ modifyTVar registry $ filter ((/= name) . fst)

readRegistry :: Registry -> IO [(String, ThreadId)]
readRegistry registry = garbageCollect registry *> atomically (readTVar registry)

garbageCollect :: Registry -> IO ()
garbageCollect registry = do
  reg <- atomically $ readTVar registry
  garbage <- filterM (fmap not . isAlive) (map snd reg)
  atomically $ modifyTVar registry $ filter ((`notElem` garbage) . snd)
  return ()