|
|
|
@ -14,11 +14,16 @@ import Control.Exception |
|
|
|
import Data.List.NonEmpty |
|
|
|
import Data.List.NonEmpty |
|
|
|
import qualified Data.Text as T |
|
|
|
import qualified Data.Text as T |
|
|
|
import qualified Data.ByteString.Lazy as BL |
|
|
|
import qualified Data.ByteString.Lazy as BL |
|
|
|
|
|
|
|
import qualified Data.ByteString.Char8 as B8 |
|
|
|
import qualified Data.List as L |
|
|
|
import qualified Data.List as L |
|
|
|
import Data.Text.Encoding |
|
|
|
import Data.Text.Encoding |
|
|
|
|
|
|
|
import Data.Time.Clock |
|
|
|
|
|
|
|
import Data.IORef |
|
|
|
import System.ZMQ4 |
|
|
|
import System.ZMQ4 |
|
|
|
import System.Log.Logger |
|
|
|
import System.Log.Logger |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Safe |
|
|
|
|
|
|
|
|
|
|
|
data QuoteSourceClientHandle = QuoteSourceClientHandle { |
|
|
|
data QuoteSourceClientHandle = QuoteSourceClientHandle { |
|
|
|
tid :: ThreadId, |
|
|
|
tid :: ThreadId, |
|
|
|
completionMvar :: MVar () |
|
|
|
completionMvar :: MVar () |
|
|
|
@ -27,21 +32,31 @@ data QuoteSourceClientHandle = QuoteSourceClientHandle { |
|
|
|
startQuoteSourceClient :: BoundedChan Tick -> [T.Text] -> Context -> T.Text -> IO QuoteSourceClientHandle |
|
|
|
startQuoteSourceClient :: BoundedChan Tick -> [T.Text] -> Context -> T.Text -> IO QuoteSourceClientHandle |
|
|
|
startQuoteSourceClient chan tickers ctx endpoint = do |
|
|
|
startQuoteSourceClient chan tickers ctx endpoint = do |
|
|
|
compMv <- newEmptyMVar |
|
|
|
compMv <- newEmptyMVar |
|
|
|
|
|
|
|
now <- getCurrentTime |
|
|
|
|
|
|
|
lastHeartbeat <- newIORef now |
|
|
|
tid <- forkIO $ do |
|
|
|
tid <- forkIO $ do |
|
|
|
sock <- socket ctx Sub |
|
|
|
sock <- createAndConnectSocket |
|
|
|
connect sock $ T.unpack endpoint |
|
|
|
finally (clientThread sock lastHeartbeat) (cleanup compMv sock) |
|
|
|
mapM_ (\t -> subscribe sock $ encodeUtf8 t) tickers |
|
|
|
|
|
|
|
finally (clientThread sock) (cleanup compMv sock) |
|
|
|
|
|
|
|
return QuoteSourceClientHandle { tid = tid, completionMvar = compMv } |
|
|
|
return QuoteSourceClientHandle { tid = tid, completionMvar = compMv } |
|
|
|
where |
|
|
|
where |
|
|
|
clientThread sock = forever $ do |
|
|
|
clientThread sock lastHeartbeat = forever $ do |
|
|
|
evs <- poll 200 [Sock sock [In] Nothing] |
|
|
|
evs <- poll 200 [Sock sock [In] Nothing] |
|
|
|
when ((L.length . L.head) evs > 0) $ do |
|
|
|
when ((L.length . L.head) evs > 0) $ do |
|
|
|
rawTick <- fmap BL.fromStrict <$> receiveMulti sock |
|
|
|
rawTick <- fmap BL.fromStrict <$> receiveMulti sock |
|
|
|
case deserializeTick rawTick of |
|
|
|
if headMay rawTick == Just "SYSTEM#HEARTBEAT" |
|
|
|
Just tick -> writeChan chan tick |
|
|
|
then do |
|
|
|
Nothing -> warningM "QuoteSource.Client" "Error: can't deserialize tick" |
|
|
|
now <- getCurrentTime |
|
|
|
|
|
|
|
writeIORef lastHeartbeat now |
|
|
|
|
|
|
|
else case deserializeTick rawTick of |
|
|
|
|
|
|
|
Just tick -> writeChan chan tick |
|
|
|
|
|
|
|
Nothing -> warningM "QuoteSource.Client" "Error: can't deserialize tick" |
|
|
|
cleanup compMv sock = close sock >> putMVar compMv () |
|
|
|
cleanup compMv sock = close sock >> putMVar compMv () |
|
|
|
|
|
|
|
createAndConnectSocket = do |
|
|
|
|
|
|
|
sock <- socket ctx Sub |
|
|
|
|
|
|
|
connect sock $ T.unpack endpoint |
|
|
|
|
|
|
|
mapM_ (\t -> subscribe sock $ encodeUtf8 t) tickers |
|
|
|
|
|
|
|
subscribe sock $ B8.pack "SYSTEM#HEARTBEAT" |
|
|
|
|
|
|
|
return sock |
|
|
|
|
|
|
|
|
|
|
|
stopQuoteSourceClient :: QuoteSourceClientHandle -> IO () |
|
|
|
stopQuoteSourceClient :: QuoteSourceClientHandle -> IO () |
|
|
|
stopQuoteSourceClient handle = yield >> killThread (tid handle) >> readMVar (completionMvar handle) |
|
|
|
stopQuoteSourceClient handle = yield >> killThread (tid handle) >> readMVar (completionMvar handle) |
|
|
|
|