Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deadlock when exiting withFluentLogger block #4

Merged
merged 3 commits into from Apr 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions fluent-logger.cabal
Expand Up @@ -21,6 +21,7 @@ library
hs-source-dirs: src
exposed-modules: Network.Fluent.Logger
, Network.Fluent.Logger.Packable
other-modules: Network.Fluent.Logger.ForkWrapper
ghc-options: -Wall
build-depends: base ==4.*
, bytestring
Expand Down
7 changes: 4 additions & 3 deletions src/Network/Fluent/Logger.hs
Expand Up @@ -37,7 +37,7 @@ import Network.Socket.Options ( setRecvTimeout, setSendTimeout )
import Network.Socket.ByteString.Lazy ( sendAll, recv )
import Control.Monad ( void, forever, when )
import Control.Applicative ( (<$>) )
import Control.Concurrent ( ThreadId, forkIO, killThread, threadDelay )
import Control.Concurrent ( ThreadId, killThread, threadDelay )
import Control.Concurrent.STM ( atomically, orElse
, TChan, newTChanIO, readTChan, peekTChan, writeTChan
, TVar, newTVarIO, readTVar, modifyTVar
Expand All @@ -50,6 +50,7 @@ import Data.Time.Clock.POSIX ( getPOSIXTime )
import System.Random ( randomRIO )

import Network.Fluent.Logger.Packable
import Network.Fluent.Logger.ForkWrapper (forkIOUnmasked)

-- | Wrap close / sClose (deprecated)
close :: NS.Socket -> IO ()
Expand Down Expand Up @@ -139,7 +140,7 @@ runSender logger = forever $ filterException $ bracket (connectFluent logger) cl
filterException action = catches action [Handler passAsyncException, Handler dropOtherExceptions]
handleSocket sock = do
flag <- newEmptyTMVarIO
bracket (forkIO $ setFlagWhenClose sock flag) killThread (const $ sendFluent logger sock flag)
bracket (forkIOUnmasked $ setFlagWhenClose sock flag) killThread (const $ sendFluent logger sock flag)

connectFluent :: FluentLoggerSender -> IO NS.Socket
connectFluent logger = exponentialBackoff $ getSocket host port timeout where
Expand Down Expand Up @@ -190,7 +191,7 @@ newFluentLogger set = do
, fluentLoggerSenderBuffered = tvar
, fluentLoggerSenderSettings = set
}
tid <- forkIO $ runSender sender
tid <- forkIOUnmasked $ runSender sender
let logger = FluentLogger
{ fluentLoggerSender = sender
, fluentLoggerThread = tid
Expand Down
18 changes: 18 additions & 0 deletions src/Network/Fluent/Logger/ForkWrapper.hs
@@ -0,0 +1,18 @@
{-# LANGUAGE CPP #-}
module Network.Fluent.Logger.ForkWrapper (forkIOUnmasked) where

import qualified Control.Concurrent as CC
#if MIN_VERSION_base(4,3,0)
#else
import qualified Control.Exception as CE
#endif

forkIOUnmasked :: IO () -> IO CC.ThreadId
#if MIN_VERSION_base(4,4,0)
forkIOUnmasked action = CC.forkIOWithUnmask (\unmask -> unmask action)
#elif MIN_VERSION_base(4,3,0)
forkIOUnmasked action = CC.forkIOUnmasked
#else
forkIOUnmasked action = if CE.blocked then CE.unblock $ CC.forkIO action else CC.forkIO action
#endif

11 changes: 10 additions & 1 deletion test/Network/Fluent/LoggerSpec.hs
Expand Up @@ -24,6 +24,7 @@ spec = do
it "posts a message with given time" postWithTimePostsMessageWithGivenTime
describe "withFluentLogger" $ do
it "disconnects when the scope is over" withFluentLoggerDisconnect
it "exits normally even if you don't wait for the message to be received" withFluentLoggerExit


postSettings =
Expand Down Expand Up @@ -131,4 +132,12 @@ withFluentLoggerDisconnect =
content `shouldBe` "foobar"
threadDelay 5000
server `shouldHaveConns` 0


withFluentLoggerExit :: IO ()
withFluentLoggerExit =
(withMockServer :: (MockServer Object -> IO ()) -> IO ()) $ \server -> do
withFluentLogger postSettings $ \logger -> do
post logger "hoge" ("foobar" :: String)
-- immediately exit the block without waiting for the message
threadDelay 5000
server `shouldHaveConns` 0