Skip to content

Commit

Permalink
Merge pull request haskell#61 from bgamari/master
Browse files Browse the repository at this point in the history
Move away from Chan to STM
  • Loading branch information
judah authored Sep 21, 2017
2 parents f17e58f + c534c65 commit 8e539a4
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 33 deletions.
13 changes: 7 additions & 6 deletions System/Console/Haskeline/Backend/Posix.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Foreign.C.Types
import qualified Data.Map as Map
import System.Posix.Terminal hiding (Interrupt)
import Control.Monad
import Control.Concurrent.STM
import Control.Concurrent hiding (throwTo)
import Data.Maybe (catMaybes)
import System.Posix.Signals.Exts
Expand Down Expand Up @@ -201,16 +202,16 @@ lookupChars (TreeMap tm) (c:cs) = case Map.lookup c tm of
-----------------------------

withPosixGetEvent :: (MonadException m, MonadReader Prefs m)
=> Chan Event -> Handles -> [(String,Key)]
=> TChan Event -> Handles -> [(String,Key)]
-> (m Event -> m a) -> m a
withPosixGetEvent eventChan h termKeys f = wrapTerminalOps h $ do
baseMap <- getKeySequences (ehIn h) termKeys
withWindowHandler eventChan
$ f $ liftIO $ getEvent (ehIn h) baseMap eventChan

withWindowHandler :: MonadException m => Chan Event -> m a -> m a
withWindowHandler :: MonadException m => TChan Event -> m a -> m a
withWindowHandler eventChan = withHandler windowChange $
Catch $ writeChan eventChan WindowResize
Catch $ atomically $ writeTChan eventChan WindowResize

withSigIntHandler :: MonadException m => m a -> m a
withSigIntHandler f = do
Expand All @@ -224,7 +225,7 @@ withHandler signal handler f = do
old_handler <- liftIO $ installHandler signal handler Nothing
f `finally` liftIO (installHandler signal old_handler Nothing)

getEvent :: Handle -> TreeMap Char Key -> Chan Event -> IO Event
getEvent :: Handle -> TreeMap Char Key -> TChan Event -> IO Event
getEvent h baseMap = keyEventLoop $ do
cs <- getBlockOfChars h
return [KeyInput $ lexKeys baseMap cs]
Expand Down Expand Up @@ -282,7 +283,7 @@ posixRunTerm ::
-> (forall m . (MonadException m, CommandMonad m) => EvalTerm (PosixT m))
-> IO RunTerm
posixRunTerm hs layoutGetters keys wrapGetEvent evalBackend = do
ch <- newChan
ch <- newTChanIO
fileRT <- posixFileRunTerm hs
return fileRT
{ termOps = Left TermOps
Expand All @@ -293,7 +294,7 @@ posixRunTerm hs layoutGetters keys wrapGetEvent evalBackend = do
, saveUnusedKeys = saveKeys ch
, evalTerm = mapEvalTerm
(runPosixT hs) lift evalBackend
, externalPrint = writeChan ch . ExternalPrint
, externalPrint = atomically . writeTChan ch . ExternalPrint
}
, closeTerm = do
flushEventQueue (putStrOut fileRT) ch
Expand Down
8 changes: 4 additions & 4 deletions System/Console/Haskeline/Backend/Win32.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ getNumberOfEvents h = alloca $ \numEventsPtr -> do
$ c_GetNumberOfConsoleInputEvents h numEventsPtr
fmap fromEnum $ peek numEventsPtr

getEvent :: HANDLE -> Chan Event -> IO Event
getEvent :: HANDLE -> TChan Event -> IO Event
getEvent h = keyEventLoop (eventReader h)

