content: {-# LANGUAGE Trustworthy #-}
  {-# LANGUAGE CPP #-}
  #ifdef __GLASGOW_HASKELL__
  {-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}
  #endif
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  Control.Concurrent.SampleVar
  -- Copyright   :  (c) The University of Glasgow 2001
  -- License     :  BSD-style (see the file libraries/base/LICENSE)
  -- 
  -- Maintainer  :  libraries@haskell.org
  -- Stability   :  experimental
  -- Portability :  non-portable (concurrency)
  --
  -- Sample variables
  --
  -----------------------------------------------------------------------------
  
  module Control.Concurrent.SampleVar
         (
           -- * Sample Variables
           SampleVar,         -- :: type _ =
   
           newEmptySampleVar, -- :: IO (SampleVar a)
           newSampleVar,      -- :: a -> IO (SampleVar a)
           emptySampleVar,    -- :: SampleVar a -> IO ()
           readSampleVar,     -- :: SampleVar a -> IO a
           writeSampleVar,    -- :: SampleVar a -> a -> IO ()
           isEmptySampleVar,  -- :: SampleVar a -> IO Bool
  
         ) where
  
  import Prelude
  
  import Control.Concurrent.MVar
  
  import Control.Exception ( mask_ )
  
  import Data.Functor ( (<$>) )
  
  import Data.Typeable
  
  #include "Typeable.h"
  
  -- |
  -- Sample variables are slightly different from a normal 'MVar':
  -- 
  --  * Reading an empty 'SampleVar' causes the reader to block.
  --    (same as 'takeMVar' on empty 'MVar')
  -- 
  --  * Reading a filled 'SampleVar' empties it and returns value.
  --    (same as 'takeMVar')
  -- 
  --  * Writing to an empty 'SampleVar' fills it with a value, and
  --    potentially, wakes up a blocked reader (same as for 'putMVar' on
  --    empty 'MVar').
  --
  --  * Writing to a filled 'SampleVar' overwrites the current value.
  --    (different from 'putMVar' on full 'MVar'.)
  
  newtype SampleVar a = SampleVar ( MVar ( Int    -- 1  == full
                                                  -- 0  == empty
                                                  -- <0 no of readers blocked
                                         , MVar a
                                         )
                                  )
      deriving (Eq)
  
  INSTANCE_TYPEABLE1(SampleVar,sampleVarTc,"SampleVar")
  
  -- |Build a new, empty, 'SampleVar'
  newEmptySampleVar :: IO (SampleVar a)
  newEmptySampleVar = do
     v <- newEmptyMVar
     SampleVar <$> newMVar (0,v)
  
  -- |Build a 'SampleVar' with an initial value.
  newSampleVar :: a -> IO (SampleVar a)
  newSampleVar a = do
     v <- newMVar a
     SampleVar <$> newMVar (1,v)
  
  -- |If the SampleVar is full, leave it empty.  Otherwise, do nothing.
  emptySampleVar :: SampleVar a -> IO ()
  emptySampleVar (SampleVar v) = mask_ $ do
     s@(readers, var) <- takeMVar v
     if readers > 0 then do
       _ <- takeMVar var
       putMVar v (0,var)
      else
       putMVar v s
  
  -- |Wait for a value to become available, then take it and return.
  readSampleVar :: SampleVar a -> IO a
  readSampleVar (SampleVar svar) = mask_ $ do
  --
  -- filled => make empty and grab sample
  -- not filled => try to grab value, empty when read val.
  --
     (readers,val) <- takeMVar svar
     let readers' = readers-1
     readers' `seq` putMVar svar (readers',val)
     takeMVar val
  
  -- |Write a value into the 'SampleVar', overwriting any previous value that
  -- was there.
  writeSampleVar :: SampleVar a -> a -> IO ()
  writeSampleVar (SampleVar svar) v = mask_ $ do
  --
  -- filled => overwrite
  -- not filled => fill, write val
  --
     s@(readers,val) <- takeMVar svar
     case readers of
       1 ->
         swapMVar val v >>
         putMVar svar s
       _ ->
         putMVar val v >>
         let readers' = min 1 (readers+1)
         in readers' `seq` putMVar svar (readers', val)
  
  -- | Returns 'True' if the 'SampleVar' is currently empty.
  --
  -- Note that this function is only useful if you know that no other
  -- threads can be modifying the state of the 'SampleVar', because
  -- otherwise the state of the 'SampleVar' may have changed by the time
  -- you see the result of 'isEmptySampleVar'.
  --
  isEmptySampleVar :: SampleVar a -> IO Bool
  isEmptySampleVar (SampleVar svar) = do
     (readers, _) <- readMVar svar
     return (readers <= 0)
  
  