diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
@@ -0,0 +1,3 @@
+## 0
+* Initial version
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2015 Edward Kmett
+
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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/README.markdown b/README.markdown
new file mode 100644
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,13 @@
+## rcu
+
+[![Build Status](https://secure.travis-ci.org/ekmett/rcu.png?branch=master)](http://travis-ci.org/ekmett/rcu)
+
+This package is an exploration of read-copy-update in Haskell based loosely on the API in [Relativistic Programming in Haskell](http://web.cecs.pdx.edu/~walpole/papers/haskell2015.pdf) by Cooper and Walpole, but converted to using STM in the spirit of Howard and Walpole.
+
+## Contact Information
+
+Contributions and bug reports are welcome!
+
+Please feel free to contact me through github or on the #haskell IRC channel on irc.freenode.net.
+
+-Edward Kmett
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,46 @@
+#!/usr/bin/runhaskell
+\begin{code}
+{-# OPTIONS_GHC -Wall #-}
+module Main (main) where
+
+import Data.List ( nub )
+import Data.Version ( showVersion )
+import Distribution.Package ( PackageName(PackageName), PackageId, InstalledPackageId, packageVersion, packageName )
+import Distribution.PackageDescription ( PackageDescription(), TestSuite(..) )
+import Distribution.Simple ( defaultMainWithHooks, UserHooks(..), simpleUserHooks )
+import Distribution.Simple.Utils ( rewriteFile, createDirectoryIfMissingVerbose )
+import Distribution.Simple.BuildPaths ( autogenModulesDir )
+import Distribution.Simple.Setup ( BuildFlags(buildVerbosity), fromFlag)
+import Distribution.Simple.LocalBuildInfo ( withLibLBI, withTestLBI, LocalBuildInfo(), ComponentLocalBuildInfo(componentPackageDeps) )
+import Distribution.Verbosity ( Verbosity )
+import System.FilePath ( (</>) )
+
+main :: IO ()
+main = defaultMainWithHooks simpleUserHooks
+  { buildHook = \pkg lbi hooks flags -> do
+     generateBuildModule (fromFlag (buildVerbosity flags)) pkg lbi
+     buildHook simpleUserHooks pkg lbi hooks flags
+  , postHaddock = \args flags pkg lbi ->
+     postHaddock simpleUserHooks args flags pkg lbi
+  }
+
+generateBuildModule :: Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
+generateBuildModule verbosity pkg lbi = do
+  let dir = autogenModulesDir lbi
+  createDirectoryIfMissingVerbose verbosity True dir
+  withLibLBI pkg lbi $ \_ libcfg -> do
+    withTestLBI pkg lbi $ \suite suitecfg -> do
+      rewriteFile (dir </> "Build_" ++ testName suite ++ ".hs") $ unlines
+        [ "module Build_" ++ testName suite ++ " where"
+        , "deps :: [String]"
+        , "deps = " ++ (show $ formatdeps (testDeps libcfg suitecfg))
+        ]
+  where
+    formatdeps = map (formatone . snd)
+    formatone p = case packageName p of
+      PackageName n -> n ++ "-" ++ showVersion (packageVersion p)
+
+testDeps :: ComponentLocalBuildInfo -> ComponentLocalBuildInfo -> [(InstalledPackageId, PackageId)]
+testDeps xs ys = nub $ componentPackageDeps xs ++ componentPackageDeps ys
+
+\end{code}
diff --git a/examples/ListInt.hs b/examples/ListInt.hs
new file mode 100644
--- /dev/null
+++ b/examples/ListInt.hs
@@ -0,0 +1,49 @@
+import Control.Concurrent.MVar (takeMVar)
+import Control.Concurrent.RCU
+import Control.Monad (forM, forM_, replicateM)
+import Data.List (group, intercalate)
+import Prelude hiding (read)
+
+data List s a = Nil | Cons a (SRef s (List s a))
+
+snapshot :: Int -> List s Int -> ReadingRCU s Int
+snapshot acc Nil         = return acc
+snapshot acc (Cons x rn) = snapshot (x + acc) =<< readSRef rn
+
+reader :: Int -> Int -> SRef s (List s Int) -> ReadingRCU s Int
+reader 0 acc _    = return acc
+reader n acc head = do
+  acc' <- snapshot acc =<< readSRef head
+  reader (n - 1) acc' head
+
+deleteMiddle :: SRef s (List s a) -> WritingRCU s ()
+deleteMiddle rl = do
+  Cons a rn <- readSRef rl
+  Cons _ rm <- readSRef rn
+  writeSRef rl $ Cons a rm 
+
+testList :: RCU s (SRef s (List s Int))
+testList = do
+  tail <- newSRef Nil
+  c1   <- newSRef $ Cons (- 1) tail
+  c2   <- newSRef $ Cons 1     c1
+  newSRef $ Cons 1 c2
+
+main :: IO ()
+main = do 
+  outs <- runRCU $ do
+    -- initialize list
+    head <- testList
+    -- spawn 8 readers, each records 100000 snapshots of the list
+    rts <- replicateM 8 $ forking $ reading $ reader 100000 0 head
+    -- spawn a writer to delete the middle node
+    wt  <- forking $ writing $ deleteMiddle head
+    
+    -- wait for the readers to finish and print snapshots
+    outs <- forM rts $ \rt -> do 
+      v <- joining rt
+      return $ show (rcuThreadId rt) ++ ": " ++ show v
+    -- wait for the writer to finish
+    joining wt
+    return outs
+  forM_ outs putStrLn
diff --git a/examples/RPManyListMoveTest.hs b/examples/RPManyListMoveTest.hs
new file mode 100644
--- /dev/null
+++ b/examples/RPManyListMoveTest.hs
@@ -0,0 +1,162 @@
+{-# LANGUAGE NamedFieldPuns #-}
+import Control.Concurrent.RCU
+import Control.Concurrent.MVar (takeMVar)
+import Control.Monad (foldM, forM, forM_, replicateM, when)
+import Data.List ((\\), foldl1', group, intercalate)
+import Debug.Trace (trace)
+
+data RPList s a = Nil
+                | Cons a (SRef s (RPList s a))
+
+data ReaderStats = ReaderStats { total   :: !Integer
+                               , full    :: !Integer
+                               , missing :: !Integer }
+                   deriving Show
+
+sappend' s1 s2 = 
+  s1 `seq` s2 `seq` ReaderStats { total   = total   s1 + total   s2
+                                , full    = full    s1 + full    s2
+                                , missing = missing s1 + missing s2 }
+
+
+snapshot :: Show a => RPList s a -> RPR s [a]
+snapshot Nil         = return []
+snapshot (Cons x rn) = do 
+  l    <- readSRef rn
+  --writeSRef rn (Cons undefined rn)
+  rest <- snapshot l
+  return $ x : rest
+
+readSection :: Show a => SRef s (RPList s a) -> RPR s [a]
+readSection head = do
+  snapshot =<< readSRef head
+
+readThread :: [Char] -> Int -> SRef s (RPList s Char) -> RPE s ReaderStats
+readThread ls n head = 
+  let st = ReaderStats { total = 0, full = 0, missing = 0 }
+      ft = ReaderStats { total = 1, full = 1, missing = 0 }
+      et = ReaderStats { total = 1, full = 0, missing = 1 }
+      trav st _ = do
+        xs <- st `seq` reading $ readSection head
+        -- use a strict append operation to avoid building up a giant thunk
+        return $ st `sappend'` if null $ ls \\ xs then ft else et
+      res = foldM trav st [1..n]
+  in res `seq` res
+
+testList :: RP s (SRef s (RPList s Char))
+testList = do
+  tail <- newSRef Nil
+  c4   <- newSRef $ Cons 'E' tail
+  c3   <- newSRef $ Cons 'D' c4
+  c2   <- newSRef $ Cons 'C' c3
+  c1   <- newSRef $ Cons 'B' c2
+  newSRef $ Cons 'A' c1
+
+compactShow :: (Show a, Eq a) => [a] -> String
+compactShow xs = intercalate ", " $ map (\xs -> show (length xs) ++ " x " ++ show (head xs)) $ group xs
+
+-- left half of a zipper
+-- n = 0 gets the first element
+-- n = 1 gets the second elemnt, followed by the first
+-- ...
+nNext :: (Monad (m s), RPRead m s) => Int -> SRef s (RPList s a) -> m s [(RPList s a)]
+nNext n head = return . snd =<< foldM scroll (head, []) [1..(max n 0)]
+  where scroll (r,ns) _ = do
+          node <- readSRef r
+          return $ case node of (Cons _ r') -> (r', node:ns)
+                                _           -> error "nNext tried to go past the end of the list"
+
+moveNodeForward :: Int -> Int -> SRef s (RPList s a) -> RPW s ()
+moveNodeForward i n head = do
+  when (n <= 0) $ error "nullary move operation not allowed"
+  -- get relevant references at earlier position
+  (Cons ep rep):_   <- nNext i head
+  (Cons x rx)       <- readSRef rep
+  esn               <- readSRef rx
+  -- get relevant references at later position
+  (Cons lp rlp):_   <- nNext n rep
+  -- the new node will need a reference to the new predecessor's current successor
+  rlp'              <- copySRef rlp
+  -- link in a new copy of the node after its new predecessor
+  writeSRef rlp $! Cons x rlp'
+  -- we're writing against traversal order, so we don't need to synchronize here.
+  -- unlink the old copy of the node at its original position
+  writeSRef rep $! esn
+
+moveBforward :: SRef s (RPList s a) -> RPW s ()
+moveBforward head = do
+  (Cons a ra)    <- readSRef head  -- [A,B,C,D,E]
+  bn@(Cons b rb) <- readSRef ra
+  (Cons c rc)    <- readSRef rb
+  (Cons d rd)    <- readSRef rc
+  -- duplicate the reference to E
+  rd'            <- copySRef rd
+  -- link in a new B after D
+  writeSRef rd $! Cons b rd'        -- [A,B,C,D,B,E]
+  --synchronizeRP -- interaction of write order and traversal order means you don't need this
+  -- unlink the old B
+  writeSRef ra bn                  -- [A,C,D,B,E]
+
+moveCback :: SRef s (RPList s a) -> RPW s ()
+moveCback head = do
+  (Cons a rb)    <- readSRef head
+  (Cons b rc)    <- readSRef rb
+  -- duplicate pointer to B
+  rb'            <- copySRef rb
+  (Cons c rd)    <- readSRef rc
+  de             <- readSRef rd
+  -- link in a new C after A
+  writeSRef rb $ Cons c rb'
+  -- any reader who starts during this grace period 
+  -- sees either "ABCD" or "ACBCD"
+  synchronizeRP
+  -- unlink the old C
+  writeSRef rc de
+  -- any reader who starts during this grace period 
+  -- sees either "ACBCD" or "ACBD" 
+
+moveCbackNoSync :: SRef s (RPList s a) -> RPW s ()
+moveCbackNoSync head = do
+  (Cons a rb)    <- readSRef head
+  (Cons b rc)    <- readSRef rb
+  -- duplicate reference to B
+  rb'            <- copySRef rb
+  (Cons c rd)    <- readSRef rc
+  de             <- readSRef rd
+  -- link in a new C after A
+  writeSRef rb $! Cons c rb'
+  -- any reader who starts after this write is issued
+  -- sees either "ABCD" or "ACBCD"
+  --synchronizeRP -- this operation is NOT safe to omit, 
+                  -- because write order and traversal order are the same
+  -- unlink the old C
+  writeSRef rc de
+  -- any reader who starts after this write is issued
+  -- sees "ABD", "ACBCD", or "ACBD" 
+
+main :: IO ()
+main = do 
+  let n = 100
+  rsts <- replicateM n $ do
+    runRP $ do
+      -- initialize list
+      head <- testList
+      -- spawn 8 readers, each records 10000 snapshots of the list
+      --rts  <- replicateM 8 $ forking $ replicateM 400000 $ reading $ readSection head
+      rts  <- replicateM 29 $ forking $ readThread "ABCDE" 400000 head
+      -- spawn a writer to delete the middle node
+      --wt   <- forking $ writing $ moveCback head
+      --wt   <- forking $ writing $ moveCbackNoSync head
+      wt   <- forking $ writing $ moveBforward head
+      --wt   <- forking $ writing $ moveNodeForward 1 1 head
+      --wt <- forking $ writing $ return ()
+      
+      -- wait for the readers to finish and print snapshots
+      rsts <- forM rts $ \rt -> do 
+        rst <- joining rt
+        rst `seq` return $ rst
+      -- wait for the writer to finish
+      joining wt
+      return $ foldl1' sappend' rsts
+  let rst = foldl1' sappend' rsts
+  putStrLn $ "n: " ++ show n ++ ", " ++ show rst
diff --git a/rcu.cabal b/rcu.cabal
new file mode 100644
--- /dev/null
+++ b/rcu.cabal
@@ -0,0 +1,82 @@
+name:          rcu
+category:      Data
+version:       0
+license:       BSD3
+cabal-version: >= 1.22
+license-file:  LICENSE
+author:        Edward A. Kmett
+maintainer:    Edward A. Kmett <ekmett@gmail.com>
+stability:     provisional
+homepage:      http://github.com/ekmett/rcu/
+bug-reports:   http://github.com/ekmett/rcu/issues
+copyright:     Copyright (C) 2015 Edward A. Kmett
+build-type:    Custom
+tested-with:   GHC == 7.10.1, GHC == 7.10.2
+synopsis:      STM-based Read-Copy-Update
+description:   STM-based Read-Copy-Update
+
+extra-source-files:
+  examples/*.hs
+  CHANGELOG.markdown
+  README.markdown
+
+source-repository head
+  type: git
+  location: git://github.com/ekmett/rcu.git
+
+-- You can disable the doctests test suite with -f-test-doctests
+flag test-doctests
+  default: True
+  manual: True
+
+-- You can disable the doctests test suite with -f-test-doctests
+flag test-hlint
+  default: True
+  manual: True
+
+library
+  build-depends:
+    base >= 4.8 && < 5,
+    stm >= 2.4.4 && < 2.5,
+    transformers >= 0.4 && < 0.5
+
+  exposed-modules:
+    Control.Concurrent.RCU
+    Control.Concurrent.RCU.Class
+    Control.Concurrent.RCU.Internal
+
+  ghc-options: -Wall -fwarn-tabs
+
+  hs-source-dirs: src
+  default-language: Haskell2010
+
+test-suite doctests
+  type:           exitcode-stdio-1.0
+  main-is:        doctests.hs
+  ghc-options:    -Wall -threaded
+  hs-source-dirs: tests
+  default-language: Haskell2010
+
+  if !flag(test-doctests)
+    buildable: False
+  else
+    build-depends:
+      base >= 4.8,
+      directory      >= 1.0,
+      doctest        >= 0.9.1,
+      filepath,
+      parallel
+
+test-suite hlint
+  type: exitcode-stdio-1.0
+  main-is: hlint.hs
+  ghc-options: -w -threaded -rtsopts -with-rtsopts=-N
+  hs-source-dirs: tests
+  default-language: Haskell2010
+
+  if !flag(test-hlint)
+    buildable: False
+  else
+    build-depends:
+      base,
+      hlint >= 1.7
diff --git a/src/Control/Concurrent/RCU.hs b/src/Control/Concurrent/RCU.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Concurrent/RCU.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE Trustworthy #-}
+-----------------------------------------------------------------------------
+-- |
+-- Copyright   :  (C) 2015 Edward Kmett
+-- License     :  BSD-style (see the file LICENSE)
+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-----------------------------------------------------------------------------
+module Control.Concurrent.RCU
+  ( SRef
+  , RCU, runRCU
+  , MonadNew(..)
+  , MonadReading(..)
+  , MonadWriting(..)
+  , MonadRCU(..)
+  -- * Implementation Details
+  , ReadingRCU
+  , WritingRCU
+  , RCUThread(rcuThreadId)
+  ) where
+
+import Control.Concurrent.RCU.Internal
diff --git a/src/Control/Concurrent/RCU/Class.hs b/src/Control/Concurrent/RCU/Class.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Concurrent/RCU/Class.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE Trustworthy #-}
+-----------------------------------------------------------------------------
+-- |
+-- Copyright   :  (C) 2015 Edward Kmett
+-- License     :  BSD-style (see the file LICENSE)
+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-----------------------------------------------------------------------------
+module Control.Concurrent.RCU.Class
+  ( SRef
+  , MonadNew(..)
+  , MonadReading(..)
+  , MonadWriting(..)
+  , MonadRCU(..)
+  ) where
+
+import Control.Concurrent.RCU.Internal
diff --git a/src/Control/Concurrent/RCU/Internal.hs b/src/Control/Concurrent/RCU/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Concurrent/RCU/Internal.hs
@@ -0,0 +1,374 @@
+{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DefaultSignatures #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# OPTIONS_HADDOCK not-home #-}
+-----------------------------------------------------------------------------
+-- |
+-- Copyright   :  (C) 2015 Edward Kmett
+-- License     :  BSD-style (see the file LICENSE)
+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- STM-based RCU with concurrent writers
+-----------------------------------------------------------------------------
+module Control.Concurrent.RCU.Internal
+  ( SRef(..)
+  , RCUThread(..)
+  , RCU(..)
+  , runRCU
+  , ReadingRCU(..)
+  , WritingRCU(..)
+  , MonadNew(..)
+  , MonadReading(..)
+  , copySRef
+  , MonadWriting(..)
+  , MonadRCU(..)
+  ) where
+
+import Control.Applicative
+import Control.Concurrent
+import Control.Concurrent.STM
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.Except
+import Control.Monad.Trans.Identity
+import Control.Monad.Trans.Maybe
+import Control.Monad.Trans.Reader
+import Data.Coerce
+import Data.Int
+import Prelude hiding (read, Read)
+import qualified Control.Monad.Trans.RWS.Lazy as Lazy
+import qualified Control.Monad.Trans.RWS.Strict as Strict
+import qualified Control.Monad.Trans.State.Lazy as Lazy
+import qualified Control.Monad.Trans.State.Strict as Strict
+import qualified Control.Monad.Trans.Writer.Lazy as Lazy
+import qualified Control.Monad.Trans.Writer.Strict as Strict
+
+--------------------------------------------------------------------------------
+-- * Shared References
+--------------------------------------------------------------------------------
+
+-- | Shared references
+newtype SRef s a = SRef { unSRef :: TVar a }
+  deriving Eq
+
+--------------------------------------------------------------------------------
+-- * MonadNew
+--------------------------------------------------------------------------------
+
+class Monad m => MonadNew s m | m -> s where
+  -- | Build a new shared reference
+  newSRef :: a -> m (SRef s a)
+  default newSRef :: (m ~ t n, MonadTrans t, MonadNew s n) => a -> m (SRef s a)
+  newSRef a = lift (newSRef a)
+
+instance MonadNew s m => MonadNew s (ReaderT e m)
+instance (MonadNew s m, Monoid w) => MonadNew s (Strict.WriterT w m)
+instance (MonadNew s m, Monoid w) => MonadNew s (Lazy.WriterT w m)
+instance MonadNew s' m => MonadNew s' (Strict.StateT s m)
+instance MonadNew s' m => MonadNew s' (Lazy.StateT s m)
+instance (MonadNew s' m, Monoid w) => MonadNew s' (Strict.RWST r w s m)
+instance (MonadNew s' m, Monoid w) => MonadNew s' (Lazy.RWST r w s m)
+instance MonadNew s m => MonadNew s (ExceptT e m)
+instance MonadNew s m => MonadNew s (MaybeT m)
+instance MonadNew s m => MonadNew s (IdentityT m)
+
+--------------------------------------------------------------------------------
+-- * MonadReading
+--------------------------------------------------------------------------------
+
+-- | This is a read-side critical section
+class MonadNew s m => MonadReading s m | m -> s where
+  -- | Read a shared reference.
+  readSRef :: SRef s a -> m a
+  default readSRef :: (m ~ t n, MonadTrans t, MonadReading s n) => SRef s a -> m a
+  readSRef r = lift (readSRef r)
+  {-# INLINE readSRef #-}
+
+-- | Copy a shared reference.
+copySRef :: MonadReading s m => SRef s a -> m (SRef s a)
+copySRef r = do
+  a <- readSRef r
+  newSRef a
+{-# INLINE copySRef #-}
+
+instance MonadReading s m => MonadReading s (ReaderT e m)
+instance (MonadReading s m, Monoid w) => MonadReading s (Strict.WriterT w m)
+instance (MonadReading s m, Monoid w) => MonadReading s (Lazy.WriterT w m)
+instance MonadReading s' m => MonadReading s' (Strict.StateT s m)
+instance MonadReading s' m => MonadReading s' (Lazy.StateT s m)
+instance (MonadReading s' m, Monoid w) => MonadReading s' (Strict.RWST r w s m)
+instance (MonadReading s' m, Monoid w) => MonadReading s' (Lazy.RWST r w s m)
+instance MonadReading s m => MonadReading s (ExceptT e m)
+instance MonadReading s m => MonadReading s (MaybeT m)
+instance MonadReading s m => MonadReading s (IdentityT m)
+
+--------------------------------------------------------------------------------
+-- * MonadWriting
+--------------------------------------------------------------------------------
+
+-- | This is a write-side critical section
+class MonadReading s m => MonadWriting s m | m -> s where
+  -- | Write to a shared reference.
+  writeSRef :: SRef s a -> a -> m ()
+  default writeSRef :: (m ~ t n, MonadTrans t, MonadWriting s n) => SRef s a -> a -> m ()
+  writeSRef r a = lift (writeSRef r a)
+
+  -- | Synchronize with other writers.
+  --
+  -- No other writer can straddle this time bound. It will either see writes from before, or writes after, but never 
+  -- some of both!
+  synchronize :: m ()
+  default synchronize :: (m ~ t n, MonadTrans t, MonadWriting s n) => m ()
+  synchronize = lift synchronize
+
+instance MonadWriting s m => MonadWriting s (ReaderT e m)
+instance (MonadWriting s m, Monoid w) => MonadWriting s (Strict.WriterT w m)
+instance (MonadWriting s m, Monoid w) => MonadWriting s (Lazy.WriterT w m)
+instance MonadWriting s m => MonadWriting s (Strict.StateT s m)
+instance MonadWriting s m => MonadWriting s (Lazy.StateT s m)
+instance (MonadWriting s m, Monoid w) => MonadWriting s (Strict.RWST r w s m)
+instance (MonadWriting s m, Monoid w) => MonadWriting s (Lazy.RWST r w s m)
+instance MonadWriting s m => MonadWriting s (IdentityT m)
+instance MonadWriting s m => MonadWriting s (ExceptT e m)
+instance MonadWriting s m => MonadWriting s (MaybeT m)
+
+--------------------------------------------------------------------------------
+-- * MonadRCU
+--------------------------------------------------------------------------------
+
+-- | This is the executor service that can fork, join and execute critical sections.
+class
+  ( MonadReading s (Reading m)
+  , MonadWriting s (Writing m)
+  , MonadNew s m
+  ) => MonadRCU s m | m -> s where
+
+  -- | A read-side critical section
+  type Reading m :: * -> *
+
+  -- | A write-side critical section
+  type Writing m :: * -> *
+
+  -- | Threads we can fork and join
+  type Thread m :: * -> *
+
+  -- | Fork a thread
+  forking  :: m a -> m (Thread m a)
+
+  -- | Join a thread
+  joining  :: Thread m a -> m a
+
+  -- | Run a read-side critical section
+  reading :: Reading m a -> m a
+
+  -- | Run a write-side critical section
+  writing :: Writing m a -> m a
+
+instance MonadRCU s m => MonadRCU s (ReaderT e m) where
+  type Reading (ReaderT e m) = ReaderT e (Reading m)
+  type Writing (ReaderT e m) = ReaderT e (Writing m)
+  type Thread (ReaderT e m) = Thread m
+  forking (ReaderT f)  = ReaderT $ \a -> forking (f a)
+  joining = lift . joining
+  reading (ReaderT f) = ReaderT $ \a -> reading (f a)
+  writing (ReaderT f) = ReaderT $ \a -> writing (f a)
+  {-# INLINE forking #-}
+  {-# INLINE joining #-}
+  {-# INLINE reading #-}
+  {-# INLINE writing #-}
+
+instance MonadRCU s m => MonadRCU s (IdentityT m) where
+  type Reading (IdentityT m) = Reading m
+  type Writing (IdentityT m) = Writing m
+  type Thread (IdentityT m) = Thread m
+  forking (IdentityT m) = IdentityT (forking m)
+  joining = lift . joining
+  reading m = IdentityT (reading m)
+  writing m = IdentityT (writing m)
+  {-# INLINE forking #-}
+  {-# INLINE joining #-}
+  {-# INLINE reading #-}
+  {-# INLINE writing #-}
+
+instance MonadRCU s m => MonadRCU s (ExceptT e m) where
+  type Reading (ExceptT e m) = ExceptT e (Reading m)
+  type Writing (ExceptT e m) = ExceptT e (Writing m)
+  type Thread (ExceptT e m) = ExceptT e (Thread m)
+  forking (ExceptT m) = lift $ ExceptT <$> forking m
+  joining (ExceptT m) = ExceptT $ joining m
+  reading (ExceptT m) = ExceptT $ reading m
+  writing (ExceptT m) = ExceptT $ writing m
+  {-# INLINE forking #-}
+  {-# INLINE joining #-}
+  {-# INLINE reading #-}
+  {-# INLINE writing #-}
+
+instance MonadRCU s m => MonadRCU s (MaybeT m) where
+  type Reading (MaybeT m) = MaybeT (Reading m)
+  type Writing (MaybeT m) = MaybeT (Writing m)
+  type Thread (MaybeT m) = MaybeT (Thread m)
+  forking (MaybeT m) = lift $ MaybeT <$> forking m
+  joining (MaybeT m) = MaybeT $ joining m
+  reading (MaybeT m) = MaybeT $ reading m
+  writing (MaybeT m) = MaybeT $ writing m
+  {-# INLINE forking #-}
+  {-# INLINE joining #-}
+  {-# INLINE reading #-}
+  {-# INLINE writing #-}
+
+instance (MonadRCU s m, Monoid e) => MonadRCU s (Strict.WriterT e m) where
+  type Reading (Strict.WriterT e m) = Strict.WriterT e (Reading m)
+  type Writing (Strict.WriterT e m) = Strict.WriterT e (Writing m)
+  type Thread (Strict.WriterT e m) = Strict.WriterT e (Thread m)
+  forking (Strict.WriterT m) = lift $ Strict.WriterT <$> forking m
+  joining (Strict.WriterT m) = Strict.WriterT $ joining m
+  reading (Strict.WriterT m) = Strict.WriterT $ reading m
+  writing (Strict.WriterT m) = Strict.WriterT $ writing m
+  {-# INLINE forking #-}
+  {-# INLINE joining #-}
+  {-# INLINE reading #-}
+  {-# INLINE writing #-}
+
+instance (MonadRCU s m, Monoid e) => MonadRCU s (Lazy.WriterT e m) where
+  type Reading (Lazy.WriterT e m) = Lazy.WriterT e (Reading m)
+  type Writing (Lazy.WriterT e m) = Lazy.WriterT e (Writing m)
+  type Thread (Lazy.WriterT e m) = Lazy.WriterT e (Thread m)
+  forking (Lazy.WriterT m) = lift $ Lazy.WriterT <$> forking m
+  joining (Lazy.WriterT m) = Lazy.WriterT $ joining m
+  reading (Lazy.WriterT m) = Lazy.WriterT $ reading m
+  writing (Lazy.WriterT m) = Lazy.WriterT $ writing m
+  {-# INLINE forking #-}
+  {-# INLINE joining #-}
+  {-# INLINE reading #-}
+  {-# INLINE writing #-}
+
+--------------------------------------------------------------------------------
+-- * Read-Side Critical Sections
+--------------------------------------------------------------------------------
+
+-- | This is the basic read-side critical section for an RCU computation
+newtype ReadingRCU s a = ReadingRCU { runReadingRCU :: IO a } deriving (Functor, Applicative, Monad)
+
+instance MonadNew s (ReadingRCU s) where
+  newSRef = r where
+    r :: forall a. a -> ReadingRCU s (SRef s a)
+    r = coerce (newTVarIO :: a -> IO (TVar a))
+
+instance MonadReading s (ReadingRCU s) where
+  readSRef = r where
+    r :: forall a. SRef s a -> ReadingRCU s a
+    r = coerce (readTVarIO :: TVar a -> IO a)
+  {-# INLINE readSRef #-}
+
+--------------------------------------------------------------------------------
+-- * Write-Side Critical Sections
+--------------------------------------------------------------------------------
+
+-- | This is the basic write-side critical section for an RCU computation
+newtype WritingRCU s a = WritingRCU { runWritingRCU :: TVar Int64 -> STM a }
+  deriving Functor
+
+instance Applicative (WritingRCU s) where
+  pure a = WritingRCU $ \ _ -> pure a
+  WritingRCU mf <*> WritingRCU ma = WritingRCU $ \c -> mf c <*> ma c
+
+instance Monad (WritingRCU s) where
+  return a = WritingRCU $ \ _ -> pure a
+  WritingRCU m >>= f = WritingRCU $ \ c -> do
+    a <- m c
+    runWritingRCU (f a) c
+  fail s = WritingRCU $ \ _ -> fail s
+
+instance Alternative (WritingRCU s) where
+  empty = WritingRCU $ \ _ -> empty
+  WritingRCU ma <|> WritingRCU mb = WritingRCU $ \c -> ma c <|> mb c
+
+instance MonadPlus (WritingRCU s) where
+  mzero = WritingRCU $ \ _ -> mzero
+  WritingRCU ma `mplus` WritingRCU mb = WritingRCU $ \c -> ma c `mplus` mb c
+
+instance MonadNew s (WritingRCU s) where
+  newSRef a = WritingRCU $ \_ -> SRef <$> newTVar a
+
+instance MonadReading s (WritingRCU s) where
+  readSRef (SRef r) = WritingRCU $ \ _ -> readTVar r
+  {-# INLINE readSRef #-}
+
+instance MonadWriting s (WritingRCU s) where
+  writeSRef (SRef r) a = WritingRCU $ \ _ -> writeTVar r a
+  synchronize = WritingRCU $ \ c -> modifyTVar' c (+1)
+
+--------------------------------------------------------------------------------
+-- * RCU Context
+--------------------------------------------------------------------------------
+
+-- | This is an RCU computation. It can use 'forking' and 'joining' to form
+-- new threads, and then you can use 'reading' and 'writing' to run classic
+-- read-side and write-side RCU computations. Contention between multiple
+-- write-side computations is managed by STM.
+newtype RCU s a = RCU { unRCU :: TVar Int64 -> IO a }
+  deriving Functor
+
+instance Applicative (RCU s) where
+  pure = return
+  (<*>) = ap
+
+instance Monad (RCU s) where
+  return a = RCU $ \ _ -> return a
+  RCU m >>= f = RCU $ \s -> do
+    a <- m s
+    unRCU (f a) s
+
+instance MonadNew s (RCU s) where
+  newSRef a = RCU $ \_ -> SRef <$> newTVarIO a
+
+-- | This is a basic 'RCU' thread. It may be embellished when running in a more
+-- exotic context.
+data RCUThread s a = RCUThread
+  { rcuThreadId :: {-# UNPACK #-} !ThreadId
+  , rcuThreadVar :: {-# UNPACK #-} !(MVar a)
+  }
+
+instance MonadRCU s (RCU s) where
+  type Reading (RCU s) = ReadingRCU s
+  type Writing (RCU s) = WritingRCU s
+  type Thread (RCU s) = RCUThread s
+  forking (RCU m) = RCU $ \ c -> do
+    result <- newEmptyMVar
+    tid <- forkIO $ do
+      x <- m c
+      putMVar result x
+    return (RCUThread tid result)
+  joining (RCUThread _ m) = RCU $ \ _ -> readMVar m
+  reading (ReadingRCU m) = RCU $ \ _ -> m
+  writing (WritingRCU m) = RCU $ \ c -> atomically $ do
+    _ <- readTVar c -- deliberately incur a data dependency!
+    m c
+  {-# INLINE forking #-}
+  {-# INLINE joining #-}
+  {-# INLINE reading #-}
+  {-# INLINE writing #-}
+
+instance MonadIO (RCU s) where
+  liftIO m = RCU $ \ _ -> m
+  {-# INLINE liftIO #-}
+
+-- | Run an RCU computation.
+runRCU :: (forall s. RCU s a) -> IO a
+runRCU m = do
+  c <- newTVarIO 0
+  unRCU m c
+{-# INLINE runRCU #-}
+
diff --git a/tests/doctests.hsc b/tests/doctests.hsc
new file mode 100644
--- /dev/null
+++ b/tests/doctests.hsc
@@ -0,0 +1,78 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Main (doctests)
+-- Copyright   :  (C) 2012-14 Edward Kmett
+-- License     :  BSD-style (see the file LICENSE)
+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- This module provides doctests for a project based on the actual versions
+-- of the packages it was built with. It requires a corresponding Setup.lhs
+-- to be added to the project
+-----------------------------------------------------------------------------
+module Main where
+
+import Build_doctests (deps)
+#if __GLASGOW_HASKELL__ < 710
+import Control.Applicative
+#endif
+import Control.Monad
+import Data.List
+import System.Directory
+import System.FilePath
+import Test.DocTest
+
+##if defined(mingw32_HOST_OS)
+##if defined(i386_HOST_ARCH)
+##define USE_CP
+import Control.Applicative
+import Control.Exception
+import Foreign.C.Types
+foreign import stdcall "windows.h SetConsoleCP" c_SetConsoleCP :: CUInt -> IO Bool
+foreign import stdcall "windows.h GetConsoleCP" c_GetConsoleCP :: IO CUInt
+##elif defined(x86_64_HOST_ARCH)
+##define USE_CP
+import Control.Applicative
+import Control.Exception
+import Foreign.C.Types
+foreign import ccall "windows.h SetConsoleCP" c_SetConsoleCP :: CUInt -> IO Bool
+foreign import ccall "windows.h GetConsoleCP" c_GetConsoleCP :: IO CUInt
+##endif
+##endif
+
+-- | Run in a modified codepage where we can print UTF-8 values on Windows.
+withUnicode :: IO a -> IO a
+##ifdef USE_CP
+withUnicode m = do
+  cp <- c_GetConsoleCP
+  (c_SetConsoleCP 65001 >> m) `finally` c_SetConsoleCP cp
+##else
+withUnicode m = m
+##endif
+
+main :: IO ()
+main = withUnicode $ getSources >>= \sources -> doctest $
+    "-isrc"
+  : "-idist/build/autogen"
+  : "-optP-include"
+  : "-optPdist/build/autogen/cabal_macros.h"
+  : "-hide-all-packages"
+#ifdef TRUSTWORTHY
+  : "-DTRUSTWORTHY=1"
+#endif
+  : map ("-package="++) deps ++ sources
+
+getSources :: IO [FilePath]
+getSources = filter (isSuffixOf ".hs") <$> go "src"
+  where
+    go dir = do
+      (dirs, files) <- getFilesAndDirectories dir
+      (files ++) . concat <$> mapM go dirs
+
+getFilesAndDirectories :: FilePath -> IO ([FilePath], [FilePath])
+getFilesAndDirectories dir = do
+  c <- map (dir </>) . filter (`notElem` ["..", "."]) <$> getDirectoryContents dir
+  (,) <$> filterM doesDirectoryExist c <*> filterM doesFileExist c
diff --git a/tests/hlint.hs b/tests/hlint.hs
new file mode 100644
--- /dev/null
+++ b/tests/hlint.hs
@@ -0,0 +1,23 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Main (hlint)
+-- Copyright   :  (C) 2013-2015 Edward Kmett
+-- License     :  BSD-style (see the file LICENSE)
+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- This module runs HLint on the lens source tree.
+-----------------------------------------------------------------------------
+module Main where
+
+import Control.Monad
+import Language.Haskell.HLint
+import System.Environment
+import System.Exit
+
+main :: IO ()
+main = do
+    args <- getArgs
+    hints <- hlint $ ["src", "--cpp-define=HLINT", "--cpp-ansi"] ++ args
+    unless (null hints) exitFailure