eventReader :: HANDLE -> IO [Event]
Expand Down Expand Up @@ -377,7 +377,7 @@ win32TermStdin = do
win32Term :: MaybeT IO RunTerm
win32Term = do
hs <- consoleHandles
ch <- liftIO newChan
ch <- liftIO newTChanIO
fileRT <- liftIO $ fileRunTerm stdin
return fileRT
{ termOps = Left TermOps {
Expand All @@ -387,14 +387,14 @@ win32Term = do
, saveUnusedKeys = saveKeys ch
, evalTerm = EvalTerm (runReaderT' hs . runDraw)
(Draw . lift)
, externalPrint = writeChan ch . ExternalPrint
, externalPrint = atomically . writeTChan ch . ExternalPrint
}
, closeTerm = do
flushEventQueue (putStrOut fileRT) ch
closeHandles hs
}

win32WithEvent :: MonadException m => Handles -> Chan Event
win32WithEvent :: MonadException m => Handles -> TChan Event
-> (m Event -> m a) -> m a
win32WithEvent h eventChan f = f $ liftIO $ getEvent (hIn h) eventChan

Expand Down
36 changes: 15 additions & 21 deletions System/Console/Haskeline/Term.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import System.Console.Haskeline.Prefs(Prefs)
import System.Console.Haskeline.Completion(Completion)

import Control.Concurrent
import Control.Concurrent.STM
import Data.Word
import Control.Exception (fromException, AsyncException(..),bracket_)
import Data.Typeable
Expand Down Expand Up @@ -50,12 +51,12 @@ data TermOps = TermOps
-- Without it, if you are using another thread to process the logging
-- and write on screen via exposed externalPrint, latest writes from
-- this thread are not able to cross the thread boundary in time.
flushEventQueue :: (String -> IO ()) -> Chan Event -> IO ()
flushEventQueue :: (String -> IO ()) -> TChan Event -> IO ()
flushEventQueue print' eventChan = yield >> loopUntilFlushed
where loopUntilFlushed = do
flushed <- isEmptyChan eventChan
flushed <- atomically $ isEmptyTChan eventChan
if flushed then return () else do
event <- readChan eventChan
event <- atomically $ readTChan eventChan
case event of
ExternalPrint str -> do
print' (str ++ "\n") >> loopUntilFlushed
Expand Down Expand Up @@ -121,36 +122,29 @@ data Event
| ExternalPrint String
deriving Show

keyEventLoop :: IO [Event] -> Chan Event -> IO Event
keyEventLoop :: IO [Event] -> TChan Event -> IO Event
keyEventLoop readEvents eventChan = do
-- first, see if any events are already queued up (from a key/ctrl-c
-- event or from a previous call to getEvent where we read in multiple
-- keys)
isEmpty <- isEmptyChan eventChan
isEmpty <- atomically $ isEmptyTChan eventChan
if not isEmpty
then readChan eventChan
then atomically $ readTChan eventChan
else do
lock <- newEmptyMVar
tid <- forkIO $ handleErrorEvent (readerLoop lock)
readChan eventChan `finally` do
putMVar lock ()
killThread tid
tid <- forkIO $ handleErrorEvent readerLoop
atomically (readTChan eventChan) `finally` killThread tid
where
readerLoop lock = do
readerLoop = do
es <- readEvents
if null es
then readerLoop lock
else -- Use the lock to work around the fact that writeList2Chan
-- isn't atomic. Otherwise, some events could be ignored if
-- the subthread is killed before it saves them in the chan.
bracket_ (putMVar lock ()) (takeMVar lock) $
writeList2Chan eventChan es
then readerLoop
else atomically $ mapM_ (writeTChan eventChan) es
handleErrorEvent = handle $ \e -> case fromException e of
Just ThreadKilled -> return ()
_ -> writeChan eventChan (ErrorEvent e)
_ -> atomically $ writeTChan eventChan (ErrorEvent e)

saveKeys :: Chan Event -> [Key] -> IO ()
saveKeys ch = writeChan ch . KeyInput
saveKeys :: TChan Event -> [Key] -> IO ()
saveKeys ch = atomically . writeTChan ch . KeyInput

data Layout = Layout {width, height :: Int}
deriving (Show,Eq)
Expand Down
4 changes: 2 additions & 2 deletions haskeline.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Library
-- We require ghc>=7.4.1 (base>=4.5) to use the base library encodings, even
-- though it was implemented in earlier releases, due to GHC bug #5436 which
-- wasn't fixed until 7.4.1
Build-depends: base >=4.5 && < 4.11, containers>=0.4 && < 0.6,
Build-depends: base >=4.5 && < 4.12, containers>=0.4 && < 0.6,
directory>=1.1 && < 1.4, bytestring>=0.9 && < 0.11,
filepath >= 1.2 && < 1.5, transformers >= 0.2 && < 0.6,
process >= 1.0 && < 1.7
process >= 1.0 && < 1.7, stm >= 2.4 && < 2.5
Default-Language: Haskell98
Default-Extensions:
ForeignFunctionInterface, Rank2Types, FlexibleInstances,
Expand Down

0 comments on commit 8e539a4

Please # to comment.