3

有没有办法runResourceT限制一个单一的生命周期Sink

我正在尝试构建一个Sink包装可能无限数量的Sinks. 这适用于线程,但我试图在没有线程的情况下做到这一点。似乎应该是可能的。由于范围界定,我遇到了障碍runResourceT:我的资源管理粒度太粗(但功能齐全)或太细(完全损坏)。

{-# LANGUAGE FlexibleContexts #-}

import Control.Monad.Trans (lift)
import Control.Monad.Trans.Resource
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BC8 (pack)
import Data.Conduit
import qualified Data.Conduit.Binary as Cb
import qualified Data.Conduit.List as Cl
import System.FilePath ((<.>))

test :: IO ()
test =
  runResourceT
      $ Cl.sourceList (fmap (BC8.pack . show) [(1 :: Int)..1000])
     $$ rotateResourceHog "/tmp/foo"

-- |
-- files are allocated on demand but handles are released at the same time
rotateResourceHog
  :: MonadResource m
  => FilePath -> Sink ByteString m ()
rotateResourceHog filePath = step 0 where
  step i = do
    x <- Cl.peek
    case x of
      Just _  -> do
        chunkWriter $ filePath <.> show (i :: Integer)
        -- loop
        step $ i+1

      Nothing -> return ()

-- |
-- files are allocated on demand but handles are released immediately
rotateUsingClosedHandles
  :: (MonadBaseControl IO m, MonadResource m)
  => FilePath -> Sink ByteString m ()
rotateUsingClosedHandles filePath = step 0 where
  step i = do
    x <- Cl.peek
    case x of
      Just _  -> do
        transPipe runResourceT . chunkWriter $ filePath <.> show (i :: Integer)
        -- loop
        step $ i+1

      Nothing -> return ()

chunkWriter
  :: MonadResource m
  => FilePath -> Sink ByteString m ()
chunkWriter filePath = do
  _ <- lift $ allocate (putStrLn "alloc") (\ _ -> putStrLn "free")

  -- the actual conduit chain is more complicated
  Cl.isolate 100 =$= Cb.sinkFile filePath
4

1 回答 1

2

ResourceT仅用于在特殊情况下清理资源。它不是为了提供快速的最终确定,只是保证最终确定。为了及时起见,conduit提供了自己的工具来处理清理。在您的情况下,您正在寻找两者:您希望清理尽可能早地发生,并且即使在抛出异常的情况下也会发生。为此,您应该使用bracketP. 例如:

chunkWriter
  :: MonadResource m
  => FilePath -> Sink ByteString m ()
chunkWriter filePath = bracketP
    (putStrLn "alloc")
    (\() -> putStrLn "free")
    (\() -> Cl.isolate 100 =$= Cb.sinkFile filePath)

这导致了 alloc 和 free 输出的所需交错。

于 2012-08-16T04:45:11.430 回答