diff --git a/BoundedChan.cabal b/BoundedChan.cabal
new file mode 100644
--- /dev/null
+++ b/BoundedChan.cabal
@@ -0,0 +1,20 @@
+name:       BoundedChan
+category:   Concurrency
+version:    1.0.0.0
+license:    BSD3
+author:     Adam Wick <awick@galois.com>
+maintainer: Adam Wick <awick@galois.com>
+stability:  stable
+build-type: Simple
+cabal-version: >= 1.6
+tested-with: GHC ==6.10
+synopsis:      Implementation of bounded Chans.
+description:   This library introduces BoundedChan. BoundedChan works exactly
+               like Chan -- and, indeed, has the exact same interface -- but
+               is guaranteed to operate in a bounded amount of space.
+
+Library
+  build-depends: base >= 3, array
+  exposed-modules: Control.Concurrent.BoundedChan
+  GHC-Options: -O2 -Wall -funbox-strict-fields
+
diff --git a/Control/Concurrent/BoundedChan.hs b/Control/Concurrent/BoundedChan.hs
new file mode 100644
--- /dev/null
+++ b/Control/Concurrent/BoundedChan.hs
@@ -0,0 +1,77 @@
+-- |Implements bounded channels. In these channels, you can give a
+-- rough maximum number of elements, and you will be guaranteed that no more
+-- than that number of elements will be pending within the channel.
+--
+-- This boundedness is ideal when you will be (or may be) writing to a channel
+-- faster than you are able to read from it.
+--
+-- This module supports all the functions of Control.Concurrent.Chan except
+-- unGetChan and dupChan, which are not supported for bounded channels.
+module Control.Concurrent.BoundedChan(
+         BoundedChan
+       , newBoundedChan
+       , writeChan
+       , readChan
+       , isEmptyChan
+       , getChanContents
+       , writeList2Chan
+       )
+  where
+
+import Control.Concurrent.MVar
+import Control.Monad(replicateM)
+import Data.Array
+import System.IO.Unsafe(unsafeInterleaveIO)
+
+-- |BoundedChan is an abstract data type representing an unbounded channel.
+data BoundedChan a = BC {
+       _size     :: Int
+     , _contents :: Array Int (MVar a)
+     , _writePos :: MVar Int
+     , _readPos  :: MVar Int
+     }
+-- LOCK ORDERING: A -> B -> C
+
+-- |Create a new bounded chan with size n. 
+newBoundedChan :: Int -> IO (BoundedChan a)
+newBoundedChan x = do
+  entls   <- replicateM x newEmptyMVar
+  wpos    <- newMVar 0
+  rpos    <- newMVar 0
+  let entries = listArray (0, x - 1) entls
+  return (BC x entries wpos rpos)
+
+-- |Write an element to the channel. If the channel is full, this routine will
+-- block until it is able to write. If you have multiple writers, be careful
+-- here, because the unlocking is not guaranteed to avoid starvation.
+writeChan :: BoundedChan a -> a -> IO ()
+writeChan (BC size contents wposMV _) x = do
+  wpos <- takeMVar wposMV
+  putMVar wposMV $! (wpos + 1) `mod` size
+  putMVar (contents ! wpos) x
+
+-- |Read an element from the channel. If the channel is empty, this routine 
+-- will block until it is able to read.
+readChan :: BoundedChan a -> IO a
+readChan (BC size contents _ rposMV) = do
+  rpos <- takeMVar rposMV
+  putMVar rposMV $! (rpos + 1) `mod` size
+  takeMVar (contents ! rpos)
+
+isEmptyChan :: BoundedChan a -> IO Bool
+isEmptyChan (BC _ contents _ rposMV) = do
+  rpos <- takeMVar rposMV
+  res <- isEmptyMVar (contents ! rpos)
+  putMVar rposMV rpos
+  return res
+
+getChanContents :: BoundedChan a -> IO [a]
+getChanContents ch = unsafeInterleaveIO $ do
+                       x  <- readChan ch
+                       xs <- getChanContents ch
+                       return (x:xs)
+
+-- |Write a list of elements to the channel. Note that this may block as it
+-- writes the list into the channel.
+writeList2Chan :: BoundedChan a -> [a] -> IO ()
+writeList2Chan ch ls = mapM_ (writeChan ch) ls
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
