diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,84 @@
+# Pipes-Safe v2.2.0
+
+`pipes-safe` builds upon
+[the `pipes` library](https://github.com/Gabriel439/Haskell-Pipes-Library) to
+provide exception safety and resource management.
+
+## Quick start
+
+* Install the [Haskell Platform](http://www.haskell.org/platform/)
+* `cabal install pipes-safe`
+
+The official tutorial is on
+[Hackage](http://hackage.haskell.org/package/pipes-safe).
+
+## Features
+
+* *Resource Safety*: Guarantee finalization using `finally`, `bracket`, and more
+
+* *Exception Safety*: Even against asynchronous exceptions!
+
+* *Laziness*: Only acquire resources when you need them
+
+* *Promptness*: Finalize resources early when you are done with them
+
+* *Native Exception Handling*: Catch and resume from exceptions inside pipes
+
+* *No Buy-in*: Mix resource-safe pipes with unmanaged pipes using `hoist`
+
+## Outline
+
+Use `pipes-safe` for production code where you need deterministic and prompt
+release of resources in the fact of exceptions or premature pipe termination.
+`pipes-safe` lets you safely acquire resources and handle exceptions within
+pipelines.
+
+## Development Status
+
+`pipes-safe` is mostly stable.  Research into prompter finalization alternatives
+will take place in a separate library.
+
+## Community Resources
+
+Use the same resources as the core `pipes` library to learn more, contribute, or
+request help:
+
+* [Haskell wiki page](http://www.haskell.org/haskellwiki/Pipes)
+
+* [Mailing list](mailto:haskell-pipes@googlegroups.com) ([Google Group](https://groups.google.com/forum/?fromgroups#!forum/haskell-pipes))
+
+## How to contribute
+
+* Build derived libraries
+
+* Write `pipes-safe` tutorials
+
+## License (BSD 3-clause)
+
+Copyright (c) 2013 Gabriel Gonzalez
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* 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.
+
+* Neither the name of Gabriel Gonzalez nor the names of other 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.
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,3 @@
+# Version 2.2.1
+
+* Raise upper-bound on `exceptions` dependency.
diff --git a/pipes-safe.cabal b/pipes-safe.cabal
--- a/pipes-safe.cabal
+++ b/pipes-safe.cabal
@@ -1,9 +1,10 @@
 Name: pipes-safe
-Version: 2.2.0
+Version: 2.2.1
 Cabal-Version: >=1.8.0.2
 Build-Type: Simple
 License: BSD3
 License-File: LICENSE
+Extra-Source-Files: README.md changelog.md
 Copyright: 2013, 2014 Gabriel Gonzalez
 Author: Gabriel Gonzalez
 Maintainer: Gabriel439@gmail.com
@@ -36,7 +37,7 @@
     Build-Depends:
         base         >= 4       && < 5  ,
         containers   >= 0.3.0.0 && < 0.6,
-        exceptions   >= 0.6     && < 0.7,
+        exceptions   >= 0.6     && < 0.8,
         transformers >= 0.2.0.0 && < 0.5,
         pipes        >= 4.0.0   && < 4.2
     Exposed-Modules:
diff --git a/src/Pipes/Safe.hs b/src/Pipes/Safe.hs
--- a/src/Pipes/Safe.hs
+++ b/src/Pipes/Safe.hs
@@ -123,7 +123,7 @@
 import qualified Data.Map as M
 import Data.Monoid (Monoid)
 import Pipes (Proxy, Effect, Effect', runEffect)
-import Pipes.Internal (unsafeHoist, Proxy(..))
+import Pipes.Internal (Proxy(..))
 import Pipes.Lift (liftCatchError)
 
 data Restore m = Unmasked | Masked (forall x . m x -> m x)
@@ -134,34 +134,35 @@
     -> ((forall x . Proxy a' a b' b m x -> Proxy a' a b' b m x)
         -> Proxy a' a b' b m r)
     -> Proxy a' a b' b m r
-liftMask maskFunction k = do
-        ioref <- liftIO (newIORef Unmasked)
-        let unmask
-                :: forall y . (Monad m)
-                => Proxy a' a b' b m y -> Proxy a' a b' b m y
-            unmask p = do
-                mRestore <- liftIO (readIORef ioref)
-                case mRestore of
-                    Unmasked       -> p
-                    Masked restore -> do
-                        r <- unsafeHoist restore p
-                        lift $ restore $ return ()
-                        return r
-            loop p = case p of
-                Request a' fa  -> Request a' (loop . fa )
-                Respond b  fb' -> Respond b  (loop . fb')
-                M m0           -> M $ maskFunction $ \restore -> do
-                    liftIO $ writeIORef ioref (Masked restore)
-                    let loop' m = do
-                            p' <- m
-                            case p' of
-                                M m' -> loop' m'
-                                _    -> return p'
-                    p' <- loop' m0
-                    liftIO $ writeIORef ioref  Unmasked
-                    return (loop p')
-                Pure r         -> Pure r
-        loop (k unmask)
+liftMask maskVariant k = do
+    ioref <- liftIO $ newIORef Unmasked
+
+    let -- mask adjacent actions in base monad
+        loop :: Proxy a' a b' b m r -> Proxy a' a b' b m r
+        loop (Request a' fa ) = Request a' (loop . fa )
+        loop (Respond b  fb') = Respond b  (loop . fb')
+        loop (M m)            = M $ maskVariant $ \unmaskVariant -> do
+            -- stash base's unmask and merge action
+            liftIO $ writeIORef ioref $ Masked unmaskVariant
+            m >>= chunk >>= return . loop
+        loop (Pure r)         = Pure r
+
+        -- unmask adjacent actions in base monad
+        unmask :: forall q. Proxy a' a b' b m q -> Proxy a' a b' b m q
+        unmask (Request a' fa ) = Request a' (unmask . fa )
+        unmask (Respond b  fb') = Respond b  (unmask . fb')
+        unmask (M m)            = M $ do
+            -- retrieve base's unmask and apply to merged action
+            Masked unmaskVariant <- liftIO $ readIORef ioref
+            unmaskVariant (m >>= chunk >>= return . unmask)
+        unmask (Pure q)         = Pure q
+
+        -- merge adjacent actions in base monad
+        chunk :: forall s. Proxy a' a b' b m s -> m (Proxy a' a b' b m s)
+        chunk (M m) = m >>= chunk
+        chunk s     = return s
+
+    loop $ k unmask
 
 instance (MonadThrow m) => MonadThrow (Proxy a' a b' b m) where
     throwM = lift . throwM
