goatee-gtk-0.3.1.3: src/Game/Goatee/Ui/Gtk/Latch.hs
-- This file is part of Goatee.
--
-- Copyright 2014-2018 Bryan Gardiner
--
-- Goatee is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- Goatee 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 Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with Goatee. If not, see <http://www.gnu.org/licenses/>.
module Game.Goatee.Ui.Gtk.Latch (
Latch,
newLatch,
withLatchOn,
whenLatch, whenLatchOff, whenLatchOn,
) where
import Control.Concurrent.MVar (MVar, newMVar, putMVar, takeMVar)
import Control.Exception (finally)
import Control.Monad (when)
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
-- | A binary switch that is off unless held on during the execution of some IO
-- process. The state of a latch can be read at any time, but can be held on by
-- at most one thread at a time.
data Latch = Latch
{ latchValue :: IORef Bool
, latchLock :: MVar ()
}
-- | Creates a new latch that is off.
newLatch :: IO Latch
newLatch = do
value <- newIORef False
lock <- newMVar ()
return Latch { latchValue = value
, latchLock = lock
}
-- | Flips the latch on, executes the action, and finally flips the latch off
-- again (even if the action throws an exception). A latch may be held on by at
-- most one thread at a time. If a second thread tries to turn the latch on, it
-- will block.
withLatchOn :: Latch -> IO a -> IO a
withLatchOn (Latch value lock) io = do
takeMVar lock
finally (writeIORef value True >> io) $ do
writeIORef value False
putMVar lock ()
-- | Executes an action only when the latch is on (if given true) or off (if
-- given false).
whenLatch :: Bool -> Latch -> IO () -> IO ()
whenLatch value latch io = do
actual <- readIORef $ latchValue latch
when (value == actual) io
-- | Executes an action only when a latch is off.
whenLatchOff :: Latch -> IO () -> IO ()
whenLatchOff = whenLatch False
-- | Executes an action only when a latch is on.
whenLatchOn :: Latch -> IO () -> IO ()
whenLatchOn = whenLatch True