diff --git a/tests/Atomics.hs b/tests/Atomics.hs
new file mode 100644
--- /dev/null
+++ b/tests/Atomics.hs
@@ -0,0 +1,160 @@
+module Atomics (atomicsMain) where
+
+import Control.Concurrent
+import Data.Atomics.Counter.Fat
+import Data.Atomics
+import Data.IORef
+import Control.Monad
+import qualified Data.Set as Set
+import Data.List
+import Data.Bits
+
+atomicsMain :: IO ()
+atomicsMain = do
+    putStrLn "Testing atomic-primops:"
+    -- ------
+    putStr "    counter smoke test... "
+    counterSane
+    putStrLn "OK"
+    -- ------
+    putStr "    counter overflow... "
+    testCounterOverflow
+    putStrLn "OK"
+    -- ------
+    putStr "    counter is atomic... "
+    counterTest
+    putStrLn "OK"
+    -- ------
+    putStr "    CAS... "
+    testConsistentSuccessFailure
+    putStrLn "OK"
+
+-- catch real stupid bugs before machine gets hot:
+counterSane :: IO ()
+counterSane = do
+    cntr <- newCounter 1337
+    n <- readCounter cntr
+    unless (n == 1337) $ error "newCounter magnificently broken"
+    n2 <- incrCounter 1 cntr
+    n2' <- readCounter cntr
+    unless (n2 == 1338 && n2' == 1338) $ error "incrCounter magnificently broken"
+
+cHUNK_SIZE, maxInt, minInt :: Int
+cHUNK_SIZE = 32 -- MUST REMAIN POWER OF TWO for now
+-- make sure incrCounter doesn't silently change to Integer or something
+maxInt = maxBound
+minInt = minBound
+
+-- Test some properties of our counter we'd like to assume:
+testCounterOverflow :: IO ()
+testCounterOverflow = do
+    let x `ourMod` y = 
+           -- also do a little sanity checking for the bitwise mod over Int
+           -- that we use heavily, and expect in some cases to roll over w/out
+           -- breaks:
+           let xmy = x .&. (y-1)
+            in if xmy == x `mod` y
+                    then xmy
+                    else error "Our bitwise mod isn't working the way we expect in testCounterOverflow"
+    cntr <- newCounter (maxInt - (cHUNK_SIZE `div` 2)) 
+    spanningCntr <- replicateM cHUNK_SIZE (incrCounter 1 cntr)
+    -- make sure our test is working
+    if all (>0) spanningCntr || all (<0) spanningCntr
+        then error "Sequence meant to span maxBound of counter not actually spanning"
+        else return ()
+
+    let l = map (`ourMod` cHUNK_SIZE) spanningCntr
+        l' = (dropWhile (/= 0) l) ++ (takeWhile (/= 0) l)
+
+    -- (1) test that we overflow the counter without any breaks and our mod function is working properly:
+    unless (l' == [0..(cHUNK_SIZE - 1)]) $ 
+        error $ "Uh Oh: "++(show l')
+
+    -- (2) test that Ints and counter overflow in exactly the same way
+    let spanningInts = take cHUNK_SIZE $ iterate (+1) (maxInt - (cHUNK_SIZE `div` 2) + 1) 
+    unless (spanningInts == spanningCntr) $ do
+        error $ "Ints overflow differently than counter: "++
+                "\nInt: "++(show spanningInts)++
+                "\nCounter: "++(show spanningCntr)
+
+    -- (We don't use this property)
+    cntr2 <- newCounter maxInt
+    mbnd <- incrCounter 1 cntr2
+    unless (mbnd == minInt) $ 
+        error $ "Incrementing counter at maxbound didn't yield minBound"
+
+    -- (3) test subtraction across boundary: count - newFirstIndex, for window spanning boundary.
+    cntr3 <- newCounter (maxInt - 1)
+    let ls = take 30 $ iterate (+1) $ maxInt - 10
+    cs <- mapM (\x-> fmap (subtract x) $ incrCounter 1 cntr3) ls
+    unless (cs == replicate 30 10) $ 
+        error $ "Derp. We don't know how subtraction works: "++(show cs)
+
+-- Test these assumptions:
+--   1) If a CAS fails in thread 1 then another CAS (in thread 2, say) succeeded; i.e. no false negatives
+--   2) In the case that thread 1's CAS failed, the ticket returned with (False,tk) will contain that newly-written value from thread 2
+testConsistentSuccessFailure :: IO ()
+testConsistentSuccessFailure = do
+    var <- newIORef "0"
+
+    sem <- newIORef (0::Int)
+    outs <- replicateM 2 newEmptyMVar 
+
+    _ <- forkSync sem 2 $ test "a" var (outs!!0)
+    _ <- forkSync sem 2 $ test "b" var (outs!!1)
+
+    mapM takeMVar outs >>= examine
+       -- w/r/t (2) above: we only try to find an element read along with False
+       -- which wasn't sent by another thread, which isn't ideal
+ where attempts = 100000
+       test tag var out = do
+         
+         res <- forM [(1::Int)..attempts] $ \x-> do
+                    let str = (tag++(show x))
+                    tk <- readForCAS var
+                    (b,tk') <- casIORef var tk str
+                    return (if b then str else peekTicket tk' , b)
+         putMVar out res
+
+       examine [res1, res2] = do
+         -- any failures in either should be marked as successes in the other
+         let (successes1,failures1) = (\(x,y)-> (Set.fromList $ map fst x, map fst y)) $ partition snd res1
+             (successes2,failures2) = (\(x,y)-> (Set.fromList $ map fst x, map fst y)) $ partition snd res2
+             ok1 = all (flip Set.member successes2) failures1
+             ok2 = all (flip Set.member successes1) failures2
+         if ok1 && ok2
+             then when (length failures1 < (attempts `div` 6) || length failures2 < (attempts `div` 6)) $
+                    error "There was not enough contention to trust test. Please retry."
+             else do print res1
+                     print res2
+                     error "FAILURE!"
+       examine _ = error "Fix testConsistentSuccessFailure"
+
+                   
+forkSync :: IORef Int -> Int -> IO () -> IO ThreadId
+forkSync sem target io = 
+    forkIO $ (busyWait >> io)
+  where busyWait =
+           atomicModifyIORef' sem (\n-> (n+1,())) >> wait
+        wait = do
+            n <- readIORef sem
+            unless (n == target) wait
+    
+
+counterTest :: IO ()
+counterTest = do
+    let n = 10000000
+    nOut <- testAtomicCounter n
+    when (nOut /= n) $
+        error $ "Counter broken: expecting "++(show n)++" got "++(show nOut)
+
+testAtomicCounter :: Int -> IO Int
+testAtomicCounter n = do
+  procs <- getNumCapabilities
+
+  counter <- newCounter (0::Int)
+  dones <- replicateM procs newEmptyMVar ; starts <- replicateM procs newEmptyMVar
+  mapM_ (\(start1,done1)-> forkIO $ takeMVar start1 >> replicateM_ (n `div` procs) (incrCounter 1 counter) >> putMVar done1 ()) $ zip starts dones
+  mapM_ (\v-> putMVar v ()) starts ; mapM_ (\v-> takeMVar v) dones
+  
+  readCounter counter
diff --git a/tests/Chan003.hs b/tests/Chan003.hs
new file mode 100644
--- /dev/null
+++ b/tests/Chan003.hs
@@ -0,0 +1,25 @@
+module Chan003 (checkDeadlocksWriter) where
+
+import Control.Concurrent
+import qualified Control.Concurrent.Chan.Unagi as U
+import Control.Exception
+import Control.Monad
+
+-- OBSOLETE FOR NOW; we have more clever and careful deadlock tests in
+-- Deadlocks
+
+-- test for deadlocks from async exceptions raised in writer
+checkDeadlocksWriter :: Int -> IO ()
+checkDeadlocksWriter n = void $
+  replicateM_ n $ do
+         (i,o) <- U.newChan
+         wStart <- newEmptyMVar
+         wid <- forkIO (putMVar wStart () >> ( forever $ U.writeChan i (0::Int)) )
+         -- wait for writer to start
+         takeMVar wStart >> threadDelay 1
+         throwTo wid ThreadKilled
+         -- did killing the writer damage queue for writes or reads?
+         U.writeChan i (1::Int)
+         z <- U.readChan o
+         unless (z == 0) $
+            error "Writer never got a chance to write!"
diff --git a/tests/Deadlocks.hs b/tests/Deadlocks.hs
new file mode 100644
--- /dev/null
+++ b/tests/Deadlocks.hs
@@ -0,0 +1,103 @@
+module Deadlocks (deadlocksMain) where
+
+import Control.Concurrent.MVar
+import Control.Concurrent(getNumCapabilities,threadDelay,forkIO)
+import Control.Exception
+import Control.Monad
+
+import Implementations
+
+
+deadlocksMain :: IO ()
+deadlocksMain = do
+    let tries = 50000
+    
+    putStrLn "==================="
+    putStrLn "Testing Unagi:"
+    -- ------
+    putStr $ "    Checking for deadlocks from killed reader, x"++show tries++"... "
+    checkDeadlocksReader unagiImpl tries
+    putStrLn "OK"
+    -- ------
+    putStr $ "    Checking for deadlocks from killed writer, x"++show tries++"... "
+    checkDeadlocksWriter unagiImpl tries
+    putStrLn "OK"
+    
+    putStrLn "==================="
+    putStrLn "Testing Unagi.Unboxed:"
+    -- ------
+    putStr $ "    Checking for deadlocks from killed reader, x"++show tries++"... "
+    checkDeadlocksReader unboxedUnagiImpl tries
+    putStrLn "OK"
+    -- ------
+    putStr $ "    Checking for deadlocks from killed writer, x"++show tries++"... "
+    checkDeadlocksWriter unboxedUnagiImpl tries
+    putStrLn "OK"
+
+
+
+-- -- Chan002.hs -- --
+
+
+
+-- test for deadlocks caused by async exceptions in reader.
+checkDeadlocksReader :: Implementation inc outc Int -> Int -> IO ()
+checkDeadlocksReader (newChan,writeChan,readChan,_) times = do
+  -- this might become an argument, indicating whether a killed reader might
+  -- result in one missing element (currently all do)
+  let mightDropOne = True
+  procs <- getNumCapabilities
+  let run _       0 = putStrLn ""
+      run retries n = do
+         when (retries > (times `div` 5)) $
+            error "This test is taking too long. Please retry, and if still failing send the log to me"
+         (i,o) <- newChan
+         -- if we don't have at least three cores, then we need to write enough messages in first, before killing reader.
+         maybeWid <- if procs > 4 -- NOTE 4 might mean only two real cores, so be conservative here.
+                        then do wStart <- newEmptyMVar
+                                wid <- forkIO $ (putMVar wStart () >> (forever $ writeChan i (0::Int)))
+                                takeMVar wStart >> threadDelay 1 -- wait until we're writing
+                                return $ Just wid
+                             
+                        else do replicateM_ 10000 $ writeChan i (0::Int)
+                                return Nothing
+         rStart <- newEmptyMVar
+         rid <- forkIO $ (putMVar rStart () >> (forever $ void $ readChan o))
+         takeMVar rStart >> threadDelay 1
+         throwTo rid ThreadKilled
+         -- did killing reader damage queue for reads or writes?
+         writeChan i 1 `onException` ( putStrLn "Exception from writeChan 1")
+         when mightDropOne $
+            writeChan i 2 `onException` ( putStrLn "Exception from last writeChan 2")
+         z <- readChan o `onException` ( putStrLn "Exception from last readChan")
+         -- clean up:
+         case maybeWid of 
+              Just wid -> throwTo wid ThreadKilled ; _ -> return ()
+         case z of
+              0 -> putStr "." >> run retries (n-1)
+              -- reader probably killed while blocked or before writer even wrote anything
+              1 -> putStr "+" >> run (retries+1) n 
+              2 | not mightDropOne -> error "Fix tests; 2 when not mightDropOne"
+                | otherwise -> putStr "*" >> run (retries+1) n
+              _ -> error "Fix checkDeadlocksReader test; not 0, 1, or 2"
+  run 0 times
+
+
+-- -- Chan003.hs -- --
+
+
+-- test for deadlocks from async exceptions raised in writer
+checkDeadlocksWriter :: Implementation inc outc Int -> Int -> IO ()
+checkDeadlocksWriter (newChan,writeChan,readChan,_) n = void $
+  replicateM_ n $ do
+         (i,o) <- newChan
+         wStart <- newEmptyMVar
+         wid <- forkIO (putMVar wStart () >> ( forever $ writeChan i (0::Int)) )
+         -- wait for writer to start
+         takeMVar wStart >> threadDelay 1
+         throwTo wid ThreadKilled
+         -- did killing the writer damage queue for writes or reads?
+         writeChan i (1::Int)
+         z <- readChan o
+         unless (z == 0) $
+            error "Writer never got a chance to write!"
diff --git a/tests/DupChan.hs b/tests/DupChan.hs
new file mode 100644
--- /dev/null
+++ b/tests/DupChan.hs
@@ -0,0 +1,81 @@
+module DupChan (dupChanMain) where
+
+-- implementation-agnostic tests of `dupChan`
+
+import Control.Concurrent.MVar
+import Control.Concurrent(forkIO,throwTo)
+import Control.Exception(AsyncException(ThreadKilled))
+import Control.Monad
+
+import Implementations
+
+
+dupChanMain :: IO ()
+dupChanMain = do
+    putStrLn "==================="
+    putStrLn "Test dupChan Unagi:"
+    -- ------
+    putStr "    Reader/Reader... "
+    replicateM_ 1000 $ dupChanTest1 unagiImpl 50000
+    putStrLn "OK"
+    -- ------
+    putStr "    Writer/dupChan+Reader... "
+    replicateM_ 1000 $ dupChanTest2 unagiImpl 10000
+    putStrLn "OK"
+    putStrLn "==================="
+    putStrLn "Test dupChan Unagi.Unboxed:"
+    -- ------
+    putStr "    Reader/Reader... "
+    replicateM_ 1000 $ dupChanTest1 unboxedUnagiImpl 50000
+    putStrLn "OK"
+    -- ------
+    putStr "    Writer/dupChan+Reader... "
+    replicateM_ 1000 $ dupChanTest2 unboxedUnagiImpl 10000
+    putStrLn "OK"
+
+-- Check output where dupChan at known point in input stream, with two
+-- concurrent readers.
+dupChanTest1 :: Implementation inc outc Int -> Int -> IO ()
+dupChanTest1 (newChan,writeChan,readChan,dupChan) n = do
+    let s1 = [1.. ndiv2]
+        s2 = [(ndiv2+1)..n]
+        ndiv2 = n `div` 2
+    (i,o) <- newChan
+    mapM_ (writeChan i) s1
+    oDup <- dupChan i
+    mapM_ (writeChan i) s2
+
+    out <- newEmptyMVar
+    outDup <- newEmptyMVar
+
+    _ <- forkIO $ (replicateM n (readChan o) >>= putMVar out)
+    _ <- forkIO $ (replicateM ndiv2 (readChan oDup) >>= putMVar outDup)
+
+    x <- takeMVar out
+    y <- takeMVar outDup
+
+    unless (x == s1++s2) $
+        error ""
+    unless (y == s2) $ 
+        error $ "dupChan returned unexpected results: "++(show y)
+
+-- Check concurrent writes with dupChan + reads, check all reads are some
+-- contiguous part of the input stream.
+dupChanTest2 :: Implementation inc outc Int -> Int -> IO ()
+dupChanTest2 (newChan,writeChan,readChan,dupChan) n = do
+    (i,o) <- newChan
+    out <- newEmptyMVar
+    writer <- forkIO $ mapM_ (writeChan i) [(0::Int)..]
+    
+    s1 <- replicateM n (readChan o)
+    _ <- forkIO (dupChan i >>= replicateM n . readChan >>= putMVar out)
+    s2 <- replicateM n (readChan o)
+    s3 <- takeMVar out
+
+    throwTo writer ThreadKilled 
+
+    unless (all increm [s1,s2,s3]) $ do
+        print [s1,s2,s3]
+        error "All read streams should be incrementing by one, without breaks"
+  where increm [] = error "Fix dupChanTest2"
+        increm xss@(x:_) = xss == take n [x..]
diff --git a/tests/Implementations.hs b/tests/Implementations.hs
new file mode 100644
--- /dev/null
+++ b/tests/Implementations.hs
@@ -0,0 +1,14 @@
+module Implementations where
+
+import qualified Control.Concurrent.Chan.Unagi as U
+import qualified Control.Concurrent.Chan.Unagi.Unboxed as UU
+import qualified Data.Primitive as P
+
+type Implementation inc outc a = (IO (inc a, outc a), inc a -> a -> IO (), outc a -> IO a, inc a -> IO (outc a))
+
+unagiImpl :: Implementation U.InChan U.OutChan a
+unagiImpl =  (U.newChan, U.writeChan, U.readChan, U.dupChan)
+
+unboxedUnagiImpl :: (P.Prim a)=> Implementation UU.InChan UU.OutChan a
+unboxedUnagiImpl = (UU.newChan, UU.writeChan, UU.readChan, UU.dupChan)
+
diff --git a/tests/IndexedMVar.hs b/tests/IndexedMVar.hs
new file mode 100644
--- /dev/null
+++ b/tests/IndexedMVar.hs
@@ -0,0 +1,35 @@
+module IndexedMVar (indexedMVarMain) where
+
+import Utilities
+import Control.Monad
+import Control.Concurrent
+import Control.Exception(onException)
+import Data.IORef
+
+indexedMVarMain :: IO ()
+indexedMVarMain = do
+    putStr "Test indexedMVar... "
+    replicateM_ 1000 $ 
+        indexedMVarTest 128
+    putStrLn "OK"
+
+indexedMVarTest :: Int -> IO ()
+indexedMVarTest n = do
+    let xs = [0..n]
+    mvIx <- newIndexedMVar
+    start <- newIORef (0::Int)
+    _ <- forkIO $ do
+           writeIORef start 1
+           mapM_ (\x-> putMVarIx mvIx x x `onException` (putStr "In writer: ")) xs
+    
+    busyWait start
+    mapM_ (\x-> (readMVarIx mvIx x `onException` (putStr "In reader: ")) >>= assertEq x) xs
+        where assertEq x x' = when (x /= x') $ error $ (show x)++"/="++(show x')
+
+-- we should really abstract this out and use elsewhere
+busyWait :: IORef Int -> IO ()
+busyWait ref = busy where
+    busy = do
+         -- N.B. w/ readIORef we loop forever:
+        n <- atomicModifyIORef ref (\x-> (x,x)) 
+        unless (n == 1) busy
diff --git a/tests/Smoke.hs b/tests/Smoke.hs
new file mode 100644
--- /dev/null
+++ b/tests/Smoke.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE BangPatterns #-}
+module Smoke (smokeMain) where
+
+import Control.Monad
+import Control.Concurrent(forkIO)
+import qualified Control.Concurrent.Chan as C
+import Data.List
+
+import Implementations
+
+smokeMain :: IO ()
+smokeMain = do
+    putStrLn "==================="
+    putStrLn "Testing Unagi:"
+    -- ------
+    putStr "    FIFO smoke test... "
+    fifoSmoke unagiImpl 100000
+    putStrLn "OK"
+    -- ------
+    testContention unagiImpl 2 2 1000000
+
+    putStrLn "==================="
+    putStrLn "Testing Unagi.Unboxed:"
+    -- ------
+    putStr "    FIFO smoke test... "
+    fifoSmoke unboxedUnagiImpl 100000
+    putStrLn "OK"
+    -- ------
+    testContention unboxedUnagiImpl 2 2 1000000
+
+
+fifoSmoke :: Implementation inc outc Int -> Int -> IO ()
+fifoSmoke (newChan,writeChan,readChan,_) n = do
+    (i,o) <- newChan
+    mapM_ (writeChan i) [1..n]
+    nsOut <- replicateM n $ readChan o
+    unless (nsOut == [1..n]) $
+        error "Cough!"
+
+testContention :: Implementation inc outc Int -> Int -> Int -> Int -> IO ()
+testContention (newChan,writeChan,readChan,_) writers readers n = do
+  let nNice = n - rem n (lcm writers readers)
+             -- e.g. [[1,2,3,4,5],[6,7,8,9,10]] for 2 2 10
+      groups = map (\i-> [i.. i - 1 + nNice `quot` writers]) $ [1, (nNice `quot` writers + 1).. nNice]
+                     -- force list; don't change --
+  out <- C.newChan
+
+  (i,o) <- newChan
+  -- some will get blocked indefinitely:
+  void $ replicateM readers $ forkIO $ forever $
+      readChan o >>= C.writeChan out
+  
+  putStrLn $ "Sending "++(show $ length $ concat groups)++" messages, with "++(show readers)++" readers and "++(show writers)++" writers."
+  mapM_ (forkIO . mapM_ (writeChan i)) groups
+
+  ns <- replicateM nNice (C.readChan out)
+  isEmpty <- C.isEmptyChan out
+  if sort ns == [1..nNice] && isEmpty
+      then let d = interleaving ns
+            in if d < 0.75
+                 then putStrLn $ "Not enough interleaving of threads: "++(show $ d)++". Please try again or report a bug"
+                 else putStrLn $ "Success, with interleaving pct of "++(show $ d)++" (closer to 1 means we have higher confidence in the test)."
+      else error "What we put in isn't what we got out :("
+
+-- --------- Helpers:
+
+-- approx measure of interleaving (and hence contention) in test
+interleaving :: (Num a, Eq a) => [a] -> Float
+interleaving [] = 0
+interleaving (x:xs) =  (snd $ foldl' countNonIncr (x,0) xs) / l
+  where l = fromIntegral $ length xs
+        countNonIncr (x0,!cnt) x1 = (x1, if x1 == x0+1 then cnt else cnt+1)
diff --git a/tests/Unagi.hs b/tests/Unagi.hs
new file mode 100644
--- /dev/null
+++ b/tests/Unagi.hs
@@ -0,0 +1,195 @@
+module Unagi (unagiMain) where
+
+-- Unagi-chan-specific tests
+
+import Control.Concurrent.Chan.Unagi
+import qualified Control.Concurrent.Chan.Unagi.Internal as UI
+import Control.Monad
+import qualified Data.Primitive as P
+import Data.IORef
+
+import Control.Concurrent(forkIO,threadDelay)
+import Control.Concurrent.MVar
+import Control.Exception
+import Data.Atomics.Counter.Fat
+
+unagiMain :: IO ()
+unagiMain = do
+    putStrLn "==================="
+    putStrLn "Testing Unagi details:"
+    -- ------
+    putStr "Smoke test at different starting offsets, spanning overflow... "
+    mapM_ smoke $ [ (maxBound - UI.sEGMENT_LENGTH - 1) .. maxBound] 
+                  ++ [minBound .. (minBound + UI.sEGMENT_LENGTH + 1)]
+    putStrLn "OK"
+    -- ------
+    putStr "Correct first write... "
+    mapM_ correctFirstWrite [ (maxBound - 7), maxBound, minBound, 0]
+    putStrLn "OK"
+    -- ------
+    putStr "Correct initial writes... "
+    mapM_ correctInitialWrites [ (maxBound - UI.sEGMENT_LENGTH), (maxBound - UI.sEGMENT_LENGTH) - 1, maxBound, minBound, 0]
+    putStrLn "OK"
+    -- ------
+    let tries = 50000
+    putStrLn $ "Checking for deadlocks from killed Unagi reader in a fancy way, x"++show tries
+    checkDeadlocksReaderUnagi tries
+
+
+smoke :: Int -> IO ()
+smoke n = smoke1 n >> smoke2 n
+
+-- www.../rrr... spanning overflow
+smoke1 :: Int -> IO ()
+smoke1 n = do
+    (i,o) <- UI.newChanStarting n
+    let inp = [0 .. (UI.sEGMENT_LENGTH * 3)]
+    mapM_ (writeChan i) inp
+    outp <- getChanContents o
+    if and (zipWith (==) inp outp)
+        then return ()
+        else error $ "Smoke test failed with starting offset of: "++(show n)
+
+-- w/r/w/r... spanning overflow
+smoke2 :: Int -> IO ()
+smoke2 n = do
+    (i,o) <- UI.newChanStarting n
+    let inp = [0 .. (UI.sEGMENT_LENGTH * 3)]
+    mapM_ (check i o) inp
+ where check i o x = do
+         writeChan i x
+         x' <- readChan o
+         if x == x'
+            then return ()
+            else error $ "Smoke test failed with starting offset of: "++(show n)++"at write: "++(show x)
+
+correctFirstWrite :: Int -> IO ()
+correctFirstWrite n = do
+    (i,UI.OutChan (UI.ChanEnd _ _ arrRef)) <- UI.newChanStarting n
+    writeChan i ()
+    (UI.StreamHead _ (UI.Stream arr _)) <- readIORef arrRef
+    cell <- P.readArray arr 0
+    case cell of
+         UI.Written () -> return ()
+         _ -> error "Expected a Write at index 0"
+
+-- check writes by doing a segment+1-worth of reads by hand
+-- also check that segments pre-allocated as expected
+correctInitialWrites :: Int -> IO ()
+correctInitialWrites startN = do
+    (i,(UI.OutChan (UI.ChanEnd _ _ arrRef))) <- UI.newChanStarting startN
+    let writes = [0..UI.sEGMENT_LENGTH]
+    mapM_ (writeChan i) writes
+    (UI.StreamHead _ (UI.Stream arr next)) <- readIORef arrRef
+    -- check all of first segment:
+    forM_ (init writes) $ \ix-> do
+        cell <- P.readArray arr ix
+        case cell of
+             UI.Written n
+                | n == ix -> return ()
+                | otherwise -> error $ "Expected a Write at index "++(show ix)++" of same value but got "++(show n)
+             _ -> error $ "Expected a Write at index "++(show ix)
+    -- check last write:
+    lastSeg  <- readIORef next
+    case lastSeg of
+         (UI.Next (UI.Stream arr2 next2)) -> do
+            cell <- P.readArray arr2 0
+            case cell of
+                 UI.Written n 
+                    | n == last writes -> return ()
+                    | otherwise -> error $ "Expected last write at index "++(show $ last writes)++" of same value but got "++(show n)
+                 _ -> error "Expected a last Write"
+            -- check pre-allocation:
+            n2 <- readIORef next2
+            case n2 of 
+                (UI.Next (UI.Stream _ next3)) -> do
+                    n3 <- readIORef next3
+                    case n3 of
+                        UI.NoSegment -> return ()
+                        _ -> error "Too many segments pre-allocated!"
+                _ -> error "Next segment was not pre-allocated!"
+         _ -> error "No last segment present!"
+
+
+
+{- NOTE: we would like this to pass (see trac #9030) but are happy to note that
+ -       it fails which somewhat validates the test below
+
+testBlockedRecovery = do
+    (i,o) <- newChan
+    v <- newEmptyMVar
+    rid <- forkIO (putMVar v () >> readChan o)
+    takeMVar v
+    threadDelay 1000
+    throwTo rid ThreadKilled
+    -- we race the exception-handler in `readChan` here...
+    writeChan i ()
+    -- In a buggy implementation, this would consistently win failing by losing
+    -- the message and raising BlockedIndefinitely here:
+    readChan o
+-}
+
+
+-- test for deadlocks caused by async exceptions in reader.
+checkDeadlocksReaderUnagi :: Int -> IO ()
+checkDeadlocksReaderUnagi times = do
+  let run 0 normalRetries numRace = putStrLn $ "Lates: "++(show normalRetries)++", Races: "++(show numRace)
+      run n normalRetries numRace
+       | (normalRetries + numRace) > (times `div` 5) = error "This test is taking too long. Please retry, and if still failing send the log to me"
+       | otherwise = do
+         -- we'll kill the reader with our special exception half the time,
+         -- expecting that we never get our race condition on those runs:
+         let usingReadChanOnException = even n
+
+         (i,o) <- UI.newChanStarting 0
+         -- preload a chan with 0s
+         let numPreloaded = 10000
+         replicateM_ numPreloaded $ writeChan i (0::Int)
+
+         rStart <- newEmptyMVar
+         saved <- newEmptyMVar -- for holding potential saved (IO a) from blocked readChanOnException
+         rid <- forkIO $ (\rd-> putMVar rStart () >> (forever $ void $ rd o)) $
+                    if usingReadChanOnException 
+                        then flip readChanOnException ( putMVar saved )
+                        else readChan
+         takeMVar rStart >> threadDelay 1
+         throwTo rid ThreadKilled
+
+         -- did killing reader damage queue for reads or writes?
+         writeChan i 1 `onException` ( putStrLn "Exception from first writeChan!")
+         writeChan i 2 `onException` ( putStrLn "Exception from second writeChan!")
+         finalRead <- readChan o `onException` ( putStrLn "Exception from final readChan!")
+         
+         oCnt <- readCounter $ (\(UI.OutChan(UI.ChanEnd _ cntr _))-> cntr) o
+         iCnt <- readCounter $ (\(UI.InChan _ (UI.ChanEnd _ cntr _))-> cntr) i
+         unless (iCnt == numPreloaded + 1) $ 
+            error "The InChan counter doesn't match what we'd expect from numPreloaded!"
+
+         case finalRead of
+              0 -> if oCnt <= 0 -- (technically, -1 means hasn't started yet)
+                      -- reader didn't have a chance to get started
+                     then putStr "0" >> run n (normalRetries+1) numRace
+                      -- normal run; we tested that killing a reader didn't
+                      -- break chan for other readers/writers:
+                     else putStr "." >> run (n-1) normalRetries numRace
+              --
+              -- Rare. Reader was killed after reading all pre-loaded messages
+              -- but before starting what would be the blocking read:
+              1 | oCnt == numPreloaded -> putStr "X" >> run n normalRetries (numRace + 1)
+                | otherwise -> error $ "Having read final 1, "++
+                                       "Expecting a counter value of "++(show numPreloaded)++
+                                       " but got: "++(show oCnt)
+              2 -> do when usingReadChanOnException $ do
+                        shouldBe1 <- join $ takeMVar saved
+                        unless (shouldBe1 == 1) $
+                          error "The handler for our readChanOnException should only have returned 1"
+                      unless (oCnt == numPreloaded + 1) $
+                        error $ "Having read final 2, "++
+                                "Expecting a counter value of "++(show $ numPreloaded+1)++
+                                " but got: "++(show oCnt)
+                      putStr "+" >> run n (normalRetries + 1) numRace
+
+              _ -> error "Fix your #$%@ test!"
+
+  run times 0 0
+  putStrLn ""
diff --git a/tests/UnagiUnboxed.hs b/tests/UnagiUnboxed.hs
new file mode 100644
--- /dev/null
+++ b/tests/UnagiUnboxed.hs
@@ -0,0 +1,197 @@
+module UnagiUnboxed (unagiUnboxedMain) where
+
+-- Unagi-chan-specific tests
+--
+-- Forked from tests/Unagi.hs at 337600f
+
+import Control.Concurrent.Chan.Unagi.Unboxed
+import qualified Control.Concurrent.Chan.Unagi.Unboxed.Internal as UI
+import Control.Monad
+import qualified Data.Primitive as P
+import Data.IORef
+
+import Control.Concurrent(forkIO,threadDelay)
+import Control.Concurrent.MVar
+import Control.Exception
+import Data.Atomics.Counter.Fat
+
+unagiUnboxedMain :: IO ()
+unagiUnboxedMain = do
+    putStrLn "==================="
+    putStrLn "Testing Unagi.Unboxed details:"
+    -- ------
+    -- ------
+    putStr "Smoke test at different starting offsets, spanning overflow... "
+    mapM_ smoke $ [ (maxBound - UI.sEGMENT_LENGTH - 1) .. maxBound] 
+                  ++ [minBound .. (minBound + UI.sEGMENT_LENGTH + 1)]
+    putStrLn "OK"
+    -- ------
+    putStr "Correct first write... "
+    mapM_ correctFirstWrite [ (maxBound - 7), maxBound, minBound, 0]
+    putStrLn "OK"
+    -- ------
+    putStr "Correct initial writes... "
+    mapM_ correctInitialWrites [ (maxBound - UI.sEGMENT_LENGTH), (maxBound - UI.sEGMENT_LENGTH) - 1, maxBound, minBound, 0]
+    putStrLn "OK"
+    -- ------
+    let tries = 50000
+    putStrLn $ "Checking for deadlocks from killed Unagi reader in a fancy way, x"++show tries
+    checkDeadlocksReaderUnagi tries
+
+
+smoke :: Int -> IO ()
+smoke n = smoke1 n >> smoke2 n
+
+-- www.../rrr... spanning overflow
+smoke1 :: Int -> IO ()
+smoke1 n = do
+    (i,o) <- UI.newChanStarting n
+    let inp = [0 .. (UI.sEGMENT_LENGTH * 3)]
+    mapM_ (writeChan i) inp
+    outp <- getChanContents o
+    if and (zipWith (==) inp outp)
+        then return ()
+        else error $ "Smoke test failed with starting offset of: "++(show n)
+
+-- w/r/w/r... spanning overflow
+smoke2 :: Int -> IO ()
+smoke2 n = do
+    (i,o) <- UI.newChanStarting n
+    let inp = [0 .. (UI.sEGMENT_LENGTH * 3)]
+    mapM_ (check i o) inp
+ where check i o x = do
+         writeChan i x
+         x' <- readChan o
+         if x == x'
+            then return ()
+            else error $ "Smoke test failed with starting offset of: "++(show n)++"at write: "++(show x)
+
+correctFirstWrite :: Int -> IO ()
+correctFirstWrite n = do
+    (i,UI.OutChan (UI.ChanEnd _ arrRef)) <- UI.newChanStarting n
+    writeChan i (7::Int)
+    (UI.StreamHead _ (UI.Stream sigArr eArr _ _)) <- readIORef arrRef
+    cell <- UI.readElementArray eArr 0 :: IO Int
+    case cell of
+         7 -> return ()
+         _ -> error "Expected a write at index 0 of 7"
+    sigCell <- P.readByteArray sigArr 0 :: IO Int
+    unless (sigCell == 1) $ -- i.e. cellWritten
+         error "Expected cellWritten at sigArr!0"
+
+-- check writes by doing a segment+1-worth of reads by hand
+-- also check that segments pre-allocated as expected
+correctInitialWrites :: Int -> IO ()
+correctInitialWrites startN = do
+    (i,(UI.OutChan (UI.ChanEnd _ arrRef))) <- UI.newChanStarting startN
+    let writes = [0..UI.sEGMENT_LENGTH]
+    mapM_ (writeChan i) writes
+    (UI.StreamHead _ (UI.Stream sigArr eArr _ next)) <- readIORef arrRef
+    -- check all of first segment:
+    forM_ (init writes) $ \ix-> do
+        cell <- UI.readElementArray eArr ix :: IO Int
+        sigCell <- P.readByteArray sigArr ix :: IO Int
+        unless (cell == ix && sigCell == 1) $
+            error $ "Expected a write at index "++(show ix)++" of same value but got "++(show cell)++" with signal "++(show sigCell)
+    -- check last write:
+    lastSeg  <- readIORef next
+    case lastSeg of
+         (UI.Next (UI.Stream sigArr2 eArr2 _ next2)) -> do
+            cell <- UI.readElementArray eArr2 0 :: IO Int
+            sigCell <- P.readByteArray sigArr2 0 :: IO Int
+            unless (cell == last writes && sigCell == 1) $
+                error $ "Expected last write at index "++(show $ last writes)++" of same value but got "++(show cell)
+            -- check pre-allocation:
+            n2 <- readIORef next2
+            case n2 of 
+                (UI.Next (UI.Stream _ _ _ next3)) -> do
+                    n3 <- readIORef next3
+                    case n3 of
+                        UI.NoSegment -> return ()
+                        _ -> error "Too many segments pre-allocated!"
+                _ -> error "Next segment was not pre-allocated!"
+         _ -> error "No last segment present!"
+
+
+
+{- NOTE: we would like this to pass (see trac #9030) but are happy to note that
+ -       it fails which somewhat validates the test below
+
+testBlockedRecovery = do
+    (i,o) <- newChan
+    v <- newEmptyMVar
+    rid <- forkIO (putMVar v () >> readChan o)
+    takeMVar v
+    threadDelay 1000
+    throwTo rid ThreadKilled
+    -- we race the exception-handler in `readChan` here...
+    writeChan i ()
+    -- In a buggy implementation, this would consistently win failing by losing
+    -- the message and raising BlockedIndefinitely here:
+    readChan o
+-}
+
+
+-- test for deadlocks caused by async exceptions in reader.
+checkDeadlocksReaderUnagi :: Int -> IO ()
+checkDeadlocksReaderUnagi times = do
+  let run 0 normalRetries numRace = putStrLn $ "Lates: "++(show normalRetries)++", Races: "++(show numRace)
+      run n normalRetries numRace
+       | (normalRetries + numRace) > (times `div` 5) = error "This test is taking too long. Please retry, and if still failing send the log to me"
+       | otherwise = do
+         -- we'll kill the reader with our special exception half the time,
+         -- expecting that we never get our race condition on those runs:
+         let usingReadChanOnException = even n
+
+         (i,o) <- UI.newChanStarting 0
+         -- preload a chan with 0s
+         let numPreloaded = 10000
+         replicateM_ numPreloaded $ writeChan i (0::Int)
+
+         rStart <- newEmptyMVar
+         saved <- newEmptyMVar -- for holding potential saved (IO a) from blocked readChanOnException
+         rid <- forkIO $ (\rd-> putMVar rStart () >> (forever $ void $ rd o)) $
+                    if usingReadChanOnException 
+                        then flip readChanOnException ( putMVar saved )
+                        else readChan
+         takeMVar rStart >> threadDelay 1
+         throwTo rid ThreadKilled
+
+         -- did killing reader damage queue for reads or writes?
+         writeChan i 1 `onException` ( putStrLn "Exception from first writeChan!")
+         writeChan i 2 `onException` ( putStrLn "Exception from second writeChan!")
+         finalRead <- readChan o `onException` ( putStrLn "Exception from final readChan!")
+         
+         oCnt <- readCounter $ (\(UI.OutChan(UI.ChanEnd cntr _))-> cntr) o
+         iCnt <- readCounter $ (\(UI.InChan (UI.ChanEnd cntr _))-> cntr) i
+         unless (iCnt == numPreloaded + 1) $ 
+            error "The InChan counter doesn't match what we'd expect from numPreloaded!"
+
+         case finalRead of
+              0 -> if oCnt <= 0 -- (technically, -1 means hasn't started yet)
+                      -- reader didn't have a chance to get started
+                     then putStr "0" >> run n (normalRetries+1) numRace
+                      -- normal run; we tested that killing a reader didn't
+                      -- break chan for other readers/writers:
+                     else putStr "." >> run (n-1) normalRetries numRace
+              --
+              -- Rare. Reader was killed after reading all pre-loaded messages
+              -- but before starting what would be the blocking read:
+              1 | oCnt == numPreloaded -> putStr "X" >> run n normalRetries (numRace + 1)
+                | otherwise -> error $ "Having read final 1, "++
+                                       "Expecting a counter value of "++(show numPreloaded)++
+                                       " but got: "++(show oCnt)
+              2 -> do when usingReadChanOnException $ do
+                        shouldBe1 <- join $ takeMVar saved
+                        unless (shouldBe1 == 1) $
+                          error "The handler for our readChanOnException should only have returned 1"
+                      unless (oCnt == numPreloaded + 1) $
+                        error $ "Having read final 2, "++
+                                "Expecting a counter value of "++(show $ numPreloaded+1)++
+                                " but got: "++(show oCnt)
+                      putStr "+" >> run n (normalRetries + 1) numRace
+
+              _ -> error "Fix your #$%@ test!"
+
+  run times 0 0
+  putStrLn ""
diff --git a/unagi-chan.cabal b/unagi-chan.cabal
--- a/unagi-chan.cabal
+++ b/unagi-chan.cabal
@@ -1,5 +1,5 @@
 name:                unagi-chan
-version:             0.1.0.1
+version:             0.1.0.2
 
 synopsis:            Fast and scalable concurrent queues for x86, with a Chan-like API
 
@@ -84,6 +84,16 @@
   -- I guess we need to put 'src' here to get access to Internal modules
   hs-source-dirs: tests, src
   main-is: Main.hs
+  other-modules:
+      Atomics
+    , Chan003
+    , Deadlocks
+    , DupChan
+    , Implementations
+    , IndexedMVar
+    , Smoke
+    , Unagi
+    , UnagiUnboxed
   build-depends:       base
                      , primitive>=0.5.3
                      , atomic-primops==0.6.0.5
