Skip to content

Commit 5f63939

Browse files
committed
Avoid test race condition
In a few tests the server's socket is not fully "conditioned" when the client and server threads start. The server tries to finish socket setup, but this races against the client thread sending the message, which may be processed by the kernel with default socket options (observed intermittently in tests on FreeBSD). A more complete solution would be to provide setup hooks for the server setup initialisation, so that the threads don't start until the server socket is ready. Ideally the socket options can be set before the server socket is "bound", ensuring that no messages can come in too early. This is not presently warranted, but perhaps some day...
1 parent d565d9e commit 5f63939

File tree

1 file changed

+25
-4
lines changed

1 file changed

+25
-4
lines changed

tests/Network/Socket/ByteStringSpec.hs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
module Network.Socket.ByteStringSpec (main, spec) where
44

5+
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
56
import Data.Bits
67
import Data.Maybe
78
import Control.Monad
@@ -235,10 +236,17 @@ spec = do
235236
-- This test behaves strange on AppVeyor and I don't know why so skip
236237
-- TOS for now.
237238
isAppVeyor <- isJust <$> lookupEnv "APPVEYOR"
239+
240+
-- Avoid race condition between the client sending the message and
241+
-- the server finishing its socket configuration. Otherwise the
242+
-- message may be received with default socket options!
243+
serverReady <- newEmptyMVar
244+
238245
let server sock = do
239246
whenSupported RecvIPv4TTL $ setSocketOption sock RecvIPv4TTL 1
240247
whenSupported RecvIPv4PktInfo $ setSocketOption sock RecvIPv4PktInfo 1
241248
whenSupported RecvIPv4TOS $ setSocketOption sock RecvIPv4TOS 1
249+
putMVar serverReady ()
242250

243251
(_, _, cmsgs, _) <- recvMsg sock 1024 128 mempty
244252

@@ -249,39 +257,52 @@ spec = do
249257
((lookupCmsg CmsgIdIPv4TTL cmsgs >>= decodeCmsg) :: Maybe IPv4TTL) `shouldNotBe` Nothing
250258
whenSupported RecvIPv4TOS $
251259
((lookupCmsg CmsgIdIPv4TOS cmsgs >>= decodeCmsg) :: Maybe IPv4TOS) `shouldNotBe` Nothing
252-
client sock addr = sendTo sock seg addr
260+
client sock addr = takeMVar serverReady >> sendTo sock seg addr
253261

254262
seg = C.pack "This is a test message"
255263
udpTest client server
256264

257265
it "receives control messages for IPv6" $ do
266+
-- Avoid race condition between the client sending the message and
267+
-- the server finishing its socket configuration. Otherwise the
268+
-- message may be received with default socket options!
269+
serverReady <- newEmptyMVar
270+
258271
let server sock = do
259272
whenSupported RecvIPv6HopLimit $ setSocketOption sock RecvIPv6HopLimit 1
260273
whenSupported RecvIPv6TClass $ setSocketOption sock RecvIPv6TClass 1
261274
whenSupported RecvIPv6PktInfo $ setSocketOption sock RecvIPv6PktInfo 1
262-
(_, _, cmsgs, _) <- recvMsg sock 1024 128 mempty
275+
putMVar serverReady ()
263276

277+
(_, _, cmsgs, _) <- recvMsg sock 1024 128 mempty
264278

265279
whenSupported RecvIPv6HopLimit $
266280
((lookupCmsg CmsgIdIPv6HopLimit cmsgs >>= decodeCmsg) :: Maybe IPv6HopLimit) `shouldNotBe` Nothing
267281
whenSupported RecvIPv6TClass $
268282
((lookupCmsg CmsgIdIPv6TClass cmsgs >>= decodeCmsg) :: Maybe IPv6TClass) `shouldNotBe` Nothing
269283
whenSupported RecvIPv6PktInfo $
270284
((lookupCmsg CmsgIdIPv6PktInfo cmsgs >>= decodeCmsg) :: Maybe IPv6PktInfo) `shouldNotBe` Nothing
271-
client sock addr = sendTo sock seg addr
285+
client sock addr = takeMVar serverReady >> sendTo sock seg addr
272286

273287
seg = C.pack "This is a test message"
274288
udpTest6 client server
275289

276290
it "receives truncated control messages" $ do
291+
-- Avoid race condition between the client sending the message and
292+
-- the server finishing its socket configuration. Otherwise the
293+
-- message may be received with default socket options!
294+
serverReady <- newEmptyMVar
295+
277296
let server sock = do
278297
whenSupported RecvIPv4TTL $ setSocketOption sock RecvIPv4TTL 1
279298
whenSupported RecvIPv4TOS $ setSocketOption sock RecvIPv4TOS 1
280299
whenSupported RecvIPv4PktInfo $ setSocketOption sock RecvIPv4PktInfo 1
300+
putMVar serverReady ()
301+
281302
(_, _, _, flags) <- recvMsg sock 1024 10 mempty
282303
flags .&. MSG_CTRUNC `shouldBe` MSG_CTRUNC
283304

284-
client sock addr = sendTo sock seg addr
305+
client sock addr = takeMVar serverReady >> sendTo sock seg addr
285306

286307
seg = C.pack "This is a test message"
287308
udpTest client server

0 commit comments

Comments
 (0)