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  b7dcd3732e4db1d6f819349abc33e30f66c40b56 (commit)
      from  714f050973cfc116da359f7861790bfe4e4129da (commit)


Summary of changes:
 src/Snap/Internal/Http/Server.hs              |    1 -
 src/Snap/Internal/Http/Server/LibevBackend.hs |  133 +++++++++++++++++--------
 2 files changed, 89 insertions(+), 45 deletions(-)

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 b7dcd3732e4db1d6f819349abc33e30f66c40b56
Author: Gregory Collins <[email protected]>
Date:   Fri Jun 4 00:52:15 2010 -0400

    Change the way we manage the set of active connections in the libev backend.
    
    The previous approach (modifying a common MVar) was causing us to choke 
under
    heavy load; now threads register themselves with an edit list behind an 
IORef,
    and a minder thread reads the edits and fixes up the table once every 
couple of
    seconds.

diff --git a/src/Snap/Internal/Http/Server.hs b/src/Snap/Internal/Http/Server.hs
index f98d74d..6388395 100644
--- a/src/Snap/Internal/Http/Server.hs
+++ b/src/Snap/Internal/Http/Server.hs
@@ -26,7 +26,6 @@ import           Data.Monoid
 import           Data.Version
 import           Foreign.C.Types
 import           Foreign.ForeignPtr
-import           Foreign.Ptr (nullPtr)
 import           GHC.Conc
 import           Prelude hiding (catch, show, Show)
 import qualified Prelude
diff --git a/src/Snap/Internal/Http/Server/LibevBackend.hs 
b/src/Snap/Internal/Http/Server/LibevBackend.hs
index 8e8f648..2b4238c 100644
--- a/src/Snap/Internal/Http/Server/LibevBackend.hs
+++ b/src/Snap/Internal/Http/Server/LibevBackend.hs
@@ -40,8 +40,11 @@ import           Data.ByteString (ByteString)
 import           Data.ByteString.Internal (c2w, w2c)
 import qualified Data.ByteString.Unsafe as B
 import qualified Data.ByteString as B
+import           Data.DList (DList)
+import qualified Data.DList as D
 import           Data.IORef
 import           Data.Iteratee.WrappedByteString
+import qualified Data.List as List
 import           Data.Set (Set)
 import qualified Data.Set as Set
 import           Data.Typeable
@@ -60,23 +63,24 @@ import           Snap.Internal.Debug
 
 
 data Backend = Backend
