HsSVN-0.3: Subversion/Stream/Pipe.hs
module Subversion.Stream.Pipe
( newPipe
)
where
import Control.Concurrent.STM
import qualified Data.ByteString as Strict
import qualified Data.ByteString.Lazy as Lazy
import Subversion.Stream
data Pipe
= Pipe {
pReadRequest :: TVar Int -- 讀込み側が要求し、未だ書込まれてゐないバイト數
, pWrittenStr :: TVar Lazy.ByteString -- 要求に應へて書込まれ、未だ讀込まれてゐない文字列
, pIsClosed :: TVar Bool -- パイプが閉ぢられた
}
newPipe :: IO Stream
newPipe = do req <- newTVarIO 0
str <- newTVarIO Lazy.empty
isC <- newTVarIO False
let pipe = Pipe {
pReadRequest = req
, pWrittenStr = str
, pIsClosed = isC
}
actions = StreamActions {
saRead = mkReadAction pipe
, saWrite = mkWriteAction pipe
, saClose = mkCloseAction pipe
}
newStream actions
mkReadAction :: Pipe -> ReadAction
mkReadAction pipe reqLen
= do nextAction <- tryToRead True
nextAction
where
tryToRead :: Bool -> IO (IO Strict.ByteString)
tryToRead writeRequestIfNeeded
= atomically $
do str <- readTVar (pWrittenStr pipe)
if Lazy.null str then
-- 書込まれた文字列が無いので、要求されたバイト數を
-- パイプに書いて書込みを待つ。但しパイプが閉ぢられ
-- てゐたら空文字列を返して EOF を示す。
do isClosed <- readTVar (pIsClosed pipe)
if isClosed then
return (return Strict.empty)
else
-- 要求バイト數を書き込むのは一度だけ。
if writeRequestIfNeeded then
do writeTVar (pReadRequest pipe) reqLen
return $ do nextAction <- tryToRead False
nextAction
else
retry
else
-- reqLen バイトを上限としてバッファの頭を切り取る。
do let (readStr, remaining) = Lazy.splitAt (fromIntegral reqLen) str
writeTVar (pWrittenStr pipe) remaining
return (return (Strict.concat (Lazy.toChunks readStr)))
mkWriteAction :: Pipe -> WriteAction
mkWriteAction pipe str
= atomically $
do let inputLen = Strict.length str
isClosed <- readTVar (pIsClosed pipe)
if isClosed then
-- パイプが閉ぢられてゐたら書込まれた文字列を捨てる
-- FIXME: 本當にそれで良いのか?
return inputLen
else
do requestedBytes <- readTVar (pReadRequest pipe)
writtenStr <- readTVar (pWrittenStr pipe)
let maxBytesToWrite = requestedBytes - (fromIntegral $ Lazy.length writtenStr)
if maxBytesToWrite > 0 then
-- 要求されてゐるので書込む
do let strToWrite = Strict.take maxBytesToWrite str
bytesToWrite = Strict.length strToWrite
writeTVar (pReadRequest pipe) (requestedBytes - bytesToWrite)
writeTVar (pWrittenStr pipe) (writtenStr `Lazy.append` (Lazy.fromChunks [strToWrite]))
return bytesToWrite
else
-- 要求されてゐないので retry
retry
mkCloseAction :: Pipe -> CloseAction
mkCloseAction pipe
= atomically $
writeTVar (pIsClosed pipe) True