module Sound.SC3.Server.Connection (
Connection
, open
, close
, send
, withListener
) where
import Control.Concurrent (ThreadId, forkIO, myThreadId)
import Control.Monad (forever)
import Control.Concurrent.MVar
import qualified Control.Exception as E
import Control.Monad (void)
import qualified Data.HashTable.IO as H
import Sound.OSC.FD (OSC(..), Packet, Transport)
import qualified Sound.OSC.FD as OSC
type Listener = Packet -> IO ()
type ListenerMap = H.CuckooHashTable ThreadId Listener
data Connection = forall t . Transport t => Connection t (MVar ListenerMap)
listeners :: Connection -> MVar ListenerMap
listeners (Connection _ l) = l
recvLoop :: Connection -> IO ()
recvLoop (Connection t ls) = forever $
OSC.recvPacket t >>= \osc -> withMVar ls $ H.mapM_ (\(_, l) -> l osc)
open :: Transport t => t -> IO Connection
open t = do
ls <- newMVar =<< H.new
let c = Connection t ls
void $ forkIO $ recvLoop c
return c
close :: Connection -> IO ()
close (Connection t _) = OSC.close t
send :: OSC o => Connection -> o -> IO ()
send (Connection t _) = OSC.sendOSC t
addListener :: Connection -> Listener -> IO (ThreadId, Maybe Listener)
addListener c l = do
uid <- myThreadId
withMVar (listeners c) $ \lm -> do
l' <- H.lookup lm uid
H.insert lm uid l
return (uid, l')
removeListener :: Connection -> ThreadId -> Maybe Listener -> IO ()
removeListener c uid Nothing = withMVar (listeners c) $ \ls -> H.delete ls uid
removeListener c uid (Just l) = withMVar (listeners c) $ \ls -> H.insert ls uid l
withListener :: Connection -> (Packet -> IO ()) -> IO a -> IO a
withListener c l = do
E.bracket
(addListener c l)
(uncurry (removeListener c))
. const