packages feed

hbro-1.2.0.0: library/Hbro/IPC.hs

{-# LANGUAGE TemplateHaskell, TupleSections #-}
-- | Designed to be imported as @qualified@.
module Hbro.IPC
    ( routine
    , sendMessage
    ) where

-- {{{ Imports
import Hbro.IPC.Signals
import Hbro.Logger
import Hbro.Prelude

import Control.Lens hiding(Action, Context)

import System.ZMQ4.Monadic (ZMQ, Rep(..), Req(..), runZMQ)
import qualified System.ZMQ4.Monadic as ZMQ
-- }}}

-- | Designed to be run in a distinct thread
routine :: Text -> Signals -> ZMQ z ()
routine uri signals = do
    socket <- ZMQ.socket Rep
    io . infoM "hbro.ipc" $ "Opening IPC socket at: " ++ uri
    ZMQ.bind socket (unpack uri)

    forever $ do
      message <- receive socket
      io . debugM "hbro.ipc" $ "Received command: " ++ message

      case words message of
          []                -> send socket "ERROR Empty command"
          command:arguments -> do
              atomically $ putTMVar nextCommand (Command command, arguments)
              response' <- atomically $ takeTMVar response
              io . debugM "hbro.ipc" $ "Sending response: " ++ tshow response'
              send socket $ either ("ERROR " ++) id response'
    where
      nextCommand = signals^.nextCommandL
      response    = signals^.responseL

-- {{{ Utils
-- | Send message to given socket
send :: (ZMQ.Sender a) => ZMQ.Socket z a -> Text -> ZMQ z ()
send socket payload = ZMQ.send socket [] (encodeUtf8 payload)

-- | Send a single command to the given socket (which must be 'Rep'), and return the answer
sendMessage :: (BaseIO m)
            => Text   -- ^ Target socket URI
            -> Text   -- ^ Message
            -> m Text
sendMessage socketURI message = runZMQ $ do
    socket <- ZMQ.socket Req
    ZMQ.bind socket (unpack socketURI)
    io . debugM "hbro.ipc" $ "Sending message to IPC socket at: " ++ socketURI
    send socket message
    receive socket

-- | Wait for a message to be received from given socket
receive :: (ZMQ.Receiver a) => ZMQ.Socket z a -> ZMQ z Text
receive socket = decodeUtf8 <$> ZMQ.receive socket

-- | Same as 'sendMessage', but for all running instances of the browser.
-- sendMessageToAll :: (MonadBase IO m, MonadReader t m, HasConfig t) => String -> m [String]
-- sendMessageToAll message = do
--     dir  <- readConfig socketDir
--     getAllProcessIDs >>= mapM ((`sendMessage` message) . (`socketPath` dir))
-- }}}