Skip to content

Commit caae577

Browse files
authored
Handle metrics port bind in second swarm (#2574)
* Closes #2539 Compare Messages in first swarm: ``` [Metrics API] started on :6543 ``` In second: ``` [Metrics API] Can not start on port '6543': Network.Socket.bind: resource busy (Address already in use) ```
1 parent 665cf27 commit caae577

File tree

2 files changed

+18
-3
lines changed

2 files changed

+18
-3
lines changed

app/game/Swarm/App.hs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,19 @@ import Brick.BChan
2727
import Control.Carrier.Lift (runM)
2828
import Control.Carrier.Throw.Either (runThrow)
2929
import Control.Concurrent (forkIO, threadDelay)
30+
import Control.Exception (bracket, try)
3031
import Control.Lens (Setter', view, (%~), (?~), (^.))
3132
import Control.Monad (forever, void, when)
3233
import Control.Monad.IO.Class (liftIO)
3334
import Data.IORef (IORef, modifyIORef, newIORef, readIORef, writeIORef)
35+
import Data.List.NonEmpty qualified as NE
3436
import Data.Maybe (fromMaybe)
3537
import Data.Text qualified as T
3638
import Data.Text.IO qualified as T
3739
import GitHash (GitInfo)
3840
import Graphics.Vty qualified as V
3941
import Graphics.Vty.CrossPlatform qualified as V
42+
import Network.Socket qualified as Net
4043
import Swarm.Failure (SystemFailure)
4144
import Swarm.Game.State.Runtime
4245
import Swarm.Log (LogSource (SystemLog), Severity (..))
@@ -135,9 +138,17 @@ defaultMetrics = 6543
135138
startMetricsThread :: Maybe Int -> Store -> IO (Either String Int)
136139
startMetricsThread (Just 0) _ = pure $ Left "Metrics API disabled."
137140
startMetricsThread mPort store = do
138-
let p = fromMaybe 6543 mPort
139-
_ <- WaiMetrics.forkServerWith store "localhost" p
140-
pure $ Right p
141+
let port = fromMaybe 6543 mPort
142+
portCheck <- checkPortFree port
143+
case portCheck of
144+
Right () -> Right port <$ WaiMetrics.forkServerWith store "localhost" port
145+
Left e -> pure . Left $ "Can not start on port '" <> show port <> "': " <> show e
146+
where
147+
checkPortFree :: Int -> IO (Either IOError ())
148+
checkPortFree port = do
149+
let hints = Net.defaultHints {Net.addrFlags = [Net.AI_PASSIVE], Net.addrSocketType = Net.Stream}
150+
addr <- NE.head <$> Net.getAddrInfo (Just hints) Nothing (Just $ show port)
151+
try @IOError . bracket (Net.openSocket addr) Net.close $ flip Net.bind (Net.addrAddress addr)
141152

142153
-- | Create a channel for app events.
143154
--

swarm.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ common murmur3
276276
common natural-sort
277277
build-depends: natural-sort >=0.1.2 && <0.2
278278

279+
common network
280+
build-depends: network >=3.2 && <3.3
281+
279282
common nonempty-containers
280283
build-depends: nonempty-containers >=0.3.4 && <0.3.6
281284

@@ -1168,6 +1171,7 @@ executable swarm
11681171
http-client-tls,
11691172
http-types,
11701173
lens,
1174+
network,
11711175
optparse-applicative,
11721176
text,
11731177
vty,

0 commit comments

Comments
 (0)