@@ -11,6 +11,10 @@ module Network.Socket.SockAddr (
11
11
, recvBufMsg
12
12
) where
13
13
14
+ import Control.Exception (try , throwIO , IOException )
15
+ import System.Directory (removeFile )
16
+ import System.IO.Error (isAlreadyInUseError , isDoesNotExistError )
17
+
14
18
import qualified Network.Socket.Buffer as G
15
19
import qualified Network.Socket.Name as G
16
20
import qualified Network.Socket.Syscall as G
@@ -41,7 +45,27 @@ connect = G.connect
41
45
-- 'defaultPort' is passed then the system assigns the next available
42
46
-- use port.
43
47
bind :: Socket -> SockAddr -> IO ()
44
- bind = G. bind
48
+ bind s a = case a of
49
+ SockAddrUnix p -> do
50
+ -- gracefully handle the fact that UNIX systems don't clean up closed UNIX
51
+ -- domain sockets, inspired by https://stackoverflow.com/a/13719866
52
+ res <- try (G. bind s a)
53
+ case res of
54
+ Right () -> pure ()
55
+ Left e -> if not (isAlreadyInUseError (e :: IOException ))
56
+ then throwIO e
57
+ else do
58
+ -- socket might be in use, try to connect
59
+ res2 <- try (G. connect s a)
60
+ case res2 of
61
+ Right () -> close s >> throwIO e
62
+ Left e2 -> if not (isDoesNotExistError (e2 :: IOException ))
63
+ then throwIO e
64
+ else do
65
+ -- socket not actually in use, remove it and retry bind
66
+ removeFile p
67
+ G. bind s a
68
+ _ -> G. bind s a
45
69
46
70
-- | Accept a connection. The socket must be bound to an address and
47
71
-- listening for connections. The return value is a pair @(conn,
0 commit comments