diff --git a/Control/Concurrent/STM.hs b/Control/Concurrent/STM.hs
--- a/Control/Concurrent/STM.hs
+++ b/Control/Concurrent/STM.hs
@@ -29,7 +29,9 @@
 	module Control.Concurrent.STM.TVar,
 #ifdef __GLASGOW_HASKELL__
 	module Control.Concurrent.STM.TMVar,
-	module Control.Concurrent.STM.TChan,
+        module Control.Concurrent.STM.TChan,
+        module Control.Concurrent.STM.TQueue,
+        module Control.Concurrent.STM.TBQueue,
 #endif
 	module Control.Concurrent.STM.TArray
   ) where
@@ -41,3 +43,5 @@
 import Control.Concurrent.STM.TChan
 #endif
 import Control.Concurrent.STM.TArray
+import Control.Concurrent.STM.TQueue
+import Control.Concurrent.STM.TBQueue
diff --git a/Control/Concurrent/STM/TBQueue.hs b/Control/Concurrent/STM/TBQueue.hs
new file mode 100644
--- /dev/null
+++ b/Control/Concurrent/STM/TBQueue.hs
@@ -0,0 +1,179 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+{-# LANGUAGE CPP, DeriveDataTypeable #-}
+
+#if __GLASGOW_HASKELL__ >= 701
+{-# LANGUAGE Trustworthy #-}
+#endif
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Control.Concurrent.STM.TBQueue
+-- Copyright   :  (c) The University of Glasgow 2012
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+-- 
+-- Maintainer  :  libraries@haskell.org
+-- Stability   :  experimental
+-- Portability :  non-portable (requires STM)
+--
+-- 'TBQueue' is a bounded version of 'TQueue'. The queue has a maximum
+-- capacity set when it is created.  If the queue already contains the
+-- maximum number of elements, then 'writeTBQueue' blocks until an
+-- element is removed from the queue.
+--
+-- The implementation is based on the traditional purely-functional
+-- queue representation that uses two lists to obtain amortised /O(1)/
+-- enqueue and dequeue operations.
+--
+-----------------------------------------------------------------------------
+
+module Control.Concurrent.STM.TBQueue (
+        -- * TBQueue
+	TBQueue,
+	newTBQueue,
+	newTBQueueIO,
+	readTBQueue,
+	tryReadTBQueue,
+	peekTBQueue,
+	tryPeekTBQueue,
+	writeTBQueue,
+        unGetTBQueue,
+        isEmptyTBQueue,
+  ) where
+
+import Data.Typeable
+import GHC.Conc
+
+#define _UPK_(x) {-# UNPACK #-} !(x)
+
+-- | 'TBQueue' is an abstract type representing a bounded FIFO channel.
+data TBQueue a
+   = TBQueue _UPK_(TVar Int)  -- CR: read capacity
+             _UPK_(TVar [a])  -- R:  elements waiting to be read
+             _UPK_(TVar Int)  -- CW: write capacity
+             _UPK_(TVar [a])  -- W:  elements written (head is most recent)
+  deriving Typeable
+
+instance Eq (TBQueue a) where
+  TBQueue a _ _ _ == TBQueue b _ _ _ = a == b
+
+-- Total channel capacity remaining is CR + CW. Reads only need to
+-- access CR, writes usually need to access only CW but sometimes need
+-- CR.  So in the common case we avoid contention between CR and CW.
+--
+--   - when removing an element from R:
+--     CR := CR + 1
+--
+--   - when adding an element to W:
+--     if CW is non-zero
+--         then CW := CW - 1
+--         then if CR is non-zero
+--                 then CW := CR - 1; CR := 0
+--                 else **FULL**
+
+-- |Build and returns a new instance of 'TBQueue'
+newTBQueue :: Int   -- ^ maximum number of elements the queue can hold
+           -> STM (TBQueue a)
+newTBQueue size = do
+  read  <- newTVar []
+  write <- newTVar []
+  rsize <- newTVar 0
+  wsize <- newTVar size
+  return (TBQueue rsize read wsize write)
+
+-- |@IO@ version of 'newTBQueue'.  This is useful for creating top-level
+-- 'TBQueue's using 'System.IO.Unsafe.unsafePerformIO', because using
+-- 'atomically' inside 'System.IO.Unsafe.unsafePerformIO' isn't
+-- possible.
+newTBQueueIO :: Int -> IO (TBQueue a)
+newTBQueueIO size = do
+  read  <- newTVarIO []
+  write <- newTVarIO []
+  rsize <- newTVarIO 0
+  wsize <- newTVarIO size
+  return (TBQueue rsize read wsize write)
+
+-- |Write a value to a 'TBQueue'; blocks if the queue is full.
+writeTBQueue :: TBQueue a -> a -> STM ()
+writeTBQueue (TBQueue rsize _read wsize write) a = do
+  w <- readTVar wsize
+  if (w /= 0)
+     then do writeTVar wsize (w - 1)
+     else do
+          r <- readTVar rsize
+          if (r /= 0)
+             then do writeTVar rsize 0
+                     writeTVar wsize (r - 1)
+             else retry
+  listend <- readTVar write
+  writeTVar write (a:listend)
+
+-- |Read the next value from the 'TBQueue'.
+readTBQueue :: TBQueue a -> STM a
+readTBQueue (TBQueue rsize read _wsize write) = do
+  xs <- readTVar read
+  r <- readTVar rsize
+  writeTVar rsize (r + 1)
+  case xs of
+    (x:xs') -> do
+      writeTVar read xs'
+      return x
+    [] -> do
+      ys <- readTVar write
+      case ys of
+        [] -> retry
+        _  -> do
+          let (z:zs) = reverse ys -- NB. lazy: we want the transaction to be
+                                  -- short, otherwise it will conflict
+          writeTVar write []
+          writeTVar read zs
+          return z
+
+-- | A version of 'readTBQueue' which does not retry. Instead it
+-- returns @Nothing@ if no value is available.
+tryReadTBQueue :: TBQueue a -> STM (Maybe a)
+tryReadTBQueue c = fmap Just (readTBQueue c) `orElse` return Nothing
+
+-- | Get the next value from the @TBQueue@ without removing it,
+-- retrying if the channel is empty.
+peekTBQueue :: TBQueue a -> STM a
+peekTBQueue c = do
+  x <- readTBQueue c
+  unGetTBQueue c x
+  return x
+
+-- | A version of 'peekTBQueue' which does not retry. Instead it
+-- returns @Nothing@ if no value is available.
+tryPeekTBQueue :: TBQueue a -> STM (Maybe a)
+tryPeekTBQueue c = do
+  m <- tryReadTBQueue c
+  case m of
+    Nothing -> return Nothing
+    Just x  -> do
+      unGetTBQueue c x
+      return m
+
+-- |Put a data item back onto a channel, where it will be the next item read.
+-- Blocks if the queue is full.
+unGetTBQueue :: TBQueue a -> a -> STM ()
+unGetTBQueue (TBQueue rsize read wsize _write) a = do
+  r <- readTVar rsize
+  if (r > 0)
+     then do writeTVar rsize (r - 1)
+     else do
+          w <- readTVar wsize
+          if (w > 0)
+             then writeTVar wsize (w - 1)
+             else retry
+  xs <- readTVar read
+  writeTVar read (a:xs)
+
+-- |Returns 'True' if the supplied 'TBQueue' is empty.
+isEmptyTBQueue :: TBQueue a -> STM Bool
+isEmptyTBQueue (TBQueue _rsize read _wsize write) = do
+  xs <- readTVar read
+  case xs of
+    (_:_) -> return False
+    [] -> do ys <- readTVar write
+             case ys of
+               [] -> return True
+               _  -> return False
diff --git a/Control/Concurrent/STM/TChan.hs b/Control/Concurrent/STM/TChan.hs
--- a/Control/Concurrent/STM/TChan.hs
+++ b/Control/Concurrent/STM/TChan.hs
@@ -24,16 +24,23 @@
 #ifdef __GLASGOW_HASKELL__
 	-- * TChans
 	TChan,
-	newTChan,
+
+        -- ** Construction
+        newTChan,
 	newTChanIO,
+	newBroadcastTChan,
+	newBroadcastTChanIO,
+        dupTChan,
+
+        -- ** Reading and writing
 	readTChan,
 	tryReadTChan,
 	peekTChan,
 	tryPeekTChan,
 	writeTChan,
-	dupTChan,
-	unGetTChan,
-	isEmptyTChan
+        unGetTChan,
+        isEmptyTChan,
+        cloneTChan
 #endif
   ) where
 
@@ -42,13 +49,17 @@
 
 import Data.Typeable (Typeable)
 
+#define _UPK_(x) {-# UNPACK #-} !(x)
+
 -- | 'TChan' is an abstract type representing an unbounded FIFO channel.
-data TChan a = TChan (TVar (TVarList a)) (TVar (TVarList a)) deriving Typeable
+data TChan a = TChan _UPK_(TVar (TVarList a))
+                     _UPK_(TVar (TVarList a))
+  deriving (Eq, Typeable)
 
 type TVarList a = TVar (TList a)
-data TList a = TNil | TCons a (TVarList a)
+data TList a = TNil | TCons a _UPK_(TVarList a)
 
--- |Build and returns a new instance of 'TChan'
+-- |Build and return a new instance of 'TChan'
 newTChan :: STM (TChan a)
 newTChan = do
   hole <- newTVar TNil
@@ -67,6 +78,40 @@
   write <- newTVarIO hole
   return (TChan read write)
 
+-- | Create a write-only 'TChan'.  More precisely, 'readTChan' will 'retry'
+-- even after items have been written to the channel.  The only way to read
+-- a broadcast channel is to duplicate it with 'dupTChan'.
+--
+-- Consider a server that broadcasts messages to clients:
+--
+-- >serve :: TChan Message -> Client -> IO loop
+-- >serve broadcastChan client = do
+-- >    myChan <- dupTChan broadcastChan
+-- >    forever $ do
+-- >        message <- readTChan myChan
+-- >        send client message
+--
+-- The problem with using 'newTChan' to create the broadcast channel is that if
+-- it is only written to and never read, items will pile up in memory.  By
+-- using 'newBroadcastTChan' to create the broadcast channel, items can be
+-- garbage collected after clients have seen them.
+newBroadcastTChan :: STM (TChan a)
+newBroadcastTChan = do
+    dummy_hole <- newTVar TNil
+    write_hole <- newTVar TNil
+    read <- newTVar dummy_hole
+    write <- newTVar write_hole
+    return (TChan read write)
+
+-- | @IO@ version of 'newBroadcastTChan'.
+newBroadcastTChanIO :: IO (TChan a)
+newBroadcastTChanIO = do
+    dummy_hole <- newTVarIO TNil
+    write_hole <- newTVarIO TNil
+    read <- newTVarIO dummy_hole
+    write <- newTVarIO write_hole
+    return (TChan read write)
+
 -- |Write a value to a 'TChan'.
 writeTChan :: TChan a -> a -> STM ()
 writeTChan (TChan _read write) a = do
@@ -143,4 +188,12 @@
   case head of
     TNil -> return True
     TCons _ _ -> return False
+
+-- |Clone a 'TChan': similar to dupTChan, but the cloned channel starts with the
+-- same content available as the original channel.
+cloneTChan :: TChan a -> STM (TChan a)
+cloneTChan (TChan read write) = do
+  readpos <- readTVar read
+  new_read <- newTVar readpos
+  return (TChan new_read write)
 #endif
diff --git a/Control/Concurrent/STM/TQueue.hs b/Control/Concurrent/STM/TQueue.hs
new file mode 100644
--- /dev/null
+++ b/Control/Concurrent/STM/TQueue.hs
@@ -0,0 +1,137 @@
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+{-# LANGUAGE CPP, DeriveDataTypeable #-}
+
+#if __GLASGOW_HASKELL__ >= 701
+{-# LANGUAGE Trustworthy #-}
+#endif
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Control.Concurrent.STM.TQueue
+-- Copyright   :  (c) The University of Glasgow 2012
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+-- 
+-- Maintainer  :  libraries@haskell.org
+-- Stability   :  experimental
+-- Portability :  non-portable (requires STM)
+--
+-- A 'TQueue' is like a 'TChan', with two important differences:
+--
+--  * it has faster throughput than both 'TChan' and 'Chan' (although
+--    the costs are amortised, so the cost of individual operations
+--    can vary a lot).
+--
+--  * it does /not/ provide equivalents of the 'dupTChan' and
+--    'cloneTChan' operations.
+--
+-- The implementation is based on the traditional purely-functional
+-- queue representation that uses two lists to obtain amortised /O(1)/
+-- enqueue and dequeue operations.
+--
+-----------------------------------------------------------------------------
+
+module Control.Concurrent.STM.TQueue (
+        -- * TQueue
+	TQueue,
+	newTQueue,
+	newTQueueIO,
+	readTQueue,
+	tryReadTQueue,
+	peekTQueue,
+	tryPeekTQueue,
+	writeTQueue,
+        unGetTQueue,
+        isEmptyTQueue,
+  ) where
+
+import GHC.Conc
+
+import Data.Typeable (Typeable)
+
+-- | 'TQueue' is an abstract type representing an unbounded FIFO channel.
+data TQueue a = TQueue {-# UNPACK #-} !(TVar [a])
+                       {-# UNPACK #-} !(TVar [a])
+  deriving Typeable
+
+instance Eq (TQueue a) where
+  TQueue a _ == TQueue b _ = a == b
+
+-- |Build and returns a new instance of 'TQueue'
+newTQueue :: STM (TQueue a)
+newTQueue = do
+  read  <- newTVar []
+  write <- newTVar []
+  return (TQueue read write)
+
+-- |@IO@ version of 'newTQueue'.  This is useful for creating top-level
+-- 'TQueue's using 'System.IO.Unsafe.unsafePerformIO', because using
+-- 'atomically' inside 'System.IO.Unsafe.unsafePerformIO' isn't
+-- possible.
+newTQueueIO :: IO (TQueue a)
+newTQueueIO = do
+  read  <- newTVarIO []
+  write <- newTVarIO []
+  return (TQueue read write)
+
+-- |Write a value to a 'TQueue'.
+writeTQueue :: TQueue a -> a -> STM ()
+writeTQueue (TQueue _read write) a = do
+  listend <- readTVar write
+  writeTVar write (a:listend)
+
+-- |Read the next value from the 'TQueue'.
+readTQueue :: TQueue a -> STM a
+readTQueue (TQueue read write) = do
+  xs <- readTVar read
+  case xs of
+    (x:xs') -> do writeTVar read xs'
+                  return x
+    [] -> do ys <- readTVar write
+             case ys of
+               [] -> retry
+               _  -> case reverse ys of
+                       [] -> error "readTQueue"
+                       (z:zs) -> do writeTVar write []
+                                    writeTVar read zs
+                                    return z
+
+-- | A version of 'readTQueue' which does not retry. Instead it
+-- returns @Nothing@ if no value is available.
+tryReadTQueue :: TQueue a -> STM (Maybe a)
+tryReadTQueue c = fmap Just (readTQueue c) `orElse` return Nothing
+
+-- | Get the next value from the @TQueue@ without removing it,
+-- retrying if the channel is empty.
+peekTQueue :: TQueue a -> STM a
+peekTQueue c = do
+  x <- readTQueue c
+  unGetTQueue c x
+  return x
+
+-- | A version of 'peekTQueue' which does not retry. Instead it
+-- returns @Nothing@ if no value is available.
+tryPeekTQueue :: TQueue a -> STM (Maybe a)
+tryPeekTQueue c = do
+  m <- tryReadTQueue c
+  case m of
+    Nothing -> return Nothing
+    Just x  -> do
+      unGetTQueue c x
+      return m
+
+-- |Put a data item back onto a channel, where it will be the next item read.
+unGetTQueue :: TQueue a -> a -> STM ()
+unGetTQueue (TQueue read _write) a = do
+  xs <- readTVar read
+  writeTVar read (a:xs)
+
+-- |Returns 'True' if the supplied 'TQueue' is empty.
+isEmptyTQueue :: TQueue a -> STM Bool
+isEmptyTQueue (TQueue read write) = do
+  xs <- readTVar read
+  case xs of
+    (_:_) -> return False
+    [] -> do ys <- readTVar write
+             case ys of
+               [] -> return True
+               _  -> return False
diff --git a/Control/Sequential/STM.hs b/Control/Sequential/STM.hs
--- a/Control/Sequential/STM.hs
+++ b/Control/Sequential/STM.hs
@@ -14,7 +14,9 @@
 	TVar, newTVar, newTVarIO, readTVar, readTVarIO, writeTVar
     ) where
 
+#if __GLASGOW_HASKELL__ < 705
 import Prelude hiding (catch)
+#endif
 import Control.Applicative (Applicative(pure, (<*>)))
 import Control.Exception
 import Data.IORef
diff --git a/stm.cabal b/stm.cabal
--- a/stm.cabal
+++ b/stm.cabal
@@ -1,11 +1,27 @@
 name:		stm
-version:        2.3
+version:        2.4
 license:	BSD3
 license-file:	LICENSE
 maintainer:	libraries@haskell.org
 synopsis:	Software Transactional Memory
 category:       Concurrency
-description:	A modular composable concurrency abstraction.
+description:
+ A modular composable concurrency abstraction.
+ .
+ Changes in version 2.4
+ .
+ * Added "Control.Concurrent.STM.TQueue" (a faster @TChan@)
+ .
+ * Added "Control.Concurrent.STM.TBQueue" (a bounded channel based on @TQueue@)
+ .
+ * @TChan@ has an @Eq@ instances
+ .
+ * Added @newBroadcastTChan@ and @newBroadcastTChanIO@
+ .
+ * Some performance improvements for @TChan@
+ .
+ * Added @cloneTChan@
+
 build-type:     Simple
 cabal-version:  >=1.6
 
@@ -22,6 +38,8 @@
     Control.Concurrent.STM.TVar
     Control.Concurrent.STM.TChan
     Control.Concurrent.STM.TMVar
+    Control.Concurrent.STM.TQueue
+    Control.Concurrent.STM.TBQueue
     Control.Monad.STM
   other-modules:
     Control.Sequential.STM