-    { _acceptSocket      :: Socket
-    , _acceptFd          :: CInt
-    , _connectionQueue   :: Chan CInt
-    , _evLoop            :: EvLoopPtr
-    , _acceptIOCallback  :: FunPtr IoCallback
-    , _acceptIOObj       :: EvIoPtr
-
-      -- FIXME: we don't need _loopThread
-    , _loopThread        :: MVar ThreadId
-    , _mutexCallbacks    :: (FunPtr MutexCallback, FunPtr MutexCallback)
-    , _loopLock          :: MVar ()
-    , _asyncCb           :: FunPtr AsyncCallback
-    , _asyncObj          :: EvAsyncPtr
-    , _killCb            :: FunPtr AsyncCallback
-    , _killObj           :: EvAsyncPtr
-    , _connectionThreads :: MVar (Set ThreadId)
-    , _backendCPU        :: Int
+    { _acceptSocket      :: !Socket
+    , _acceptFd          :: !CInt
+    , _connectionQueue   :: !(Chan CInt)
+    , _evLoop            :: !EvLoopPtr
+    , _acceptIOCallback  :: !(FunPtr IoCallback)
+    , _acceptIOObj       :: !EvIoPtr
+    , _mutexCallbacks    :: !(FunPtr MutexCallback, FunPtr MutexCallback)
+    , _loopLock          :: !(MVar ())
+    , _asyncCb           :: !(FunPtr AsyncCallback)
+    , _asyncObj          :: !EvAsyncPtr
+    , _killCb            :: !(FunPtr AsyncCallback)
+    , _killObj           :: !EvAsyncPtr
+    , _connectionThreads :: !(MVar (Set ThreadId))
+    , _connThreadEdits   :: !(IORef (DList (Set ThreadId -> Set ThreadId)))
+    , _connThreadId      :: !(MVar ThreadId)
+    , _connThreadIsDone  :: !(MVar ())
+    , _threadActivity    :: !(MVar ())
+    , _backendCPU        :: !Int
     }
 
 
@@ -185,10 +189,12 @@ new (sock,sockFd) cpu = do
     evIoInit accIO accCB sockFd ev_read
     evIoStart lp accIO
 
-    -- an MVar for the loop thread, and one to keep track of the set of active
-    -- threads
-    threadMVar <- newEmptyMVar
-    threadSetMVar <- newMVar Set.empty
+    -- thread set stuff
+    connThreadMVar <- newEmptyMVar
+    connSet        <- newMVar Set.empty
+    editsRef       <- newIORef D.empty
+    connThreadDone <- newEmptyMVar
+    threadActivity <- newMVar ()
 
     let b = Backend sock
                     sockFd
@@ -196,18 +202,23 @@ new (sock,sockFd) cpu = do
                     lp
                     accCB
                     accIO
-                    threadMVar
                     (mc1,mc2)
                     looplock
                     asyncCB
                     asyncObj
                     killCB
                     killObj
-                    threadSetMVar
+                    connSet
+                    editsRef
+                    connThreadMVar
+                    connThreadDone
+                    threadActivity
                     cpu
 
-    tid <- forkOnIO cpu $ loopThread b
-    putMVar threadMVar tid
+    forkOnIO cpu $ loopThread b
+
+    conntid <- forkOnIO cpu $ connTableSeqThread b
+    putMVar connThreadMVar conntid
 
     debug $ "Backend.new: loop spawned"
     return b
@@ -230,7 +241,7 @@ loopThread backend = do
 
 acceptCallback :: CInt -> Chan CInt -> IoCallback
 acceptCallback accFd chan _loopPtr _ioPtr _ = do
-    debug "inside acceptCallback"  
+    debug "inside acceptCallback"
     r <- c_accept accFd
 
     case r of
@@ -293,7 +304,7 @@ stop b = ignoreException $ do
 
     debug $ "Backend.stop: waiting at most 10 seconds for connection threads 
to die"
     waitForThreads b $ seconds 10
-    debug $ "Backend.stop: all threads dead, unlooping"
+    debug $ "Backend.stop: all threads presumed dead, unlooping"
 
     withMVar lock $ \_ -> do
         -- FIXME: hlibev should export EVUNLOOP_ALL
@@ -343,10 +354,21 @@ timerCallback tmv _ _ _ = do
     throwTo tid TimeoutException
 
 
+addThreadSetEdit :: Backend -> (Set ThreadId -> Set ThreadId) -> IO ()
+addThreadSetEdit backend edit = do
+    atomicModifyIORef (_connThreadEdits backend) $ \els ->
+        (D.snoc els edit, ())
+
+    tryPutMVar (_threadActivity backend) ()
+    return ()
+
+
+
 freeConnection :: Connection -> IO ()
 freeConnection conn = ignoreException $ do
     withMVar loopLock $ \_ -> block $ do
         debug $ "freeConnection (" ++ show fd ++ ")"
+
         c_close fd
 
         -- stop and free timer object
@@ -363,24 +385,21 @@ freeConnection conn = ignoreException $ do
         freeEvIo ioRdObj
         freeIoCallback ioRdCb
 
-        -- remove the thread id from the backend set
-        tid <- readMVar threadMVar
-        modifyMVar_ tsetMVar $ \s -> do
-            let !s' = Set.delete tid s
-            return $! s'
+        tid <- readMVar $ _connThread conn
+
+        -- schedule the removal of the thread id from the backend set
+        addThreadSetEdit backend (Set.delete tid)
 
         -- wake up the event loop so it can be apprised of the changes
         evAsyncSend loop asyncObj
 
   where
     backend    = _backend conn
-    tsetMVar   = _connectionThreads backend
     loop       = _evLoop backend
     loopLock   = _loopLock backend
     asyncObj   = _asyncObj backend
 
     fd         = _socketFd conn
-    threadMVar = _connThread conn
     ioWrObj    = _connWriteIOObj conn
     ioWrCb     = _connWriteIOCallback conn
     ioRdObj    = _connReadIOObj conn
@@ -393,18 +412,48 @@ ignoreException :: IO () -> IO ()
 ignoreException = handle (\(_::SomeException) -> return ())
 
 
+connTableSeqThread :: Backend -> IO ()
+connTableSeqThread backend = loop `finally` putMVar threadDone ()
+  where
+    threadDone = _connThreadIsDone backend
+    editsRef   = _connThreadEdits backend
+    table      = _connectionThreads backend
+    activity   = _threadActivity backend
+
+    loop = do
+        takeMVar activity
+
+        -- grab the edits
+        edits <- atomicModifyIORef editsRef $ \t -> (D.empty, D.toList t)
+
+        -- apply the edits
+        modifyMVar_ table $ \t -> block $ do
+               let !t' = List.foldl' (flip ($)) t edits
+               return t'
+
+        -- zzz
+        threadDelay 1000000
+        loop
+
+
 freeBackend :: Backend -> IO ()
 freeBackend backend = ignoreException $ block $ do
     -- note: we only get here after an unloop
 
-    withMVar tsetMVar $ \set -> do
-        mapM_ killThread $ Set.toList set
+    readMVar (_connThreadId backend) >>= killThread
+    takeMVar $ _connThreadIsDone backend
 
-    debug $ "Backend.freeBackend: wait at most 2 seconds for threads to die"
-    waitForThreads backend $ seconds 2
+    -- read edits and obtain final thread table
+    threads <- withMVar (_connectionThreads backend) $ \table -> do
+                   edits <- liftM D.toList $
+                            readIORef (_connThreadEdits backend)
 
-    debug $ "Backend.freeBackend: all threads dead"
+                   let !t = List.foldl' (flip ($)) table edits
+                   return $ Set.toList t
 
+    mapM_ killThread threads
+
+    debug $ "Backend.freeBackend: all threads killed"
     debug $ "Backend.freeBackend: destroying resources"
     freeEvIo acceptObj
     freeIoCallback acceptCb
@@ -428,7 +477,6 @@ freeBackend backend = ignoreException $ block $ do
     fd          = _acceptFd backend
     acceptObj   = _acceptIOObj backend
     acceptCb    = _acceptIOCallback backend
-    tsetMVar    = _connectionThreads backend
     asyncObj    = _asyncObj backend
     asyncCb     = _asyncCb backend
     killObj     = _killObj backend
@@ -518,12 +566,9 @@ withConnection backend cpu proc = go
 
         tid <- forkOnIO cpu $ threadProc conn
 
-        modifyMVar_ (_connectionThreads backend) $ ins tid
+        addThreadSetEdit backend (Set.insert tid)
         putMVar thrmv tid
 
-      where
-        ins !thr !s = let !r = Set.insert thr s in return (r `seq` r)
-
 
 data BackendTerminatedException = BackendTerminatedException
    deriving (Typeable)
-----------------------------------------------------------------------


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

Reply via email to