2

HTTPクライアントが接続を切断したとき(または他の現実の世界が発生したとき)にクリーンアップを実行する方法を理解できません。にラップしようとしましSourceaddCleanupが、呼び出されません。

これが、無限のソースストリーミングバイトストリングの私の最小限の例です。

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
import Data.ByteString.Lazy.Char8 ()

import Control.Monad
import Control.Monad.Trans
import Control.Concurrent (threadDelay)

import Data.Conduit
import Blaze.ByteString.Builder (Builder)
import qualified Blaze.ByteString.Builder.ByteString as BBBB
import qualified Data.ByteString.Char8 as BS

stream :: Source (ResourceT IO) (Flush Builder)
stream = addCleanup (\_ -> liftIO $ putStrLn "cleanup.") $ do
    liftIO $ putStrLn "source started."
    yield Flush

    forever $ do
        yield $ bchunk "whatever"
        yield Flush
        liftIO $ threadDelay 10000

app :: Application
app req = do
    liftIO $ putStrLn "in the handler."
    return $ ResponseSource status200 [("Content-Type", "text/plain")] stream

main :: IO ()
main = run 3000 app

bchunk = Chunk . BBBB.fromByteString . BS.pack

httpリクエストでヒットすると、「起動」通知が表示され、streamデータのパージが開始されます。ただし、接続を閉じた後、「クリーンアップ」は行われません。メッセージが表示され、アクションは実行されないため、実際のコードでリソースがリークします。

4

1 に答える 1

3

allocateクリーンアップの推奨される方法は、で定義されたまたはregister関数を使用することだと思いますControl.Monad.Trans.Resource.MonadResourceResponseSourceこれにより、例外の有無にかかわらず、終了時にハンドラーが呼び出されます。

コードを見ると、addCleanup通常の(例外的ではない)完了にのみ使用されます。

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
import Data.ByteString.Lazy.Char8 ()

import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Resource
import Control.Concurrent (threadDelay)

import Data.Conduit
import Blaze.ByteString.Builder (Builder)
import qualified Blaze.ByteString.Builder.ByteString as BBBB
import qualified Data.ByteString.Char8 as BS

stream :: MonadResource m => Source m (Flush Builder)
stream = do
  -- the release key can be used for early cleanup
  _releaseKey <- lift . register $ putStrLn "cleanup."

  liftIO $ putStrLn "source started."
  yield Flush

  forever $ do
    yield $ bchunk "whatever"
    yield Flush
    liftIO $ threadDelay 10000

app :: Application
app _ = do
  liftIO $ putStrLn "in the handler."
  return $ ResponseSource status200 [("Content-Type", "text/plain")] stream

main :: IO ()
main = run 3000 app

bchunk :: String -> Flush Builder
bchunk = Chunk . BBBB.fromByteString . BS.pack
于 2012-08-30T09:15:06.143 に答える