ivar-simple (empty) → 0.1.0.0
raw patch · 7 files changed
+307/−0 lines, 7 filesdep +basesetup-changed
Dependencies added: base
Files
- LICENSE +28/−0
- README +32/−0
- Setup.lhs +3/−0
- ivar-simple.cabal +29/−0
- src/Data/IVar/Simple.hs +82/−0
- src/Data/IVar/Simple/IChan.hs +65/−0
- src/Data/IVar/Simple/MIChan.hs +68/−0
+ LICENSE view
@@ -0,0 +1,28 @@+Copyright (c) 2008, Bertram Felgenhauer++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this+ software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
@@ -0,0 +1,32 @@+What is this?+-------------++ivar-simple provides immutable, write-once variables (IVars) in the+Data.IVar.Simple module.++It also provides two more experimental channel implementations built on+top of IVars,++ Data.IVar.Simple.IChan:+ multi-cast channels with write-once semantics.++ Data.IVar.Simple.MIChan:+ channels with write semantics similar to Control.Concurrent.Chan++Comparison to data-ivar+-----------------------++Both data-ivar and ivar-simple provide a write-once variable. That's where+the similarities end:++- Reading an IVar with data-ivar is an IO operation. In ivar-simple it's a+ pure function.+- data-ivar provides a 'Reader' monoid, monad, etc. for reading from one of+ several IVars. ivar-simple has no such functionality.+- The data-ivar implementation can, in principle, add arbitrary IO actions+ that are called when an IVar is written. (This detail is not exposed,+ however)+- Technically, ivar-simple tries for efficiency by exploiting the existing+ locking structures in the RTS; in particular, reading a full IVar for a+ second time is as cheap as evaluating a record selector.+ I have no performance numbers for this.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runghc+> import Distribution.Simple (defaultMain)+> main = defaultMain
+ ivar-simple.cabal view
@@ -0,0 +1,29 @@+Name: ivar-simple+Version: 0.1.0.0+Category: Concurrency+Stability: experimental+Copyright: (c) 2008, 2009 Bertram Felgenhauer+Maintainer: Bertram Felgenhauer <int-e@gmx.de>+License: MIT+License-File: LICENSE+Synopsis: Write once concurrency primitives.+Description:+ @IVar@s are write-once variables.+ .+ They can be read, an operation that will block until a value was written+ to the variable. They can be written to exactly once.+Cabal-Version: >= 1.4+Build-Type: Simple+Extra-Source-Files:+ README++Library+ HS-Source-Dirs: src+ Exposed-Modules:+ Data.IVar.Simple+ Data.IVar.Simple.IChan+ Data.IVar.Simple.MIChan+ Build-Depends: base+ -- workaround for http://hackage.haskell.org/trac/ghc/ticket/2756+ if impl(ghc <= 6.10.1)+ GHC-Options: -fno-state-hack
+ src/Data/IVar/Simple.hs view
@@ -0,0 +1,82 @@+-- |+-- Module : Data.IVar.Simple+-- Copyright : (c) 2008, 2009 Bertram Felgenhauer+-- License : BSD3+--+-- Maintainer : Bertram Felgenhauer <int-e@gmx.de>+-- Stability : experimental+-- Portability : ghc+--+-- 'IVar's are write-once variables.+--+-- Similarily to 'MVar's, 'IVar's can be either empty or filled. Once filled,+-- they keep their value indefinitely - they are immutable.+--+-- Reading from an empty 'IVar' will block until the 'IVar' is filled. Because+-- the value read will never change, this is a pure computation.+--+module Data.IVar.Simple (+ IVar,+ new,+ newFull,+ read,+ tryRead,+ write,+ tryWrite,+) where++import Control.Concurrent.MVar+import Control.Exception+import Control.Monad+import System.IO.Unsafe+import Prelude hiding (read)++-- | A write-once (/immutable/) Variable+data IVar a = IVar (MVar ()) (MVar a) a++-- | Creates a new, empty 'IVar'.+new :: IO (IVar a)+new = do+ lock <- newMVar ()+ trans <- newEmptyMVar+ let {-# NOINLINE value #-}+ value = unsafePerformIO $ takeMVar trans+ return (IVar lock trans value)++-- | Create a new filled 'IVar'.+--+-- This is slightly cheaper than creating a new 'IVar' and then writing to it.+newFull :: a -> IO (IVar a)+newFull value = do+ lock <- newEmptyMVar+ return (IVar lock (error "unused MVar") value)++-- | Returns the value of an 'IVar'.+--+-- The evaluation will block until a value is written to the 'IVar' if there+-- is no value yet.+read :: IVar a -> a+read (IVar _ _ value) = value++-- | Try to read an 'IVar'. Returns 'Nothing' if there is not value yet.+tryRead :: IVar a -> IO (Maybe a)+tryRead (IVar lock _ value) = do+ empty <- isEmptyMVar lock+ if empty then return (Just value) else return Nothing++-- | Writes a value to an 'IVar'. Raises a 'BlockedIndefinitely' exception if+-- it fails.+write :: IVar a -> a -> IO ()+write ivar value = do+ result <- tryWrite ivar value+ when (not result) $ throwIO BlockedIndefinitely+-- Note: It would be easier to block forever when the IVar is full. However,+-- the thread would likely not be garbage collected then.++-- | Writes a value to an 'IVar'. Returns 'True' if successful.+tryWrite :: IVar a -> a -> IO Bool+tryWrite (IVar lock trans _) value = do+ a <- tryTakeMVar lock+ case a of+ Just _ -> putMVar trans value >> return True+ Nothing -> return False
+ src/Data/IVar/Simple/IChan.hs view
@@ -0,0 +1,65 @@+-- |+-- Module : Data.IVar.Simple.IChan+-- Copyright : (c) 2008, 2009 Bertram Felgenhauer+-- License : BSD3+--+-- Maintainer : Bertram Felgenhauer <int-e@gmx.de>+-- Stability : experimental+-- Portability : ghc+--+-- An 'IChan's is a type of multicast channel built on top of 'IVar.IVar's.+-- It supports multiple readers. The 'IChan' data type represents the head+-- of a channel.+--+-- Writing to an 'IChan' head has write-once semantics similar to 'IVar.IVar's:+-- only the first of several attempts to write to the head will succeed,+-- returning a new 'IChan' head for writing more values.+--+module Data.IVar.Simple.IChan (+ IChan,+ new,+ read,+ write,+ tryWrite,+) where++import Prelude hiding (read)+import qualified Data.IVar.Simple as IVar+import Control.Monad+import Control.Concurrent.MVar++-- | A channel head+newtype IChan a = IChan (IVar.IVar (a, IChan a))++-- | Create a new channel.+new :: IO (IChan a)+new = IChan `fmap` IVar.new++-- | Returns the contents of a channel as a list, starting at the channel+-- head.+--+-- This is a pure computation. Forcing elements of the list may, however,+-- block.+read :: IChan a -> [a]+read (IChan as) = let (a, ic) = IVar.read as in a : read ic++-- | Write a single value to the channel.+--+-- Raises a 'BlockedIndefinitely' exception if a value has already been+-- written to the channel. Otherwise, returns a new channel head for+-- writing further values.+write :: IChan a -> a -> IO (IChan a)+write (IChan as) a = do+ ic <- new+ IVar.write as (a, ic)+ return ic++-- | Attempts to write a single value to the channel.+--+-- If a value has already been written, returns 'Nothing'. Otherwise,+-- returns a new channel head for writing further values.+tryWrite :: IChan a -> a -> IO (Maybe (IChan a))+tryWrite (IChan as) a = do+ ic <- new+ success <- IVar.tryWrite as (a, ic)+ return (guard success >> return ic)
+ src/Data/IVar/Simple/MIChan.hs view
@@ -0,0 +1,68 @@+-- |+-- Module : Data.IVar.Simple.MIChan+-- Copyright : (c) 2008, 2009 Bertram Felgenhauer+-- License : BSD3+--+-- Maintainer : Bertram Felgenhauer <int-e@gmx.de>+-- Stability : experimental+-- Portability : ghc+--+-- An 'MIChan' is a multicast channel built on top of an 'IChan.IChan'.+--+-- Like 'IChan.IChan', this supports multiple readers. It is comparable to+-- a @Control.Concurrent.Chan.Chan@ for the writing end: Each write will+-- append an element to the channel. No writes will fail.++module Data.IVar.Simple.MIChan (+ -- $comparison+ MIChan,+ new,+ read,+ write,+ writeList,+) where++import Prelude hiding (read)+import qualified Data.IVar.Simple.IChan as IChan+import Control.Monad+import Control.Concurrent.MVar++-- | A multicast channel.+newtype MIChan a = MIChan (MVar (IChan.IChan a))++-- | Create a new multicast channel.+new :: IO (MIChan a)+new = do+ ic <- IChan.new+ MIChan `fmap` newMVar ic++-- | Return the list of values that the channel represents.+read :: MIChan a -> IO [a]+read (MIChan mic) = do+ IChan.read `fmap` readMVar mic++-- | Send a value across the channel.+write :: MIChan a -> a -> IO ()+write (MIChan mic) value = do+ modifyMVar_ mic (\ic -> IChan.write ic value)++-- | Send several values across the channel, atomically.+writeList :: MIChan a -> [a] -> IO ()+writeList (MIChan mic) values = do+ modifyMVar_ mic (\ic -> foldM IChan.write ic values)++-- $comparison+-- Comparison to @Control.Concurrent.Chan@:+--+-- > Control.Concurrent.Chan.Chan => Data.MIChan+-- > newChan => new+-- > writeChan => write+-- > getChanContents => read+-- > writeList2Chan => writeList+--+-- These can't be implemented:+--+-- > readChan (can't steal items from other readers)+-- > unGetChan (there is no separate reading end)+-- > isEmptyChan (needs an IO interface for reading)+-- > dupChan (not needed)