-- Author: Andy Stewart -- Maintainer: Andy Stewart -- -- Copyright (C) 2010 Andy Stewart, all rights reserved. -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . {-# LANGUAGE ScopedTypeVariables #-} module Manatee.Toolkit.Gtk.Concurrent where import Control.Monad import Control.Concurrent import Control.Concurrent.STM import Graphics.UI.Gtk import Manatee.Toolkit.General.STM import qualified Control.Exception as Exc data ViewChannel a = ViewChannel {viewChannel :: TChan a ,viewChannelLock :: TVar Bool} -- | Fork GUI IO. forkGuiIO :: IO a -> (a -> IO ()) -> IO (MVar a, ThreadId, ThreadId) forkGuiIO calcAction guiAction = do -- Create signal MVar variable. signal <- newEmptyMVar -- Build new thread for long-time calculation. calcThreadId <- forkIO $ calcAction >>= putMVar signal -- fill signal when calculation finish -- Build new thread for listen signal. guiThreadId <- onGuiSignal signal guiAction -- post GUI action to Gtk+ thread when catch finish signal return (signal, calcThreadId, guiThreadId) -- | Similar `forkGuiIO`, except return () forkGuiIO_ :: IO a -> (a -> IO ()) -> IO () forkGuiIO_ calcAction guiAction = forkGuiIO calcAction guiAction >> return () -- | Post GUI Action to Gtk+ thread when catch signal. onGuiSignal :: MVar a -> (a -> IO ()) -> IO ThreadId onGuiSignal signal guiAction = forkIO $ takeMVar signal >>= postGUIAsync . guiAction -- | Create view channel. -- If widget destroy, stop read broadcast channel. createViewChannel :: WidgetClass widget => TChan a -> widget -> IO (ViewChannel a) createViewChannel channel widget = do -- Create channel and lock. chan <- dupTChanIO channel lock <- newTVarIO True -- After render view widget destroy lock channel stop read thread. widget `onDestroy` writeTVarIO lock False -- Return ViewChannel. return $ ViewChannel chan lock -- | Listen view channel. listenViewChannel :: ViewChannel a -> (a -> IO ()) -> IO () listenViewChannel vChannel@(ViewChannel {viewChannel = channel ,viewChannelLock = channelLock}) action = do forkIO $ readTChanIO channel >>= \x -> do isLive <- readTVarIO channelLock when isLive $ postGUIAsync $ Exc.catch (do action x listenViewChannel vChannel action) (\ (_ :: Exc.SomeException) -> putStrLn "listenViewChannel : Catch exception, stop read broadcast channel.") return () -- | Listen buffer channel. listenBufferChannel :: TChan a -> (a -> IO ()) -> IO () listenBufferChannel channel action = do forkIO $ readTChanIO channel >>= \x -> postGUIAsync $ Exc.catch (do action x listenBufferChannel channel action) (\ (_ :: Exc.SomeException) -> putStrLn "listenBufferChannel : Catch exception, stop read broadcast channel.") return ()