This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "snap-server".

The branch, master has been updated
       via  d0783a072d3730e5ca19aab7858dbf91f3d838ff (commit)
      from  0a4102ab961911136a683471d6dea9ac110734a3 (commit)


Summary of changes:
 src/Snap/Internal/Http/Server/TimeoutManager.hs    |  170 ++++++++++++++++++++
 .../Internal/Http/Server/TimeoutManager/Tests.hs   |   76 +++++++++
 2 files changed, 246 insertions(+), 0 deletions(-)
 create mode 100644 src/Snap/Internal/Http/Server/TimeoutManager.hs
 create mode 100644 test/suite/Snap/Internal/Http/Server/TimeoutManager/Tests.hs

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit d0783a072d3730e5ca19aab7858dbf91f3d838ff
Author: Gregory Collins <[email protected]>
Date:   Sun Mar 20 19:15:03 2011 +0100

    ...it helps if you actually check in the files

diff --git a/src/Snap/Internal/Http/Server/TimeoutManager.hs 
b/src/Snap/Internal/Http/Server/TimeoutManager.hs
new file mode 100644
index 0000000..dee1093
--- /dev/null
+++ b/src/Snap/Internal/Http/Server/TimeoutManager.hs
@@ -0,0 +1,170 @@
+{-# LANGUAGE BangPatterns #-}
+
+module Snap.Internal.Http.Server.TimeoutManager
+  ( TimeoutManager
+  , TimeoutHandle
+  , initialize
+  , stop
+  , register
+  , tickle
+  , cancel
+  ) where
+
+------------------------------------------------------------------------------
+import           Control.Concurrent
+import           Control.Exception
+import           Control.Monad
+import           Data.IORef
+import           Foreign.C.Types
+
+------------------------------------------------------------------------------
+data State = Deadline !CTime
+           | Canceled
+
+
+------------------------------------------------------------------------------
+data TimeoutHandle = TimeoutHandle {
+      _killAction :: !(IO ())
+    , _state      :: !(IORef State)
+    , _hGetTime   :: !(IO CTime)
+    }
+
+
+------------------------------------------------------------------------------
+data TimeoutManager = TimeoutManager {
+      _defaultTimeout :: !Int
+    , _getTime        :: !(IO CTime)
+    , _connections    :: !(IORef [TimeoutHandle])
+    , _inactivity     :: !(IORef Bool)
+    , _morePlease     :: !(MVar ())
+    , _managerThread  :: !(MVar ThreadId)
+    }
+
+
+------------------------------------------------------------------------------
+-- | Create a new TimeoutManager.
+initialize :: Int               -- ^ default timeout
+           -> IO CTime          -- ^ function to get current time
+           -> IO TimeoutManager
+initialize defaultTimeout getTime = do
+    conns <- newIORef []
+    inact <- newIORef False
+    mp    <- newEmptyMVar
+    mthr  <- newEmptyMVar
+
+    let tm = TimeoutManager defaultTimeout getTime conns inact mp mthr
+
+    thr <- forkIO $ managerThread tm
+    putMVar mthr thr
+    return tm
+
+
+------------------------------------------------------------------------------
+-- | Stop a TimeoutManager.
+stop :: TimeoutManager -> IO ()
+stop tm = readMVar (_managerThread tm) >>= killThread
+
+
+------------------------------------------------------------------------------
+-- | Register a new connection with the TimeoutManager.
+register :: IO ()               -- ^ action to run when the timeout deadline is
+                                -- exceeded.
+         -> TimeoutManager      -- ^ manager to register with.
+         -> IO TimeoutHandle
+register killAction tm = do
+    now <- getTime
+    let !state = Deadline $ now + toEnum defaultTimeout
+    stateRef <- newIORef state
+
+    let !h = TimeoutHandle killAction stateRef getTime
+    atomicModifyIORef connections $ \x -> (h:x, ())
+
+    inact <- readIORef inactivity
+    when inact $ do
+        -- wake up manager thread
+        writeIORef inactivity False
+        _ <- tryPutMVar morePlease ()
+        return ()
+    return h
+
+  where
+    getTime        = _getTime tm
+    inactivity     = _inactivity tm
+    morePlease     = _morePlease tm
+    connections    = _connections tm
+    defaultTimeout = _defaultTimeout tm
+
+
+------------------------------------------------------------------------------
+-- | Tickle the timeout on a connection to be N seconds into the future.
+tickle :: TimeoutHandle -> Int -> IO ()
+tickle th n = do
+    now <- getTime
+
+    let state = Deadline $ now + toEnum n
+    writeIORef stateRef state
+
+  where
+    getTime  = _hGetTime th
+    stateRef = _state th
+
+
+------------------------------------------------------------------------------
+-- | Cancel a timeout.
+cancel :: TimeoutHandle -> IO ()
+cancel h = writeIORef (_state h) Canceled
+
+
+------------------------------------------------------------------------------
+managerThread :: TimeoutManager -> IO ()
+managerThread tm = loop `finally` (readIORef connections >>= destroyAll)
+  where
+    --------------------------------------------------------------------------
+    connections = _connections tm
+    getTime     = _getTime tm
+    inactivity  = _inactivity tm
+    morePlease  = _morePlease tm
+    waitABit    = threadDelay 5000000
+
+    --------------------------------------------------------------------------
+    loop = do
+        waitABit
+        handles <- atomicModifyIORef connections (\x -> ([],x))
+
+        if null handles
+          then do
+            -- we're inactive, go to sleep until we get new threads
+            writeIORef inactivity True
+            takeMVar morePlease
+          else do
+            now   <- getTime
+            dlist <- processHandles now handles id
+            atomicModifyIORef connections (\x -> (dlist x, ()))
+
+        loop
+
+    --------------------------------------------------------------------------
+    processHandles !now handles initDlist = go handles initDlist
+      where
+        go [] !dlist = return dlist
+
+        go (x:xs) !dlist = do
+            state   <- readIORef $ _state x
+            !dlist' <- case state of
+                         Canceled   -> return dlist
+                         Deadline t -> if t <= now
+                                         then do
+                                           _killAction x
+                                           return dlist
+                                         else return (dlist . (x:))
+            go xs dlist'
+
+    --------------------------------------------------------------------------
+    destroyAll = mapM_ diediedie
+
+    --------------------------------------------------------------------------
+    diediedie x = do
+        state <- readIORef $ _state x
+        case state of
+          Canceled -> return ()
+          _        -> _killAction x
diff --git a/test/suite/Snap/Internal/Http/Server/TimeoutManager/Tests.hs 
b/test/suite/Snap/Internal/Http/Server/TimeoutManager/Tests.hs
new file mode 100644
index 0000000..d90f6cb
--- /dev/null
+++ b/test/suite/Snap/Internal/Http/Server/TimeoutManager/Tests.hs
@@ -0,0 +1,76 @@
+module Snap.Internal.Http.Server.TimeoutManager.Tests
+  ( tests ) where
+
+import           Control.Concurrent
+import           Data.IORef
+import           Data.Maybe
+import           System.PosixCompat.Time
+import           System.Timeout
+import           Test.Framework
+import           Test.Framework.Providers.HUnit
+import           Test.HUnit hiding (Test, path)
+
+import qualified Snap.Internal.Http.Server.TimeoutManager as TM
+
+tests :: [Test]
+tests = [ testOneTimeout
+        , testOneTimeoutAfterInactivity
+        , testCancel
+        , testTickle ]
+
+
+testOneTimeout :: Test
+testOneTimeout = testCase "timeout/oneTimeout" $ do
+    mgr <- TM.initialize 3 epochTime
+    oneTimeout mgr
+
+
+testOneTimeoutAfterInactivity :: Test
+testOneTimeoutAfterInactivity =
+    testCase "timeout/oneTimeoutAfterInactivity" $ do
+        mgr <- TM.initialize 3 epochTime
+        threadDelay $ 7 * seconds
+        oneTimeout mgr
+
+oneTimeout :: TM.TimeoutManager -> IO ()
+oneTimeout mgr = do
+    mv  <- newEmptyMVar
+    _   <- TM.register (putMVar mv ()) mgr
+    m   <- timeout (6*seconds) $ takeMVar mv
+    assertBool "timeout fired" $ isJust m
+    TM.stop mgr
+
+
+testTickle :: Test
+testTickle = testCase "timeout/tickle" $ do
+    mgr <- TM.initialize 8 epochTime
+    ref <- newIORef (0 :: Int)
+    h <- TM.register (writeIORef ref 1) mgr
+    threadDelay $ 5 * seconds
+    b0 <- readIORef ref
+    assertEqual "b0" 0 b0
+    TM.tickle h 8
+    threadDelay $ 5 * seconds
+    b1 <- readIORef ref
+    assertEqual "b1" 0 b1
+    threadDelay $ 8 * seconds
+    b2 <- readIORef ref
+    assertEqual "b2" 1 b2
+    TM.stop mgr
+
+
+testCancel :: Test
+testCancel = testCase "timeout/cancel" $ do
+    mgr <- TM.initialize 3 epochTime
+    ref <- newIORef (0 :: Int)
+    h <- TM.register (writeIORef ref 1) mgr
+    threadDelay $ 1 * seconds
+    TM.cancel h
+    threadDelay $ 5 * seconds
+    b0 <- readIORef ref
+    assertEqual "b0" 0 b0
+    TM.stop mgr
+
+
+seconds :: Int
+seconds = (10::Int) ^ (6::Int)
-----------------------------------------------------------------------


hooks/post-receive
-- 
snap-server
_______________________________________________
Snap mailing list
[email protected]
http://mailman-mail5.webfaction.com/listinfo/snap

Reply via email to