diff --git a/example/Example.hs b/example/Example.hs
deleted file mode 100644
--- a/example/Example.hs
+++ /dev/null
@@ -1,40 +0,0 @@
-module Main where
-
-import System.Posix.IO
-import System.Posix.IO.Select
-import Control.Concurrent
-import Control.Concurrent.STM.OrElseIO
-import Control.Concurrent.STM
-import Control.Concurrent.STM.TMVar
-import Control.Concurrent.STM.TChan
-
-flags :: OpenFileFlags
-flags = OpenFileFlags { append = False, exclusive = False, noctty = False, nonBlock = True, trunc = False }
-
--- Needs the threaded runtime!
-main :: IO ()
-main = 
-    newEmptyTMVarIO >>= \t ->
-    forkIO (interruptor t) >>
-    putStrLn "The interruptor has been launched." >>
-    newTChanIO >>= \c ->
-    forkIO (interruptor2 c) >>
-    putStrLn "The TChan interruptor has been launched." >>
-    openFd "fifo" ReadOnly Nothing flags >>= \fd -> 
-    openFd "fifo2" ReadOnly Nothing flags >>= \fd2 -> 
-    openFd "fifo3" ReadOnly Nothing flags >>= \fd3 -> 
-    openFd "fifo4" ReadOnly Nothing flags >>= \fd4 -> 
-    putStrLn "The file was opened, so let the waiting game begin!" >>
-    runOrElse (select [fd,fd2,fd3,fd4] [] [] (Time 9 0)) (readTChan c) >>= \ret ->
-        case ret of
-          Left 0 -> putStrLn "select() timed out on the files."
-          Left x -> putStrLn "There's data in one of the files!"
-          Right s -> putStrLn $ "Oh no, the interruptor is here: " ++ s   
-
-interruptor :: TMVar String -> IO ()
-interruptor t = threadDelay (10 * 1000000) >>
-                atomically (putTMVar t "Bwahahaha, the interruptor was first!")
-
-interruptor2 :: TChan String -> IO ()
-interruptor2 c = threadDelay (10 * 1000000) >>
-                 atomically (writeTChan c "Bwhaha, TChan interruptor was first!")
diff --git a/example/FdSetExample.hs b/example/FdSetExample.hs
new file mode 100644
--- /dev/null
+++ b/example/FdSetExample.hs
@@ -0,0 +1,40 @@
+module Main where
+
+import Prelude hiding (elem)
+import System.Posix.IO
+import System.Posix.IO.Select.FdSet.Unsafe
+import System.Posix.IO.Select.Types
+
+flags :: OpenFileFlags
+flags = OpenFileFlags { append = False, exclusive = False, noctty = False, nonBlock = True, trunc = False }
+
+main :: IO ()
+main = openFd "fifo" ReadOnly Nothing flags >>= \fd -> 
+       openFd "fifo2" ReadOnly Nothing flags >>= \fd2 -> 
+       openFd "fifo3" ReadOnly Nothing flags >>= \fd3 -> 
+       openFd "fifo4" ReadOnly Nothing flags >>= \fd4 -> 
+       putStrLn "Making" >>
+       let
+           set = fromList [fd, fd3]
+           set2 = insert fd4 set
+           set3 = fromList [fd, fd3]
+           set4 = remove fd4 (remove fd3 (remove fd2 (remove fd set3)))
+       in
+         print (fd `elem` set) >> 
+         print (fd2 `elem` set) >> 
+         print (fd3 `elem` set) >>
+         print (fd4 `elem` set) >> 
+         print [fd, fd3] >>
+         print (unsafeToList set) >>
+         print (fd `elem` set2) >> 
+         print (fd2 `elem` set2) >> 
+         print (fd3 `elem` set2) >>
+         print (fd4 `elem` set2) >>
+         print (fd `elem` set3) >> 
+         print (fd2 `elem` set3) >> 
+         print (fd3 `elem` set3) >>
+         print (fd4 `elem` set3) >>
+         print (fd `elem` set4) >> 
+         print (fd2 `elem` set4) >> 
+         print (fd3 `elem` set4) >>
+         print (fd4 `elem` set4) 
diff --git a/example/SelectExample.hs b/example/SelectExample.hs
new file mode 100644
--- /dev/null
+++ b/example/SelectExample.hs
@@ -0,0 +1,41 @@
+module Main where
+
+import System.Posix.IO
+import System.Posix.IO.Select.FdSet.Unsafe
+import System.Posix.IO.Select
+import System.Posix.IO.Select.Types
+import System.Posix.Types
+import Control.Concurrent
+import Control.Concurrent.STM.OrElseIO
+import Control.Concurrent.STM
+import Control.Concurrent.STM.TMVar
+import Control.Concurrent.STM.TChan
+
+flags :: OpenFileFlags
+flags = OpenFileFlags { append = False, exclusive = False, noctty = False, nonBlock = True, trunc = False }
+
+main :: IO ()
+main =
+    newEmptyTMVarIO >>= \t ->
+    forkIO (interruptor t) >>
+    putStrLn "The interruptor has been launched." >>
+    newTChanIO >>= \c ->
+    forkIO (interruptor2 c) >>
+    putStrLn "The TChan interruptor has been launched." >>
+    openFd "fifo" ReadOnly Nothing flags >>= \fd -> 
+    openFd "fifo2" ReadOnly Nothing flags >>= \fd2 -> 
+    openFd "fifo3" ReadOnly Nothing flags >>= \fd3 -> 
+    openFd "fifo4" ReadOnly Nothing flags >>= \fd4 -> 
+    putStrLn "The file was opened, so let the waiting game begin!" >>
+    runOrElse (select' [fd,fd2,fd3,fd4] [] [] (finite 5 0)) (readTChan c) >>= \ret ->
+        case ret of
+          Left x -> putStrLn ("select() returned: " ++ show x)
+          Right s -> putStrLn ("Oh no, the interruptor is here: " ++ s   )
+
+interruptor :: TMVar String -> IO ()
+interruptor t = threadDelay (10 * 1000000) >>
+                atomically (putTMVar t "Bwahahaha, the interruptor was first!")
+
+interruptor2 :: TChan String -> IO ()
+interruptor2 c = threadDelay (15 * 1000000) >>
+                 atomically (writeTChan c "Bwhaha, TChan interruptor was first!")
diff --git a/example/SelectExample2.hs b/example/SelectExample2.hs
new file mode 100644
--- /dev/null
+++ b/example/SelectExample2.hs
@@ -0,0 +1,56 @@
+module Main where
+
+import System.IO
+import System.Posix.IO
+import System.Posix.IO.Select.FdSet.Unsafe
+import System.Posix.IO.Select
+import System.Posix.IO.Select.Types
+import System.Posix.Types
+import Control.Concurrent
+import Control.Concurrent.STM.OrElseIO
+import Control.Concurrent.STM
+import Control.Concurrent.STM.TMVar
+import Control.Concurrent.STM.TChan
+
+flags :: OpenFileFlags
+flags = OpenFileFlags { append = False, exclusive = False, noctty = False, nonBlock = True, trunc = False }
+
+main :: IO ()
+main =
+    handleToFd stdin >>= \stdinfd  ->
+    newEmptyTMVarIO >>= \t ->
+    forkIO (interruptor t) >>
+    putStrLn "The interruptor has been launched." >>
+    newTChanIO >>= \c ->
+    forkIO (interruptor2 c) >>
+    putStrLn "The TChan interruptor has been launched." >>
+    openFd "fifo" ReadOnly Nothing flags >>= \fd -> 
+    openFd "fifo2" ReadOnly Nothing flags >>= \fd2 -> 
+    openFd "fifo3" ReadOnly Nothing flags >>= \fd3 -> 
+    openFd "fifo4" ReadOnly Nothing flags >>= \fd4 -> 
+    let
+        readfds = [fd, fd2, fd3, stdinfd, fd4]
+        writefds = []
+        exceptfds = []
+    in
+    putStrLn "The file was opened, so let the waiting game begin!" >>
+    runOrElse (select (fromList readfds) empty empty (finite 9 0)) (readTChan c) >>= \ret ->
+        case ret of
+          Left x -> (
+                     case x of
+                       Error -> putStrLn ("Select error.")
+                       Timeout -> putStrLn ("Select timed out.")
+                       Ready n _ _ _ -> putStrLn ("There are " ++ show n ++ " readies.")
+                    ) >>
+                    putStrLn ("Ready read: " ++ (show (inList readfds (readyRead x)))) >>
+                    putStrLn ("Ready write: " ++ (show (inList writefds (readyWrite x)))) >>
+                    putStrLn ("Ready except: " ++ (show (inList exceptfds (readyException x))))
+          Right s -> putStrLn ("Oh no, the interruptor is here: " ++ s  )
+
+interruptor :: TMVar String -> IO ()
+interruptor t = threadDelay (10 * 1000000) >>
+                atomically (putTMVar t "Bwahahaha, the interruptor was first!")
+
+interruptor2 :: TChan String -> IO ()
+interruptor2 c = threadDelay (15 * 1000000) >>
+                 atomically (writeTChan c "Bwhaha, TChan interruptor was first!")
diff --git a/example/TestStdin.hs b/example/TestStdin.hs
new file mode 100644
--- /dev/null
+++ b/example/TestStdin.hs
@@ -0,0 +1,17 @@
+module Main where
+
+import System.Posix.IO.Select
+import System.Posix.IO.Select.Types
+import System.Posix.IO
+import System.Posix.Types
+import System.IO
+
+main :: IO ()
+main = do
+  fd <- handleToFd stdin
+  n <- select'' [fd] [] [] (finite 0 500)
+  case n of
+    0 ->  print "timeout"
+    -1 -> error "select failed"
+    _ ->  print "event"
+  return ()
diff --git a/example/build.sh b/example/build.sh
new file mode 100644
--- /dev/null
+++ b/example/build.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+p=$(pwd)
+
+cobjs="../src/System/Posix/IO/Select/FdSet/cbits.o ../src/System/Posix/IO/Select/cbits.o"
+
+cd ../src/System/Posix/IO/Select/FdSet/
+hsc2hs Internal.hsc
+gcc -DNDEBUG -Wall -std=c99 -c cbits.c
+cd ..
+gcc -Wall -std=c99 -lrt -c cbits.c
+hsc2hs Types.hsc
+cd $p
+
+ghc -fforce-recomp -threaded -o stdin-test --make TestStdin.hs -i../src $cobjs
+ghc -fforce-recomp -threaded -o fdset-example --make FdSetExample.hs -i../src $cobjs
+ghc -fforce-recomp -threaded -o select-example --make SelectExample.hs -i../src $cobjs
+ghc -fforce-recomp -threaded -o select-example2 --make SelectExample2.hs -i../src $cobjs
+
diff --git a/select.cabal b/select.cabal
--- a/select.cabal
+++ b/select.cabal
@@ -1,5 +1,5 @@
 Name:                select
-Version:             0.3
+Version:             0.4
 Synopsis:            Wrap the select(2) POSIX function
 Homepage:            http://nonempty.org/software/haskell-select
 License:             BSD3
@@ -9,35 +9,60 @@
 Copyright:           2012 Gard Spreemann
 Category:            System
 Build-type:          Simple
-Extra-source-files:  src/System/Posix/IO/Select/select_wrapper.c src/System/Posix/IO/Select/select_wrapper.h
+Extra-source-files:  src/System/Posix/IO/Select/cbits.c, 
+		     src/System/Posix/IO/Select/cbits.h,
+		     src/System/Posix/IO/Select/FdSet/cbits.c,
+		     src/System/Posix/IO/Select/FdSet/cbits.h
 Cabal-version:       >=1.2
 Description:         While tinkering on a project, I frequently found myself
 		     having to make FFI calls to @select(2)@. This package provides an interface to that system call.
-		     It also used to expose an STM interface for running @select(2)@ with alternative STM actions,
-		     but that functionality was split into the stm-orelse-io package from version 0.3.
 		     .
-		     Changes in version 0.3:
+		     Changes in version 0.4:
 		     .
-		     * Split all STM-related functionality into a separate package, stm-orelse-io, independent
-		       of select.
+		     * Introduce an interface to @fd_set@.
 		     .
+		     * Add functions with various amounts of abstraction; in particular, 'select'''
+		      matches 'select' in previous versions.
+		     .
+		     * Retry @select(2)@ in case of interruption by signals.
+		     .
+		     * Remove dependence on the vector package.
+		     .
+		     WARNINGS:
+		     .
+		     * Behavior with write and exception file descriptors is under-tested. 
+		       Feedback is welcome.
+		     .
+		     * This package is far from complete, and should be tested throughly before being relied upon.
+		     .
 		     TODO:
 		     .
-		     * Provide a type for @fd_set@ that can be passed to and from C so that we can have a version 
-		     of 'System.Posix.IO.select' that reports /which/ file descriptors are ready, instead of how many.
- 		     Its type will be something like @[Fd] -> [Fd] -> [Fd] -> Timeout -> IO ([Fd], [Fd], [Fd])@.
+		     * Return the error code specified by errno if @select@ fails.
 		     .
+		     * Possibly move the select retry code from C to Haskell.
+		     .
+		     * Reorder functions to make the documentation more logical.
+		     .
+		     * Use hsc2hs's #const to get preprocessor constants (removes some of FdSet's cbits.c).
+		     .
 		     /NOTE/: I feel I'm occupying prime namespace realestate with a package name
