{-# LANGUAGE CPP #-} module AsyncTransmitter(asyncTransmitterF,asyncTransmitterF',closerF) where import Sockets import Srequest import FRequest import Message(message) import NullF(getK,nullK,nullF) import Fudget() import FudgetIO import CompOps((>==<)) import IoF(ioF) import DialogueIO hiding (IOError) import qualified PackedString as PS import Queue(QUEUE,empty,enter,qremove) {- asyncTransmitterF has two states: - Idle state: we have nothing to send and don't want to be notified when the socket becomes writable. - Blocked state: we have something to send and want to be notified when the socket becomes writable. idleK handles the idle state. Transistion to the idleState is done with goIdleK. idleK should only be called when in the idle state. blockedK handles the blocked state. Transition to the blocked state is done with goBlockedK. blockedK should only be called when in the blocked state. writeK is called in blocked mode when the socket become writable. If there is something left in the buffer to write, writeK writes a chunk to the socket and continues in blocked mode, otherwise writeK switches to idle mode. -} asyncTransmitterF socket = closerF socket >==< asyncTransmitterF' socket asyncTransmitterF' socket = -- Start in idle mode ioF idleK where -- To be called while in idle mode only: idleK = getMsg (const idleK) high where high "" = closeK high str = goBlockedK (buf1 str) -- To swich from blocked to idle mode: goIdleK = select [] $ idleK -- To switch from idle mode to blocked mode: goBlockedK buf = select [OutputSocketDe socket] $ blockedK buf initblocksize -- To be called while in blocked mode only: blockedK buf n = getMsg low high where low (DResp (AsyncInput (_,SocketWritable))) = writeK buf n high str = blockedK (putbuf buf str) n -- To be called while in blocked mode, when socket becomes writable: writeK buf n = case getbuf buf n of Empty -> goIdleK EoS -> closeK More ps buf' -> sIOerr (WriteSocketPS socket ps) errK okK where okK (Wrote n') = blockedK buf' n errK _ = blockedK buf' n -- Ignore errors?! closeK = select [] $ --sIOsucc (CloseSocket socket) $ -- Can't close the socket until the receiver (if any) has deselected it! putHigh () $ nullK closerF socket = getHigh $ \ _ -> sIOsucc (CloseSocket socket) $ nullF getMsg l h = getK $ message l h data Buffer = Buf String (QUEUE String) data GetBuf = More PS.PackedString Buffer | Empty | EoS --buf0 = Buf "" empty buf1 str = Buf str empty -- pre: str/="" putbuf (Buf s q) str = Buf s (enter q str) -- str=="" to indicate eos getbuf (Buf s q) n = getbuf' s q where getbuf' "" q = case qremove q of Nothing -> Empty Just (s,q') -> if null s then EoS else getbuf'' s q' getbuf' s q = getbuf'' s q getbuf'' s q = case splitAt n s of (s1,s2) -> More (PS.packString s1) (Buf s2 q) initblocksize = 512 :: Int #if defined(__GLASGOW_HASKELL__) || defined(__PFE__) nullPS = PS.nullPS #else nullPS = PS.null #endif