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
@@ -97,9 +97,8 @@
 -- 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
+    read <- newTVar (error "reading from a TChan created by newBroadcastTChan; use dupTChan first")
     write <- newTVar write_hole
     return (TChan read write)
 
diff --git a/Control/Concurrent/STM/TSem.hs b/Control/Concurrent/STM/TSem.hs
new file mode 100644
--- /dev/null
+++ b/Control/Concurrent/STM/TSem.hs
@@ -0,0 +1,54 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Control.Concurrent.STM.TSem
+-- 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)
+--
+-- 'TSem': transactional semaphores.
+--
+-----------------------------------------------------------------------------
+
+{-# LANGUAGE DeriveDataTypeable #-}
+module Control.Concurrent.STM.TSem (
+      TSem, newTSem, waitTSem, signalTSem
+  ) where
+
+import Control.Concurrent.STM
+import Control.Monad
+import Data.Typeable
+import Control.Exception
+
+-- | 'TSem' is a transactional semaphore.  It holds a certain number
+-- of units, and units may be acquired or released by 'waitTSem' and
+-- 'signalTSem' respectively.  When the 'TSem' is empty, 'waitTSem'
+-- blocks.
+--
+-- Note that 'TSem' has no concept of fairness, and there is no
+-- guarantee that threads blocked in `waitTSem` will be unblocked in
+-- the same order; in fact they will all be unblocked at the same time
+-- and will fight over the 'TSem'.  Hence 'TSem' is not suitable if
+-- you expect there to be a high number of threads contending for the
+-- resource.  However, like other STM abstractions, 'TSem' is
+-- composable.
+--
+newtype TSem = TSem (TVar Int)
+  deriving (Eq, Typeable)
+
+newTSem :: Int -> STM TSem
+newTSem i = fmap TSem (newTVar i)
+
+waitTSem :: TSem -> STM ()
+waitTSem (TSem t) = do
+  i <- readTVar t
+  when (i <= 0) retry
+  writeTVar t $! (i-1)
+
+signalTSem :: TSem -> STM ()
+signalTSem (TSem t) = do
+  i <- readTVar t
+  writeTVar t $! i+1
+
diff --git a/Control/Monad/STM.hs b/Control/Monad/STM.hs
--- a/Control/Monad/STM.hs
+++ b/Control/Monad/STM.hs
@@ -57,13 +57,29 @@
 
 #ifdef __GLASGOW_HASKELL__
 #if ! (MIN_VERSION_base(4,3,0))
+import Control.Applicative
+import Control.Monad (ap)
+#endif
+#endif
+
+
+#ifdef __GLASGOW_HASKELL__
+#if ! (MIN_VERSION_base(4,3,0))
 instance MonadPlus STM where
   mzero = retry
   mplus = orElse
+
+instance Applicative STM where
+  pure = return
+  (<*>) = ap
+
+instance Alternative STM where
+  empty = retry
+  (<|>) = orElse
 #endif
 
-check :: Bool -> STM a
-check b = if b then return undefined else retry
+check :: Bool -> STM ()
+check b = if b then return () else retry
 #endif
 
 #if ! (MIN_VERSION_base(4,3,0))
diff --git a/stm.cabal b/stm.cabal
--- a/stm.cabal
+++ b/stm.cabal
@@ -1,5 +1,5 @@
 name:		stm
-version:        2.4
+version:        2.4.2
 license:	BSD3
 license-file:	LICENSE
 maintainer:	libraries@haskell.org
@@ -8,6 +8,14 @@
 description:
  A modular composable concurrency abstraction.
  .
+ Changes in version 2.4.2
+ .
+ * Added "Control.Concurrent.STM.TSem" (transactional semaphore)
+ .
+ Changes in version 2.4.1
+ .
+ * Added Applicative/Alternative instances of STM for GHC <7.0
+ .
  Changes in version 2.4
  .
  * Added "Control.Concurrent.STM.TQueue" (a faster @TChan@)
@@ -40,6 +48,7 @@
     Control.Concurrent.STM.TMVar
     Control.Concurrent.STM.TQueue
     Control.Concurrent.STM.TBQueue
+    Control.Concurrent.STM.TSem
     Control.Monad.STM
   other-modules:
     Control.Sequential.STM