-		     like select. I'll happily let myself be chased away if anybody else wants
+		     like select. I'll happily let myself be chased away if someone more qualified wants
 		     to use this package name. Let me know.
 
 Library
   Exposed-modules:     System.Posix.IO.Select,
-                       System.Posix.IO.Select.Types
+                       System.Posix.IO.Select.Types,
+                       System.Posix.IO.Select.FdSet,
+                       System.Posix.IO.Select.FdSet.Unsafe
+  Other-modules:       Misc,
+                       System.Posix.IO.Select.FdSet.Internal
   
   hs-source-dirs:      src
-  Build-depends:       base >= 4 && <5, vector
+  Build-depends:       base >= 4 && <5
   
-  c-sources:           src/System/Posix/IO/Select/select_wrapper.c
+  c-sources:           src/System/Posix/IO/Select/cbits.c,
+                       src/System/Posix/IO/Select/FdSet/cbits.c
+  include-dirs:        src/System/Posix/IO/Select/
+                       src/System/Posix/IO/Select/FdSet/
   cc-options:          -std=c99 -Wall -DNDEBUG 
-
+  extra-libraries:     rt
diff --git a/src/Misc.hs b/src/Misc.hs
new file mode 100644
--- /dev/null
+++ b/src/Misc.hs
@@ -0,0 +1,23 @@
+-- | Some internally used convenience functions.
+module Misc where
+
+import Foreign.C.Types
+import Foreign.ForeignPtr
+import Foreign.Storable
+import Foreign.Ptr
+
+mallocInitForeignPtr :: (Storable a) => (Ptr a -> IO ()) -> IO (ForeignPtr a)
+mallocInitForeignPtr initializer = mallocForeignPtr >>= \fptr ->
+                                   withForeignPtr fptr initializer >>
+                                   return fptr
+
+nullForeignPtr :: IO (ForeignPtr a)
+nullForeignPtr = newForeignPtr_ nullPtr
+
+cError :: CInt -> Bool
+cError = (== -1)
+
+cTrue :: CInt -> Bool
+cTrue 0 = False
+cTrue _ = True 
+
diff --git a/src/System/Posix/IO/Select.hs b/src/System/Posix/IO/Select.hs
--- a/src/System/Posix/IO/Select.hs
+++ b/src/System/Posix/IO/Select.hs
@@ -1,48 +1,102 @@
 {-# LANGUAGE ForeignFunctionInterface #-}
 
--- | Interface to the @select(2)@ POSIX function.
--- 
--- TODO: 
+-- | Interface to @select(2)@. The arguments to the functions exposed
+-- here must fulfill the same requirements as the arguments for
+-- @select@ itself; see the man page.
 --
--- * Make a version of @'select'@ that shows which file descriptors
--- are ready. (Needs a type for fd_set that can be passed to and from
--- C).
-module System.Posix.IO.Select (select, Timeout(Never, Time)) where
+-- If the @select@ call made by any of these functions is interrupted
+-- (@select@ returning @-1@ and @errno@ being @EINTR@) before the
+-- given time has elapsed, the call will be retried. If the specified
+-- amount of time has passed and @select@ is still being interrupted,
+-- the functions below will make one last attempt with timeout
+-- zero. If that call too is interrupted, behavior will be as if
+-- @select@ returned an error.
+module System.Posix.IO.Select (select, select', select'') where
 
-import System.Posix.IO.Select.Types
+import System.Posix.IO.Select.Types 
+import qualified System.Posix.IO.Select.FdSet as FS
+import qualified System.Posix.IO.Select.FdSet.Unsafe as FSUNSAFE
+import qualified System.Posix.IO.Select.FdSet.Internal as FSI
 import Foreign
 import Foreign.C.Types
 import System.Posix.Types
-import qualified Data.Vector.Storable as V -- For contiguous memory and passing to C. Should perhaps be an Array instead.
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import Misc
 
-foreign import ccall "select_wrapper.h select_wrapper"
-        c_select_wrapper :: Ptr CInt -> CInt -> 
-                            Ptr CInt -> CInt -> 
-                            Ptr CInt -> CInt ->
-                            CChar -> CLong -> CLong -> 
-                            IO CInt
+foreign import ccall "Select/cbits.h xselect"
+        c_xselect :: Fd -> Ptr () -> Ptr () -> Ptr () -> Ptr () -> IO CInt
+--                   nfds  readfds   writefds  exceptfds timeout    
 
--- | @'select' readFds writeFds exceptFds timeout@ calls the
--- @select(2)@ function with the file descriptors in @readFds@ as the
--- FD set to watch for read readiness, and similarly for @writeFds@
--- and @exceptFds@, with @timeout@ specifying the timeout. The return
--- value is that of the call.
-select :: [Fd] -> [Fd] -> [Fd] -> Timeout -> IO CInt
-select readFds writeFds exceptFds timeout =
-    let
-        rfds = V.fromList (map fromIntegral readFds) :: V.Vector CInt -- Make contiguous memory.
-        nr = fromIntegral (V.length rfds)
-        wfds = V.fromList (map fromIntegral writeFds) :: V.Vector CInt
-        nw = fromIntegral (V.length wfds)
-        efds = V.fromList (map fromIntegral exceptFds) :: V.Vector CInt
-        ne = fromIntegral (V.length efds)
-        (tFlag, s, us) = timeoutToC timeout
-    in
-      -- Yikes, the next line is ugly. But stare at unsafeWith, and you'll see this is right.
-      V.unsafeWith rfds $ \pr -> V.unsafeWith wfds $ \pw -> V.unsafeWith efds $ \pe -> 
-      c_select_wrapper pr nr pw nw pe ne tFlag s us 
+-- | The first 'FS.FdSet' is watched for readiness to read, the second
+-- for readiness to write, and the third for exceptions. The 'Timeout'
+-- argument specifies when @select@ should time out (if at all). 
+select :: FS.FdSet -> FS.FdSet -> FS.FdSet -> Timeout -> IO Result
+select rfds wfds efds timeout =
+    (
+     case timeout of
+       Never -> nullForeignPtr
+       Time timeval -> mallocInitForeignPtr ((flip poke) timeval)
+    ) >>= \timevalp ->
+    FS.duplicate rfds >>= \(FSI.FdSet rfdsp brfd) ->
+    FS.duplicate wfds >>= \(FSI.FdSet wfdsp bwfd) ->
+    FS.duplicate efds >>= \(FSI.FdSet efdsp befd) ->
+    withForeignPtr rfdsp $ \rfdsp' -> 
+    withForeignPtr wfdsp $ \wfdsp' -> 
+    withForeignPtr efdsp $ \efdsp' ->
+    withForeignPtr timevalp $ \timevalp' -> 
+    c_xselect (1 + maximum [brfd, bwfd, befd]) rfdsp' wfdsp' efdsp' (castPtr timevalp') >>= \ret ->
+    return (
+            if cError ret
+            then Error
+            else if cTrue ret
+                 then Ready ret (FSI.FdSet rfdsp brfd) (FSI.FdSet wfdsp bwfd) (FSI.FdSet efdsp befd)
+                 else Timeout
+           )
 
-timeoutToC :: Timeout -> (CChar, CLong, CLong)
-timeoutToC Never = (0, 0, 0)
-timeoutToC (Time s us) = (1, fromIntegral s, fromIntegral us)
+-- | A simpler version of 'select' that uses file descriptor lists
+-- instead of 'FS.FdSet's. 'Nothing' is returned in case @select@ gives
+-- an error, otherwise @'Just' (rfds, wfds, efds)@ is returned, where
+-- @rfds@, @wfds@ and @efds@ are lists of ready file descriptors (they
+-- may be empty, such as in the case of @select@ timing out).
+select' :: [Fd] -> [Fd] -> [Fd] -> Timeout -> IO (Maybe ([Fd], [Fd], [Fd]))
+select' rfds wfds efds timeout =
+    (
+     case timeout of
+       Never -> nullForeignPtr
+       Time timeval -> mallocInitForeignPtr ((flip poke) timeval)
+    ) >>= \timevalp ->
+    FS.fromList rfds >>= \rset@(FSI.FdSet rfdsp brfd) ->
+    FS.fromList wfds >>= \wset@(FSI.FdSet wfdsp bwfd) ->
+    FS.fromList efds >>= \eset@(FSI.FdSet efdsp befd) ->
+    withForeignPtr rfdsp $ \rfdsp' -> 
+    withForeignPtr wfdsp $ \wfdsp' -> 
+    withForeignPtr efdsp $ \efdsp' ->
+    withForeignPtr timevalp $ \timevalp' -> 
+    c_xselect (1 + maximum [brfd, bwfd, befd]) rfdsp' wfdsp' efdsp' (castPtr timevalp') >>= \ret ->
+    if cError ret
+    then return Nothing
+    else FS.inList rfds rset >>= \rfds' ->
+         FS.inList wfds wset >>= \wfds' ->
+         FS.inList efds eset >>= \efds' ->
+         return $ Just (rfds', wfds', efds')
 
+-- | This simpler version of 'select' takes the same arguments as
+-- 'select'', but only returns the return value of @select(2)@ itself
+-- (i.e. @-1@ on error, otherwise the number of ready file descriptors
+-- in total).
+select'' :: [Fd] -> [Fd] -> [Fd] -> Timeout -> IO CInt
+select'' rfds wfds efds timeout =
+    (
+     case timeout of
+       Never -> nullForeignPtr
+       Time timeval -> mallocInitForeignPtr ((flip poke) timeval)
+    ) >>= \timevalp ->
+    FS.fromList rfds >>= \rset@(FSI.FdSet rfdsp brfd) ->
+    FS.fromList wfds >>= \wset@(FSI.FdSet wfdsp bwfd) ->
+    FS.fromList efds >>= \eset@(FSI.FdSet efdsp befd) ->
+    withForeignPtr rfdsp $ \rfdsp' -> 
+    withForeignPtr wfdsp $ \wfdsp' -> 
+    withForeignPtr efdsp $ \efdsp' ->
+    withForeignPtr timevalp $ \timevalp' -> 
+    c_xselect (1 + maximum [brfd, bwfd, befd]) rfdsp' wfdsp' efdsp' (castPtr timevalp')
diff --git a/src/System/Posix/IO/Select/FdSet.hs b/src/System/Posix/IO/Select/FdSet.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/FdSet.hs
@@ -0,0 +1,100 @@
+-- | Interface to @fd_set@. See @select(2)@.  
+--
+-- The type 'I.FdSet' is opaque, but is implemented internally as a
+-- pointer to an @fd_set@. All operations on 'I.FdSet's must adhere to
+-- the requirements of @FD_CLR@, @FD_ISSET@, @FD_SET@ and @FD_ZERO@
+-- (see @select(2)@). This includes requiring /valid/ file descriptors
+-- for all operations. Most functions in this module are kept in the
+-- 'IO' monad to make it easier to guarantee validity of the file
+-- descriptors, but since invalid ones seem to work fine in practice
+-- (at least on Linux), the module
+-- "System.Posix.IO.Select.FdSet.Unsafe" provides a non-'IO'
+-- interface.
+--
+-- Functions that return an 'I.FdSet', such as 'insert', copy the
+-- underlying @fd_set@ in order to be referentially transparent.
+--
+-- In the documentation that follows, a file descriptor is said to be
+-- /in range/ if it is non-negative and strictly smaller than the
+-- system-defined @FD_SETSIZE@. Many functions silently ignore file
+-- descriptors that are not in range.
+module System.Posix.IO.Select.FdSet (I.FdSet(), fromList, insert, insertList, empty, elem,
+                                     remove, removeList, inList, inRange, bound, duplicate) where
+
+import Prelude hiding (elem)
+import qualified System.Posix.IO.Select.FdSet.Internal as I
+import Foreign
+import qualified System.IO.Unsafe as UNSAFE
+import System.Posix.Types
+import Misc
+import Control.Monad
+
+-- | Create an 'FdSet' from a list of file descriptors. File
+-- descriptors not in range (see above) are silently ignored.
+fromList :: [Fd] -> IO I.FdSet
+fromList fds = I.allocate' >>= \ptr ->
+               withForeignPtr ptr I.c_fd_zero_wrapper >>
+               mapM_ ((flip I.insert') ptr) (filter inRange fds) >>
+               return (I.FdSet ptr (maximum (0:fds)))
+
+-- | Insert a file descriptor.
+insert :: Fd -> I.FdSet -> IO I.FdSet 
+insert fd = insertList [fd]
+
+-- | Insert multiple file descriptors. This is more efficient than
+-- multiple 'insert's (only a single copy of the set is made).
+insertList :: [Fd] -> I.FdSet -> IO I.FdSet
+insertList fds (I.FdSet ptr l) = 
+    I.duplicate' ptr >>= \newPtr ->
+    mapM_ ((flip I.insert') newPtr) (filter inRange fds) >>
+    return (I.FdSet newPtr (max l (maximum (0:fds))))
+
+-- | An empty 'FdSet'.
+empty :: IO I.FdSet
+empty = I.allocate' >>= \ptr ->
+        withForeignPtr ptr I.c_fd_zero_wrapper >>
+        return (I.FdSet ptr 0)
+
+-- | Test for membership. Recall that POSIX allows undefined behavior
+-- if the file descriptor is not valid (it does, however, seem to work
+-- fine on Linux).
+elem :: Fd -> I.FdSet -> IO Bool
+elem fd (I.FdSet ptr _) = I.elem' fd ptr >>= (return . cTrue)
+
+-- | Remove a file descriptor.
+remove :: Fd -> I.FdSet -> IO I.FdSet
+remove fd = removeList [fd]
+
+-- | Remove multiple file descriptors. This is more efficient than
+-- multiple 'remove's (only a single copy of the set is made).
+removeList :: [Fd] -> I.FdSet -> IO I.FdSet
+removeList fds (I.FdSet ptr l) = 
+    I.duplicate' ptr >>= \newPtr ->
+    mapM_ ((flip I.remove') newPtr) (filter inRange fds) >>
+    return (I.FdSet newPtr l) -- We don't actually shrink the maximum fd here. Should be ok!
+
+-- | @'inList' fds fdset@ gives a list of all file descriptors in
+-- @fd@ that are in @fdset@.
+inList :: [Fd] -> I.FdSet -> IO [Fd]
+inList fds (I.FdSet ptr l) = filterM (\fd -> I.elem' fd ptr >>= (return . cTrue)) fds'
+    where
+      fds' = filter (\fd -> fd <= l && inRange fd) fds
+
+-- | Test if a file descriptor is in range (see introduction).
+inRange :: Fd -> Bool
+inRange fd = fd' >= 0 && fd' < limit
+    where
+      fd' = fromIntegral fd
+      limit = UNSAFE.unsafePerformIO I.c_fd_setsize_wrapper
+
+-- | This file descriptor is at least as large as the largest in the
+-- set. If no file descriptors have ever been removed from the set,
+-- the value is /the largest/ in the set, but this assumption may not
+-- hold after removals or other operations.
+bound :: I.FdSet -> Fd
+bound (I.FdSet _ l) = l
+
+-- | Copy an 'FdSet'.
+duplicate :: I.FdSet -> IO I.FdSet
+duplicate (I.FdSet ptr l) = I.duplicate' ptr >>= \newPtr ->
+                            return (I.FdSet newPtr l)
diff --git a/src/System/Posix/IO/Select/FdSet/Internal.hsc b/src/System/Posix/IO/Select/FdSet/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/FdSet/Internal.hsc
@@ -0,0 +1,51 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+module System.Posix.IO.Select.FdSet.Internal where
+
+import Foreign
+import Foreign.C.Types
+import System.Posix.Types
+import Misc
+
+-- NOT an instance of storable. We treat fdset as opaque, maintaing only a pointer.
+data FdSet = FdSet (ForeignPtr ()) Fd
+
+#include <sys/select.h>
+#include "cbits.h"
+#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
+
+remove' :: Fd -> ForeignPtr () -> IO ()
+remove' fd ptr = withForeignPtr ptr (c_fd_clr_wrapper (fromIntegral fd))
+                            
+insert' :: Fd -> ForeignPtr () -> IO ()
+insert' fd ptr = withForeignPtr ptr (c_fd_set_wrapper (fromIntegral fd))
+
+elem' :: Fd -> ForeignPtr () -> IO CInt
+elem' fd ptr = withForeignPtr ptr (c_fd_isset_wrapper (fromIntegral fd))
+
+pointer :: FdSet -> ForeignPtr ()
+pointer (FdSet p _) = p
+
+allocate' :: IO (ForeignPtr ())
+allocate' = mallocForeignPtrBytes #{size fd_set}
+-- debugging: putStrLn ("Allocating " ++ show #{size fd_set} ++ ".") >>  -- Remove when finished testing.
+
+duplicate' :: ForeignPtr () -> IO (ForeignPtr ())
+duplicate' ptr = allocate' >>= \newPtr ->
+                 withForeignPtr ptr (\ptr' -> withForeignPtr newPtr (\newPtr' -> copyBytes newPtr' ptr' #{size fd_set})) >>
+                 return newPtr
+
+foreign import ccall "cbits.h fd_zero_wrapper"
+        c_fd_zero_wrapper :: Ptr () -> IO ()
+
+foreign import ccall "cbits.h fd_set_wrapper"
+        c_fd_set_wrapper :: CInt -> Ptr () -> IO ()
+
+foreign import ccall "cbits.h fd_clr_wrapper"
+        c_fd_clr_wrapper :: CInt -> Ptr () -> IO ()
+
+foreign import ccall "cbits.h fd_isset_wrapper"
+        c_fd_isset_wrapper :: CInt -> Ptr () -> IO CInt
+
+foreign import ccall "cbits.h fd_setsize_wrapper"
+        c_fd_setsize_wrapper :: IO CInt
diff --git a/src/System/Posix/IO/Select/FdSet/Unsafe.hs b/src/System/Posix/IO/Select/FdSet/Unsafe.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/FdSet/Unsafe.hs
@@ -0,0 +1,56 @@
+-- | Pure interface to @fd_set@.
+--
+-- As far as I can tell, the data structure and functions in
+-- "System.Posix.IO.Select.FdSet" are referentially transparent, and
+-- it should be OK to escape the 'IO' monad. However, POSIX requires
+-- that all operations on @fd_set@s are done with /valid/ file
+-- descriptors. This can potentially be hard to ensure with lazy
+-- evaluation in a pure setting, so for now this module bears the
+-- unsafe label. On Linux, invalid file descriptors /seem/ to be just
+-- fine.
+--
+--  See "System.Posix.IO.Select.FdSet" for documentation in
+--  general. All functions here essentially just add
+--  'UNSAFE.unsafePerformIO'.
+module System.Posix.IO.Select.FdSet.Unsafe (F.FdSet(), fromList, insert, insertList,
+                                    elem, remove, removeList, inList,
+                                    empty, unsafeToList) where
+
+import Prelude hiding (elem)
+import System.Posix.Types
+import qualified System.Posix.IO.Select.FdSet as F
+import qualified System.IO.Unsafe as UNSAFE
+
+fromList :: [Fd] -> F.FdSet
+fromList = UNSAFE.unsafePerformIO . F.fromList
+
+empty :: F.FdSet
+empty = UNSAFE.unsafePerformIO F.empty
+
+insert :: Fd -> F.FdSet -> F.FdSet
+insert fd fdset = UNSAFE.unsafePerformIO (F.insert fd fdset)
+
+insertList :: [Fd] -> F.FdSet -> F.FdSet
+insertList fds fdset = UNSAFE.unsafePerformIO (F.insertList fds fdset)
+
+elem :: Fd -> F.FdSet -> Bool
+elem fd fdset = UNSAFE.unsafePerformIO (F.elem fd fdset)
+
+remove :: Fd -> F.FdSet -> F.FdSet
+remove fd fdset = UNSAFE.unsafePerformIO (F.remove fd fdset)
+
+removeList :: [Fd] -> F.FdSet -> F.FdSet
+removeList fds fdset = UNSAFE.unsafePerformIO (F.removeList fds fdset)
+
+inList :: [Fd] -> F.FdSet -> [Fd]
+inList fds fdset = UNSAFE.unsafePerformIO (F.inList fds fdset)
+
+-- toList :: F.FdSet -> [Fd]
+-- toList = UNSAFE.unsafePerformIO . F.toList
+
+-- | This converts an 'FdSet' to a list by testing every file
+-- descriptor in range for membership, which tends to involve invalid
+-- file descriptors, giving undefined behavior according to POSIX. Use
+-- 'inList' if possible.
+unsafeToList :: F.FdSet -> [Fd]
+unsafeToList fdset = inList [(fromIntegral 0)..(F.bound fdset)] fdset
diff --git a/src/System/Posix/IO/Select/FdSet/cbits.c b/src/System/Posix/IO/Select/FdSet/cbits.c
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/FdSet/cbits.c
@@ -0,0 +1,27 @@
+#include <sys/select.h>
+#include "cbits.h"
+
+void fd_clr_wrapper(int fd, fd_set* set)
+{
+  FD_CLR(fd, set);
+}
+
+int fd_isset_wrapper(int fd, fd_set* set)
+{
+  return FD_ISSET(fd, set);
+}
+
+void fd_set_wrapper(int fd, fd_set* set)
+{
+  FD_SET(fd, set);
+}
+
+void fd_zero_wrapper(fd_set* set)
+{
+  FD_ZERO(set);
+}
+
+int fd_setsize_wrapper()
+{
+  return FD_SETSIZE;
+}
diff --git a/src/System/Posix/IO/Select/FdSet/cbits.h b/src/System/Posix/IO/Select/FdSet/cbits.h
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/FdSet/cbits.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <sys/select.h>
+
+void fd_clr_wrapper(int fd, fd_set* set);
+int fd_isset_wrapper(int fd, fd_set* set);
+void fd_set_wrapper(int fd, fd_set* set);
+void fd_zero_wrapper(fd_set* set);
+int fd_setsize_wrapper();
diff --git a/src/System/Posix/IO/Select/MVar.hs b/src/System/Posix/IO/Select/MVar.hs
deleted file mode 100644
--- a/src/System/Posix/IO/Select/MVar.hs
+++ /dev/null
@@ -1,17 +0,0 @@
--- | Treat the POSIX @select(2)@ function as an 'MVar' 'CInt'.
-
-module System.Posix.IO.Select.MVar (select, Timeout(Never, Time)) where
-
-import System.Posix.IO.Select.Types
-import qualified System.Posix.IO.Select as S
-import Foreign.C.Types
-import Control.Concurrent.MVar
-import qualified Control.Concurrent.MVarIO as MIO
-import System.Posix.Types
-
--- | This version of 'S.select' immediately returns and makes the
--- return value of the @select(2)@ call available as an 'MVar'
--- 'CInt'. See 'S.select' for argument information.
-select :: [Fd] -> [Fd] -> [Fd] -> Timeout -> IO (MVar CInt)
-select readFds writeFds exceptFds timeout = MIO.run (S.select readFds writeFds exceptFds timeout)
-
diff --git a/src/System/Posix/IO/Select/STM.hs b/src/System/Posix/IO/Select/STM.hs
deleted file mode 100644
--- a/src/System/Posix/IO/Select/STM.hs
+++ /dev/null
@@ -1,51 +0,0 @@
--- | Treat the POSIX @select(2)@ function as a 'TMVar' 'CInt'.
-
-module System.Posix.IO.Select.STM (select, selectOrTakeTMVar, selectOrElse, selectOrElse', selectOrReadTChan, Timeout(Never, Time)) where
-
-import System.Posix.IO.Select.Types
-import qualified System.Posix.IO.Select as S
-import Foreign.C.Types
-import Control.Concurrent.STM
-import qualified Control.Concurrent.STM.RunOrElse as ROE
-import qualified Control.Concurrent.STM.TMVarIO as TIO
-import System.Posix.Types
-
--- | This version of 'S.select' immediately returns and makes the
--- return value of the @select(2)@ call available as a 'TMVar'
--- 'CInt'. See 'S.select' for argument information.
-select :: [Fd] -> [Fd] -> [Fd] -> Timeout -> IO (TMVar CInt)
-select readFds writeFds exceptFds timeout = TIO.run (S.select readFds writeFds exceptFds timeout)
-
--- | The parameters are the same as for 'select', except for the
--- addition of a 'TMVar'. The function returns as soon as either the
--- @select(2)@ has completed, or the 'TMVar' is full. 
---
--- The return value is either the return value of @select(2)@, or the
--- content of the 'TMVar'. If the 'TMVar' becomes available first,
--- then the @select(2)@ call may hang around forever or until it times
--- out (as specified by the 'Timeout' parameter). If the @select(2)@
--- returns before the 'TMVar' is available, the 'TMVar' is guaranteed
--- to be left in place.
---
--- See also 'selectOrReadTChan' and 'selectOrElse'. Note that
--- 'selectOrTakeTMVar' and the former are special cases of the latter.
---
--- (Incidentally, 'selectOrTakeTMVar' is the task I really wanted to
--- accomplish, and solving it just turned into this little library).
-selectOrTakeTMVar :: [Fd] -> [Fd] -> [Fd] -> Timeout -> TMVar a -> IO (Either CInt a)
-selectOrTakeTMVar readFds writeFds exceptFds timeout mv = selectOrElse readFds writeFds exceptFds timeout (takeTMVar mv)
-
--- | Analogous to 'selectOrTakeTMVar', except with a general STM
--- action in place of taking a 'TMVar'.
-selectOrElse :: [Fd] -> [Fd] -> [Fd] -> Timeout -> STM a -> IO (Either CInt a)
-selectOrElse readFds writeFds exceptFds timeout stm = ROE.runOrElse (S.select readFds writeFds exceptFds timeout) stm
-
--- | A version of 'selectOrElse' that prefers the STM operation to
--- 'select' when both are available.
-selectOrElse' :: STM a -> [Fd] -> [Fd] -> [Fd] -> Timeout -> IO (Either a CInt)
-selectOrElse' stm readFds writeFds exceptFds timeout = ROE.runOrElse' stm (S.select readFds writeFds exceptFds timeout)
-
--- | Special case of 'selectOrElse' where the STM action is reading a
--- 'TChan'.
-selectOrReadTChan :: [Fd] -> [Fd] -> [Fd] -> Timeout -> TChan a -> IO (Either CInt a)
-selectOrReadTChan readFds writeFds exceptFds timeout tc = selectOrElse readFds writeFds exceptFds timeout (readTChan tc)
diff --git a/src/System/Posix/IO/Select/Types.hs b/src/System/Posix/IO/Select/Types.hs
deleted file mode 100644
--- a/src/System/Posix/IO/Select/Types.hs
+++ /dev/null
@@ -1,10 +0,0 @@
--- | Some types used by the other modules in this package.
-module System.Posix.IO.Select.Types where
-
-type Seconds = Int
-type Microseconds = Int
-
--- | A timeout of @'Never'@ tells @select(2)@ to never time out, while
--- @'Time' s us@ sets the timeout parameters to @s@ seconds and @us@
--- microseconds.
-data Timeout = Never | Time Seconds Microseconds
diff --git a/src/System/Posix/IO/Select/Types.hsc b/src/System/Posix/IO/Select/Types.hsc
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/Types.hsc
@@ -0,0 +1,65 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- | Various types.
+module System.Posix.IO.Select.Types where
+
+import Foreign.C.Types
+import Foreign.Storable
+import qualified System.Posix.IO.Select.FdSet.Unsafe as FSUNSAFE
+import qualified System.Posix.IO.Select.FdSet as FS
+
+-- | A timeout of @'Never'@ tells @select(2)@ to never time out, while
+-- @'Time'@ sets a finite timeout.
+data Timeout = Never | Time CTimeval
+
+-- | A 'Storable' instance for @struct timeval@ (see @select(2)@). The
+-- first argument corresponds to @tv_sec@, the second to @tv_usec@.
+data CTimeval = CTimeval CLong CLong
+
+-- | @'finite' s us@ tells @select@ to time out after @s@ seconds and
+-- @us@ microseconds.
+finite :: CLong -> CLong -> Timeout
+finite s us = Time (CTimeval s us)
+
+#include <sys/time.h>
+#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
+instance Storable CTimeval where
+    sizeOf _ = #{size struct timeval}
+    alignment _ = #{alignment struct timeval}
+    peek ptr = #{peek struct timeval, tv_sec} ptr >>= \s ->
+               #{peek struct timeval, tv_usec} ptr >>= \us ->
+               return (CTimeval s us)
+    poke ptr (CTimeval s us) = #{poke struct timeval, tv_sec} ptr s >>
+                               #{poke struct timeval, tv_usec} ptr us
+
+-- | Abstraction for the return value of @select@. @'Error'@ means
+-- that @select@ returned an error, while 'Timeout' means it
+-- timed out. @'Ready' n rfds wfds efds@ specifies that it returned
+-- because the file descriptors in @rfds@, @wfds@ and @efds@ are ready
+-- in their respective ways (see @select(2)@), with @n@ descriptors in
+-- total.
+data Result = Error | Timeout | Ready CInt FS.FdSet FS.FdSet FS.FdSet
+
+-- | The total number of ready file descriptors across all three sets.
+numReady :: Result -> CInt
+numReady Error = 0
+numReady Timeout  = 0
+numReady (Ready n _ _ _) = n
+
+-- | The file descriptors ready for reading.
+readyRead :: Result -> FS.FdSet
+readyRead Error = FSUNSAFE.empty
+readyRead Timeout = FSUNSAFE.empty
+readyRead (Ready _ rfds _ _) = rfds
+
+-- | The file descriptors ready for writing.
+readyWrite :: Result -> FS.FdSet
+readyWrite Error = FSUNSAFE.empty
+readyWrite Timeout = FSUNSAFE.empty
+readyWrite (Ready _ _ wfds _) = wfds
+
+-- | The file descriptors having exceptional conditions.
+readyException :: Result -> FS.FdSet
+readyException Error = FSUNSAFE.empty
+readyException Timeout = FSUNSAFE.empty
+readyException (Ready _ _ _ efds) = efds
diff --git a/src/System/Posix/IO/Select/cbits.c b/src/System/Posix/IO/Select/cbits.c
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/cbits.c
@@ -0,0 +1,94 @@
+#define _POSIX_C_SOURCE 200112L
+#include <stdlib.h>
+#include <sys/select.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+#include "cbits.h"
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+
+int xselect(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout)
+{
+  struct timespec remaining;
+  int ret;
+  struct timespec start;
+  struct timespec current;
+  struct timespec stop;
+  if (timeout != NULL)
+  {
+    remaining.tv_sec = timeout->tv_sec;
+    remaining.tv_nsec = timeout->tv_usec*1000;
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    stop.tv_sec = start.tv_sec + timeout->tv_sec;
+    stop.tv_nsec = start.tv_nsec + timeout->tv_usec*1000;
+  }
+  fd_set readfds_orig = *readfds;
+  fd_set writefds_orig = *writefds;
+  fd_set exceptfds_orig = *exceptfds;
+
+  for (;;)
+  {
+    #ifndef NDEBUG
+    if (timeout != NULL)
+    {
+      printf("C: Remaining time: %ld s, %ld ns\n", remaining.tv_sec, remaining.tv_nsec);
+      printf("C: Will select for %ld s, %ld us\n", timeout->tv_sec, timeout->tv_usec);
+    }
+    else
+      printf("C: Will select forever.\n");
+    #endif
+
+    ret = select(nfds, readfds, writefds, exceptfds, timeout);
+
+    if (ret == -1 && errno == EINTR)
+    {
+      #ifndef NDEBUG
+      printf("C: Was interrupted.\n");
+      #endif
+      if (timeout != NULL) // Case of finite timeouts.
+      {
+	clock_gettime(CLOCK_MONOTONIC, &current);
+	remaining.tv_sec = stop.tv_sec - current.tv_sec;
+	remaining.tv_nsec = stop.tv_nsec - current.tv_nsec;
+
+	while (remaining.tv_nsec > 1000000000)
+	{
+	  remaining.tv_sec++;
+	  remaining.tv_nsec -= 1000000000;
+	}
+	while (remaining.tv_nsec < 0)
+	{
+	  remaining.tv_sec--;
+	  remaining.tv_nsec += 1000000000;
+	}
+	if (remaining.tv_sec < 0)
+	{
+	  remaining.tv_sec = 0;
+	  remaining.tv_nsec = 0;
+	}
+
+        #ifndef NDEBUG
+	printf("C: Was interrupted with %ld s, %ld ns remaining\n", remaining.tv_sec, remaining.tv_nsec);
+        #endif
+
+	// Set up for retry.
+	timeout->tv_sec = remaining.tv_sec;
+	timeout->tv_usec = remaining.tv_nsec/1000;
+      }
+      // Set up for retry (also in infinite case).
+      *readfds = readfds_orig;
+      *writefds = writefds_orig;
+      *exceptfds = exceptfds_orig;
+      #ifndef NDEBUG
+      printf("C: Retrying select.\n");
+      #endif
+    }
+    else
+      return ret;
+  }
+
+  return -2;   // Cannot happen.
+}
diff --git a/src/System/Posix/IO/Select/cbits.h b/src/System/Posix/IO/Select/cbits.h
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/IO/Select/cbits.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <sys/select.h>
+#include <sys/time.h>
+
+void subtract(struct timespec* result, const struct timespec* x, const struct timespec* y);
+int xselect(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
diff --git a/src/System/Posix/IO/Select/select_wrapper.c b/src/System/Posix/IO/Select/select_wrapper.c
deleted file mode 100644
--- a/src/System/Posix/IO/Select/select_wrapper.c
+++ /dev/null
@@ -1,73 +0,0 @@
-#include <unistd.h>
-#include <sys/time.h>
-#include <sys/select.h>
-#ifndef NDEBUG
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#endif
-
-#include "select_wrapper.h"
-
-int select_wrapper(const int* readfds, int num_readfds,
-		   const int* writefds, int num_writefds,
-		   const int* exceptfds, int num_exceptfds,
-		   char timeout, long secs, long usecs)
-{
-  struct timeval tv;
-  tv.tv_sec = secs;
-  tv.tv_usec = usecs;
-
-  int nfds = -1;
-
-  fd_set rfds;
-  FD_ZERO(&rfds);
-  for (int i = 0; i < num_readfds; i++)
-  {
-    #ifndef NDEBUG
-    printf("select_wrapper.c: Registering read FD %d.\n", readfds[i]);
-    #endif
-    FD_SET(readfds[i], &rfds);
-    if (readfds[i] > nfds)
-      nfds = readfds[i];
-  }
-
-  fd_set wfds;
-  FD_ZERO(&wfds);
-  for (int i = 0; i < num_writefds; i++)
-  {
-    #ifndef NDEBUG
-    printf("select_wrapper.c: Registering write FD %d.\n", writefds[i]);
-    #endif
-    FD_SET(writefds[i], &wfds);
-    if (writefds[i] > nfds)
-      nfds = writefds[i];
-  }
-
-  fd_set efds;
-  FD_ZERO(&efds);
-  for (int i = 0; i < num_exceptfds; i++)
-  {
-    #ifndef NDEBUG
-    printf("select_wrapper.c: Registering exception FD %d.\n", exceptfds[i]);
-    #endif
-    FD_SET(exceptfds[i], &efds);
-    if (exceptfds[i] > nfds)
-      nfds = exceptfds[i];
-  }  
-
-  nfds++;
-  int ret = -1;
-  if (timeout)
-    ret = select(nfds, &rfds, &wfds, &efds, &tv);
-  else
-    ret = select(nfds, &rfds, &wfds, &efds, NULL);
-
-  #ifndef NDEBUG
-  printf("select_wrapper.c: Returning %d.\n", ret);
-  if (ret == -1)
-    printf("select_wrapper.c: Error code %d: %s\n", errno, strerror(errno));
-  #endif
-
-  return ret;
-}
diff --git a/src/System/Posix/IO/Select/select_wrapper.h b/src/System/Posix/IO/Select/select_wrapper.h
deleted file mode 100644
--- a/src/System/Posix/IO/Select/select_wrapper.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-int select_wrapper(const int* readfds, int num_readfds,
-		   const int* writefds, int num_writefds,
-		   const int* exceptfds, int num_exceptfds,
-		   char timeout, long secs, long usecs);
-
