diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Revision history for Z-IO
+
+## 0.1.0.0  -- 2020-09-19
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017-2020, Haskell Z Team
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of winter nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,83 @@
+<p align=center>
+  <img src="https://github.com/haskell-Z/Z/raw/master/projectZ.svg">
+</p>
+
+## Z-IO
+
+[![Linux Build Status](https://img.shields.io/travis/haskell-z/z-io/master.svg?label=Linux%20build)](https://travis-ci.org/haskell-z/z-io)
+
+This package provides basic IO operations:
+
+* IO resource management, resource pool
+* File system
+* Network: DNS, TCP, UDP and IPC
+* Buffered input and output
+* High performance logger
+* High performance timer
+
+## Dependencies
+
+On *nix system, libuv >= 1.32 are required to build this library, e.g.
+
+```base
+# on ubuntu
+sudo apt-get install libuv1 libuv1-dev
+# on mac
+brew install libuv
+```
+
+
+## Example usage
+
+```haskell
+> :set -XOverloadedStrings  
+> import Z.IO.Network
+> import Z.IO.Resource
+> import Z.IO.Buffered
+> 
+> -- call getAddrInfo to perform DNS
+> head <$> getAddrInfo Nothing "www.bing.com" "http"
+AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = SocketFamily 2, addrSocketType = SocketType 1, addrProtocol = ProtocolNumber 6, addrAddress = 204.79.197.200:80, addrCanonName = }
+>
+> import qualified Z.Data.Text as T
+> -- send a simple HTTP request
+> :{
+| let addr = SocketAddrInet 80 (tupleToInetAddr (13,107,21,200))
+| in withResource (initTCPClient defaultTCPClientConfig{ tcpRemoteAddr = addr}) $ \ tcp -> do
+|     i <- newBufferedInput defaultChunkSize tcp 
+|     o <- newBufferedOutput defaultChunkSize tcp
+|     flushBuffer o
+|     readBuffer i >>= pure . T.validate
+| :}
+"HTTP/1.1 200 OK\r\nDate: Sat, 19 Sep 2020 06:11:08 GMT\r\nContent-Length: 0\r\n\r\n"
+>
+> -- Start a TCP echo server, use @nc -v localhost 8080@ to test
+> :{
+| startTCPServer defaultTCPServerConfig{
+| tcpListenAddr = SocketAddrInet 8080 inetLoopback,
+| tcpServerWorker = \ tcp -> do
+|     i <- newBufferedInput defaultChunkSize tcp 
+|     o <- newBufferedOutput defaultChunkSize tcp
+|     forever $ readBuffer i >>= writeBuffer o >> flushBuffer o
+| }
+| :}
+```
+
+## Dev guide
+
++ GHC(>=8.10.2) 
++ cabal-install(>=3.4)
+
+```bash
+# get code
+git clone --recursive git@github.com:haskell-Z/z-io.git 
+cd z-data
+# build
+cabal build
+# test
+cabal run Z-IO-Test
+# install 
+cabal install
+# generate document
+cabal haddock
+```
diff --git a/Z-IO.cabal b/Z-IO.cabal
new file mode 100644
--- /dev/null
+++ b/Z-IO.cabal
@@ -0,0 +1,253 @@
+name:                       Z-IO
+version:                    0.1.0.0
+synopsis:                   A simple and high performance IO toolkit for Haskell
+description:                This package provides a simple and high performance IO toolkit for Haskell, including
+                            packed vectors, unicode texts, socket, file system, timers and more!
+license:                    BSD3
+license-file:               LICENSE
+author:                     Dong Han, Tao He
+maintainer:                 winterland1989@gmail.com
+copyright:                  (c) Dong Han, 2017-2020
+                            (c) Tao He, 2017-2019
+category:                   Data
+build-type:                 Simple
+cabal-version:              >=1.10
+homepage:                   https://github.com/haskell-Z/Z-IO
+bug-reports:                https://github.com/haskell-Z/Z-IO/issues
+
+extra-source-files:         ChangeLog.md
+                            README.md
+                            LICENSE
+
+                            include/hs_uv.h
+                            cbits/hs_uv_base.c
+                            cbits/hs_uv_stream.c
+                            cbits/hs_uv_udp.c
+                            cbits/hs_uv_file.c
+                            cbits/hs_uv_dns.c
+
+                            -- libuv C sources (for Windows)
+                            third_party/libuv/include/uv.h
+                            third_party/libuv/include/uv/win.h
+                            third_party/libuv/include/uv/tree.h
+                            third_party/libuv/include/uv/errno.h
+                            third_party/libuv/include/uv/threadpool.h
+                            third_party/libuv/include/uv/version.h
+                            third_party/libuv/include/uv/stdint-msvc2008.h
+
+                            third_party/libuv/src/fs-poll.c
+                            third_party/libuv/src/heap-inl.h
+                            third_party/libuv/src/idna.c
+                            third_party/libuv/src/idna.h
+                            third_party/libuv/src/inet.c
+                            third_party/libuv/src/queue.h
+                            third_party/libuv/src/strscpy.c
+                            third_party/libuv/src/strscpy.h
+                            third_party/libuv/src/threadpool.c
+                            third_party/libuv/src/timer.c
+                            third_party/libuv/src/uv-common.c
+                            third_party/libuv/src/uv-common.h
+                            third_party/libuv/src/uv-data-getter-setters.c
+                            third_party/libuv/src/version.c
+
+                            third_party/libuv/src/win/async.c
+                            third_party/libuv/src/win/atomicops-inl.h
+                            third_party/libuv/src/win/core.c
+                            third_party/libuv/src/win/detect-wakeup.c
+                            third_party/libuv/src/win/dl.c
+                            third_party/libuv/src/win/error.c
+                            third_party/libuv/src/win/fs.c
+                            third_party/libuv/src/win/fs-event.c
+                            third_party/libuv/src/win/getaddrinfo.c
+                            third_party/libuv/src/win/getnameinfo.c
+                            third_party/libuv/src/win/handle.c
+                            third_party/libuv/src/win/handle-inl.h
+                            third_party/libuv/src/win/internal.h
+                            third_party/libuv/src/win/loop-watcher.c
+                            third_party/libuv/src/win/pipe.c
+                            third_party/libuv/src/win/poll.c
+                            third_party/libuv/src/win/process.c
+                            third_party/libuv/src/win/process-stdio.c
+                            third_party/libuv/src/win/req-inl.h
+                            third_party/libuv/src/win/signal.c
+                            third_party/libuv/src/win/snprintf.c
+                            third_party/libuv/src/win/stream.c
+                            third_party/libuv/src/win/stream-inl.h
+                            third_party/libuv/src/win/tcp.c
+                            third_party/libuv/src/win/thread.c
+                            third_party/libuv/src/win/tty.c
+                            third_party/libuv/src/win/udp.c
+                            third_party/libuv/src/win/util.c
+                            third_party/libuv/src/win/winapi.c
+                            third_party/libuv/src/win/winapi.h
+                            third_party/libuv/src/win/winsock.c
+                            third_party/libuv/src/win/winsock.h
+
+source-repository head
+    type:     git
+    location: git://github.com/haskell-Z/Z-IO.git
+
+flag no-pkg-config
+    description: Don't use pkg-config to check for library dependences
+    default: False
+    manual: True
+
+flag integer-simple
+    description:
+        Use the [simple integer library](http://hackage.haskell.org/package/integer-simple)
+        instead of [integer-gmp](http://hackage.haskell.org/package/integer-gmp)
+    default: False
+    manual: False
+
+library
+    exposed-modules:        Z.IO.FileSystem
+                            Z.IO.FileSystem.Threaded
+                            Z.IO.Network.SocketAddr
+                            Z.IO.Network.DNS
+                            Z.IO.Network.TCP
+                            Z.IO.Network.UDP
+                            Z.IO.Network.IPC
+                            Z.IO.Network
+                            Z.IO.Buffered
+                            Z.IO.Exception
+                            Z.IO.LowResTimer
+                            Z.IO.Logger
+                            Z.IO.Resource
+                            Z.IO.StdStream
+
+                            Z.IO.UV.Errno
+                            Z.IO.UV.FFI
+                            Z.IO.UV.Manager
+                            Z.IO.UV.Win
+
+    build-depends:          base                    >= 4.12 && <5.0
+                          , Z-Data                  >= 0.1.1 && < 0.2
+                          , ghc-prim                >= 0.6.1 && < 0.6.2
+                          , primitive               >= 0.7.1 && < 0.7.2
+                          , exceptions              == 0.10.*
+                          , scientific              == 0.3.*
+                          , hashable                == 1.3.*
+                          , case-insensitive        == 1.2.*
+                          , time                    >= 1.8 && < 2.0
+                          , deepseq                 >= 1.4 && < 1.5
+                          , QuickCheck              >= 2.10
+                          , stm                     == 2.5.*
+                          , unordered-containers    == 0.2.*
+                          , tagged                  == 0.8.*
+
+    if flag(integer-simple)
+        cpp-options:        -DINTEGER_SIMPLE
+        build-depends:      integer-simple >= 0.1 && < 0.5
+    else
+        cpp-options:        -DINTEGER_GMP
+        build-depends:      integer-gmp >= 0.2 && < 1.1
+
+    include-dirs:           include
+    includes:               hs_uv.h
+    install-includes:       hs_uv.h
+    cc-options:             -march=native
+    c-sources:              cbits/hs_uv_base.c
+                            cbits/hs_uv_stream.c
+                            cbits/hs_uv_udp.c
+                            cbits/hs_uv_file.c
+                            cbits/hs_uv_dns.c
+
+    -- Note: The c-sources list is taken from libuv's Makefile.mingw, needs to be
+    -- updated when we bump up libuv's version.
+    -- header files are ignored here, otherwise will confuse linker
+    if os(windows)
+        c-sources:          third_party/libuv/src/fs-poll.c
+                            -- third_party/libuv/src/heap-inl.h
+                            third_party/libuv/src/idna.c
+                            third_party/libuv/src/inet.c
+                            -- third_party/libuv/src/queue.h
+                            third_party/libuv/src/strscpy.c
+                            -- third_party/libuv/src/strscpy.h
+                            third_party/libuv/src/threadpool.c
+                            third_party/libuv/src/timer.c
+                            third_party/libuv/src/uv-data-getter-setters.c
+                            third_party/libuv/src/uv-common.c
+                            -- third_party/libuv/src/uv-common.h
+                            third_party/libuv/src/version.c
+
+                            third_party/libuv/src/win/async.c
+                            -- third_party/libuv/src/win/atomicops-inl.h
+                            third_party/libuv/src/win/core.c
+                            third_party/libuv/src/win/detect-wakeup.c
+                            third_party/libuv/src/win/dl.c
+                            third_party/libuv/src/win/error.c
+                            third_party/libuv/src/win/fs-event.c
+                            third_party/libuv/src/win/fs.c
+                            third_party/libuv/src/win/getaddrinfo.c
+                            third_party/libuv/src/win/getnameinfo.c
+                            third_party/libuv/src/win/handle.c
+                            -- third_party/libuv/src/win/handle-inl.h
+                            -- third_party/libuv/src/win/internal.h
+                            third_party/libuv/src/win/loop-watcher.c
+                            third_party/libuv/src/win/pipe.c
+                            third_party/libuv/src/win/poll.c
+                            third_party/libuv/src/win/process-stdio.c
+                            third_party/libuv/src/win/process.c
+                            -- third_party/libuv/src/win/req-inl.h
+                            third_party/libuv/src/win/signal.c
+                            third_party/libuv/src/win/stream.c
+                            -- third_party/libuv/src/win/stream-inl.h
+                            third_party/libuv/src/win/tcp.c
+                            third_party/libuv/src/win/thread.c
+                            third_party/libuv/src/win/tty.c
+                            third_party/libuv/src/win/udp.c
+                            third_party/libuv/src/win/util.c
+                            third_party/libuv/src/win/winapi.c
+                            -- third_party/libuv/src/win/winapi.h
+                            third_party/libuv/src/win/winsock.c
+                            -- third_party/libuv/src/win/winsock.h
+
+        cc-options:         -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600 -march=native
+        cpp-options:        -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600
+        include-dirs:       third_party/libuv/include
+                            third_party/libuv/src
+        -- The C runtime dependencies are imposed by libuv.
+        extra-libraries:    psapi, Iphlpapi, userenv, Ws2_32
+
+    else
+        if flag(no-pkg-config)
+            extra-libraries:    uv
+        else
+            pkgconfig-depends:  libuv >= 1.14
+
+    default-language:       Haskell2010
+    build-tools:            hsc2hs, hspec-discover
+    ghc-options:            -Wall
+
+test-suite Z-IO-Test
+    type:                   exitcode-stdio-1.0
+    main-is:                Spec.hs
+    hs-source-dirs:         test/
+    build-depends:          base
+                          , Z-IO
+                          , Z-Data
+                          , hspec                   >= 2.5.4
+                          , hashable
+                          , HUnit
+                          , QuickCheck              >= 2.10
+                          , quickcheck-instances
+                          , word8
+                          , scientific
+                          , primitive
+
+    other-modules:          Z.IO.FileSystemSpec
+                            Z.IO.FileSystem.ThreadedSpec
+                            Z.IO.LowResTimerSpec
+                            Z.IO.ResourceSpec
+                            Z.IO.UDPSpec
+
+    ghc-options:            -threaded
+    default-language:       Haskell2010
+
+    if flag(integer-simple)
+        cpp-options: -DINTEGER_SIMPLE
+        build-depends: integer-simple >= 0.1 && < 0.5
+    else
+        cpp-options: -DINTEGER_GMP
+        build-depends: integer-gmp >= 0.2 && < 1.1
+
diff --git a/Z/IO/Buffered.hs b/Z/IO/Buffered.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Buffered.hs
@@ -0,0 +1,344 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE ImplicitParams #-}
+
+{-|
+Module      : Z.IO.Buffered
+Description : Buffered IO interface
+Copyright   : (c) Dong Han, 2017-2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provide buffered IO interface.
+
+-}
+
+module Z.IO.Buffered
+  ( -- * Input & Output device
+    Input(..), Output(..)
+    -- * Buffered Input
+  , BufferedInput
+  , newBufferedInput
+  , readBuffer
+  , unReadBuffer
+  , readParser
+  , readExactly
+  , readToMagic, readToMagic'
+  , readLine, readLine'
+  , readAll, readAll'
+    -- * Buffered Output
+  , BufferedOutput
+  , newBufferedOutput
+  , writeBuffer
+  , writeBuilder
+  , flushBuffer
+    -- * Exceptions
+  , ShortReadException(..)
+    -- * common buffer size
+  , V.defaultChunkSize
+  , V.smallChunkSize
+  ) where
+
+import           Control.Monad
+import           Control.Monad.Primitive     (ioToPrim, primToIO)
+import           Control.Monad.ST
+import           Data.IORef
+import           Data.Primitive.PrimArray
+import           Data.Typeable
+import           Data.Word
+import           Foreign.Ptr
+import           Z.Data.Array
+import qualified Z.Data.Builder.Base       as B
+import qualified Z.Data.Parser             as P
+import qualified Z.Data.Vector             as V
+import qualified Z.Data.Vector.Base        as V
+import           Z.Data.PrimRef.PrimIORef
+import           Z.Foreign
+import           Z.IO.Exception
+
+-- | Input device
+--
+-- 'readInput' should return 0 on EOF.
+--
+class Input i where
+    readInput :: HasCallStack => i -> Ptr Word8 -> Int -> IO Int
+
+-- | Output device
+--
+-- 'writeOutput' should not return until all data are written (may not
+-- necessarily flushed to hardware, that should be done in device specific way).
+--
+class Output o where
+    writeOutput :: HasCallStack => o -> Ptr Word8 -> Int -> IO ()
+
+-- | Input device with buffer, NOT THREAD SAFE!
+--
+-- * A 'BufferedInput' should not be used in multiple threads, there's no locking mechanism to protect
+--   buffering state.
+--
+-- * A 'Input' device should only be used with a single 'BufferedInput', If multiple 'BufferedInput' s
+--   are opened on a same 'Input' device, the behaviour is undefined.
+--
+data BufferedInput i = BufferedInput
+    { bufInput    :: i
+    , bufPushBack :: {-# UNPACK #-} !(IORef V.Bytes)
+    , inputBuffer :: {-# UNPACK #-} !(IORef (MutablePrimArray RealWorld Word8))
+    }
+
+-- | Output device with buffer, NOT THREAD SAFE!
+--
+-- * A 'BufferedOutput' should not be used in multiple threads, there's no locking mechanism to protect
+--   buffering state.
+--
+-- * A 'Output' device should only be used with a single 'BufferedOutput', If multiple 'BufferedOutput' s
+--   are opened on a same 'BufferedOutput' device, the output will be interleaved.
+--
+data BufferedOutput o = BufferedOutput
+    { bufOutput     :: o
+    , bufIndex      :: {-# UNPACK #-} !Counter
+    , outputBuffer  :: {-# UNPACK #-} !(MutablePrimArray RealWorld Word8)
+    }
+
+-- | Open a new buffered input with given buffer size, e.g. 'V.defaultChunkSize'.
+newBufferedInput :: Int     -- ^ Input buffer size
+                 -> input
+                 -> IO (BufferedInput input)
+newBufferedInput bufSiz i = do
+    pb <- newIORef V.empty
+    buf <- newPinnedPrimArray (max bufSiz 0)
+    inputBuffer <- newIORef buf
+    return (BufferedInput i pb inputBuffer)
+
+-- | Open a new buffered output with given buffer size, e.g. 'V.defaultChunkSize'.
+newBufferedOutput :: Int    -- ^ Output buffer size
+                  -> output
+                  -> IO (BufferedOutput output)
+newBufferedOutput bufSiz o = do
+    index <- newPrimIORef 0
+    buf <- newPinnedPrimArray (max bufSiz 0)
+    return (BufferedOutput o index buf)
+
+-- | Request bytes from 'BufferedInput'.
+--
+-- The buffering logic is quite simple:
+--
+-- If we have pushed back bytes, directly return it, otherwise we read using buffer size.
+-- If we read N bytes, and N is larger than half of the buffer size, then we freeze buffer and return,
+-- otherwise we copy buffer into result and reuse buffer afterward.
+--
+readBuffer :: (HasCallStack, Input i) => BufferedInput i -> IO V.Bytes
+readBuffer BufferedInput{..} = do
+    pb <- readIORef bufPushBack
+    if V.null pb
+    then do
+        rbuf <- readIORef inputBuffer
+        bufSiz <- getSizeofMutablePrimArray rbuf
+        l <- readInput bufInput (mutablePrimArrayContents rbuf) bufSiz
+        if l < bufSiz `quot` 2                -- read less than half size
+        then do
+            mba <- newPrimArray l              -- copy result into new array
+            copyMutablePrimArray mba 0 rbuf 0 l
+            ba <- unsafeFreezePrimArray mba
+            return $! V.fromArr ba 0 l
+        else do                                -- freeze buf into result
+            when (bufSiz /= 0) $ do
+                buf' <- newPinnedPrimArray bufSiz
+                writeIORef inputBuffer buf'
+            ba <- unsafeFreezePrimArray rbuf
+            return $! V.fromArr ba 0 l
+    else do
+        writeIORef bufPushBack V.empty
+        return pb
+
+-- | Read exactly N bytes
+--
+-- If EOF reached before N bytes read, a 'ShortReadException' will be thrown
+--
+readExactly :: (HasCallStack, Input i) => Int -> BufferedInput i -> IO V.Bytes
+readExactly n0 h0 = V.concat `fmap` (go h0 n0)
+  where
+    go h n = do
+        chunk <- readBuffer h
+        let l = V.length chunk
+        if l > n
+        then do
+            let (lastChunk, rest) = V.splitAt n chunk
+            unReadBuffer rest h
+            return [lastChunk]
+        else if l == n
+            then return [chunk]
+            else if l == 0
+                then
+                    throwIO (ShortReadException
+                        (IOEInfo "" "unexpected EOF reached" callStack))
+                else do
+                    chunks <- go h (n - l)
+                    return (chunk : chunks)
+
+-- | Read all chunks from a 'BufferedInput'.
+readAll :: (HasCallStack, Input i) => BufferedInput i -> IO [V.Bytes]
+readAll i = loop []
+  where
+    loop acc = do
+        chunk <- readBuffer i
+        if V.null chunk
+        then return $! reverse (chunk:acc)
+        else loop (chunk:acc)
+
+-- | Read all chunks from a 'BufferedInput', and concat chunks together.
+readAll' :: (HasCallStack, Input i) => BufferedInput i -> IO V.Bytes
+readAll' i = V.concat <$> readAll i
+
+data ShortReadException = ShortReadException IOEInfo deriving (Show, Typeable)
+
+instance Exception ShortReadException where
+    toException = ioExceptionToException
+    fromException = ioExceptionFromException
+
+-- | Push bytes back into buffer.
+--
+unReadBuffer :: (HasCallStack, Input i) => V.Bytes -> BufferedInput i -> IO ()
+unReadBuffer pb' BufferedInput{..} = do
+    modifyIORef' bufPushBack $ \ pb -> pb' `V.append` pb
+
+-- | Read buffer and parse with 'Parser'.
+--
+-- This function will continuously draw data from input before parsing finish.
+readParser :: (HasCallStack, Input i) => P.Parser a -> BufferedInput i -> IO (V.Bytes, Either P.ParseError a)
+readParser p i = do
+    bs <- readBuffer i
+    P.parseChunks p (readBuffer i) bs
+
+-- | Read until reach a magic bytes
+--
+-- If EOF is reached before meet a magic byte, partial bytes are returned.
+readToMagic :: (HasCallStack, Input i) => Word8 -> BufferedInput i -> IO V.Bytes
+readToMagic magic0 h0 = V.concat `fmap` (go h0 magic0)
+  where
+    go h magic = do
+        chunk <- readBuffer h
+        if V.null chunk
+        then return []
+        else case V.elemIndex magic chunk of
+            Just i -> do
+                let (lastChunk, rest) = V.splitAt (i+1) chunk
+                unReadBuffer rest h
+                return [lastChunk]
+            Nothing -> do
+                chunks <- go h magic
+                return (chunk : chunks)
+
+-- | Read until reach a magic bytes
+--
+-- If EOF is reached before meet a magic byte, a 'ShortReadException' will be thrown.
+readToMagic' :: (HasCallStack, Input i) => Word8 -> BufferedInput i -> IO V.Bytes
+readToMagic' magic0 h0 = V.concat `fmap` (go h0 magic0)
+  where
+    go h magic = do
+        chunk <- readBuffer h
+        if V.null chunk
+        then throwIO (ShortReadException
+            (IOEInfo "" "unexpected EOF reached" callStack))
+        else case V.elemIndex magic chunk of
+            Just i -> do
+                let (lastChunk, rest) = V.splitAt (i+1) chunk
+                unReadBuffer rest h
+                return [lastChunk]
+            Nothing -> do
+                chunks <- go h magic
+                return (chunk : chunks)
+
+-- | Read to a linefeed ('\n' or '\r\n'), return 'Bytes' before it.
+--
+-- If EOF is reached before meet a magic byte, partial line is returned.
+readLine :: (HasCallStack, Input i) => BufferedInput i -> IO V.Bytes
+readLine i = do
+    bs@(V.PrimVector arr s l) <- readToMagic 10 i
+    if l == 0
+    then return bs
+    else return $ case bs `V.indexMaybe` (l-2) of
+        Nothing -> V.PrimVector arr s (l-1)
+        Just r | r == 13   -> V.PrimVector arr s (l-2)
+               | otherwise -> V.PrimVector arr s (l-1)
+
+-- | Read to a linefeed ('\n' or '\r\n'), return 'Bytes' before it.
+--
+-- If EOF reached before meet a '\n', a 'ShortReadException' will be thrown.
+readLine' :: (HasCallStack, Input i) => BufferedInput i -> IO V.Bytes
+readLine' i = do
+    bs@(V.PrimVector arr s l) <- readToMagic' 10 i
+    if l == 0
+    then return bs
+    else return $ case bs `V.indexMaybe` (l-2) of
+        Nothing -> V.PrimVector arr s (l-1)
+        Just r | r == 13   -> V.PrimVector arr s (l-2)
+               | otherwise -> V.PrimVector arr s (l-1)
+
+--------------------------------------------------------------------------------
+
+-- | Write 'V.Bytes' into buffered handle.
+--
+-- Copy 'V.Bytes' to buffer if it can hold, otherwise
+-- write both buffer(if not empty) and 'V.Bytes'.
+--
+writeBuffer :: (Output o) => BufferedOutput o -> V.Bytes -> IO ()
+writeBuffer o@BufferedOutput{..} v@(V.PrimVector ba s l) = do
+    i <- readPrimIORef bufIndex
+    bufSiz <- getSizeofMutablePrimArray outputBuffer
+    if i + l <= bufSiz
+    then do
+        -- current buffer can hold it
+        copyPrimArray outputBuffer i ba s l   -- copy to buffer
+        writePrimIORef bufIndex (i+l)              -- update index
+    else do
+        if (i > 0)
+        then do
+            -- flush the buffer
+            withMutablePrimArrayContents outputBuffer $ \ ptr -> writeOutput bufOutput ptr i
+            writePrimIORef bufIndex 0
+
+            writeBuffer o v -- try write to buffer again
+        else
+            withPrimVectorSafe v (writeOutput bufOutput)
+
+
+-- | Write 'V.Bytes' into buffered handle.
+--
+-- Copy 'V.Bytes' to buffer if it can hold, otherwise
+-- write both buffer(if not empty) and 'V.Bytes'.
+--
+writeBuilder :: (Output o) => BufferedOutput o -> B.Builder a -> IO ()
+writeBuilder BufferedOutput{..} (B.Builder b) = do
+    i <- readPrimIORef bufIndex
+    originBufSiz <- getSizeofMutablePrimArray outputBuffer
+    _ <- primToIO (b (B.OneShotAction action) (lastStep originBufSiz) (B.Buffer outputBuffer i))
+    return ()
+  where
+    action :: V.Bytes -> ST RealWorld ()
+    action bytes = ioToPrim (withPrimVectorSafe bytes (writeOutput bufOutput))
+
+    lastStep :: Int -> a -> B.BuildStep RealWorld
+    lastStep originBufSiz _ (B.Buffer buf offset)
+        | sameMutablePrimArray buf outputBuffer = ioToPrim $ do
+            writePrimIORef bufIndex offset   -- record new buffer index
+            return []
+        | offset >= originBufSiz = ioToPrim $ do
+            withMutablePrimArrayContents buf $ \ ptr -> writeOutput bufOutput ptr offset
+            writePrimIORef bufIndex 0
+            return [] -- to match 'BuildStep' return type
+        | otherwise = ioToPrim $ do
+            copyMutablePrimArray outputBuffer 0 buf 0 offset
+            writePrimIORef bufIndex offset
+            return [] -- to match 'BuildStep' return type
+
+-- | Flush the buffer into output device(if not empty).
+--
+flushBuffer :: Output f => BufferedOutput f -> IO ()
+flushBuffer BufferedOutput{..} = do
+    i <- readPrimIORef bufIndex
+    withMutablePrimArrayContents outputBuffer $ \ ptr -> writeOutput bufOutput ptr i
+    writePrimIORef bufIndex 0
diff --git a/Z/IO/Exception.hs b/Z/IO/Exception.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Exception.hs
@@ -0,0 +1,276 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-|
+Module      : Z.IO.Exception
+Description : Extensible IO exceptions
+Copyright   : (c) Dong Han, 2017-2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module implemented extensible io exception following approach described in /An Extensible Dynamically-Typed
+Hierarchy of Exceptions/ by Simon Marlow. The implementation in this module has simplified to meet common need.
+User who want to catch certain type of exceptions can directly use exception types this module provide,
+which are modeled after @IOErrorType@ from "GHC.IO.Exception".
+
+
+Functions from this package will throw exceptions from this module only instead of the old 'IOError' on IO exceptions.
+Exceptions from this module contain 'IOEInfo' which is pretty detailed, but this also require user of this module
+do some extra work to keep error message's quality(provide CallStack, device informations, etc.).
+New defined IO exceptions are encouraged to include a 'IOEInfo', since it helps a lot when debugging.
+
+Example for library author defining new io exception:
+
+@
+  data MyNetworkException = MyNetworkException IOEInfo ... deriving (Show, Typeable)
+  instance Exception MyNetworkException where
+        toException = ioExceptionToException
+        fromException = ioExceptionFromException
+@
+
+If you're dealing with OS's errno directly, you should convert the errno to libuv's errno in C side with
+'uv_translate_sys_error' from @hs_uv.h@, then use 'throwUVIfMinus/throwUVError' from this module.
+
+-}
+
+module Z.IO.Exception
+  ( -- * The 'SomeIOException' type
+    SomeIOException(..)
+  , ioExceptionToException
+  , ioExceptionFromException
+    -- * Builtin io exception types
+  , IOEInfo(..)
+  , AlreadyExists(..)
+  , NoSuchThing(..)
+  , ResourceBusy(..)
+  , ResourceExhausted(..)
+  , EOF(..)
+  , IllegalOperation(..)
+  , PermissionDenied(..)
+  , UnsatisfiedConstraints(..)
+  , SystemError(..)
+  , ProtocolError(..)
+  , OtherError(..)
+  , InvalidArgument(..)
+  , InappropriateType(..)
+  , HardwareFault(..)
+  , UnsupportedOperation(..)
+  , TimeExpired(..)
+  , ResourceVanished(..)
+  , Interrupted(..)
+    -- * Throw io exceptions
+  , throwOOMIfNull
+  , throwUVIfMinus
+  , throwUVIfMinus_
+  , throwECLOSED
+  , throwECLOSEDSTM
+  , throwUVError
+    -- * Re-exports
+  , module Control.Exception
+  , HasCallStack
+  , CallStack
+  , callStack
+  ) where
+
+import Control.Exception hiding (IOException)
+import Control.Monad
+import Control.Concurrent.STM
+import Data.Typeable
+import Foreign.Ptr
+import Foreign.C.Types
+import GHC.Stack
+import Z.IO.UV.Errno
+
+-- | The root type of all io exceptions, you can catch all io exception by catching this root type.
+--
+data SomeIOException = forall e . Exception e => SomeIOException e
+    deriving Typeable
+
+instance Show SomeIOException where
+    show (SomeIOException e) = show e
+
+instance Exception SomeIOException
+
+ioExceptionToException :: Exception e => e -> SomeException
+ioExceptionToException = toException . SomeIOException
+
+ioExceptionFromException :: Exception e => SomeException -> Maybe e
+ioExceptionFromException x = do
+    SomeIOException a <- fromException x
+    cast a
+
+#define IOE(e) data e = e IOEInfo deriving (Show, Typeable);  \
+               instance Exception e where                     \
+                   { toException = ioExceptionToException     \
+                   ; fromException = ioExceptionFromException \
+                   }
+IOE(AlreadyExists)
+IOE(NoSuchThing)
+IOE(ResourceBusy)
+IOE(ResourceExhausted)
+IOE(EOF)
+IOE(IllegalOperation)
+IOE(PermissionDenied)
+IOE(UnsatisfiedConstraints)
+IOE(SystemError)
+IOE(ProtocolError)
+IOE(OtherError)
+IOE(InvalidArgument)
+IOE(InappropriateType)
+IOE(HardwareFault)
+IOE(UnsupportedOperation)
+IOE(TimeExpired)
+IOE(ResourceVanished)
+IOE(Interrupted)
+
+--------------------------------------------------------------------------------
+
+-- | Throw 'ResourceExhausted' if allocation return a 'nullPtr'.
+--
+throwOOMIfNull :: HasCallStack
+               => IO (Ptr a)    -- ^ the allocation action
+               -> IO (Ptr a)
+throwOOMIfNull f = do
+    addr <- f
+    if addr == nullPtr
+        then throwIO (ResourceExhausted (IOEInfo "OOM" "out of memory when doing allocation" callStack))
+        else return addr
+
+-- | Throw appropriate IO exception if return value < 0 (libuv's convention).
+--
+throwUVIfMinus :: (HasCallStack, Integral a)
+               => IO a    -- ^ the IO action
+               -> IO a
+throwUVIfMinus f = do
+    errno <- f
+    let errno' = fromIntegral errno
+    if errno' < 0
+        then do
+            name <- uvErrName errno'
+            desc <- uvStdError errno'
+            throwUVError errno' (IOEInfo name desc callStack)
+        else return errno
+
+-- | Throw appropriate IO exception if return value < 0, otherwise ignore the result.
+--
+throwUVIfMinus_ :: (HasCallStack, Integral a)
+                => IO a    -- ^ the IO action
+                -> IO ()
+throwUVIfMinus_ f = do
+    errno <- f
+    let errno' = fromIntegral errno
+    when (errno' < 0) $ do
+        name <- uvErrName errno'
+        desc <- uvStdError errno'
+        throwUVError errno' (IOEInfo name desc callStack)
+
+-- | Throw 'E.ResourceVanished' with name 'ECLOSED' and description 'resource is closed'.
+--
+throwECLOSED :: HasCallStack => IO a
+throwECLOSED = throwIO (ResourceVanished
+    (IOEInfo "ECLOSED" "resource is closed" callStack))
+
+throwECLOSEDSTM :: HasCallStack => STM a
+throwECLOSEDSTM = throwSTM (ResourceVanished
+    (IOEInfo "ECLOSED" "resource is closed" callStack))
+
+--------------------------------------------------------------------------------
+
+-- | IO exceptions informations.
+--
+data IOEInfo = IOEInfo
+    { ioeName        :: String      -- ^ the errno name, e.g. EADDRINUSE, etc. empty if no errno.
+    , ioeDescription :: String      -- ^ description for this io error, can be errno description, or some custom description if no errno.
+    , ioeCallStack   :: CallStack   -- ^ lightweight partial call-stack
+    }
+
+instance Show IOEInfo where
+    show (IOEInfo errno desc cstack) =
+         "{name:" ++ errno ++
+         ", description:" ++ desc ++
+         ", callstack:" ++ prettyCallStack cstack ++ "}"
+
+throwUVError :: CInt -> IOEInfo -> IO a
+throwUVError e info = case e of
+    UV_EOF             -> throwIO (EOF                     info)
+    UV_E2BIG           -> throwIO (ResourceExhausted       info)
+    UV_EACCES          -> throwIO (PermissionDenied        info)
+    UV_EADDRINUSE      -> throwIO (ResourceBusy            info)
+    UV_EADDRNOTAVAIL   -> throwIO (UnsupportedOperation    info)
+    UV_EAFNOSUPPORT    -> throwIO (UnsupportedOperation    info)
+    UV_EAGAIN          -> throwIO (ResourceExhausted       info)
+    UV_EAI_ADDRFAMILY  -> throwIO (UnsupportedOperation    info)
+    UV_EAI_AGAIN       -> throwIO (ResourceExhausted       info)
+    UV_EAI_BADFLAGS    -> throwIO (UnsupportedOperation    info)
+    UV_EAI_BADHINTS    -> throwIO (UnsupportedOperation    info)
+    UV_EAI_CANCELED    -> throwIO (ResourceVanished        info)
+    UV_EAI_FAIL        -> throwIO (OtherError              info)
+    UV_EAI_FAMILY      -> throwIO (UnsupportedOperation    info)
+    UV_EAI_MEMORY      -> throwIO (ResourceExhausted       info)
+    UV_EAI_NODATA      -> throwIO (NoSuchThing             info)
+    UV_EAI_NONAME      -> throwIO (NoSuchThing             info)
+    UV_EAI_OVERFLOW    -> throwIO (InvalidArgument         info)
+    UV_EAI_PROTOCOL    -> throwIO (ProtocolError           info)
+    UV_EAI_SERVICE     -> throwIO (UnsupportedOperation    info)
+    UV_EAI_SOCKTYPE    -> throwIO (UnsupportedOperation    info)
+    UV_EALREADY        -> throwIO (AlreadyExists           info)
+    UV_EBADF           -> throwIO (InvalidArgument         info)
+    UV_EBUSY           -> throwIO (ResourceBusy            info)
+    UV_ECANCELED       -> throwIO (ResourceVanished        info)
+    UV_ECHARSET        -> throwIO (OtherError              info)
+    UV_ECONNABORTED    -> throwIO (ResourceVanished        info)
+    UV_ECONNREFUSED    -> throwIO (NoSuchThing             info)
+    UV_ECONNRESET      -> throwIO (ResourceVanished        info)
+    UV_EDESTADDRREQ    -> throwIO (InvalidArgument         info)
+    UV_EEXIST          -> throwIO (AlreadyExists           info)
+    UV_EFAULT          -> throwIO (OtherError              info)
+    UV_EFBIG           -> throwIO (PermissionDenied        info)
+    UV_EHOSTUNREACH    -> throwIO (NoSuchThing             info)
+    UV_EINTR           -> throwIO (Interrupted             info)
+    UV_EINVAL          -> throwIO (InvalidArgument         info)
+    UV_EIO             -> throwIO (HardwareFault           info)
+    UV_EISCONN         -> throwIO (AlreadyExists           info)
+    UV_EISDIR          -> throwIO (InappropriateType       info)
+    UV_ELOOP           -> throwIO (InvalidArgument         info)
+    UV_EMFILE          -> throwIO (ResourceExhausted       info)
+    UV_EMSGSIZE        -> throwIO (InvalidArgument         info)
+    UV_ENAMETOOLONG    -> throwIO (InvalidArgument         info)
+    UV_ENETDOWN        -> throwIO (ResourceVanished        info)
+    UV_ENETUNREACH     -> throwIO (NoSuchThing             info)
+    UV_ENFILE          -> throwIO (ResourceExhausted       info)
+    UV_ENOBUFS         -> throwIO (ResourceExhausted       info)
+    UV_ENODEV          -> throwIO (UnsupportedOperation    info)
+    UV_ENOENT          -> throwIO (NoSuchThing             info)
+    UV_ENOMEM          -> throwIO (ResourceExhausted       info)
+    UV_ENOPROTOOPT     -> throwIO (UnsupportedOperation    info)
+    UV_ENOSPC          -> throwIO (ResourceExhausted       info)
+    UV_ENOSYS          -> throwIO (UnsupportedOperation    info)
+    UV_ENOTCONN        -> throwIO (InvalidArgument         info)
+    UV_ENOTDIR         -> throwIO (InappropriateType       info)
+    UV_ENOTEMPTY       -> throwIO (UnsatisfiedConstraints  info)
+    UV_ENOTSOCK        -> throwIO (InvalidArgument         info)
+    UV_ENOTSUP         -> throwIO (UnsupportedOperation    info)
+    UV_EPERM           -> throwIO (PermissionDenied        info)
+    UV_EPIPE           -> throwIO (ResourceVanished        info)
+    UV_EPROTO          -> throwIO (ProtocolError           info)
+    UV_EPROTONOSUPPORT -> throwIO (ProtocolError           info)
+    UV_EPROTOTYPE      -> throwIO (ProtocolError           info)
+    UV_ERANGE          -> throwIO (UnsupportedOperation    info)
+    UV_EROFS           -> throwIO (PermissionDenied        info)
+    UV_ESHUTDOWN       -> throwIO (IllegalOperation        info)
+    UV_ESPIPE          -> throwIO (UnsupportedOperation    info)
+    UV_ESRCH           -> throwIO (NoSuchThing             info)
+    UV_ETIMEDOUT       -> throwIO (TimeExpired             info)
+    UV_ETXTBSY         -> throwIO (ResourceBusy            info)
+    UV_EXDEV           -> throwIO (UnsupportedOperation    info)
+    UV_UNKNOWN         -> throwIO (OtherError              info)
+    UV_ENXIO           -> throwIO (NoSuchThing             info)
+    UV_EMLINK          -> throwIO (ResourceExhausted       info)
+    _                  -> throwIO (OtherError              info)
diff --git a/Z/IO/FileSystem.hs b/Z/IO/FileSystem.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/FileSystem.hs
@@ -0,0 +1,363 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE MultiWayIf   #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-|
+Module      : Z.IO.FileSystem
+Description : Filesystem IO
+Copyright   : (c) Dong Han, 2017~2019
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provide IO operations related to filesystem, operations are implemented using unsafe FFIs, which should be prefered when the operations' estimated time is short(<1ms), which is much common on modern SSDs.
+
+-}
+
+module Z.IO.FileSystem
+  ( -- * regular file devices
+    File, initFile, readFile, writeFile
+    -- * opening constant
+  , FileMode(DEFAULT_MODE, S_IRWXU, S_IRUSR, S_IWUSR
+      , S_IXUSR, S_IRWXG, S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXO, S_IROTH
+      )
+  , FileFlag(O_APPEND, O_CREAT, O_DIRECT, O_DSYNC, O_EXCL
+      , O_EXLOCK, O_NOATIME, O_NOFOLLOW, O_RDONLY, O_RDWR, O_SYMLINK
+      , O_SYNC, O_TRUNC, O_WRONLY, O_RANDOM, O_SHORT_LIVED, O_SEQUENTIAL, O_TEMPORARY
+      )
+  -- * filesystem operations
+  , mkdir
+  , unlink
+  , mkdtemp
+  , rmdir
+  , DirEntType(..)
+  , scandir
+  , UVStat(..), UVTimeSpec(..)
+  , stat, lstat, fstat
+  , rename
+  , fsync, fdatasync
+  , ftruncate
+  , UVCopyFileFlag(COPYFILE_DEFAULT, COPYFILE_EXCL, COPYFILE_FICLONE)
+  , copyfile
+  , UVAccessMode(F_OK, R_OK, W_OK, X_OK)
+  , AccessResult(..)
+  , access
+  , chmod, fchmod
+  , utime, futime
+  , UVSymlinkFlag(SYMLINK_DEFAULT, SYMLINK_DIR, SYMLINK_JUNCTION)
+  , link, symlink
+  , readlink, realpath
+  ) where
+
+import           Control.Monad
+import           Data.Word
+import           Data.Int
+import           Data.IORef
+import           Foreign.Ptr
+import           Foreign.Storable               (peekElemOff)
+import           Foreign.Marshal.Alloc          (allocaBytes)
+import           Z.Data.CBytes                 as CBytes
+import           Z.Foreign
+import           Z.IO.Buffered
+import           Z.IO.Exception
+import           Z.IO.Resource
+import           Z.IO.UV.Errno
+import           Z.IO.UV.FFI
+import           Prelude hiding (writeFile, readFile)
+
+--------------------------------------------------------------------------------
+-- File
+
+-- | 'File' and its operations are NOT thread safe, use 'MVar' 'File' in multiple threads
+--
+-- libuv implements read and write method with both implict and explict offset capable.
+-- Implict offset interface is provided by 'Input' \/ 'Output' instances.
+-- Explict offset interface is provided by 'readFile' \/ 'writeFile'.
+--
+data File =  File  {-# UNPACK #-} !UVFD      -- ^ the file
+                   {-# UNPACK #-} !(IORef Bool)  -- ^ closed flag
+
+-- | If fd is -1 (closed), throw 'ResourceVanished' ECLOSED.
+checkFileClosed :: HasCallStack => File -> (UVFD -> IO a) -> IO a
+checkFileClosed (File fd closedRef) f = do
+    closed <- readIORef closedRef
+    if closed then throwECLOSED else f fd
+
+instance Input File where
+    -- readInput :: HasCallStack => File -> Ptr Word8 -> Int -> IO Int
+    -- use -1 offset to use fd's default offset
+    readInput f buf bufSiz = readFile f buf bufSiz (-1)
+
+-- | Read file with given offset
+--
+-- Read length may be smaller than buffer size.
+readFile :: HasCallStack
+           => File
+           -> Ptr Word8 -- ^ buffer
+           -> Int       -- ^ buffer size
+           -> Int64     -- ^ file offset, pass -1 to use default(system) offset
+           -> IO Int    -- ^ read length
+readFile uvf buf bufSiz off =
+    checkFileClosed uvf $ \ fd -> throwUVIfMinus $ hs_uv_fs_read fd buf bufSiz off
+
+instance Output File where
+    writeOutput f buf bufSiz = writeFile f buf bufSiz (-1)
+
+-- | Write buffer to file
+--
+-- This function will loop until all bytes are written.
+writeFile :: HasCallStack
+            => File
+            -> Ptr Word8 -- ^ buffer
+            -> Int       -- ^ buffer size
+            -> Int64     -- ^ file offset, pass -1 to use default(system) offset
+            -> IO ()
+writeFile uvf buf0 bufSiz0 off0 =
+    checkFileClosed uvf $ \fd ->  if off0 == -1 then go fd buf0 bufSiz0
+                                                else go' fd buf0 bufSiz0 off0
+  where
+    go fd !buf !bufSiz = do
+        written <- throwUVIfMinus (hs_uv_fs_write fd buf bufSiz (-1))
+        when (written < bufSiz)
+            (go fd (buf `plusPtr` written) (bufSiz-written))
+
+    go' fd !buf !bufSiz !off = do
+        written <- throwUVIfMinus (hs_uv_fs_write fd buf bufSiz off)
+        when (written < bufSiz) $
+            go' fd (buf `plusPtr` written)
+                   (bufSiz-written)
+                   (off+fromIntegral written)
+
+--------------------------------------------------------------------------------
+
+-- | init a file 'Resource', which open a file when used.
+--
+-- Resource closing is thread safe, on some versions of OSX, repeatly open and close same file 'Resource' may
+-- result in shared memory object error, use 'O_CREAT' to avoid that.
+initFile :: HasCallStack
+           => CBytes
+           -> FileFlag        -- ^ Opening flags, e.g. 'O_CREAT' @.|.@ 'O_RDWR'
+           -> FileMode      -- ^ Sets the file mode (permission and sticky bits),
+                              -- but only if the file was created, see 'DEFAULT_MODE'.
+           -> Resource File
+initFile path flags mode =
+    initResource
+        (do !fd <- withCBytes path $ \ p ->
+                throwUVIfMinus $ hs_uv_fs_open p flags mode
+            File fd <$> newIORef False)
+        (\ (File fd closedRef) -> do
+            closed <- readIORef closedRef
+            unless closed $ do
+                throwUVIfMinus_ (hs_uv_fs_close fd)
+                writeIORef closedRef True)
+
+--------------------------------------------------------------------------------
+
+-- | Equivalent to <http://linux.die.net/man/2/mkdir mkdir(2)>.
+--
+-- Note mode is currently not implemented on Windows.
+mkdir :: HasCallStack => CBytes -> FileMode -> IO ()
+mkdir path mode = throwUVIfMinus_ . withCBytes path $ \ p ->
+     hs_uv_fs_mkdir p mode
+
+-- | Equivalent to <http://linux.die.net/man/2/unlink unlink(2)>.
+unlink :: HasCallStack => CBytes -> IO ()
+unlink path = throwUVIfMinus_ (withCBytes path hs_uv_fs_unlink)
+
+
+-- | Equivalent to <mkdtemp http://linux.die.net/man/3/mkdtemp>
+--
+-- Creates a temporary directory in the most secure manner possible.
+-- There are no race conditions in the directory’s creation.
+-- The directory is readable, writable, and searchable only by the creating user ID.
+-- The user of mkdtemp() is responsible for deleting the temporary directory and
+-- its contents when done with it.
+--
+-- Note: the argument is the prefix of the temporary directory,
+-- so no need to add XXXXXX ending.
+--
+mkdtemp :: HasCallStack => CBytes -> IO CBytes
+mkdtemp path = do
+    let size = CBytes.length path
+    withCBytes path $ \ p ->
+        CBytes.create (size+7) $ \ p' -> do  -- we append "XXXXXX\NUL" in C
+            throwUVIfMinus_ (hs_uv_fs_mkdtemp p size p')
+            return (size+6)
+
+-- | Equivalent to <http://linux.die.net/man/2/rmdir rmdir(2)>.
+rmdir :: HasCallStack => CBytes -> IO ()
+rmdir path = throwUVIfMinus_ (withCBytes path hs_uv_fs_rmdir)
+
+-- | Equivalent to <http://linux.die.net/man/3/scandir scandir(3)>.
+--
+-- Note Unlike scandir(3), this function does not return the “.” and “..” entries.
+--
+-- Note On Linux, getting the type of an entry is only supported by some file systems (btrfs, ext2, ext3 and ext4 at the time of this writing), check the <http://linux.die.net/man/2/getdents getdents(2)> man page.
+scandir :: HasCallStack => CBytes -> IO [(CBytes, DirEntType)]
+scandir path = do
+    bracket
+        (withCBytes path $ \ p ->
+            allocPrimUnsafe $ \ dents ->
+                throwUVIfMinus (hs_uv_fs_scandir p dents))
+        (\ (dents, n) -> hs_uv_fs_scandir_cleanup dents n)
+        (\ (dents, n) -> forM [0..n-1] $ \ i -> do
+            dent <- peekElemOff dents i
+            (p, typ) <- peekUVDirEnt dent
+            let !typ' = fromUVDirEntType typ
+            !p' <- fromCString p
+            return (p', typ'))
+
+--------------------------------------------------------------------------------
+
+-- | Equivalent to <http://linux.die.net/man/2/stat stat(2)>
+stat :: HasCallStack => CBytes -> IO UVStat
+stat path = withCBytes path $ \ p ->
+     allocaBytes uvStatSize $ \ s -> do
+        throwUVIfMinus_ (hs_uv_fs_stat p s)
+        peekUVStat s
+
+-- | Equivalent to <http://linux.die.net/man/2/lstat lstat(2)>
+lstat :: HasCallStack => CBytes -> IO UVStat
+lstat path = withCBytes path $ \ p ->
+     allocaBytes uvStatSize $ \ s -> do
+        throwUVIfMinus_ (hs_uv_fs_lstat p s)
+        peekUVStat s
+
+-- | Equivalent to <http://linux.die.net/man/2/fstat fstat(2)>
+fstat :: HasCallStack => File -> IO UVStat
+fstat uvf = checkFileClosed uvf $ \ fd ->
+    allocaBytes uvStatSize $ \ s -> do
+        throwUVIfMinus_ (hs_uv_fs_fstat fd s)
+        peekUVStat s
+
+--------------------------------------------------------------------------------
+
+-- | Equivalent to <http://linux.die.net/man/2/rename rename(2)>.
+--
+-- Note On Windows if this function fails with UV_EBUSY, UV_EPERM or UV_EACCES, it will retry to rename the file up to four times with 250ms wait between attempts before giving up. If both path and new_path are existing directories this function will work only if target directory is empty.
+rename :: HasCallStack => CBytes -> CBytes -> IO ()
+rename path path' = throwUVIfMinus_ . withCBytes path $ \ p ->
+    withCBytes path' (hs_uv_fs_rename p)
+
+-- | Equivalent to <http://linux.die.net/man/2/fsync fsync(2)>.
+fsync :: HasCallStack => File -> IO ()
+fsync uvf = checkFileClosed uvf $ \ fd -> throwUVIfMinus_ $ hs_uv_fs_fsync fd
+
+-- | Equivalent to <http://linux.die.net/man/2/fdatasync fdatasync(2)>.
+fdatasync :: HasCallStack => File -> IO ()
+fdatasync uvf = checkFileClosed uvf $ \ fd -> throwUVIfMinus_ $ hs_uv_fs_fdatasync fd
+
+-- | Equivalent to <http://linux.die.net/man/2/ftruncate ftruncate(2)>.
+ftruncate :: HasCallStack => File -> Int64 -> IO ()
+ftruncate uvf off = checkFileClosed uvf $ \ fd -> throwUVIfMinus_ $ hs_uv_fs_ftruncate fd off
+
+-- | Copies a file from path to new_path.
+--
+-- Warning: If the destination path is created, but an error occurs while copying the data, then the destination path is removed. There is a brief window of time between closing and removing the file where another process could access the file.
+copyfile :: HasCallStack => CBytes -> CBytes -> UVCopyFileFlag -> IO ()
+copyfile path path' flag = throwUVIfMinus_ . withCBytes path $ \ p ->
+    withCBytes path' $ \ p' -> hs_uv_fs_copyfile p p' flag
+
+-- | Equivalent to <http://linux.die.net/man/2/access access(2)> on Unix.
+-- Windows uses GetFileAttributesW().
+access :: HasCallStack => CBytes -> UVAccessMode -> IO AccessResult
+access path mode = do
+     r <- withCBytes path $ \ p -> fromIntegral <$> hs_uv_fs_access p mode
+     if | r == 0           -> return AccessOK
+        | r == UV_ENOENT   -> return NoExistence
+        | r == UV_EACCES   -> return NoPermission
+        | otherwise        -> do
+            name <- uvErrName r
+            desc <- uvStdError r
+            throwUVError r (IOEInfo name desc callStack)
+
+-- | Equivalent to <http://linux.die.net/man/2/chmod chmod(2)>.
+chmod :: HasCallStack => CBytes -> FileMode -> IO ()
+chmod path mode = throwUVIfMinus_ . withCBytes path $ \ p -> hs_uv_fs_chmod p mode
+
+-- | Equivalent to <http://linux.die.net/man/2/fchmod fchmod(2)>.
+fchmod :: HasCallStack => File -> FileMode -> IO ()
+fchmod uvf mode = checkFileClosed uvf $ \ fd -> throwUVIfMinus_ $ hs_uv_fs_fchmod fd mode
+
+-- | Equivalent to <http://linux.die.net/man/2/utime utime(2)>.
+--
+-- libuv choose 'Double' type due to cross platform concerns, we only provide micro-second precision:
+--
+--   * second     = v
+--   * nanosecond = (v * 1000000) % 1000000 * 1000;
+--
+-- second and nanosecond are fields in 'UVTimeSpec' respectively.
+--
+-- Note libuv prior to v1.23.1 have issues which may result in nanosecond not set, 'futime' doesn't have
+-- that issue.
+utime :: HasCallStack
+      => CBytes
+      -> Double     -- ^ atime, i.e. access time
+      -> Double     -- ^ mtime, i.e. modify time
+      -> IO ()
+utime path atime mtime = throwUVIfMinus_ . withCBytes path $ \ p -> hs_uv_fs_utime p atime mtime
+
+-- | Equivalent to <http://linux.die.net/man/2/futime futime(2)>.
+--
+-- Same precision notes with 'utime'.
+futime :: HasCallStack => File -> Double -> Double -> IO ()
+futime uvf atime mtime = checkFileClosed uvf $ \ fd ->
+    throwUVIfMinus_ (hs_uv_fs_futime fd atime mtime)
+
+-- | Equivalent to <http://linux.die.net/man/2/link link(2)>.
+link :: HasCallStack => CBytes -> CBytes -> IO ()
+link path path' = throwUVIfMinus_ . withCBytes path $ \ p ->
+    withCBytes path' $ hs_uv_fs_link p
+
+-- | Equivalent to <http://linux.die.net/man/2/symlink symlink(2)>.
+--
+-- | Note On Windows the flags parameter can be specified to control how the symlink will be created.
+--
+--   * 'SYMLINK_DIR': indicates that path points to a directory.
+--   * 'SYMLINK_JUNCTION': request that the symlink is created using junction points.
+--
+-- On other platforms these flags are ignored.
+symlink :: HasCallStack => CBytes -> CBytes -> UVSymlinkFlag -> IO ()
+symlink path path' flag = throwUVIfMinus_ . withCBytes path $ \ p ->
+    withCBytes path' $ \ p' -> hs_uv_fs_symlink p p' flag
+
+-- | Equivalent to <http://linux.die.net/man/2/readlink readlink(2)>.
+readlink :: HasCallStack => CBytes -> IO CBytes
+readlink path = do
+    bracket
+        (withCBytes path $ \ p ->
+            allocPrimUnsafe $ \ p' ->
+                throwUVIfMinus (hs_uv_fs_readlink p p'))
+        (hs_uv_fs_readlink_cleanup . fst)
+        (fromCString . fst)
+
+
+-- | Equivalent to <http://linux.die.net/man/3/realpath realpath(3)> on Unix. Windows uses <https://msdn.microsoft.com/en-us/library/windows/desktop/aa364962(v=vs.85).aspx GetFinalPathNameByHandle>.
+--
+-- Warning This function has certain platform-specific caveats that were discovered when used in Node.
+--
+--  * macOS and other BSDs: this function will fail with UV_ELOOP if more than 32 symlinks are found while
+--    resolving the given path. This limit is hardcoded and cannot be sidestepped.
+--
+--  * Windows: while this function works in the common case, there are a number of corner cases where it doesn’t:
+--
+--      * Paths in ramdisk volumes created by tools which sidestep the Volume Manager (such as ImDisk) cannot be resolved.
+--      * Inconsistent casing when using drive letters.
+--      * Resolved path bypasses subst’d drives.
+--
+-- While this function can still be used, it’s not recommended if scenarios such as the above need to be supported.
+-- The background story and some more details on these issues can be checked <https://github.com/nodejs/node/issues/7726 here>.
+--
+-- Note This function is not implemented on Windows XP and Windows Server 2003. On these systems, UV_ENOSYS is returned.
+realpath :: HasCallStack => CBytes -> IO CBytes
+realpath path = do
+    bracket
+        (withCBytes path $ \ p ->
+            allocPrimUnsafe $ \ p' ->
+                throwUVIfMinus (hs_uv_fs_realpath p p'))
+        (hs_uv_fs_readlink_cleanup . fst)
+        (fromCString . fst)
diff --git a/Z/IO/FileSystem/Threaded.hs b/Z/IO/FileSystem/Threaded.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/FileSystem/Threaded.hs
@@ -0,0 +1,433 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-|
+Module      : Z.IO.FileSystem.Threaded
+Description : Filesystem IO using threadpool
+Copyright   : (c) Dong Han, 2017~2019
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provide IO operations related to filesystem, operations are implemented using libuv's threadpool to achieve non-block behavior (non-block here meaning won't block other haskell threads), which should be prefered when the operations' estimated time is long enough(>1ms) or running with a non-threaded haskell runtime, such as accessing network filesystem or scan a very large directory. Otherwise you may block RTS's capability thus all the other haskell threads live on it.
+
+The threadpool version operations have overheads similar to safe FFI, but provide same adventages:
+
+  * The libuv's threadpool have a limit on concurrent threads number (4 by default), which can reduce disk contention.
+  * The threadpool version works with non-threaded runtime, which doesn't have safe FFI available.
+  * The threadpool version won't relinquish current HEC (Haskell Execution Context) a.k.a. capability.
+
+-}
+
+module Z.IO.FileSystem.Threaded
+  ( -- * regular file devices
+    FileT, checkFileTClosed
+  , initFileT, readFileT, writeFileT
+    -- * opening constant
+  , FileMode(DEFAULT_MODE, S_IRWXU, S_IRUSR, S_IWUSR
+      , S_IXUSR, S_IRWXG, S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXO, S_IROTH
+      )
+  , FileFlag(O_APPEND, O_CREAT, O_DIRECT, O_DSYNC, O_EXCL
+      , O_EXLOCK, O_NOATIME, O_NOFOLLOW, O_RDONLY, O_RDWR, O_SYMLINK
+      , O_SYNC, O_TRUNC, O_WRONLY, O_RANDOM, O_SHORT_LIVED, O_SEQUENTIAL, O_TEMPORARY
+      )
+  -- * filesystem operations
+  , mkdir
+  , unlink
+  , mkdtemp
+  , rmdir
+  , DirEntType(..)
+  , scandir
+  , UVStat(..), UVTimeSpec(..)
+  , stat, lstat, fstat
+  , rename
+  , fsync, fdatasync
+  , ftruncate
+  , UVCopyFileFlag(COPYFILE_DEFAULT, COPYFILE_EXCL, COPYFILE_FICLONE)
+  , copyfile
+  , UVAccessMode(F_OK, R_OK, W_OK, X_OK)
+  , AccessResult(..)
+  , access
+  , chmod, fchmod
+  , utime, futime
+  , UVSymlinkFlag(SYMLINK_DEFAULT, SYMLINK_DIR, SYMLINK_JUNCTION)
+  , link, symlink
+  , readlink, realpath
+  ) where
+
+import           Control.Monad
+import           Data.Word
+import           Data.Int
+import           Data.IORef
+import           Z.Data.CBytes                 as CBytes
+import           Foreign.Ptr
+import           Foreign.Storable               (peekElemOff)
+import           Foreign.Marshal.Alloc          (allocaBytes)
+import           Z.Foreign
+import           Z.IO.Buffered
+import           Z.IO.Exception
+import           Z.IO.Resource
+import           Z.IO.UV.Errno
+import           Z.IO.UV.FFI
+import           Z.IO.UV.Manager
+
+--------------------------------------------------------------------------------
+-- File
+
+-- | 'FileT' and its operations are NOT thread safe, use 'MVar' 'FileT' in multiple threads.
+--
+-- Note this is a differet data type from "Z.IO.FileSystem" \'s one, the 'Input'
+-- and 'Output' instance use thread pool version functions.
+--
+-- libuv implements read and write method with both implict and explict offset capable.
+-- Implict offset interface is provided by 'Input' \/ 'Output' instances.
+-- Explict offset interface is provided by 'readFileT' \/ 'writeFileT'.
+--
+data FileT =  FileT  {-# UNPACK #-} !UVFD      -- ^ the file
+                     {-# UNPACK #-} !(IORef Bool)  -- ^ closed flag
+
+-- | If fd is -1 (closed), throw 'ResourceVanished' ECLOSED.
+checkFileTClosed :: HasCallStack => FileT -> (UVFD -> IO a) -> IO a
+checkFileTClosed (FileT fd closedRef) f = do
+    closed <- readIORef closedRef
+    if closed then throwECLOSED else f fd
+
+instance Input FileT where
+    readInput f buf bufSiz = readFileT f buf bufSiz (-1)
+
+-- | Read file with given offset
+--
+-- Read length may be smaller than buffer size.
+readFileT :: HasCallStack
+          => FileT
+          -> Ptr Word8 -- ^ buffer
+          -> Int       -- ^ buffer size
+          -> Int64     -- ^ file offset, pass -1 to use default(system) offset
+          -> IO Int    -- ^ read length
+readFileT uvf buf bufSiz off =
+    checkFileTClosed uvf  $ \ fd -> do
+        uvm <- getUVManager
+        withUVRequest uvm (hs_uv_fs_read_threaded fd buf bufSiz off)
+
+instance Output FileT where
+    writeOutput f buf bufSiz = writeFileT f buf bufSiz (-1)
+
+-- | Write buffer to file
+--
+-- This function will loop until all bytes are written.
+writeFileT :: HasCallStack
+           => FileT
+           -> Ptr Word8 -- ^ buffer
+           -> Int       -- ^ buffer size
+           -> Int64     -- ^ file offset, pass -1 to use default(system) offset
+           -> IO ()
+writeFileT uvf buf0 bufSiz0 off0 =
+    checkFileTClosed uvf $ \ fd -> do
+             (if off0 == -1 then go fd buf0 bufSiz0
+                            else go' fd buf0 bufSiz0 off0)
+  where
+    -- use -1 offset to use fd's default offset
+    go fd buf bufSiz = do
+        uvm <- getUVManager
+        written <- withUVRequest uvm
+            (hs_uv_fs_write_threaded fd buf bufSiz (-1))
+        when (written < bufSiz)
+            (go fd (buf `plusPtr` written) (bufSiz-written))
+
+    go' fd buf bufSiz !off = do
+        uvm <- getUVManager
+        written <- withUVRequest uvm
+            (hs_uv_fs_write_threaded fd buf bufSiz off)
+        when (written < bufSiz) $
+            go' fd (buf `plusPtr` written)
+                   (bufSiz-written)
+                   (off+fromIntegral written)
+
+--------------------------------------------------------------------------------
+
+-- | init a file 'Resource', which open a file when used.
+--
+-- Resource closing will wait for the referencing counter goes
+-- down to zero (no reading or writing is in process), which can
+-- be a problem if you are using multiple readers or writers in multiple threads.
+-- In that case you have to stop all reading or writing thread if you don't want to
+-- block the resource thread.
+initFileT :: HasCallStack
+          => CBytes
+          -> FileFlag        -- ^ Opening flags, e.g. 'O_CREAT' @.|.@ 'O_RDWR'
+          -> FileMode        -- ^ Sets the file mode (permission and sticky bits),
+                               -- but only if the file was created, see 'DEFAULT_MODE'.
+          -> Resource FileT
+initFileT path flags mode =
+    initResource
+        (do uvm <- getUVManager
+            fd <- withCBytes path $ \ p ->
+                withUVRequest uvm (hs_uv_fs_open_threaded p flags mode)
+            FileT (fromIntegral fd) <$> newIORef False)
+        (\ (FileT fd closedRef) -> do
+            closed <- readIORef closedRef
+            unless closed $ do
+                throwUVIfMinus_ (hs_uv_fs_close fd)
+                writeIORef closedRef True)
+
+--------------------------------------------------------------------------------
+
+-- | Equivalent to <http://linux.die.net/man/2/mkdir mkdir(2)>.
+--
+-- Note mode is currently not implemented on Windows.
+mkdir :: HasCallStack => CBytes -> FileMode -> IO ()
+mkdir path mode = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withUVRequest_ uvm (hs_uv_fs_mkdir_threaded p mode)
+
+-- | Equivalent to <http://linux.die.net/man/2/unlink unlink(2)>.
+unlink :: HasCallStack => CBytes -> IO ()
+unlink path = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withUVRequest_ uvm (hs_uv_fs_unlink_threaded p)
+
+-- | Equivalent to <mkdtemp http://linux.die.net/man/3/mkdtemp>
+--
+-- Creates a temporary directory in the most secure manner possible.
+-- There are no race conditions in the directory’s creation.
+-- The directory is readable, writable, and searchable only by the creating user ID.
+-- The user of mkdtemp() is responsible for deleting the temporary directory and
+-- its contents when done with it.
+--
+-- Note: the argument is the prefix of the temporary directory,
+-- so no need to add XXXXXX ending.
+mkdtemp :: HasCallStack => CBytes -> IO CBytes
+mkdtemp path = do
+    let size = CBytes.length path
+    withCBytes path $ \ p ->
+        CBytes.create (size+7) $ \ p' -> do  -- we append "XXXXXX\NUL" in C
+            uvm <- getUVManager
+            withUVRequest_ uvm (hs_uv_fs_mkdtemp_threaded p size p')
+            return (size+6)
+
+-- | Equivalent to <http://linux.die.net/man/2/rmdir rmdir(2)>.
+rmdir :: HasCallStack => CBytes -> IO ()
+rmdir path = do
+    uvm <- getUVManager
+    withCBytes path (void . withUVRequest uvm . hs_uv_fs_rmdir_threaded)
+
+--------------------------------------------------------------------------------
+
+-- | Equivalent to <http://linux.die.net/man/3/scandir scandir(3)>.
+--
+-- Note Unlike scandir(3), this function does not return the “.” and “..” entries.
+--
+-- Note On Linux, getting the type of an entry is only supported by some file systems (btrfs, ext2, ext3 and ext4 at the time of this writing), check the <http://linux.die.net/man/2/getdents getdents(2)> man page.
+scandir :: HasCallStack => CBytes -> IO [(CBytes, DirEntType)]
+scandir path = do
+    uvm <- getUVManager
+    bracket
+        (withCBytes path $ \ p ->
+            allocPrimSafe $ \ dents ->
+                withUVRequestEx uvm
+                    (hs_uv_fs_scandir_threaded p dents)
+                    (hs_uv_fs_scandir_extra_cleanup dents))
+        (\ (dents, n) -> hs_uv_fs_scandir_cleanup dents n)
+        (\ (dents, n) -> forM [0..n-1] $ \ i -> do
+            dent <- peekElemOff dents i
+            (p, typ) <- peekUVDirEnt dent
+            let !typ' = fromUVDirEntType typ
+            !p' <- fromCString p
+            return (p', typ'))
+
+--------------------------------------------------------------------------------
+
+-- | Equivalent to <http://linux.die.net/man/2/stat stat(2)>
+stat :: HasCallStack => CBytes -> IO UVStat
+stat path = do
+    withCBytes path $ \ p ->
+         allocaBytes uvStatSize $ \ s -> do
+            uvm <- getUVManager
+            withUVRequest_ uvm (hs_uv_fs_stat_threaded p s)
+            peekUVStat s
+
+-- | Equivalent to <http://linux.die.net/man/2/lstat lstat(2)>
+lstat :: HasCallStack => CBytes -> IO UVStat
+lstat path =
+    withCBytes path $ \ p ->
+         allocaBytes uvStatSize $ \ s -> do
+            uvm <- getUVManager
+            withUVRequest_ uvm (hs_uv_fs_lstat_threaded p s)
+            peekUVStat s
+
+-- | Equivalent to <http://linux.die.net/man/2/fstat fstat(2)>
+fstat :: HasCallStack => FileT -> IO UVStat
+fstat uvf = checkFileTClosed uvf $ \ fd ->
+     (allocaBytes uvStatSize $ \ s -> do
+        uvm <- getUVManager
+        withUVRequest_ uvm (hs_uv_fs_fstat_threaded fd s)
+        peekUVStat s)
+
+--------------------------------------------------------------------------------
+
+-- | Equivalent to <http://linux.die.net/man/2/rename rename(2)>.
+--
+-- Note On Windows if this function fails with UV_EBUSY, UV_EPERM or UV_EACCES, it will retry to rename the file up to four times with 250ms wait between attempts before giving up. If both path and new_path are existing directories this function will work only if target directory is empty.
+rename :: HasCallStack => CBytes -> CBytes -> IO ()
+rename path path' = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withCBytes path' $ \ p' ->
+            withUVRequest_ uvm (hs_uv_fs_rename_threaded p p')
+
+-- | Equivalent to <http://linux.die.net/man/2/fsync fsync(2)>.
+fsync :: HasCallStack => FileT -> IO ()
+fsync uvf = checkFileTClosed uvf $ \ fd -> do
+    uvm <- getUVManager
+    withUVRequest_ uvm (hs_uv_fs_fsync_threaded fd)
+
+-- | Equivalent to <http://linux.die.net/man/2/fdatasync fdatasync(2)>.
+fdatasync :: HasCallStack => FileT -> IO ()
+fdatasync uvf = checkFileTClosed uvf $ \ fd -> do
+    uvm <- getUVManager
+    withUVRequest_ uvm (hs_uv_fs_fdatasync_threaded fd)
+
+-- | Equivalent to <http://linux.die.net/man/2/ftruncate ftruncate(2)>.
+ftruncate :: HasCallStack => FileT -> Int64 -> IO ()
+ftruncate uvf off = checkFileTClosed uvf $ \ fd -> do
+    uvm <- getUVManager
+    withUVRequest_ uvm (hs_uv_fs_ftruncate_threaded fd off)
+
+-- | Copies a file from path to new_path.
+--
+-- Warning: If the destination path is created, but an error occurs while copying the data, then the destination path is removed. There is a brief window of time between closing and removing the file where another process could access the file.
+copyfile :: HasCallStack => CBytes -> CBytes -> UVCopyFileFlag -> IO ()
+copyfile path path' flag = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withCBytes path' $ \ p' ->
+            withUVRequest_ uvm (hs_uv_fs_copyfile_threaded p p' flag)
+
+-- | Equivalent to <http://linux.die.net/man/2/access access(2)> on Unix.
+-- Windows uses GetFileAttributesW().
+access :: HasCallStack => CBytes -> UVAccessMode -> IO AccessResult
+access path mode = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withUVRequest' uvm (hs_uv_fs_access_threaded p mode) (handleResult . fromIntegral)
+  where
+    handleResult r
+        | r == 0           = return AccessOK
+        | r == UV_ENOENT   = return NoExistence
+        | r == UV_EACCES   = return NoPermission
+        | otherwise        = do
+            name <- uvErrName r
+            desc <- uvStdError r
+            throwUVError r (IOEInfo name desc callStack)
+
+-- | Equivalent to <http://linux.die.net/man/2/chmod chmod(2)>.
+chmod :: HasCallStack => CBytes -> FileMode -> IO ()
+chmod path mode = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withUVRequest_ uvm (hs_uv_fs_chmod_threaded p mode)
+
+-- | Equivalent to <http://linux.die.net/man/2/fchmod fchmod(2)>.
+fchmod :: HasCallStack => FileT -> FileMode -> IO ()
+fchmod uvf mode = checkFileTClosed uvf $ \ fd -> do
+    uvm <- getUVManager
+    withUVRequest_ uvm (hs_uv_fs_fchmod_threaded fd mode)
+
+-- | Equivalent to <http://linux.die.net/man/2/utime utime(2)>.
+--
+-- libuv choose 'Double' type due to cross platform concerns, we only provide micro-second precision:
+--
+--   * second     = v
+--   * nanosecond = (v * 1000000) % 1000000 * 1000;
+--
+-- second and nanosecond are fields in 'UVTimeSpec' respectively.
+--
+-- Note libuv prior to v1.23.1 have issues which may result in nanosecond not set, 'futime' doesn't have
+utime :: HasCallStack
+      => CBytes
+      -> Double     -- ^ atime, i.e. access time
+      -> Double     -- ^ mtime, i.e. modify time
+      -> IO ()
+utime path atime mtime = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withUVRequest_ uvm (hs_uv_fs_utime_threaded p atime mtime)
+
+-- | Equivalent to <http://linux.die.net/man/2/futime futime(2)>.
+--
+-- Same precision notes with 'utime'.
+futime :: HasCallStack => FileT -> Double -> Double -> IO ()
+futime uvf atime mtime = checkFileTClosed uvf $ \ fd -> do
+    uvm <- getUVManager
+    withUVRequest_ uvm (hs_uv_fs_futime_threaded fd atime mtime)
+
+-- | Equivalent to <http://linux.die.net/man/2/link link(2)>.
+link :: HasCallStack => CBytes -> CBytes -> IO ()
+link path path' = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withCBytes path' $ \ p' ->
+            withUVRequest_ uvm (hs_uv_fs_link_threaded p p')
+
+-- | Equivalent to <http://linux.die.net/man/2/symlink symlink(2)>.
+--
+-- | Note On Windows the flags parameter can be specified to control how the symlink will be created.
+--
+--   * 'SYMLINK_DIR': indicates that path points to a directory.
+--   * 'SYMLINK_JUNCTION': request that the symlink is created using junction points.
+--
+-- On other platforms these flags are ignored.
+symlink :: HasCallStack => CBytes -> CBytes -> UVSymlinkFlag -> IO ()
+symlink path path' flag = do
+    uvm <- getUVManager
+    withCBytes path $ \ p ->
+        withCBytes path' $ \ p' ->
+            withUVRequest_ uvm (hs_uv_fs_symlink_threaded p p' flag)
+
+-- | Equivalent to <http://linux.die.net/man/2/readlink readlink(2)>.
+readlink :: HasCallStack => CBytes -> IO CBytes
+readlink path = do
+    uvm <- getUVManager
+    bracket
+        (withCBytes path $ \ p ->
+            allocPrimSafe $ \ p' ->
+                withUVRequestEx uvm
+                    (hs_uv_fs_readlink_threaded p p')
+                    (\ _ -> hs_uv_fs_readlink_extra_cleanup p'))
+        (hs_uv_fs_readlink_cleanup . fst)
+        (fromCString . fst)
+
+-- | Equivalent to <http://linux.die.net/man/3/realpath realpath(3)> on Unix. Windows uses <https://msdn.microsoft.com/en-us/library/windows/desktop/aa364962(v=vs.85).aspx GetFinalPathNameByHandle>.
+--
+-- Warning This function has certain platform-specific caveats that were discovered when used in Node.
+--
+--  * macOS and other BSDs: this function will fail with UV_ELOOP if more than 32 symlinks are found while
+--    resolving the given path. This limit is hardcoded and cannot be sidestepped.
+--
+--  * Windows: while this function works in the common case, there are a number of corner cases where it doesn’t:
+--
+--      * Paths in ramdisk volumes created by tools which sidestep the Volume Manager (such as ImDisk) cannot be resolved.
+--      * Inconsistent casing when using drive letters.
+--      * Resolved path bypasses subst’d drives.
+--
+-- While this function can still be used, it’s not recommended if scenarios such as the above need to be supported.
+-- The background story and some more details on these issues can be checked <https://github.com/nodejs/node/issues/7726 here>.
+--
+-- Note This function is not implemented on Windows XP and Windows Server 2003. On these systems, UV_ENOSYS is returned.
+realpath :: HasCallStack => CBytes -> IO CBytes
+realpath path = do
+    uvm <- getUVManager
+    bracket
+        (withCBytes path $ \ p ->
+            allocPrimSafe $ \ p' ->
+                withUVRequestEx uvm
+                    (hs_uv_fs_realpath_threaded p p')
+                    (\ _ -> hs_uv_fs_readlink_extra_cleanup p'))
+        (hs_uv_fs_readlink_cleanup . fst)
+        (fromCString . fst)
diff --git a/Z/IO/Logger.hs b/Z/IO/Logger.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Logger.hs
@@ -0,0 +1,205 @@
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE BangPatterns #-}
+
+{-|
+Module      : Z.IO.Logger
+Description : High performance logger
+Copyright   : (c) Dong Han, 2017-2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Simple, high performance logger. The design choice of this logger is biased towards simplicity instead of generlization:
+
+    * All log functions lives in 'IO'.
+    * By default this logger is connected to stderr, use 'setStdLogger' to customize.
+    * When logging each thread will build log 'Builder's into a small 'V.Bytes' with line buffer
+      instead of leaving all 'Builder's to the flushing thread:
+        * Logger won't keep heap data for too long simply because they're referenced by log's 'Builder'.
+        * Each logging thread only need perform a CAS to prepend log 'V.Bytes' into a list,
+          which reduces contention.
+        * Each log call is atomic, Logging order is preserved under concurrent settings.
+
+Flushing is automatic and throttled for 'debug', 'info', 'warn' to boost performance, but a 'fatal' log will always flush logger's buffer.  This could lead to a problem that if main thread exits too early logs may missed, to add a flushing when program exits, use 'withStdLogger' like:
+
+@
+import Z.IO.Logger
+
+main :: IO ()
+main = withStdLogger $ do
+    ....
+    debug "..."   -- So that this log won't be missed
+    ...
+@
+-}
+
+module Z.IO.Logger
+  ( -- * A simple Logger type
+    Logger
+  , LoggerConfig(..)
+  , newLogger
+  , loggerFlush
+  , setStdLogger
+  , getStdLogger
+  , withStdLogger
+    -- * logging functions
+  , debug
+  , info
+  , warn
+  , fatal
+  , otherLevel
+    -- * logging functions with specific logger
+  , debugWith
+  , infoWith
+  , warnWith
+  , fatalWith
+  , otherLevelWith
+  ) where
+
+import Control.Monad
+import Z.Data.Vector.Base as V
+import Z.IO.LowResTimer
+import Z.IO.StdStream
+import Z.IO.Buffered
+import System.IO.Unsafe (unsafePerformIO)
+import Z.IO.Exception
+import Data.IORef
+import Control.Concurrent.MVar
+import qualified Z.Data.Builder.Base as B
+import qualified Data.Time as Time
+
+data Logger = Logger
+    { loggerFlush           :: IO ()                -- ^ flush logger's buffer to output device
+    , loggerThrottledFlush  :: IO ()
+    , loggerBytesList       :: {-# UNPACK #-} !(IORef [V.Bytes])
+    , loggerConfig          :: {-# UNPACK #-} !LoggerConfig
+    }
+
+data LoggerConfig = LoggerConfig
+    { loggerMinFlushInterval :: {-# UNPACK #-} !Int -- ^ Minimal flush interval, see Notes on 'debug'
+    , loggerTsCache          :: IO (B.Builder ())   -- ^ A IO action return a formatted date/time string
+    , loggerLineBufSize      :: {-# UNPACK #-} !Int -- ^ Buffer size to build each log/line
+    , loggerShowDebug        :: Bool                -- ^ Set to 'False' to filter debug logs
+    , loggerShowTS           :: Bool                -- ^ Set to 'False' to disable auto data/time string prepending
+    }
+
+-- | A default logger config with
+--
+--   * debug ON
+--   * 0.1s minimal flush interval
+--   * defaultTSCache
+--   * line buffer size 128 bytes
+--   * show debug True
+--   * show timestamp True
+--   * 'BufferedOutput' buffer size equals to 'V.defaultChunkSize'.
+defaultLoggerConfig :: LoggerConfig
+defaultLoggerConfig = LoggerConfig 1 defaultTSCache 128 True True
+
+-- | A default timestamp cache with format @%Y-%m-%dT%H:%M:%S%Z@
+defaultTSCache :: IO (B.Builder ())
+{-# NOINLINE defaultTSCache #-}
+defaultTSCache = unsafePerformIO $ do
+    throttle 1 $ do
+        t <- Time.getCurrentTime
+        return . B.string8 $
+            Time.formatTime Time.defaultTimeLocale "%Y-%m-%dT%H:%M:%S%Z" t
+
+flushLog :: (HasCallStack, Output o) => MVar (BufferedOutput o) -> IORef [V.Bytes] -> IO ()
+flushLog oLock bList =
+    withMVar oLock $ \ o -> do
+        bss <- atomicModifyIORef' bList (\ bss -> ([], bss))
+        forM_ (reverse bss) (writeBuffer o)
+        flushBuffer o
+
+-- | Make a new logger
+newLogger :: Output o
+          => LoggerConfig
+          -> MVar (BufferedOutput o)
+          -> IO Logger
+newLogger config oLock = do
+    bList <- newIORef []
+    let flush = flushLog oLock bList
+    throttledFlush <- throttleTrailing_ (loggerMinFlushInterval config) flush
+    return $ Logger flush throttledFlush bList config
+
+globalLogger :: IORef Logger
+{-# NOINLINE globalLogger #-}
+globalLogger = unsafePerformIO $
+    newIORef =<< newLogger defaultLoggerConfig stderrBuf
+
+-- | Change the global logger.
+setStdLogger :: Logger -> IO ()
+setStdLogger !logger = atomicWriteIORef globalLogger logger
+
+-- | Get the global logger.
+getStdLogger :: IO Logger
+getStdLogger = readIORef globalLogger
+
+-- | Manually flush stderr logger.
+flushDefaultLogger :: IO ()
+flushDefaultLogger = getStdLogger >>= loggerFlush
+
+pushLog :: IORef [V.Bytes] -> Int -> B.Builder () -> IO ()
+pushLog blist bfsiz b = do
+    let !bs = B.buildBytesWith bfsiz b
+    atomicModifyIORef' blist (\ bss -> (bs:bss, ()))
+
+-- | Flush stderr logger when program exits.
+withStdLogger :: IO () -> IO ()
+withStdLogger = (`finally` flushDefaultLogger)
+
+--------------------------------------------------------------------------------
+
+debug :: B.Builder () -> IO ()
+debug = otherLevel "DEBUG" False
+
+info :: B.Builder () -> IO ()
+info = otherLevel "INFO" False
+
+warn :: B.Builder () -> IO ()
+warn = otherLevel "WARN" False
+
+fatal :: B.Builder () -> IO ()
+fatal = otherLevel "FATAL" True
+
+otherLevel :: B.Builder ()      -- ^ log level
+           -> Bool              -- ^ flush immediately?
+           -> B.Builder ()      -- ^ log content
+           -> IO ()
+otherLevel level flushNow b =
+    getStdLogger >>= \ logger -> otherLevelWith logger level flushNow b
+
+--------------------------------------------------------------------------------
+
+debugWith :: Logger -> B.Builder () -> IO ()
+debugWith logger = otherLevelWith logger "DEBUG" False
+
+infoWith :: Logger -> B.Builder () -> IO ()
+infoWith logger = otherLevelWith logger "INFO" False
+
+warnWith :: Logger -> B.Builder () -> IO ()
+warnWith logger = otherLevelWith logger "WARN" False
+
+fatalWith :: Logger -> B.Builder () -> IO ()
+fatalWith logger = otherLevelWith logger "FATAL" True
+
+otherLevelWith :: Logger
+               -> B.Builder ()      -- ^ log level
+               -> Bool              -- ^ flush immediately?
+               -> B.Builder ()      -- ^ log content
+               -> IO ()
+otherLevelWith logger level flushNow b = case logger of
+    (Logger flush throttledFlush blist (LoggerConfig _ tscache lbsiz showdebug showts)) -> do
+        ts <- if showts then tscache else return ""
+        when showdebug $ do
+            pushLog blist lbsiz $ do
+                B.char8 '['
+                level
+                B.char8 ']'
+                B.char8 ' '
+                when showts $ ts >> B.char8 ' '
+                b
+                B.char8 '\n'
+            if flushNow then flush else throttledFlush
diff --git a/Z/IO/LowResTimer.hs b/Z/IO/LowResTimer.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/LowResTimer.hs
@@ -0,0 +1,365 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+{-|
+Module      : Z.IO.LowResTimer
+Description : Low resolution (0.1s) timing wheel
+Copyright   : (c) Dong Han, 2017-2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provide low resolution (0.1s) timers using a timing wheel of size 128 per capability,
+each timer thread will automatically started or stopped based on demannd. register or cancel a timeout is O(1),
+and each step only need scan n/128 items given timers are registered in an even fashion.
+
+This timer is particularly suitable for high concurrent approximated IO timeout scheduling.
+You should not rely on it to provide timing information since it's very inaccurate.
+
+Reference:
+
+    * <https://github.com/netty/netty/blob/4.1/common/src/main/java/io/netty/util/HashedWheelTimer.java>
+    * <http://www.cse.wustl.edu/~cdgill/courses/cs6874/TimingWheels.ppt>
+-}
+
+
+module Z.IO.LowResTimer
+  ( -- * low resolution timers
+    registerLowResTimer
+  , registerLowResTimer_
+  , registerLowResTimerOn
+  , LowResTimer
+  , queryLowResTimer
+  , cancelLowResTimer
+  , cancelLowResTimer_
+  , timeoutLowRes
+  , timeoutLowResEx
+  , throttle
+  , throttle_
+  , throttleTrailing_
+    -- * low resolution timer manager
+  , LowResTimerManager
+  , getLowResTimerManager
+  , isLowResTimerManagerRunning
+  , lowResTimerManagerCapabilitiesChanged
+  ) where
+
+import           Z.Data.Array
+#ifndef mingw32_HOST_OS
+import           GHC.Event
+#endif
+import           Control.Concurrent
+import           Control.Monad
+import           Data.IORef
+import           GHC.Conc
+import           System.IO.Unsafe
+import           Z.Data.PrimRef.PrimIORef
+import           Z.IO.Exception
+
+--
+queueSize :: Int
+queueSize = 128
+
+-- | A simple timing wheel
+--
+data TimerList = TimerItem {-# UNPACK #-} !Counter (IO ()) TimerList | TimerNil
+
+data LowResTimerManager = LowResTimerManager
+    { lrTimerQueue :: Array (IORef TimerList)
+    , lrIndexLock :: MVar Int
+    , lrRegisterCount :: Counter
+    , lrRunningLock :: MVar Bool
+    }
+
+newLowResTimerManager :: IO LowResTimerManager
+newLowResTimerManager = do
+    indexLock <- newMVar 0
+    regCounter <- newCounter 0
+    runningLock <- newMVar False
+    queue <- newArr queueSize
+    forM_ [0..queueSize-1] $ \ i -> do
+        writeArr queue i =<< newIORef TimerNil
+    iqueue <- unsafeFreezeArr queue
+    return (LowResTimerManager iqueue indexLock regCounter runningLock)
+
+lowResTimerManager :: IORef (Array LowResTimerManager)
+{-# NOINLINE lowResTimerManager #-}
+lowResTimerManager = unsafePerformIO $ do
+    numCaps <- getNumCapabilities
+    lrtmArray <- newArr numCaps
+    forM_ [0..numCaps-1] $ \ i -> do
+        writeArr lrtmArray i =<< newLowResTimerManager
+    ilrtmArray <- unsafeFreezeArr lrtmArray
+    newIORef ilrtmArray
+
+-- | Create new low resolution timer manager on capability change.
+--
+-- Since low resolution timer manager is not hooked into RTS, you're responsible to call this function
+-- after you call 'setNumCapabilities' to match timer manager array size with new capability number.
+--
+-- This is not a must though, when we fetch timer manager we always take a modulo.
+--
+lowResTimerManagerCapabilitiesChanged :: IO ()
+lowResTimerManagerCapabilitiesChanged = do
+    lrtmArray <- readIORef lowResTimerManager
+    let oldSize = sizeofArr lrtmArray
+    numCaps <- getNumCapabilities
+    when (numCaps /= oldSize) $ do
+        lrtmArray' <- newArr numCaps
+        if numCaps < oldSize
+        then do
+            forM_ [0..numCaps-1] $ \ i -> do
+                writeArr lrtmArray' i =<< indexArrM lrtmArray i
+        else do
+            forM_ [0..oldSize-1] $ \ i -> do
+                writeArr lrtmArray' i =<< indexArrM lrtmArray i
+            forM_ [oldSize..numCaps-1] $ \ i -> do
+                writeArr lrtmArray' i =<< newLowResTimerManager
+
+        ilrtmArray' <- unsafeFreezeArr lrtmArray'
+        atomicModifyIORef' lowResTimerManager $ \ _ -> (ilrtmArray', ())
+
+-- | Get a 'LowResTimerManager' for current thread.
+--
+getLowResTimerManager :: IO LowResTimerManager
+getLowResTimerManager = do
+    (cap, _) <- threadCapability =<< myThreadId
+    lrtmArray <- readIORef lowResTimerManager
+    indexArrM lrtmArray (cap `rem` sizeofArr lrtmArray)
+
+-- | Check if a timer manager's wheel is turning
+--
+-- This is mostly for testing purpose.
+--
+isLowResTimerManagerRunning :: LowResTimerManager -> IO Bool
+isLowResTimerManagerRunning (LowResTimerManager _ _ _ runningLock) = readMVar runningLock
+
+-- | Register a new timer on current capability's timer manager, start the timing wheel if it's not turning.
+--
+-- If the action could block, you may want to run it in another thread. Example to kill a thread after 10s:
+--
+-- @
+--   registerLowResTimer 100 (forkIO $ killThread tid)
+-- @
+--
+registerLowResTimer :: Int          -- ^ timeout in unit of 0.1s
+                    -> IO ()        -- ^ the action you want to perform, it should not block
+                    -> IO LowResTimer
+registerLowResTimer t action = do
+    lrtm <- getLowResTimerManager
+    registerLowResTimerOn lrtm t action
+
+-- | 'void' ('registerLowResTimer' t action)
+registerLowResTimer_ :: Int          -- ^ timeout in unit of 0.1s
+                     -> IO ()        -- ^ the action you want to perform, it should not block
+                     -> IO ()
+registerLowResTimer_ t action = void (registerLowResTimer t action)
+
+-- | Same as 'registerLowResTimer', but allow you choose timer manager.
+--
+registerLowResTimerOn :: LowResTimerManager   -- ^ a low resolution timer manager
+                      -> Int          -- ^ timeout in unit of 0.1s
+                      -> IO ()        -- ^ the action you want to perform, it should not block
+                      -> IO LowResTimer
+registerLowResTimerOn lrtm@(LowResTimerManager queue indexLock regCounter _) t action = do
+
+    let (round_, tick) = (max 0 t) `quotRem` queueSize
+    i <- readMVar indexLock
+    tlistRef <- indexArrM queue ((i + tick) `rem` queueSize)
+    roundCounter <- newCounter round_
+    mask_ $ do
+        atomicModifyIORef' tlistRef $ \ tlist ->
+            let newList = TimerItem roundCounter action tlist
+            in (newList, ())
+        atomicAddCounter_ regCounter 1
+
+    ensureLowResTimerManager lrtm
+
+    return (LowResTimer roundCounter)  -- cancel is simple, just set the round number to -1.
+                                       -- next scan will eventually release it
+
+-- | Timer registered by 'registerLowResTimer' or 'registerLowResTimerOn'.
+--
+newtype LowResTimer = LowResTimer Counter
+
+-- | Query how many seconds remain before timer firing.
+--
+-- A return value <= 0 indictate the timer is firing or fired.
+--
+queryLowResTimer :: LowResTimer -> IO Int
+queryLowResTimer (LowResTimer c) = readPrimIORef c
+
+-- | Cancel a timer, return the remaining ticks.
+--
+-- This function have no effect after the timer is fired.
+--
+cancelLowResTimer :: LowResTimer -> IO Int
+cancelLowResTimer (LowResTimer c) = atomicOrCounter c (-1)
+
+-- | @void . cancelLowResTimer@
+--
+cancelLowResTimer_ :: LowResTimer -> IO ()
+cancelLowResTimer_ = void . cancelLowResTimer
+
+-- | similar to 'System.Timeout.timeout', this function put a limit on time which an IO can consume.
+--
+-- Note timeoutLowRes is also implemented with 'Exception' underhood, which can have some surprising
+-- effects on some devices, e.g. use 'timeoutLowRes' with reading or writing on 'UVStream's may close
+-- the 'UVStream' once a reading or writing is not able to be done in time.
+timeoutLowRes :: Int    -- ^ timeout in unit of 0.1s
+              -> IO a
+              -> IO (Maybe a)
+timeoutLowRes timeo io = do
+    mid <- myThreadId
+    catch
+        (do timer <- registerLowResTimer timeo (timeoutAThread mid)
+            r <- io
+            _ <- cancelLowResTimer timer
+            return (Just r))
+        ( \ (_ :: TimeOutException) -> return Nothing )
+  where
+    timeoutAThread tid = void . forkIO $ throwTo tid (TimeOutException tid undefined)
+
+-- | Similar to 'timeoutLowRes', but raise a 'TimeOutException' to current thread
+-- instead of return 'Nothing' if timeout.
+timeoutLowResEx :: HasCallStack
+                => Int    -- ^ timeout in unit of 0.1s
+                -> IO a
+                -> IO a
+timeoutLowResEx timeo io = do
+    mid <- myThreadId
+    timer <- registerLowResTimer timeo (timeoutAThread mid)
+    r <- io
+    _ <- cancelLowResTimer timer
+    return r
+  where
+    timeoutAThread tid = void . forkIO $ throwTo tid (TimeOutException tid callStack)
+
+data TimeOutException = TimeOutException ThreadId CallStack deriving Show
+instance Exception TimeOutException
+
+--------------------------------------------------------------------------------
+-- | Check if low resolution timer manager loop is running, start loop if not.
+--
+ensureLowResTimerManager :: LowResTimerManager -> IO ()
+ensureLowResTimerManager lrtm@(LowResTimerManager _ _ _ runningLock) = do
+    modifyMVar_ runningLock $ \ running -> do
+        unless running $ do
+            tid <- forkIO (startLowResTimerManager lrtm)
+            labelThread tid "Z-IO: low resolution time manager"    -- make sure we can see it in GHC event log
+        return True
+
+-- | Start low resolution timer loop, the loop is automatically stopped if there's no more new registrations.
+--
+startLowResTimerManager :: LowResTimerManager ->IO ()
+startLowResTimerManager lrtm@(LowResTimerManager _ _ regCounter runningLock)  = do
+    modifyMVar_ runningLock $ \ _ -> do     -- we shouldn't receive async exception here
+        c <- readPrimIORef regCounter          -- unless something terribly wrong happened, e.g., stackoverflow
+        if c > 0
+        then do
+            _ <- forkIO (fireLowResTimerQueue lrtm)  -- we offload the scanning to another thread to minimize
+                                                -- the time we holding runningLock
+            case () of
+                _
+#ifndef mingw32_HOST_OS
+                    | rtsSupportsBoundThreads -> do
+                        htm <- getSystemTimerManager
+                        void $ registerTimeout htm 100000 (startLowResTimerManager lrtm)
+#endif
+                    | otherwise -> void . forkIO $ do   -- we have to fork another thread since we're holding runningLock,
+                        threadDelay 100000              -- this may affect accuracy, but on windows there're no other choices.
+                        startLowResTimerManager lrtm
+            return True
+        else do
+            return False -- if we haven't got any registered timeout, we stop the time manager
+                         -- doing this can stop us from getting the way of idle GC
+                         -- since we're still inside runningLock, we won't miss new registration.
+
+-- | Scan the timeout queue in current tick index, and move tick index forward by one.
+--
+fireLowResTimerQueue :: LowResTimerManager -> IO ()
+fireLowResTimerQueue (LowResTimerManager queue indexLock regCounter _) = do
+    (tList, tListRef) <- modifyMVar indexLock $ \ index -> do                 -- get the index lock
+        tListRef <- indexArrM queue index
+        tList <- atomicModifyIORef' tListRef $ \ tList -> (TimerNil, tList)   -- swap current index list with an empty one
+        let !index' = (index+1) `rem` queueSize                               -- move index forward by 1
+        return (index', (tList, tListRef))                                    -- release the lock
+
+    go tList tListRef regCounter
+  where
+    go (TimerItem roundCounter action nextList) tListRef counter = do
+        r <- atomicSubCounter roundCounter 1
+        case r `compare` 0 of
+            LT -> do                                     -- if round number is less than 0, then it's a cancelled timer
+                atomicSubCounter_ counter 1
+                go nextList tListRef counter
+            EQ -> do                                     -- if round number is equal to 0, fire it
+                atomicSubCounter_ counter 1
+                catch action ( \ (_ :: SomeException) -> return () )  -- well, we really don't want timers break our loop
+                go nextList tListRef counter
+            GT -> do                                     -- if round number is larger than 0, put it back for another round
+                atomicModifyIORef' tListRef $ \ tlist -> (TimerItem roundCounter action tlist, ())
+                go nextList tListRef counter
+    go TimerNil _ _ = return ()
+
+--------------------------------------------------------------------------------
+
+-- | Cache result of an IO action for give time t.
+--
+-- This combinator is useful when you want to share IO result within a period, the action will be called
+-- on demand, and the result will be cached for t milliseconds.
+--
+-- One common way to get a shared periodical updated value is to start a seperate thread and do calculation
+-- periodically, but doing that will stop system from being idle, which stop idle GC from running,
+-- and in turn disable deadlock detection, which is too bad. This function solves that.
+throttle :: Int         -- ^ cache time in unit of 0.1s
+         -> IO a        -- ^ the original IO action
+         -> IO (IO a)   -- ^ throttled IO action
+throttle t action = do
+    resultCounter <- newCounter 0
+    resultRef <- newIORef =<< action
+    return $ do
+        c <- atomicOrCounter resultCounter (-1) -- 0x11111111 or 0x1111111111111111 depend machine word size
+        if c == 0
+        then do
+            registerLowResTimer_ t (void $ atomicAndCounter resultCounter 0)
+            !r <- action
+            atomicWriteIORef resultRef r
+            return r
+        else readIORef resultRef
+
+-- | Throttle an IO action without caching result.
+--
+-- The IO action will run at leading edge. i.e. once run, during following (t/10)s throttled action will
+-- no-ops.
+--
+-- Note the action will run in the calling thread.
+throttle_ :: Int            -- ^ cache time in unit of 0.1s
+          -> IO ()          -- ^ the original IO action
+          -> IO (IO ())     -- ^ throttled IO action
+throttle_ t action = do
+    resultCounter <- newCounter 0
+    return $ do
+        c <- atomicOrCounter resultCounter (-1) -- 0x11111111 or 0x1111111111111111 depend machine word size
+        when (c == 0) $ do
+            registerLowResTimer_ t (void $ atomicAndCounter resultCounter 0)
+            void action
+
+-- | Similar to 'throttle_' but run action in trailing edge
+--
+-- The IO action will run at trailing edge. i.e. no matter how many times throttled action
+-- are called, original action will run only once after (t/10)s.
+--
+-- Note the action will be run in a new created thread.
+throttleTrailing_ :: Int
+                  -> IO ()        -- ^ the original IO action
+                  -> IO (IO ())   -- ^ throttled IO action
+throttleTrailing_ t action = do
+    resultCounter <- newCounter 0
+    return $ do
+        c <- atomicOrCounter resultCounter (-1) -- 0x11111111 or 0x1111111111111111 depend machine word size
+        when (c == 0) . registerLowResTimer_ t . void . forkIO $ do
+            atomicAndCounter_ resultCounter 0
+            action
diff --git a/Z/IO/Network.hs b/Z/IO/Network.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Network.hs
@@ -0,0 +1,26 @@
+{-|
+Module      : Z.IO.Network
+Description : DNS, TCP, UDP, IPC
+Copyright   : (c) Dong Han, 2020
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Umbrella module to export everything you need to start using network.
+
+-}
+
+module Z.IO.Network (
+   module Z.IO.Network.SocketAddr
+ , module Z.IO.Network.DNS
+ , module Z.IO.Network.IPC
+ , module Z.IO.Network.TCP
+ , module Z.IO.Network.UDP
+ ) where
+
+import           Z.IO.Network.SocketAddr
+import           Z.IO.Network.DNS
+import           Z.IO.Network.IPC
+import           Z.IO.Network.TCP
+import           Z.IO.Network.UDP
diff --git a/Z/IO/Network/DNS.hsc b/Z/IO/Network/DNS.hsc
new file mode 100644
--- /dev/null
+++ b/Z/IO/Network/DNS.hsc
@@ -0,0 +1,375 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE RecordWildCards #-}
+
+{-|
+Module      : Z.IO.Network.DNS
+Description : TCP/UDP socket address API
+Copyright   : (c) Winterland, 2018
+License     : BSD
+Maintainer  : drkoster@qq.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides 'getAddrInfo' and 'getNameInfo'. <https://www.man7.org/linux/man-pages/man3/getnameinfo.3.html getnameinfo> and <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html getaddrinfo> equivalent.
+
+-}
+module Z.IO.Network.DNS (
+  -- * name to ip
+    getAddrInfo
+  , HostName
+  , ServiceName
+  , AddrInfoFlag(..), addrInfoFlagImplemented, addrInfoFlagMapping
+  , AddrInfo(..), defaultHints, followAddrInfo
+  -- * ip to name
+  , getNameInfo
+  , NameInfoFlag(..), nameInfoFlagMapping
+  ) where
+
+import           Data.Bits
+import           Data.List as List
+import           Foreign.C.String
+import           Foreign.C.Types
+import           Foreign.Marshal.Utils
+import           Foreign.Ptr
+import           Foreign.Storable 
+import           Z.Data.CBytes  as CBytes
+import           Z.Foreign
+import           Z.IO.Exception
+import           Z.IO.Network.SocketAddr
+import           Z.IO.UV.Win
+
+#include "hs_uv.h"
+
+-----------------------------------------------------------------------------
+
+-- | Either a host name e.g., @\"haskell.org\"@ or a numeric host
+-- address string consisting of a dotted decimal IPv4 address or an
+-- IPv6 address e.g., @\"192.168.0.1\"@.
+type HostName       = CBytes
+-- | Either a service name e.g., @\"http\"@ or a numeric port number.
+type ServiceName    = CBytes
+
+-----------------------------------------------------------------------------
+-- Address and service lookups
+
+-- | Flags that control the querying behaviour of 'getAddrInfo'.
+--   For more information, see <https://tools.ietf.org/html/rfc3493#page-25>
+data AddrInfoFlag =
+    -- | The list of returned 'AddrInfo' values will
+    --   only contain IPv4 addresses if the local system has at least
+    --   one IPv4 interface configured, and likewise for IPv6.
+    --   (Only some platforms support this.)
+      AI_ADDRCONFIG
+    -- | If 'AI_ALL' is specified, return all matching IPv6 and
+    --   IPv4 addresses.  Otherwise, this flag has no effect.
+    --   (Only some platforms support this.)
+    | AI_ALL
+    -- | The 'addrCanonName' field of the first returned
+    --   'AddrInfo' will contain the "canonical name" of the host.
+    | AI_CANONNAME
+    -- | The 'HostName' argument /must/ be a numeric
+    --   address in string form, and network name lookups will not be
+    --   attempted.
+    | AI_NUMERICHOST
+    -- | The 'ServiceName' argument /must/ be a port
+    --   number in string form, and service name lookups will not be
+    --   attempted. (Only some platforms support this.)
+    | AI_NUMERICSERV
+    -- | If no 'HostName' value is provided, the network
+    --   address in each 'SocketAddr'
+    --   will be left as a "wild card".
+    --   This is useful for server applications that
+    --   will accept connections from any client.
+    | AI_PASSIVE
+    -- | If an IPv6 lookup is performed, and no IPv6
+    --   addresses are found, IPv6-mapped IPv4 addresses will be
+    --   returned. (Only some platforms support this.)
+    | AI_V4MAPPED
+    deriving (Eq, Read, Show)
+
+addrInfoFlagMapping :: [(AddrInfoFlag, CInt)]
+addrInfoFlagMapping =
+    [
+#ifdef AI_ADDRCONFIG
+     (AI_ADDRCONFIG, #const AI_ADDRCONFIG),
+#else
+     (AI_ADDRCONFIG, 0),
+#endif
+#ifdef AI_ALL
+     (AI_ALL, #const AI_ALL),
+#else
+     (AI_ALL, 0),
+#endif
+     (AI_CANONNAME, #const AI_CANONNAME),
+     (AI_NUMERICHOST, #const AI_NUMERICHOST),
+#ifdef AI_NUMERICSERV
+     (AI_NUMERICSERV, #const AI_NUMERICSERV),
+#else
+     (AI_NUMERICSERV, 0),
+#endif
+     (AI_PASSIVE, #const AI_PASSIVE),
+#ifdef AI_V4MAPPED
+     (AI_V4MAPPED, #const AI_V4MAPPED)
+#else
+     (AI_V4MAPPED, 0)
+#endif
+    ]
+
+-- | Indicate whether the given 'AddrInfoFlag' will have any effect on this system.
+addrInfoFlagImplemented :: AddrInfoFlag -> Bool
+addrInfoFlagImplemented f = packBits addrInfoFlagMapping [f] /= 0
+
+-- | Address info
+data AddrInfo = AddrInfo {
+    addrFlags :: [AddrInfoFlag]
+  , addrFamily :: SocketFamily
+  , addrSocketType :: SocketType
+  , addrProtocol :: ProtocolNumber
+  , addrAddress :: SocketAddr
+  , addrCanonName :: CBytes
+  } deriving (Eq, Show)
+
+
+instance Storable AddrInfo where
+    sizeOf    _ = #const sizeof(struct addrinfo)
+    alignment _ = alignment (0 :: CInt)
+
+    peek p = do
+        ai_flags <- (#peek struct addrinfo, ai_flags) p
+        ai_family <- (#peek struct addrinfo, ai_family) p
+        ai_socktype <- (#peek struct addrinfo, ai_socktype) p
+        ai_protocol <- (#peek struct addrinfo, ai_protocol) p
+        ai_addr <- (#peek struct addrinfo, ai_addr) p >>= peekSocketAddr
+        ai_canonname_ptr <- (#peek struct addrinfo, ai_canonname) p
+        ai_canonname <- fromCString ai_canonname_ptr
+
+        return $ AddrInfo {
+            addrFlags = unpackBits addrInfoFlagMapping ai_flags
+          , addrFamily = ai_family
+          , addrSocketType = ai_socktype
+          , addrProtocol = ai_protocol
+          , addrAddress = ai_addr
+          , addrCanonName = ai_canonname
+          }
+
+    poke p (AddrInfo flags family sockType protocol _ _) = do
+        (#poke struct addrinfo, ai_flags) p (packBits addrInfoFlagMapping flags)
+        (#poke struct addrinfo, ai_family) p family
+        (#poke struct addrinfo, ai_socktype) p sockType
+        (#poke struct addrinfo, ai_protocol) p protocol
+        -- stuff below is probably not needed, but let's zero it for safety
+        (#poke struct addrinfo, ai_addrlen) p (0::CSize)
+        (#poke struct addrinfo, ai_addr) p nullPtr
+        (#poke struct addrinfo, ai_canonname) p nullPtr
+        (#poke struct addrinfo, ai_next) p nullPtr
+
+-- | Flags that control the querying behaviour of 'getNameInfo'.
+--   For more information, see <https://tools.ietf.org/html/rfc3493#page-30>
+data NameInfoFlag =
+    -- | Resolve a datagram-based service name.  This is
+    --   required only for the few protocols that have different port
+    --   numbers for their datagram-based versions than for their
+    --   stream-based versions.
+      NI_DGRAM
+    -- | If the hostname cannot be looked up, an IO error is thrown.
+    | NI_NAMEREQD
+    -- | If a host is local, return only the hostname part of the FQDN.
+    | NI_NOFQDN
+    -- | The name of the host is not looked up.
+    --   Instead, a numeric representation of the host's
+    --   address is returned.  For an IPv4 address, this will be a
+    --   dotted-quad string.  For IPv6, it will be colon-separated
+    --   hexadecimal.
+    | NI_NUMERICHOST
+    -- | The name of the service is not
+    --   looked up.  Instead, a numeric representation of the
+    --   service is returned.
+    | NI_NUMERICSERV
+    deriving (Eq, Read, Show)
+
+nameInfoFlagMapping :: [(NameInfoFlag, CInt)]
+
+nameInfoFlagMapping = [(NI_DGRAM, #const NI_DGRAM),
+                 (NI_NAMEREQD, #const NI_NAMEREQD),
+                 (NI_NOFQDN, #const NI_NOFQDN),
+                 (NI_NUMERICHOST, #const NI_NUMERICHOST),
+                 (NI_NUMERICSERV, #const NI_NUMERICSERV)]
+
+-- | Default hints for address lookup with 'getAddrInfo'.
+--
+-- >>> addrFlags defaultHints
+-- []
+-- >>> addrFamily defaultHints
+-- AF_UNSPEC
+-- >>> addrSocketType defaultHints
+-- NoSocketType
+-- >>> addrProtocol defaultHints
+-- 0
+
+defaultHints :: AddrInfo
+defaultHints = AddrInfo {
+    addrFlags      = []
+  , addrFamily     = AF_UNSPEC
+  , addrSocketType = SOCK_ANY
+  , addrProtocol   = IPPROTO_DEFAULT
+  , addrAddress    = SocketAddrInet portAny inetAny
+  , addrCanonName  = empty
+  }
+
+-----------------------------------------------------------------------------
+-- | Resolve a host or service name to one or more addresses.
+-- The 'AddrInfo' values that this function returns contain 'SocketAddr'
+-- values that you can use to init TCP connection.
+--
+-- This function is protocol independent.  It can return both IPv4 and
+-- IPv6 address information.
+--
+-- The 'AddrInfo' argument specifies the preferred query behaviour,
+-- socket options, or protocol.  You can override these conveniently
+-- using Haskell's record update syntax on 'defaultHints', for example
+-- as follows:
+--
+-- >>> let hints = defaultHints { addrFlags = [AI_NUMERICHOST], addrSocketType = Stream }
+--
+-- You must provide non empty value for at least one of the 'HostName'
+-- or 'ServiceName' arguments.  'HostName' can be either a numeric
+-- network address (dotted quad for IPv4, colon-separated hex for
+-- IPv6) or a hostname.  In the latter case, its addresses will be
+-- looked up unless 'AI_NUMERICHOST' is specified as a hint.  If you
+-- do not provide a 'HostName' value /and/ do not set 'AI_PASSIVE' as
+-- a hint, network addresses in the result will contain the address of
+-- the loopback interface.
+--
+-- If the query fails, this function throws an IO exception instead of
+-- returning an empty list.  Otherwise, it returns a non-empty list
+-- of 'AddrInfo' values.
+--
+-- There are several reasons why a query might result in several
+-- values.  For example, the queried-for host could be multihomed, or
+-- the service might be available via several protocols.
+--
+-- Note: the order of arguments is slightly different to that defined
+-- for @getaddrinfo@ in RFC 2553.  The 'AddrInfo' parameter comes first
+-- to make partial application easier.
+--
+-- >>> addr:_ <- getAddrInfo (Just hints) "127.0.0.1" "http"
+-- >>> addrAddress addr
+-- 127.0.0.1:80
+--
+getAddrInfo
+    :: Maybe AddrInfo -- ^ preferred socket type or protocol
+    -> HostName -- ^ host name to look up
+    -> ServiceName -- ^ service name to look up
+    -> IO [AddrInfo] -- ^ resolved addresses, with "best" first
+getAddrInfo hints host service = withUVInitDo $
+    bracket
+        (do withCBytes host $ \ ptr_h ->
+                withCBytes service $ \ ptr_s ->
+                maybeWith with filteredHints $ \ ptr_hints ->
+                fst <$> allocPrimSafe (\ ptr_ptr_addrs -> do
+                    throwUVIfMinus_ $ hs_getaddrinfo ptr_h ptr_s ptr_hints ptr_ptr_addrs))
+        freeaddrinfo
+        followAddrInfo
+  where
+#if defined(darwin_HOST_OS)
+    -- Leaving out the service and using AI_NUMERICSERV causes a
+    -- segfault on OS X 10.8.2. This code removes AI_NUMERICSERV
+    -- (which has no effect) in that case.
+    toHints h = h { addrFlags = delete AI_NUMERICSERV (addrFlags h) }
+    filteredHints = if CBytes.null service then toHints <$> hints else hints
+#else
+    filteredHints = hints
+#endif
+
+-- | Peek @addrinfo@ linked list.
+--
+followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo]
+followAddrInfo ptr_ai
+    | ptr_ai == nullPtr = return []
+    | otherwise = do
+        !a  <- peek ptr_ai
+        as <- (# peek struct addrinfo, ai_next) ptr_ai >>= followAddrInfo
+        return (a : as)
+
+-----------------------------------------------------------------------------
+
+
+-- | Resolve an address to a host or service name.
+-- This function is protocol independent.
+-- The list of 'NameInfoFlag' values controls query behaviour.
+--
+-- If a host or service's name cannot be looked up, then the numeric
+-- form of the address or service will be returned.
+--
+-- If the query fails, this function throws an IO exception.
+--
+-- >>> addr:_ <- getAddrInfo (Just defaultHints) "127.0.0.1" "http"
+-- >>> getNameInfo [NI_NUMERICHOST, NI_NUMERICSERV] True True $ addrAddress addr
+-- ("127.0.0.1", "80")
+{-
+-- >>> getNameInfo [] True True $ addrAddress addr
+-- ("localhost", "http")
+-}
+getNameInfo
+    :: [NameInfoFlag] -- ^ flags to control lookup behaviour
+    -> Bool -- ^ whether to look up a hostname
+    -> Bool -- ^ whether to look up a service name
+    -> SocketAddr -- ^ the address to look up
+    -> IO (HostName, ServiceName)
+getNameInfo flags doHost doService addr = withUVInitDo $ do
+    (host, (service, _)) <- allocCBytes (fromIntegral h_len) $ \ ptr_h ->
+        allocCBytes (fromIntegral s_len) $ \ ptr_s ->
+        withSocketAddr addr $ \ ptr_addr -> 
+            throwUVIfMinus_ $ hs_getnameinfo ptr_addr addr_len ptr_h h_len ptr_s s_len cflag
+    return (host, service)
+  where
+    addr_len = sizeOfSocketAddr addr
+    h_len = if doHost then (# const NI_MAXHOST) else 0
+    s_len = if doService then (# const NI_MAXSERV) else 0
+    cflag = packBits nameInfoFlagMapping flags
+
+
+-----------------------------------------------------------------------------
+-- | Pack a list of values into a bitmask.  The possible mappings from
+-- value to bit-to-set are given as the first argument.  We assume
+-- that each value can cause exactly one bit to be set; unpackBits will
+-- break if this property is not true.
+--
+packBits :: (Eq a, Num b, Bits b) => [(a, b)] -> [a] -> b
+{-# INLINE packBits #-}
+packBits mapping xs = List.foldl' go 0 mapping
+  where
+    go acc (k, v) | k `elem` xs = acc .|. v
+                  | otherwise   = acc
+
+-- | Unpack a bitmask into a list of values.
+unpackBits :: (Num b, Bits b) => [(a, b)] -> b -> [a]
+{-# INLINE unpackBits #-}
+-- Be permissive and ignore unknown bit values. At least on OS X,
+-- getaddrinfo returns an ai_flags field with bits set that have no
+-- entry in <netdb.h>.
+unpackBits [] _    = []
+unpackBits ((k,v):xs) r
+    | r .&. v /= 0 = k : unpackBits xs (r .&. complement v)
+    | otherwise    = unpackBits xs r
+
+-----------------------------------------------------------------------------
+foreign import ccall safe "hs_getaddrinfo"
+    hs_getaddrinfo :: CString -- ^ host 
+                      -> CString -- ^ service
+                      -> Ptr AddrInfo   -- ^ hints
+                      -> Ptr (Ptr AddrInfo) -- ^ output addrinfo linked list
+                      -> IO Int
+
+foreign import ccall unsafe "freeaddrinfo" freeaddrinfo :: Ptr AddrInfo -> IO ()
+
+foreign import ccall safe "hs_getnameinfo"
+    hs_getnameinfo :: Ptr SocketAddr
+                      -> CSize
+                      -> CString -- ^ output host 
+                      -> CSize
+                      -> CString -- ^ output service
+                      -> CSize
+                      -> CInt    -- ^ flags
+                      -> IO Int
diff --git a/Z/IO/Network/IPC.hs b/Z/IO/Network/IPC.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Network/IPC.hs
@@ -0,0 +1,180 @@
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+{-|
+Module      : Z.IO.IPC
+Description : Named pipe/Unix domain servers and clients
+Copyright   : (c) Dong Han, 2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides an API for creating IPC servers and clients. IPC Support is implemented with named pipes on Windows, and UNIX domain sockets on other operating systems.
+
+On UNIX, the local domain is also known as the UNIX domain. The path is a filesystem path name. It gets truncated to sizeof(sockaddr_un.sun_path) - 1, which varies on different operating system between 91 and 107 bytes. The typical values are 107 on Linux and 103 on macOS. The path is subject to the same naming conventions and permissions checks as would be done on file creation. It will be visible in the filesystem, and will persist until unlinked.
+
+On Windows, the local domain is implemented using a named pipe. The path must refer to an entry in \\?\pipe\ or \\.\pipe\. Any characters are permitted, but the latter may do some processing of pipe names, such as resolving .. sequences. Despite appearances, the pipe name space is flat. Pipes will not persist, they are removed when the last reference to them is closed.
+
+-}
+
+module Z.IO.Network.IPC (
+  -- * IPC Client
+    IPCClientConfig(..)
+  , defaultIPCClientConfig
+  , initIPCClient
+  -- * IPC Server
+  , IPCServerConfig(..)
+  , defaultIPCServerConfig
+  , startIPCServer
+  ) where
+
+import           Control.Concurrent.MVar
+import           Control.Monad
+import           Control.Monad.IO.Class
+import           Data.Primitive.PrimArray
+import           Foreign.Ptr
+import           GHC.Ptr
+import           Z.Data.CBytes
+import           Z.IO.Buffered
+import           Z.IO.Exception
+import           Z.IO.Resource
+import           Z.IO.UV.FFI
+import           Z.IO.UV.Manager
+import           Data.Coerce
+
+--------------------------------------------------------------------------------
+
+-- | A IPC client configuration
+--
+data IPCClientConfig = IPCClientConfig
+    { ipcClientName :: Maybe CBytes -- ^ bind to a local file path (Unix) or name (Windows),
+                                    -- won't bind if set to 'Nothing'.
+    , ipcTargetName :: CBytes       -- ^ target path (Unix) or a name (Windows).
+    }
+
+-- | Default config, connect to "./ipc".
+--
+defaultIPCClientConfig :: IPCClientConfig
+defaultIPCClientConfig = IPCClientConfig Nothing "./ipc"
+
+-- | init a IPC client 'Resource', which open a new connect when used.
+--
+initIPCClient :: HasCallStack => IPCClientConfig -> Resource UVStream
+initIPCClient (IPCClientConfig cname tname) = do
+    uvm <- liftIO getUVManager
+    client <- initIPCStream uvm
+    let hdl = uvsHandle client
+    liftIO . withCBytes tname $ \ tname_p -> do
+        forM_ cname $ \ cname' ->
+            withCBytes cname' $ \ cname_p ->
+                -- bind is safe without withUVManager
+                throwUVIfMinus_ (uv_pipe_bind hdl cname_p)
+        void . withUVRequest uvm $ \ _ -> hs_uv_pipe_connect hdl tname_p
+    return client
+
+--------------------------------------------------------------------------------
+
+-- | A IPC server configuration
+--
+data IPCServerConfig = IPCServerConfig
+    { ipcListenName       :: CBytes      -- ^ listening path (Unix) or a name (Windows).
+    , ipcListenBacklog    :: Int           -- ^ listening pipe's backlog size, should be large enough(>128)
+    , ipcServerWorker     :: UVStream -> IO ()  -- ^ worker which get an accepted IPC stream,
+                                                -- the socket will be closed upon exception or worker finishes.
+    }
+
+-- | A default hello world server on @./ipc@
+--
+-- Test it with @main = startIPCServer defaultIPCServerConfig@
+--
+defaultIPCServerConfig :: IPCServerConfig
+defaultIPCServerConfig = IPCServerConfig
+    "./ipc"
+    256
+    (\ uvs -> writeOutput uvs (Ptr "hello world"#) 11)
+
+-- | Start a server
+--
+-- Fork new worker thread upon a new connection.
+--
+startIPCServer :: HasCallStack => IPCServerConfig -> IO ()
+startIPCServer IPCServerConfig{..} = do
+    let backLog = max ipcListenBacklog 128
+    serverUVManager <- getUVManager
+    withResource (initIPCStream serverUVManager) $ \ (UVStream serverHandle serverSlot _ _) ->
+        bracket
+            (throwOOMIfNull $ hs_uv_accept_check_alloc serverHandle)
+            hs_uv_accept_check_close $
+            \ check -> do
+-- The buffer passing of accept is a litte complicated here, to get maximum performance,
+-- we do batch accepting. i.e. recv multiple client inside libuv's event loop:
+--
+-- we poke uvmanager's buffer table as a Ptr Word8, with byte size (backLog*sizeof(UVFD))
+-- inside libuv event loop, we cast the buffer back to int32_t* pointer.
+-- each accept callback push a new socket fd to the buffer, and increase a counter(buffer_size_table).
+-- backLog should be large enough(>128), so under windows we can't possibly filled it up within one
+-- uv_run, under unix we hacked uv internal to provide a stop and resume function, when backLog is
+-- reached, we will stop receiving.
+--
+-- once back to haskell side, we read all accepted sockets and fork worker threads.
+-- if backLog is reached, we resume receiving from haskell side.
+--
+-- Step 1.
+-- we allocate a new uv_check_t for given uv_stream_t, with predefined checking callback
+-- see hs_accept_check_cb in hs_uv_stream.c
+                throwUVIfMinus_ $ hs_uv_accept_check_init check
+                withCBytes ipcListenName $ \ name_p -> do
+                    m <- getBlockMVar serverUVManager serverSlot
+                    acceptBuf <- newPinnedPrimArray backLog
+                    let acceptBufPtr = coerce (mutablePrimArrayContents acceptBuf :: Ptr UVFD)
+-- Step 2.
+-- we allocate a buffer to hold accepted FDs, pass it just like a normal reading buffer.
+                    withUVManager' serverUVManager $ do
+                        -- We use buffersize as accepted fd counter, so we write 0 here
+                        pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1)
+                        throwUVIfMinus_ (uv_pipe_bind serverHandle name_p)
+                        throwUVIfMinus_ (hs_uv_listen serverHandle (max 4 (fromIntegral backLog)))
+
+                    forever $ do
+                        -- wait until accept some FDs
+                        !acceptCountDown <- takeMVar m
+-- Step 3.
+-- Copy buffer, fetch accepted FDs and fork worker threads.
+
+                        -- we lock uv manager here in case of next uv_run overwrite current accept buffer
+                        acceptBufCopy <- withUVManager' serverUVManager $ do
+                            _ <- tryTakeMVar m
+                            pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1)
+
+                            -- if acceptCountDown count to -1, we should resume on haskell side
+                            when (acceptCountDown == -1) (hs_uv_listen_resume serverHandle)
+
+                            -- copy accepted FDs
+                            let acceptCount = backLog - 1 - acceptCountDown
+                            acceptBuf' <- newPrimArray acceptCount
+                            copyMutablePrimArray acceptBuf' 0 acceptBuf (acceptCountDown+1) acceptCount
+                            unsafeFreezePrimArray acceptBuf'
+
+                        -- fork worker thread
+                        forM_ [0..sizeofPrimArray acceptBufCopy-1] $ \ i -> do
+                            let fd = indexPrimArray acceptBufCopy i
+                            if fd < 0
+                            -- minus fd indicate a server error and we should close server
+                            then throwUVIfMinus_ (return fd)
+                            -- It's important to use the worker thread's mananger instead of server's one!
+                            else void . forkBa $ do
+                                uvm <- getUVManager
+                                withResource (initUVStream (\ loop hdl -> do
+                                    throwUVIfMinus_ (uv_pipe_init loop hdl 0)
+                                    throwUVIfMinus_ (hs_uv_pipe_open hdl fd)) uvm) $ \ uvs -> do
+                                    ipcServerWorker uvs
+
+--------------------------------------------------------------------------------
+
+initIPCStream :: HasCallStack => UVManager -> Resource UVStream
+initIPCStream = initUVStream (\ loop hdl ->
+    throwUVIfMinus_ (uv_pipe_init loop hdl 0))
diff --git a/Z/IO/Network/SocketAddr.hsc b/Z/IO/Network/SocketAddr.hsc
new file mode 100644
--- /dev/null
+++ b/Z/IO/Network/SocketAddr.hsc
@@ -0,0 +1,536 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE PatternSynonyms    #-}
+
+{-|
+Module      : Z.IO.Network.SocketAddr
+Description : TCP/UDP socket address API
+Copyright   : (c) Winterland, 2018
+License     : BSD
+Maintainer  : drkoster@qq.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides necessary types and constant for low level socket address manipulating.
+
+-}
+
+module Z.IO.Network.SocketAddr
+  ( -- * name to address
+    SocketAddr(..)
+  , sockAddrFamily
+  , peekSocketAddr
+  , withSocketAddr
+  , sizeOfSocketAddr
+  , withSocketAddrStorage
+  , sizeOfSocketAddrStorage
+   -- ** IPv4 address
+  , InetAddr
+  , inetAny
+  , inetBroadcast
+  , inetNone
+  , inetLoopback
+  , inetUnspecificGroup
+  , inetAllHostsGroup
+  , inetMaxLocalGroup
+  , inetAddrToTuple
+  , tupleToInetAddr
+   -- ** IPv6 address
+  , Inet6Addr
+  , inet6Any
+  , inet6Loopback
+  , inet6AddrToTuple
+  , tupleToInet6Addr
+  , FlowInfo
+  , ScopeID
+  -- * port numbber
+  , PortNumber 
+  , portAny
+  , htons
+  , ntohs
+  , ntohl
+  , htonl
+  -- * family, type, protocol
+  , SocketFamily(..)
+  , pattern AF_UNSPEC
+  , pattern AF_INET
+  , pattern AF_INET6
+  , SocketType(..)
+  , pattern SOCK_DGRAM
+  , pattern SOCK_STREAM
+  , pattern SOCK_SEQPACKET
+  , pattern SOCK_RAW
+  , pattern SOCK_RDM
+  , pattern SOCK_ANY
+  , ProtocolNumber(..)
+  , pattern IPPROTO_DEFAULT
+  , pattern IPPROTO_IP
+  , pattern IPPROTO_TCP
+  , pattern IPPROTO_UDP
+  ) where
+
+import           Data.Bits
+import qualified Data.List                as List
+import           Data.Ratio
+import           Data.Typeable
+import           Foreign
+import           Foreign.C
+import           Numeric                  (showHex)
+import           Z.IO.Exception
+import           Z.IO.UV.Errno
+
+#include "hs_uv.h" 
+
+#if defined(i386_HOST_ARCH) && defined(mingw32_HOST_OS)
+#let CALLCONV = "stdcall"
+#else
+#let CALLCONV = "ccall"
+#endif
+
+--------------------------------------------------------------------------------
+
+#if defined(_WIN32)
+type CSaFamily = (#type unsigned short)
+#elif defined(darwin_HOST_OS)
+type CSaFamily = (#type u_char)
+#else
+type CSaFamily = (#type sa_family_t)
+#endif
+
+-- | IPv4 or IPv6 socket address, i.e. the `sockaddr_in` or `sockaddr_in6` struct.
+-- 
+data SocketAddr 
+    = SocketAddrInet
+        {-# UNPACK #-} !PortNumber  -- sin_port  (network byte order)
+        {-# UNPACK #-} !InetAddr    -- sin_addr  (ditto)
+    | SocketAddrInet6
+        {-# UNPACK #-} !PortNumber  -- sin6_port (network byte order)
+        {-# UNPACK #-} !FlowInfo    -- sin6_flowinfo (ditto)
+        {-# UNPACK #-} !Inet6Addr   -- sin6_addr (ditto)
+        {-# UNPACK #-} !ScopeID     -- sin6_scope_id (ditto)
+  deriving (Eq, Ord)
+
+instance Show SocketAddr where
+    showsPrec _ (SocketAddrInet port ia)
+       = shows ia . showString ":" . shows port
+    showsPrec _ (SocketAddrInet6 port _ ia6 _)
+       = ('[':) . shows ia6 . showString "]:" . shows port
+
+-- Taken from showIPv6 in Data.IP.Addr.
+
+-- | Show an IPv6 address in the most appropriate notation, based on recommended
+-- representation proposed by <http://tools.ietf.org/html/rfc5952 RFC 5952>.
+--
+-- /The implementation is completely compatible with the current implementation
+-- of the `inet_ntop` function in glibc./
+
+sockAddrFamily :: SocketAddr -> SocketFamily
+sockAddrFamily (SocketAddrInet _ _) = AF_INET
+sockAddrFamily (SocketAddrInet6 _ _ _ _) = AF_INET6
+
+type FlowInfo = Word32
+type ScopeID = Word32
+
+
+--------------------------------------------------------------------------------
+
+-- | Independent of endianness. For example @127.0.0.1@ is stored as @(127, 0, 0, 1)@.
+--
+-- For direct manipulation prefer 'inetAddrToTuple' and 'tupleToInetAddr'.
+--
+newtype InetAddr = InetAddr Word32 deriving (Eq, Ord)
+instance Show InetAddr where
+    showsPrec _ ia = 
+        let (a,b,c,d) = inetAddrToTuple ia
+        in shows a . ('.':) . shows b . ('.':) . shows c . ('.':) . shows d 
+{-
+-- | Parse IPv4 address in format "a.b.c.d"
+parseInetAddr:: V.Bytes -> Either P.ParseError InetAddr
+parseInetAddr = P.parse_ inetAddrParser
+
+-- | Parse IPv4 address in format "a.b.c.d", octets must be between 0 and 255. 
+inetAddrParser :: P.Parser InetAddr
+inetAddrParser = do
+    a <- oct
+    P.char8 '.'
+    b <- oct
+    P.char8 '.'
+    c <- oct
+    P.char8 '.'
+    d <- oct
+    return $! tupleToInetAddr (a,b,c,d)
+  where
+    oct = do
+        x <- P.uint :: P.Parser Integer
+        if (x > 255) 
+        then fail "all octets in an IPv4 address must be between 0 and 255"
+        else return $! fromIntegral x
+-}
+        
+-- | @0.0.0.0@
+inetAny             :: InetAddr
+inetAny              = InetAddr 0
+
+-- | @255.255.255.255@
+inetBroadcast       :: InetAddr
+inetBroadcast        = tupleToInetAddr (255,255,255,255)
+
+-- | @255.255.255.255@
+inetNone            :: InetAddr
+inetNone             = tupleToInetAddr (255,255,255,255)
+
+-- | @127.0.0.1@
+inetLoopback        :: InetAddr
+inetLoopback         = tupleToInetAddr (127,  0,  0,  1)
+
+-- | @224.0.0.0@
+inetUnspecificGroup :: InetAddr
+inetUnspecificGroup  = tupleToInetAddr (224,  0,  0,  0)
+
+-- | @224.0.0.1@
+inetAllHostsGroup   :: InetAddr
+inetAllHostsGroup    = tupleToInetAddr (224,  0,  0,  1)
+
+-- | @224.0.0.255@
+inetMaxLocalGroup   :: InetAddr
+inetMaxLocalGroup    = tupleToInetAddr (224,  0,  0,255)
+
+instance Storable InetAddr where
+    sizeOf _ = sizeOf (undefined :: Word32)
+    alignment _ = alignment (undefined :: Word32) 
+    peek p = (InetAddr . ntohl) `fmap` peekByteOff p 0
+    poke p (InetAddr ia) = pokeByteOff p 0 (htonl ia)
+
+-- | Converts 'InetAddr' to representation-independent IPv4 quadruple.
+-- For example for @127.0.0.1@ the function will return @(127, 0, 0, 1)@
+-- regardless of host endianness.
+inetAddrToTuple :: InetAddr -> (Word8, Word8, Word8, Word8)
+inetAddrToTuple (InetAddr ia) =
+    let byte i = fromIntegral (ia `shiftR` i) :: Word8
+    in (byte 24, byte 16, byte 8, byte 0)
+
+-- | Converts IPv4 quadruple to 'InetAddr'.
+tupleToInetAddr :: (Word8, Word8, Word8, Word8) -> InetAddr
+tupleToInetAddr (b3, b2, b1, b0) =
+    let x `sl` i = fromIntegral x `shiftL` i :: Word32
+    in InetAddr $ (b3 `sl` 24) .|. (b2 `sl` 16) .|. (b1 `sl` 8) .|. (b0 `sl` 0)
+
+--------------------------------------------------------------------------------
+
+-- | Independent of endianness. For example @::1@ is stored as @(0, 0, 0, 1)@.
+--
+-- For direct manipulation prefer 'inet6AddrToTuple' and 'tupleToInet6Addr'.
+--
+data Inet6Addr = Inet6Addr {-# UNPACK #-}!Word32
+                           {-# UNPACK #-}!Word32
+                           {-# UNPACK #-}!Word32
+                           {-# UNPACK #-}!Word32 deriving (Eq, Ord)
+
+
+instance Show Inet6Addr where
+    showsPrec _ ia6@(Inet6Addr a1 a2 a3 a4)
+        -- IPv4-Mapped IPv6 Address
+        | a1 == 0 && a2 == 0 && a3 == 0xffff =
+          showString "::ffff:" . shows (InetAddr a4)
+        -- IPv4-Compatible IPv6 Address (exclude IPRange ::/112)
+        | a1 == 0 && a2 == 0 && a3 == 0 && a4 >= 0x10000 =
+            showString "::" . shows (InetAddr a4)
+        -- length of longest run > 1, replace it with "::"
+        | end - begin > 1 =
+            showFields prefix . showString "::" . showFields suffix
+        | otherwise =
+            showFields fields
+      where
+        fields =
+            let (u7, u6, u5, u4, u3, u2, u1, u0) = inet6AddrToTuple ia6 in
+            [u7, u6, u5, u4, u3, u2, u1, u0]
+        showFields = foldr (.) id . List.intersperse (':':) . map showHex
+        prefix = take begin fields  -- fields before "::"
+        suffix = drop end fields    -- fields after "::"
+        begin = end + diff          -- the longest run of zeros
+        (diff, end) = minimum $
+            scanl (\c i -> if i == 0 then c - 1 else 0) 0 fields `zip` [0..]
+
+{-
+-- | Parse IPv6 address in format "a.b.c.d"
+parseInet6Addr:: V.Bytes -> Either P.ParseError Inet6Addr
+parseInet6Addr = P.parse_ inet6AddrParser
+
+-- | Parse IPv6 address in format "a.b.c.d"
+--
+-- Octets must be between 0 and 255. 
+-- Note: if octets exceed (maxBound :: Int), parser will overflow.
+inet6AddrParser :: P.Parser Inet6Addr
+inet6AddrParser = do
+    a <- oct
+    P.char8 '.'
+    b <- oct
+    P.char8 '.'
+    c <- oct
+    P.char8 '.'
+    d <- oct
+    return $! tupleToInetAddr (a,b,c,d)
+  where
+    oct = do
+        x <- P.uint :: P.Parser Int
+        if (x > 255) 
+        then fail "all octets in an IPv4 address must be between 0 and 255"
+        else return $! fromIntegral x
+-}
+
+-- | @::@
+inet6Any      :: Inet6Addr
+inet6Any       = Inet6Addr 0 0 0 0
+
+-- | @::1@
+inet6Loopback :: Inet6Addr
+inet6Loopback  = Inet6Addr 0 0 0 1
+
+inet6AddrToTuple :: Inet6Addr -> (Word16, Word16, Word16, Word16,
+                                        Word16, Word16, Word16, Word16)
+inet6AddrToTuple (Inet6Addr w3 w2 w1 w0) =
+    let high, low :: Word32 -> Word16
+        high w = fromIntegral (w `shiftR` 16)
+        low w = fromIntegral w
+    in (high w3, low w3, high w2, low w2, high w1, low w1, high w0, low w0)
+
+tupleToInet6Addr :: (Word16, Word16, Word16, Word16,
+                        Word16, Word16, Word16, Word16) -> Inet6Addr
+tupleToInet6Addr (w7, w6, w5, w4, w3, w2, w1, w0) =
+    let add :: Word16 -> Word16 -> Word32
+        high `add` low = (fromIntegral high `shiftL` 16) .|. (fromIntegral low)
+    in  Inet6Addr (w7 `add` w6) (w5 `add` w4) (w3 `add` w2) (w1 `add` w0)
+
+instance Storable Inet6Addr where
+    sizeOf _    = #size struct in6_addr
+    alignment _ = #alignment struct in6_addr
+
+    peek p = do
+        a <- peek32 p 0
+        b <- peek32 p 1
+        c <- peek32 p 2
+        d <- peek32 p 3
+        return $ Inet6Addr a b c d
+
+    poke p (Inet6Addr a b c d) = do
+        poke32 p 0 a
+        poke32 p 1 b
+        poke32 p 2 c
+        poke32 p 3 d
+
+--------------------------------------------------------------------------------
+
+peekSocketAddr :: HasCallStack => Ptr SocketAddr -> IO SocketAddr
+peekSocketAddr p = do
+    family <- (#peek struct sockaddr, sa_family) p
+    case family :: CSaFamily of
+        (#const AF_INET) -> do
+            addr <- (#peek struct sockaddr_in, sin_addr) p
+            port <- (#peek struct sockaddr_in, sin_port) p
+            return (SocketAddrInet (PortNumber port) addr)
+        (#const AF_INET6) -> do
+            port <- (#peek struct sockaddr_in6, sin6_port) p
+            flow <- (#peek struct sockaddr_in6, sin6_flowinfo) p
+            addr <- (#peek struct sockaddr_in6, sin6_addr) p
+            scope <- (#peek struct sockaddr_in6, sin6_scope_id) p
+            return (SocketAddrInet6 (PortNumber port) flow addr scope)
+
+        _ -> do let errno = UV_EAI_ADDRFAMILY
+                name <- uvErrName errno
+                desc <- uvStdError errno
+                throwUVError errno (IOEInfo name desc callStack)
+
+pokeSocketAddr :: HasCallStack => Ptr SocketAddr -> SocketAddr -> IO ()
+pokeSocketAddr p (SocketAddrInet (PortNumber port) addr) =  do
+#if defined(darwin_HOST_OS)
+    clearPtr p (#size struct sockaddr_in)
+#endif
+#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
+    (#poke struct sockaddr_in, sin_len) p ((#size struct sockaddr_in) :: Word8)
+#endif
+    (#poke struct sockaddr_in, sin_family) p ((#const AF_INET) :: CSaFamily)
+    (#poke struct sockaddr_in, sin_port) p port
+    (#poke struct sockaddr_in, sin_addr) p addr
+pokeSocketAddr p (SocketAddrInet6 (PortNumber port) flow addr scope) =  do
+#if defined(darwin_HOST_OS)
+    clearPtr p (#size struct sockaddr_in6)
+#endif
+#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
+    (#poke struct sockaddr_in6, sin6_len) p ((#size struct sockaddr_in6) :: Word8)
+#endif
+    (#poke struct sockaddr_in6, sin6_family) p ((#const AF_INET6) :: CSaFamily)
+    (#poke struct sockaddr_in6, sin6_port) p port
+    (#poke struct sockaddr_in6, sin6_flowinfo) p flow
+    (#poke struct sockaddr_in6, sin6_addr) p (addr)
+    (#poke struct sockaddr_in6, sin6_scope_id) p scope
+
+withSocketAddr :: SocketAddr -> (Ptr SocketAddr -> IO a) -> IO a
+withSocketAddr sa@(SocketAddrInet _ _) f = do
+    allocaBytesAligned
+        (#size struct sockaddr_in)
+        (#alignment struct sockaddr_in) $ \ p -> pokeSocketAddr p sa >> f p
+withSocketAddr sa@(SocketAddrInet6 _ _ _ _) f = do
+    allocaBytesAligned 
+        (#size struct sockaddr_in6) 
+        (#alignment struct sockaddr_in6) $ \ p -> pokeSocketAddr p sa >> f p
+
+sizeOfSocketAddr :: SocketAddr -> CSize
+sizeOfSocketAddr (SocketAddrInet _ _) = #size struct sockaddr_in
+sizeOfSocketAddr (SocketAddrInet6 _ _ _ _) = #size struct sockaddr_in6
+
+withSocketAddrStorage :: (Ptr SocketAddr -> IO ()) -> IO SocketAddr
+withSocketAddrStorage f = do
+    allocaBytesAligned
+        (#size struct sockaddr_storage)
+        (#alignment struct sockaddr_storage) $ \ p -> f p >> peekSocketAddr p
+
+sizeOfSocketAddrStorage :: CSize
+sizeOfSocketAddrStorage = #size struct sockaddr_storage
+
+-- The peek32 and poke32 functions work around the fact that the RFCs
+-- don't require 32-bit-wide address fields to be present.  We can
+-- only portably rely on an 8-bit field, s6_addr.
+
+s6_addr_offset :: Int
+s6_addr_offset = (#offset struct in6_addr, s6_addr)
+
+peek32 :: Ptr a -> Int -> IO Word32
+peek32 p i0 = do
+    let i' = i0 * 4
+        peekByte n = peekByteOff p (s6_addr_offset + i' + n) :: IO Word8
+        a `sl` i = fromIntegral a `shiftL` i
+    a0 <- peekByte 0
+    a1 <- peekByte 1
+    a2 <- peekByte 2
+    a3 <- peekByte 3
+    return ((a0 `sl` 24) .|. (a1 `sl` 16) .|. (a2 `sl` 8) .|. (a3 `sl` 0))
+
+poke32 :: Ptr a -> Int -> Word32 -> IO ()
+poke32 p i0 a = do
+    let i' = i0 * 4
+        pokeByte n = pokeByteOff p (s6_addr_offset + i' + n)
+        x `sr` i = fromIntegral (x `shiftR` i) :: Word8
+    pokeByte 0 (a `sr` 24)
+    pokeByte 1 (a `sr` 16)
+    pokeByte 2 (a `sr`  8)
+    pokeByte 3 (a `sr`  0)
+
+--------------------------------------------------------------------------------
+
+-- Port Numbers
+
+-- | Use the @Num@ instance (i.e. use a literal or 'fromIntegral') to create a
+-- @PortNumber@ value with the correct network-byte-ordering.
+--
+-- >>> 1 :: PortNumber
+-- 1
+-- >>> read "1" :: PortNumber
+-- 1
+newtype PortNumber = PortNumber Word16 deriving (Eq, Ord, Typeable)
+-- newtyped to prevent accidental use of sane-looking
+-- port numbers that haven't actually been converted to
+-- network-byte-order first.
+
+portAny :: PortNumber
+portAny = 0
+
+instance Show PortNumber where
+  showsPrec p pn = showsPrec p (portNumberToInt pn)
+
+instance Read PortNumber where
+  readsPrec n = map (\(x,y) -> (intToPortNumber x, y)) . readsPrec n
+
+intToPortNumber :: Int -> PortNumber
+intToPortNumber v = PortNumber (htons (fromIntegral v))
+
+portNumberToInt :: PortNumber -> Int
+portNumberToInt (PortNumber po) = fromIntegral (ntohs po)
+
+foreign import #{CALLCONV} unsafe "ntohs" ntohs :: Word16 -> Word16
+foreign import #{CALLCONV} unsafe "htons" htons :: Word16 -> Word16
+foreign import #{CALLCONV} unsafe "ntohl" ntohl :: Word32 -> Word32
+foreign import #{CALLCONV} unsafe "htonl" htonl :: Word32 -> Word32
+
+instance Enum PortNumber where
+    toEnum   = intToPortNumber
+    fromEnum = portNumberToInt
+
+instance Num PortNumber where
+   fromInteger i = intToPortNumber (fromInteger i)
+    -- for completeness.
+   (+) x y   = intToPortNumber (portNumberToInt x + portNumberToInt y)
+   (-) x y   = intToPortNumber (portNumberToInt x - portNumberToInt y)
+   negate x  = intToPortNumber (-portNumberToInt x)
+   (*) x y   = intToPortNumber (portNumberToInt x * portNumberToInt y)
+   abs n     = intToPortNumber (abs (portNumberToInt n))
+   signum n  = intToPortNumber (signum (portNumberToInt n))
+
+instance Real PortNumber where
+    toRational x = toInteger x % 1
+
+instance Integral PortNumber where
+    quotRem a b = let (c,d) = quotRem (portNumberToInt a) (portNumberToInt b) in
+                  (intToPortNumber c, intToPortNumber d)
+    toInteger a = toInteger (portNumberToInt a)
+
+instance Storable PortNumber where
+   sizeOf    _ = sizeOf    (undefined :: Word16)
+   alignment _ = alignment (undefined :: Word16)
+   poke p (PortNumber po) = poke (castPtr p) po
+   peek p = PortNumber `fmap` peek (castPtr p)
+    
+--------------------------------------------------------------------------------
+
+newtype SocketFamily = SocketFamily CInt deriving (Show, Read, Eq, Ord, Typeable)
+newtype SocketType = SocketType CInt deriving (Show, Read, Eq, Ord, Typeable)
+newtype ProtocolNumber = ProtocolNumber CInt deriving (Show, Read, Eq, Ord, Typeable)
+
+instance Storable SocketFamily where                      
+    sizeOf _ = sizeOf (undefined :: CInt)       
+    alignment _ = alignment (undefined :: CInt) 
+    peek ptr = SocketFamily `fmap` peek (castPtr ptr)             
+    poke ptr (SocketFamily v) = poke (castPtr ptr) v
+
+instance Storable SocketType where                      
+    sizeOf _ = sizeOf (undefined :: CInt)       
+    alignment _ = alignment (undefined :: CInt) 
+    peek ptr = SocketType `fmap` peek (castPtr ptr)             
+    poke ptr (SocketType v) = poke (castPtr ptr) v
+
+instance Storable ProtocolNumber where                      
+    sizeOf _ = sizeOf (undefined :: CInt)       
+    alignment _ = alignment (undefined :: CInt) 
+    peek ptr = ProtocolNumber `fmap` peek (castPtr ptr)             
+    poke ptr (ProtocolNumber v) = poke (castPtr ptr) v
+
+-- | unspecified
+pattern AF_UNSPEC :: SocketFamily
+pattern AF_UNSPEC = SocketFamily (#const AF_UNSPEC)
+-- | internetwork: UDP, TCP, etc
+pattern AF_INET :: SocketFamily
+pattern AF_INET = SocketFamily (#const AF_INET)
+-- | Internet Protocol version 6
+pattern AF_INET6 :: SocketFamily
+pattern AF_INET6 = SocketFamily (#const AF_INET6)
+
+pattern SOCK_STREAM :: SocketType
+pattern SOCK_STREAM = SocketType (#const SOCK_STREAM)
+pattern SOCK_DGRAM :: SocketType
+pattern SOCK_DGRAM = SocketType (#const SOCK_DGRAM)
+pattern SOCK_RAW :: SocketType
+pattern SOCK_RAW = SocketType (#const SOCK_RAW)
+pattern SOCK_RDM :: SocketType
+pattern SOCK_RDM = SocketType (#const SOCK_RDM)
+pattern SOCK_SEQPACKET :: SocketType
+pattern SOCK_SEQPACKET = SocketType (#const SOCK_SEQPACKET)
+-- | Used in getAddrInfo hints, for any type can be returned by getAddrInfo
+pattern SOCK_ANY :: SocketType
+pattern SOCK_ANY = SocketType 0
+
+pattern IPPROTO_DEFAULT :: ProtocolNumber
+pattern IPPROTO_DEFAULT = ProtocolNumber 0
+pattern IPPROTO_IP :: ProtocolNumber
+pattern IPPROTO_IP = ProtocolNumber (#const IPPROTO_IP)
+pattern IPPROTO_TCP :: ProtocolNumber
+pattern IPPROTO_TCP = ProtocolNumber (#const IPPROTO_TCP)
+pattern IPPROTO_UDP :: ProtocolNumber
+pattern IPPROTO_UDP = ProtocolNumber (#const IPPROTO_UDP)
diff --git a/Z/IO/Network/TCP.hs b/Z/IO/Network/TCP.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Network/TCP.hs
@@ -0,0 +1,183 @@
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+{-|
+Module      : Z.IO.Network.TCP
+Description : TCP servers and clients
+Copyright   : (c) Dong Han, 2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides an API for creating TCP servers and clients.
+
+-}
+
+module Z.IO.Network.TCP (
+  -- * TCP Client
+    TCPClientConfig(..)
+  , defaultTCPClientConfig
+  , initTCPClient
+  -- * TCP Server
+  , TCPServerConfig(..)
+  , defaultTCPServerConfig
+  , startTCPServer
+  ) where
+
+import           Control.Concurrent.MVar
+import           Control.Monad
+import           Control.Monad.IO.Class
+import           Data.Primitive.PrimArray
+import           Foreign.Ptr
+import           GHC.Ptr
+import           Z.IO.Buffered
+import           Z.IO.Exception
+import           Z.IO.Network.SocketAddr
+import           Z.IO.Resource
+import           Z.IO.UV.FFI
+import           Z.IO.UV.Manager
+import           Data.Coerce
+
+--------------------------------------------------------------------------------
+
+-- | A TCP client configuration
+--
+data TCPClientConfig = TCPClientConfig
+    { tcpClientAddr :: Maybe SocketAddr -- ^ assign a local address, or let OS pick one
+    , tcpRemoteAddr :: SocketAddr      -- ^ remote target address
+    , tcpNoDelay :: Bool             -- ^ if we want to use @TCP_NODELAY@
+    }
+
+-- | Default config, connect to @localhost:8888@.
+defaultTCPClientConfig :: TCPClientConfig
+defaultTCPClientConfig = TCPClientConfig Nothing (SocketAddrInet 8888 inetLoopback) True
+
+-- | init a TCP client 'Resource', which open a new connect when used.
+--
+initTCPClient :: HasCallStack => TCPClientConfig -> Resource UVStream
+initTCPClient TCPClientConfig{..} = do
+    uvm <- liftIO getUVManager
+    client <- initTCPStream uvm
+    let hdl = uvsHandle client
+    liftIO . withSocketAddr tcpRemoteAddr $ \ targetPtr -> do
+        forM_ tcpClientAddr $ \ tcpClientAddr' ->
+            withSocketAddr tcpClientAddr' $ \ localPtr ->
+                -- bind is safe without withUVManager
+                throwUVIfMinus_ (uv_tcp_bind hdl localPtr 0)
+        -- nodelay is safe without withUVManager
+        when tcpNoDelay $ throwUVIfMinus_ (uv_tcp_nodelay hdl 1)
+        void . withUVRequest uvm $ \ _ -> hs_uv_tcp_connect hdl targetPtr
+    return client
+
+--------------------------------------------------------------------------------
+
+-- | A TCP server configuration
+--
+data TCPServerConfig = TCPServerConfig
+    { tcpListenAddr       :: SocketAddr      -- ^ listening address
+    , tcpListenBacklog    :: Int           -- ^ listening socket's backlog size, should be large enough(>128)
+    , tcpServerWorkerNoDelay :: Bool       -- ^ if we want to use @TCP_NODELAY@
+    , tcpServerWorker     :: UVStream -> IO ()  -- ^ worker which get an accepted TCP stream,
+                                            -- the socket will be closed upon exception or worker finishes.
+    }
+
+-- | A default hello world server on localhost:8888
+--
+-- Test it with @main = startTCPServer defaultTCPServerConfig@, now try @nc -v 127.0.0.1 8888@
+--
+defaultTCPServerConfig :: TCPServerConfig
+defaultTCPServerConfig = TCPServerConfig
+    (SocketAddrInet 8888 inetAny)
+    256
+    True
+    (\ uvs -> writeOutput uvs (Ptr "hello world"#) 11)
+
+-- | Start a server
+--
+-- Fork new worker thread upon a new connection.
+--
+startTCPServer :: HasCallStack => TCPServerConfig -> IO ()
+startTCPServer TCPServerConfig{..} = do
+    let backLog = max tcpListenBacklog 128
+    serverUVManager <- getUVManager
+    withResource (initTCPStream serverUVManager) $ \ (UVStream serverHandle serverSlot _ _) ->
+        bracket
+            (throwOOMIfNull $ hs_uv_accept_check_alloc serverHandle)
+            hs_uv_accept_check_close $
+            \ check -> do
+-- The buffer passing of accept is a litte complicated here, to get maximum performance,
+-- we do batch accepting. i.e. recv multiple client inside libuv's event loop:
+--
+-- we poke uvmanager's buffer table as a Ptr Word8, with byte size (backLog*sizeof(UVFD))
+-- inside libuv event loop, we cast the buffer back to int32_t* pointer.
+-- each accept callback push a new socket fd to the buffer, and increase a counter(buffer_size_table).
+-- backLog should be large enough(>128), so under windows we can't possibly filled it up within one
+-- uv_run, under unix we hacked uv internal to provide a stop and resume function, when backLog is
+-- reached, we will stop receiving.
+--
+-- once back to haskell side, we read all accepted sockets and fork worker threads.
+-- if backLog is reached, we resume receiving from haskell side.
+--
+-- Step 1.
+-- we allocate a new uv_check_t for given uv_stream_t, with predefined checking callback
+-- see hs_accept_check_cb in hs_uv_stream.c
+                throwUVIfMinus_ $ hs_uv_accept_check_init check
+                withSocketAddr tcpListenAddr $ \ addrPtr -> do
+                    m <- getBlockMVar serverUVManager serverSlot
+                    acceptBuf <- newPinnedPrimArray backLog
+                    let acceptBufPtr = coerce (mutablePrimArrayContents acceptBuf :: Ptr UVFD)
+-- Step 2.
+-- we allocate a buffer to hold accepted FDs, pass it just like a normal reading buffer.
+                    withUVManager' serverUVManager $ do
+                        -- We use buffersize as accepted fd count(count backwards)
+                        pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1)
+                        throwUVIfMinus_ (uv_tcp_bind serverHandle addrPtr 0)
+                        throwUVIfMinus_ (hs_uv_listen serverHandle (max 4 (fromIntegral backLog)))
+
+                    forever $ do
+                        -- wait until accept some FDs
+                        !acceptCountDown <- takeMVar m
+-- Step 3.
+-- Copy buffer, fetch accepted FDs and fork worker threads.
+
+                        -- we lock uv manager here in case of next uv_run overwrite current accept buffer
+                        acceptBufCopy <- withUVManager' serverUVManager $ do
+                            _ <- tryTakeMVar m
+                            pokeBufferTable serverUVManager serverSlot acceptBufPtr (backLog-1)
+
+                            -- if acceptCountDown count to -1, we should resume on haskell side
+                            when (acceptCountDown == -1) (hs_uv_listen_resume serverHandle)
+
+                            -- copy accepted FDs
+                            let acceptCount = backLog - 1 - acceptCountDown
+                            acceptBuf' <- newPrimArray acceptCount
+                            copyMutablePrimArray acceptBuf' 0 acceptBuf (acceptCountDown+1) acceptCount
+                            unsafeFreezePrimArray acceptBuf'
+
+                        -- fork worker thread
+                        forM_ [0..sizeofPrimArray acceptBufCopy-1] $ \ i -> do
+                            let fd = indexPrimArray acceptBufCopy i
+                            if fd < 0
+                            -- minus fd indicate a server error and we should close server
+                            then throwUVIfMinus_ (return fd)
+                            -- It's important to use the worker thread's mananger instead of server's one!
+                            else void . forkBa $ do
+                                uvm <- getUVManager
+                                withResource (initUVStream (\ loop hdl -> do
+                                    throwUVIfMinus_ (uv_tcp_init loop hdl)
+                                    throwUVIfMinus_ (hs_uv_tcp_open hdl fd)) uvm) $ \ uvs -> do
+                                    when tcpServerWorkerNoDelay . throwUVIfMinus_ $
+                                        -- safe without withUVManager
+                                        uv_tcp_nodelay (uvsHandle uvs) 1
+                                    tcpServerWorker uvs
+
+--------------------------------------------------------------------------------
+
+initTCPStream :: HasCallStack => UVManager -> Resource UVStream
+initTCPStream = initUVStream (\ loop hdl ->
+    throwUVIfMinus_ (uv_tcp_init loop hdl))
diff --git a/Z/IO/Network/UDP.hs b/Z/IO/Network/UDP.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Network/UDP.hs
@@ -0,0 +1,434 @@
+{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE PatternSynonyms #-}
+
+{-|
+Module      : Z.IO.Network.UDP
+Description : UDP servers and clients
+Copyright   : (c) Dong Han, 2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides an API for creating UDP sender and receiver.
+
+* Socket FD is created lazily if no local address is provided, that means various functions
+  that need FD will throw bad FD exception if you 'initUDP' with no local address e.g. 'setTTL'.
+
+* If you want to create socket FD but don't care about which port or interface you're using,
+  use 'SocketAddrInet' 'portAny' 'inetAny' when 'initUDP'.
+
+* Prefer 'recvUDPLoop' because it can reuse receive buffer.
+
+-}
+
+module Z.IO.Network.UDP (
+  -- * TCP Client
+    UDP
+  , initUDP
+  , UDPConfig(..)
+  , defaultUDPConfig
+  , UVUDPFlag(UV_UDP_DEFAULT, UV_UDP_IPV6ONLY, UV_UDP_REUSEADDR)
+  , sendUDP
+  , UDPRecvConfig(..)
+  , defaultUDPRecvConfig
+  , recvUDPLoop
+  , recvUDP
+  , getSockName
+  -- * Connected UDP Client
+  , ConnectedUDP
+  , connectUDP
+  , disconnectUDP
+  , getPeerName
+  , sendConnectedUDP
+  -- * multicast and broadcast
+  , UVMembership(UV_JOIN_GROUP, UV_LEAVE_GROUP)
+  , setMembership
+  , setSourceMembership
+  , setMulticastLoop
+  , setMulticastTTL
+  , setMulticastInterface
+  , setBroadcast
+  , setTTL
+  ) where
+
+import Control.Concurrent.MVar
+import Control.Monad
+import Control.Monad.Primitive  (primitive_)
+import Data.Primitive.PrimArray as A
+import Data.IORef
+import Data.Word
+import Data.Int
+import Data.Bits ((.&.))
+import Foreign.Storable (peek, poke)
+import Foreign.Ptr (plusPtr)
+import Foreign.C
+import GHC.Prim                 (touch#)
+import Z.Data.Array           as A
+import Z.Data.Vector.Base     as V
+import Z.Data.Vector.Extra    as V
+import Z.Data.CBytes          as CBytes
+import Z.IO.Network.SocketAddr
+import Z.Foreign
+import Z.IO.UV.Errno          (pattern UV_EMSGSIZE)
+import Z.IO.UV.FFI
+import Z.IO.UV.Manager
+import Z.IO.Exception
+import Z.IO.Resource
+
+-- | UDP socket client.
+--
+-- UDP is not a sequential protocol, thus not an instance of 'Input/Output'.
+-- Message are received or sent individually, UDP socket client is NOT thread safe!
+-- Use 'MVar' 'UDP' in multiple threads.
+--
+data UDP = UDP
+    { udpHandle  :: {-# UNPACK #-} !(Ptr UVHandle)
+    , udpSlot    :: {-# UNPACK #-} !UVSlot
+    , udpManager :: UVManager
+    , udpSendBuffer ::  {-# UNPACK #-} !(A.MutablePrimArray RealWorld Word8)
+    , udpClosed  :: {-# UNPACK #-} !(IORef Bool)
+    }
+
+instance Show UDP where
+    show (UDP hdl slot uvm _ _) =
+        "UDP{udpHandle=" ++ show hdl ++
+                ",udpSlot=" ++ show slot ++
+                ",udpManager=" ++ show uvm ++ "}"
+
+-- | UDP options.
+--
+-- Though technically message length field in the UDP header is a max of 65535, but large packets
+-- could be more likely dropped by routers,
+-- usually a packet(IPV4) with a payload <= 508 bytes is considered safe.
+data UDPConfig = UDPConfig
+    { udpSendMsgSize :: {-# UNPACK #-} !Int           -- ^ maximum size of sending buffer
+    , udpLocalAddr :: Maybe (SocketAddr, UVUDPFlag) -- ^ do we want bind a local address before receiving & sending?
+                                                   --   set to Nothing to let OS pick a random one.
+    } deriving (Show, Eq, Ord)
+
+-- | @UDPConfig 512 Nothing@
+defaultUDPConfig :: UDPConfig
+defaultUDPConfig = UDPConfig 512 Nothing
+
+-- | Initialize a UDP socket.
+--
+initUDP :: HasCallStack
+        => UDPConfig
+        -> Resource UDP
+initUDP (UDPConfig sbsiz maddr) = initResource
+    (do uvm <- getUVManager
+        (hdl, slot) <- withUVManager uvm $ \ loop -> do
+            hdl <- hs_uv_handle_alloc loop
+            slot <- getUVSlot uvm (peekUVHandleData hdl)
+            -- clean up
+            _ <- tryTakeMVar =<< getBlockMVar uvm slot
+
+            -- init uv struct
+            (do throwUVIfMinus_ (uv_udp_init loop hdl)
+                -- bind the socket if address is available
+                forM_ maddr $ \ (addr, flag) ->
+                    withSocketAddr addr $ \ p ->
+                        throwUVIfMinus_ (uv_udp_bind hdl p flag)
+                ) `onException` hs_uv_handle_free hdl
+            return (hdl, slot)
+
+        sbuf <- A.newPinnedPrimArray (max 0 sbsiz)
+        closed <- newIORef False
+        return (UDP hdl slot uvm sbuf closed))
+    (\ (UDP hdl _ uvm _  closed) -> withUVManager' uvm $ do
+        c <- readIORef closed
+        -- hs_uv_handle_close won't return error
+        unless c $ writeIORef closed True >> hs_uv_handle_close hdl)
+
+checkUDPClosed :: HasCallStack => UDP -> IO ()
+checkUDPClosed udp = do
+    c <- readIORef (udpClosed udp)
+    when c throwECLOSED
+
+-- | Get the local IP and port of the 'UDP'.
+getSockName :: HasCallStack => UDP -> IO SocketAddr
+getSockName udp@(UDP hdl _ _ _ _) = do
+    checkUDPClosed udp
+    withSocketAddrStorage $ \ paddr ->
+        void $ withPrimUnsafe (fromIntegral sizeOfSocketAddrStorage :: CInt) $ \ plen ->
+            throwUVIfMinus_ (uv_udp_getsockname hdl paddr plen)
+
+-- | Wrapper for a connected 'UDP'.
+newtype ConnectedUDP = ConnectedUDP UDP deriving Show
+
+-- | Associate the UDP handle to a remote address and port,
+-- so every message sent by this handle is automatically sent to that destination
+connectUDP :: HasCallStack => UDP -> SocketAddr -> IO ConnectedUDP
+connectUDP udp@(UDP hdl _ _ _ _) addr = do
+    checkUDPClosed udp
+    withSocketAddr addr $ \ paddr ->
+        throwUVIfMinus_ (uv_udp_connect hdl paddr)
+    return (ConnectedUDP udp)
+
+-- | Disconnect the UDP handle from a remote address and port.
+disconnectUDP :: HasCallStack => ConnectedUDP -> IO UDP
+disconnectUDP (ConnectedUDP udp@(UDP hdl _ _ _ _)) = do
+    checkUDPClosed udp
+    throwUVIfMinus_ (uv_udp_connect hdl nullPtr)
+    return udp
+
+-- | Get the remote IP and port on 'ConnectedUDP'.
+getPeerName :: HasCallStack => ConnectedUDP -> IO SocketAddr
+getPeerName (ConnectedUDP udp@(UDP hdl _ _ _ _)) = do
+    checkUDPClosed udp
+    withSocketAddrStorage $ \ paddr ->
+        void $ withPrimUnsafe (fromIntegral sizeOfSocketAddrStorage :: CInt) $ \ plen ->
+            throwUVIfMinus_ (uv_udp_getpeername hdl paddr plen)
+
+-- | Send a UDP message with a connected UDP.
+--
+-- WARNING: A 'InvalidArgument' with errno 'UV_EMSGSIZE' will be thrown
+-- if message is larger than 'sendMsgSize'.
+sendConnectedUDP :: HasCallStack => ConnectedUDP -> V.Bytes -> IO ()
+sendConnectedUDP (ConnectedUDP udp@(UDP hdl _ uvm sbuf _)) (V.PrimVector ba s la) = mask_ $ do
+    checkUDPClosed udp
+    -- copy message to pinned buffer
+    lb <- getSizeofMutablePrimArray sbuf
+    when (la > lb) (throwUVIfMinus_ (return UV_EMSGSIZE))
+    copyPrimArray sbuf 0 ba s la
+    withMutablePrimArrayContents sbuf $ \ pbuf -> do
+        m <- withUVManager' uvm $ do
+            reqSlot <- getUVSlot uvm (hs_uv_udp_send hdl nullPtr pbuf la)
+            reqMVar <- getBlockMVar uvm reqSlot
+            _ <- tryTakeMVar reqMVar
+            return reqMVar
+        -- we can't cancel uv_udp_send_t in current libuv
+        -- and disaster will happen if buffer got collected.
+        -- so we have to turn to uninterruptibleMask_'s help.
+        -- i.e. sendUDP is an uninterruptible operation.
+        -- OS will guarantee writing a socket will not
+        -- hang forever anyway.
+        throwUVIfMinus_  (uninterruptibleMask_ $ takeMVar m)
+
+-- | Send a UDP message to target address.
+--
+-- WARNING: A 'InvalidArgument' with errno 'UV_EMSGSIZE' will be thrown
+-- if message is larger than 'sendMsgSize'.
+sendUDP :: HasCallStack => UDP -> SocketAddr -> V.Bytes -> IO ()
+sendUDP udp@(UDP hdl _ uvm sbuf _) addr (V.PrimVector ba s la) = mask_ $ do
+    checkUDPClosed udp
+    -- copy message to pinned buffer
+    lb <- getSizeofMutablePrimArray sbuf
+    when (la > lb) (throwUVIfMinus_ (return UV_EMSGSIZE))
+    copyPrimArray sbuf 0 ba s la
+    withSocketAddr addr $ \ paddr ->
+        withMutablePrimArrayContents sbuf $ \ pbuf -> do
+            m <- withUVManager' uvm $ do
+                reqSlot <- getUVSlot uvm (hs_uv_udp_send hdl paddr pbuf la)
+                reqMVar <- getBlockMVar uvm reqSlot
+                _ <- tryTakeMVar reqMVar
+                return reqMVar
+            -- we can't cancel uv_udp_send_t in current libuv
+            -- and disaster will happen if buffer got collected.
+            -- so we have to turn to uninterruptibleMask_'s help.
+            -- i.e. sendUDP is an uninterruptible operation.
+            -- OS will guarantee writing a socket will not
+            -- hang forever anyway.
+            throwUVIfMinus_  (uninterruptibleMask_ $ takeMVar m)
+
+-- | Set IP multicast loop flag. Makes multicast packets loop back to local sockets.
+setMulticastLoop :: HasCallStack => UDP -> Bool -> IO ()
+setMulticastLoop udp@(UDP hdl _ _ _ _) loop = do
+    checkUDPClosed udp
+    throwUVIfMinus_ (uv_udp_set_multicast_loop hdl (if loop then 1 else 0))
+
+-- | Set the multicast ttl.
+setMulticastTTL :: HasCallStack => UDP -> Int -> IO ()
+setMulticastTTL udp@(UDP hdl _ _ _ _) ttl = do
+    checkUDPClosed udp
+    throwUVIfMinus_ (uv_udp_set_multicast_ttl hdl (fromIntegral ttl'))
+  where ttl' = V.rangeCut ttl 1 255
+
+-- | Set the multicast interface to send or receive data on.
+setMulticastInterface :: HasCallStack => UDP -> CBytes ->IO ()
+setMulticastInterface udp@(UDP hdl _ _ _ _) iaddr = do
+    checkUDPClosed udp
+    withCBytes iaddr $ \ iaddrp ->
+        throwUVIfMinus_ (uv_udp_set_multicast_interface hdl iaddrp)
+
+-- | Set broadcast on or off.
+setBroadcast :: HasCallStack => UDP -> Bool -> IO ()
+setBroadcast udp@(UDP hdl _ _ _ _) b = do
+    checkUDPClosed udp
+    throwUVIfMinus_ (uv_udp_set_broadcast hdl (if b then 1 else 0))
+
+-- | Set the time to live.
+setTTL :: HasCallStack
+       => UDP
+       -> Int       -- ^ 1 ~ 255
+       -> IO ()
+setTTL udp@(UDP hdl _ _ _ _) ttl = do
+    checkUDPClosed udp
+    throwUVIfMinus_ (uv_udp_set_ttl hdl (fromIntegral ttl))
+
+-- | Set membership for a multicast group.
+setMembership :: HasCallStack
+              => UDP
+              -> CBytes             -- ^ Multicast address to set membership for.
+              -> CBytes             -- ^ Interface address.
+              -> UVMembership       -- ^ UV_JOIN_GROUP | UV_LEAVE_GROUP
+              -> IO ()
+setMembership udp@(UDP hdl _ _ _ _) gaddr iaddr member = do
+    checkUDPClosed udp
+    withCBytes gaddr $ \ gaddrp ->
+        withCBytes iaddr $ \ iaddrp ->
+            throwUVIfMinus_ (uv_udp_set_membership hdl gaddrp iaddrp member)
+
+-- | Set membership for a source-specific multicast group.
+setSourceMembership :: HasCallStack
+                    => UDP
+                    -> CBytes           -- ^ Multicast address to set membership for.
+                    -> CBytes           -- ^ Interface address.
+                    -> CBytes           -- ^ Source address.
+                    -> UVMembership     -- ^ UV_JOIN_GROUP | UV_LEAVE_GROUP
+                    -> IO ()
+setSourceMembership udp@(UDP hdl _ _ _ _) gaddr iaddr source member = do
+    checkUDPClosed udp
+    withCBytes gaddr $ \ gaddrp ->
+        withCBytes iaddr $ \ iaddrp ->
+            withCBytes source $ \ sourcep ->
+                throwUVIfMinus_ (uv_udp_set_source_membership hdl gaddrp iaddrp sourcep member)
+
+--------------------------------------------------------------------------------
+
+-- | Receiving buffering config.
+--
+data UDPRecvConfig = UDPRecvConfig
+    { recvMsgSize :: {-# UNPACK #-} !Int32      -- ^ maximum size of a received message
+    , recvBatchSize :: {-# UNPACK #-} !Int      -- ^ how many messages we want to receive per uv loop,
+                                                --   inside each uv_run, we do batch receiving,
+                                                --   increase this number can improve receiving performance,
+                                                --   at the cost of memory and potential GHC thread starving.
+    }
+
+-- | @UDPRecvConfig 512 6@
+defaultUDPRecvConfig :: UDPRecvConfig
+defaultUDPRecvConfig = UDPRecvConfig 512 6
+
+
+-- The buffer passing of UDP is a litte complicated here, to get maximum performance,
+-- we do batch receiving. i.e. recv multiple messages inside libuv's event loop:
+--
+--   udpRecvLargeBuffer:
+--
+--   +---------+--------------+-----------+----------+--------+---------+------------
+--   | buf siz | partial flag | addr flag |   addr   | buffer | buf siz | partial ...
+--   +--4bytes-+----4bytes----+--4bytes---+-128bytes-+-bufsiz-+---------+------------
+--   ^                                                        ^
+--   |                                                        |
+--   +---------------------+       +--------------------------+
+--                         |       |
+--                      +--+---+---+--+----
+--   udpRecvBufferArray | buf0 | buf1 | ...
+--                      +------+------+----
+--
+-- We allocate a large buffer (buffer_size * buffer_number),
+-- each time we poke the udpRecvBufferArray and its last index (size - 1) to uv manager's buffer table.
+--
+-- On libuv side each alloc callback picks the last pointer from udpRecvBufferArray, decrease last index by 1
+-- the read result is write into the `buf siz` cell, then followed with partial flag, if addr is not NULL
+-- then addr flag is 1 (otherwise 0), following addr if not NULL, the buffer is already written when
+-- recv callback is called.
+--
+-- On haskell side, we read buffer table's size, which is decreased by n(which is the times callback are called).
+-- Then we poke those cells out.
+--
+newRecvBuf :: Int32 -> Int -> IO (A.MutablePrimArray RealWorld Word8, A.MutablePrimArray RealWorld (Ptr Word8))
+newRecvBuf bufSiz bufArrSiz = do
+    rbuf <- A.newPinnedPrimArray (fromIntegral bufsiz' * bufArrSiz')
+    rbufArr <- A.newPinnedPrimArray bufArrSiz'
+
+    -- initialize buffer array with right index
+    withMutablePrimArrayContents rbuf $ \ p ->
+        forM_ [0..bufArrSiz'-1] $ \ i -> do
+            let bufNPtr = p `plusPtr` (i * fromIntegral bufsiz')
+            writePrimArray rbufArr i bufNPtr
+    return (rbuf, rbufArr)
+  where
+    -- (message size + sockaddr flag + + flag size) + sockaddr_in size + buffer
+    -- see diagram above
+    bufsiz' = 140 + (max 0 bufSiz)
+    bufArrSiz' = max 1 bufArrSiz
+
+-- | Recv UDP message within a loop
+--
+-- Loop receiving can be faster since it can reuse receiving buffer.
+recvUDPLoop :: HasCallStack
+            => UDPRecvConfig
+            -> UDP
+            -> ((Maybe SocketAddr, Bool, V.Bytes) -> IO a)
+            -> IO ()
+recvUDPLoop (UDPRecvConfig bufSiz bufArrSiz) udp worker = do
+    buf <- newRecvBuf bufSiz bufArrSiz
+    forever $ do
+        msgs <- recvUDPWith udp buf bufSiz
+        forM_ msgs worker
+
+-- | Recv messages from UDP socket, return source address if available, and a `Bool`
+-- to indicate if the message is partial (larger than receive buffer size).
+--
+recvUDP :: HasCallStack => UDPRecvConfig -> UDP -> IO [(Maybe SocketAddr, Bool, V.Bytes)]
+recvUDP (UDPRecvConfig bufSiz bufArrSiz) udp = do
+    buf <- newRecvBuf bufSiz bufArrSiz
+    recvUDPWith udp buf bufSiz
+
+recvUDPWith :: HasCallStack
+            => UDP
+            -> (A.MutablePrimArray RealWorld Word8, A.MutablePrimArray RealWorld (Ptr Word8))
+            -> Int32
+            -> IO [(Maybe SocketAddr, Bool, V.Bytes)]
+recvUDPWith udp@(UDP hdl slot uvm _ _) ((MutablePrimArray mba#), rbufArr) bufSiz = mask_ $ do
+    checkUDPClosed udp
+
+    rbufArrSiz <- getSizeofMutablePrimArray rbufArr
+    -- we have to reset the buffer size, during receiving it'll be overwritten
+    forM_ [0..rbufArrSiz-1] $ \ i -> do
+        p <- readPrimArray rbufArr i
+        poke (castPtr p :: Ptr Int32) bufSiz
+
+    -- reset buffer table's size with buffer array's length, during receiving it'll be decreased
+    withMutablePrimArrayContents rbufArr $ \ p ->
+        pokeBufferTable uvm slot (castPtr p) rbufArrSiz
+
+    m <- getBlockMVar uvm slot
+    -- clean up
+    _ <- tryTakeMVar m
+
+    throwUVIfMinus_ $ withUVManager' uvm (hs_uv_udp_recv_start hdl)
+
+    -- since we are inside mask, this is the only place
+    -- async exceptions could possibly kick in, and we should stop reading
+    r <- takeMVar m `onException` (do
+            -- normally we call 'uv_udp_recv_stop' in C read callback
+            -- but when exception raise, here's the place to stop
+            throwUVIfMinus_ $ withUVManager' uvm (uv_udp_recv_stop hdl)
+            void (tryTakeMVar m))
+
+    if r < rbufArrSiz
+    then forM [rbufArrSiz-1, rbufArrSiz-2 .. r] $ \ i -> do
+        p        <- readPrimArray rbufArr i
+        -- see the buffer struct diagram above
+        result   <- throwUVIfMinus (fromIntegral <$> peek @Int32 (castPtr p))
+        flag     <- peek @Int32 (castPtr (p `plusPtr` 4))
+        addrFlag <- peek @Int32 (castPtr (p `plusPtr` 8))
+        !addr <- if addrFlag == 1
+            then Just <$> peekSocketAddr (castPtr (p `plusPtr` 12))
+            else return Nothing
+        let !partial = flag .&. UV_UDP_PARTIAL /= 0
+        mba <- A.newPrimArray result
+        copyPtrToMutablePrimArray mba 0 (p `plusPtr` 140) result
+        ba <- A.unsafeFreezePrimArray mba
+        -- It's important to keep recv buffer alive
+        primitive_ (touch# mba#)
+        return (addr, partial, V.PrimVector ba 0 result)
+    else return []
diff --git a/Z/IO/Resource.hs b/Z/IO/Resource.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/Resource.hs
@@ -0,0 +1,284 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE RankNTypes #-}
+
+{-|
+Module      : Z.IO.Resource
+Description : The Resource monad
+Copyright   : (c) Dong Han, 2017
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module also implements Gabriel Gonzalez'd idea on 'Resource' applicative:
+<http://www.haskellforall.com/2013/06/the-resource-applicative.html>. The 'Applicative' and 'Monad' instance is
+especially useful when you want safely combine multiple resources.
+
+A high performance resource pool based on STM is also provided.
+
+-}
+
+module Z.IO.Resource (
+    -- * Resource management
+    Resource(..)
+  , initResource
+  , initResource_
+  , withResource
+  , withResource'
+    -- * Resource pool
+  , Pool
+  , PoolState(..)
+  , initPool
+  , statPool
+  , initInPool
+) where
+
+import           Control.Concurrent.STM
+import           Control.Monad
+import qualified Control.Monad.Catch as MonadCatch
+import           Control.Monad.IO.Class
+import           Z.Data.PrimRef.PrimIORef
+import           Z.IO.LowResTimer
+import           Z.IO.Exception
+
+--------------------------------------------------------------------------------
+
+-- | A 'Resource' is an 'IO' action which acquires some resource of type a and
+-- also returns a finalizer of type IO () that releases the resource.
+--
+-- The only safe way to use a 'Resource' is 'withResource' and 'withResource\'',
+-- You should not use the 'acquire' field directly, unless you want to implement your own
+-- resource management. In the later case, you should 'mask_' 'acquire' since
+-- some resource initializations may assume async exceptions are masked.
+--
+-- 'MonadIO' instance is provided so that you can lift 'IO' computation inside
+-- 'Resource', this is convenient for propagating 'Resource' around since many
+-- 'IO' computations carry finalizers.
+--
+-- A convention in Z-IO is that functions returning a 'Resource' should be
+-- named in @initXXX@ format, users are strongly recommended to follow this convention.
+--
+-- There're two additional guarantees we made in Z-IO:
+--
+--   * All resources in Z-IO can track its own liveness, throw 'ResourceVanished'
+--     exception using 'throwECLOSED' or 'throwECLOSEDSTM' when used after resource
+--     is closed.
+--
+--   * All resources' clean up action in Z-IO is idempotent.
+--
+-- Library authors providing 'initXXX' are also encouraged to provide these guarantees.
+--
+newtype Resource a = Resource { acquire :: HasCallStack => IO (a, IO ()) }
+
+-- | Create 'Resource' from create and release action.
+--
+-- Note, 'resource' doesn't open resource itself, resource is created when you use
+-- 'with' \/ 'with''.
+--
+initResource :: IO a -> (a -> IO ()) -> Resource a
+{-# INLINE initResource #-}
+initResource create release = Resource $ do
+    r <- create
+    return $ (r, release r)
+
+-- | Create 'Resource' from create and release action.
+--
+-- This function is useful when you want to add some initialization and clean up action
+-- inside 'Resource' monad.
+--
+initResource_ :: IO () -> IO () -> Resource ()
+{-# INLINE initResource_ #-}
+initResource_ create release = Resource $ do
+    r <- create
+    return $ (r, release)
+
+instance Functor Resource where
+    {-# INLINE fmap #-}
+    fmap f resource = Resource $ do
+        (a, release) <- acquire resource
+        return (f a, release)
+
+instance Applicative Resource where
+    {-# INLINE pure #-}
+    pure a = Resource (pure (a, pure ()))
+    {-# INLINE (<*>) #-}
+    resource1 <*> resource2 = Resource $ do
+        (f, release1) <- acquire resource1
+        (x, release2) <- acquire resource2 `onException` release1
+        return (f x, release2 >> release1)
+
+instance Monad Resource where
+    {-# INLINE return #-}
+    return = pure
+    {-# INLINE (>>=) #-}
+    m >>= f = Resource $ do
+        (m', release1) <- acquire m
+        (x , release2) <- acquire (f m') `onException` release1
+        return (x, release2 >> release1)
+
+instance MonadIO Resource where
+    {-# INLINE liftIO #-}
+    liftIO f = Resource $ fmap (\ a -> (a, dummyRelease)) f
+        where dummyRelease = return ()
+
+-- | Create a new resource and run some computation, resource is guarantee to
+-- be closed.
+--
+-- Be care don't leak the resource through computation return value, because
+-- after the computation finishes, the resource is closed already.
+--
+withResource :: (MonadCatch.MonadMask m, MonadIO m, HasCallStack)
+             => Resource a -> (a -> m b) -> m b
+{-# INLINABLE withResource #-}
+withResource resource k = MonadCatch.bracket
+    (liftIO (acquire resource))
+    (\(_, release) -> liftIO release)
+    (\(a, _) -> k a)
+
+-- | Create a new resource and run some computation, resource is guarantee to
+-- be closed.
+--
+-- The difference from 'with' is that the computation will receive an extra
+-- close action, which can be used to close the resource early before the whole
+-- computation finished, the close action can be called multiple times,
+-- only the first call will clean up the resource.
+--
+withResource' :: (MonadCatch.MonadMask m, MonadIO m, HasCallStack)
+              => Resource a -> (a -> m () -> m b) -> m b
+{-# INLINABLE withResource' #-}
+withResource' resource k = do
+    c <- liftIO (newCounter 0)
+    MonadCatch.bracket
+        (liftIO $ do
+            (a, release) <- (acquire resource)
+            let release' = do
+                    c' <- atomicOrCounter c 1
+                    when (c' == 0) release
+            return (a, release'))
+        (\(_, release) -> liftIO release)
+        (\(a, release) -> k a (liftIO release))
+
+--------------------------------------------------------------------------------
+
+-- | A single resource pool entry.
+data Entry a = Entry
+    (a, IO ())             -- the resource and clean up action
+    {-# UNPACK #-} !Int    -- the life remaining
+
+data PoolState = PoolClosed | PoolScanning | PoolEmpty deriving (Eq, Show)
+
+-- | A high performance resource pool based on STM.
+--
+-- We choose to not divide pool into strips due to the difficults in resource balancing. If there
+-- is a high contention on resource (see 'statPool'), just increase the maximum number of resources
+-- can be opened.
+--
+data Pool a = Pool
+    { poolResource :: Resource a
+    , poolLimit :: Int
+    , poolIdleTime :: Int
+    , poolEntries :: TVar [Entry a]
+    , poolInUse :: TVar Int
+    , poolState :: TVar PoolState
+    }
+
+-- | Initialize a resource pool with given 'Resource'
+--
+-- Like other initXXX functions, this function won't open a resource pool until you use 'withResource'.
+-- And this resource pool follow the same resource management pattern like other resources.
+--
+initPool :: Resource a
+         -> Int     -- ^ maximum number of resources can be opened
+         -> Int     -- ^ amount of time after which an unused resource can be released (in seconds).
+         -> Resource (Pool a)
+initPool res limit itime = initResource createPool closePool
+  where
+    createPool = do
+        entries <- newTVarIO []
+        inuse <- newTVarIO 0
+        state <- newTVarIO PoolEmpty
+        return (Pool res limit itime entries inuse state)
+
+    closePool (Pool _ _ _ entries _ state) = join . atomically $ do
+        c <- readTVar state
+        if c == PoolClosed
+        then return (return ())
+        else do
+            writeTVar state PoolClosed
+            return (do
+                es <- readTVarIO entries
+                forM_ es $ \ (Entry (_, close) _) ->
+                    MonadCatch.handleAll (\ _ -> return ()) close)
+
+-- | Get a resource pool's 'PoolState'
+--
+-- This function is useful when debug, under load lots of 'PoolEmpty' may indicate
+-- contention on resources, i.e. the limit on maximum number of resources can be opened
+-- should be adjusted to a higher number. On the otherhand, lots of 'PoolScanning'
+-- may indicate there're too much free resources.
+--
+statPool :: Pool a -> IO PoolState
+statPool pool = readTVarIO (poolState pool)
+
+-- | Obtain the pooled resource inside a given resource pool.
+--
+-- You shouldn't use 'withResource' with this resource after you closed the pool,
+-- an 'ResourceVanished' will be thrown.
+--
+initInPool :: Pool a -> Resource a
+initInPool (Pool res limit itime entries inuse state) = fst <$> initResource takeFromPool returnToPool
+  where
+    takeFromPool = join . atomically $ do
+        c <- readTVar state
+        if c == PoolClosed
+        then throwECLOSEDSTM
+        else do
+            es <- readTVar entries
+            case es of
+                ((Entry a _):es') -> do
+                    writeTVar entries es'
+                    return (return a)
+                _ -> do
+                    i <- readTVar inuse
+                    when (i == limit) retry
+                    modifyTVar' inuse (+1)
+                    return (acquire res `onException`
+                         atomically (modifyTVar' inuse (subtract 1)))
+
+    returnToPool a = join . atomically $ do
+        c <- readTVar state
+        case c of
+            PoolClosed -> return (snd a)
+            PoolEmpty -> do
+                modifyTVar' entries (Entry a itime:)
+                writeTVar state PoolScanning
+                return (void $ registerLowResTimer 10 scanPool)
+            _ -> do
+                modifyTVar' entries (Entry a itime:)
+                return (return ())
+
+    scanPool = do
+         join . atomically $ do
+            c <- readTVar state
+            if c == PoolClosed
+            then return (return ())
+            else do
+                es <- readTVar entries
+                if (null es)
+                then do
+                    writeTVar state PoolEmpty
+                    return (return ())
+                else do
+                    let (deadNum, dead, living) = age es 0 [] []
+                    writeTVar entries living
+                    modifyTVar' inuse (subtract deadNum)
+                    return (do
+                        forM_ dead $ \ (_, close) ->
+                            MonadCatch.handleAll (\ _ -> return ()) close
+                        void $ registerLowResTimer 10 scanPool)
+
+    age ((Entry a life):es) !deadNum dead living
+        | life > 1  = age es deadNum     dead     (Entry a (life-1):living)
+        | otherwise = age es (deadNum+1) (a:dead) living
+    age _ !deadNum dead living = (deadNum, dead, living)
diff --git a/Z/IO/StdStream.hs b/Z/IO/StdStream.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/StdStream.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE BangPatterns #-}
+
+
+{-|
+Module      : Z.IO.StdStream
+Description : TTY devices
+Copyright   : (c) Dong Han, 2018~2019
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provides stdin\/stderr\/stdout reading and writings. Usually you don't have to use 'stderr' or 'stderrBuf' directly, 'Z.IO.Logger' provides more logging utilities through @stderr@. While 'stdinBuf' and 'stdoutBuf' is useful when you write interactive programs, 'Z.IO.Buffered' module provide many reading and writing operations. Example:
+
+@
+import Control.Concurrent.MVar
+import Z.IO.LowResTimer
+import Z.IO.Buffered
+import Z.IO.StdStream
+import qualified Z.Data.Vector as V
+import qualified Z.Data.Builder as B
+main = do
+    -- read by '\n'
+    b1 <- readLineStd
+    -- read whatever user input in 3s, otherwise get Nothing
+    b2 <- timeoutLowRes 30 $ withMVar stdinBuf readBuffer
+    ...
+    putStd "hello world!"
+
+    -- Raw mode
+    setStdinTTYMode UV_TTY_MODE_RAW
+    forever $ do
+        withMVar stdinBuf $ \ i -> withMVar stdoutBuf $ \ o -> do
+            bs <- readBuffer i
+            let Just key = V.headMaybe bs
+            writeBuilder o (B.hex key)
+            flushBuffer o
+@
+
+-}
+module Z.IO.StdStream
+  ( -- * Standard input & output streams
+    StdStream
+  , isStdStreamTTY
+  , UVTTYMode(UV_TTY_MODE_NORMAL, UV_TTY_MODE_RAW)
+  , setStdinTTYMode
+  , getStdoutWinSize
+  , stdin, stdout, stderr
+  , stdinBuf, stdoutBuf, stderrBuf
+    -- * utils
+  , printStd
+  , readLineStd
+  , putStd
+  , putLineStd
+  ) where
+
+import Control.Monad
+import Control.Concurrent.MVar
+import Foreign.C.Types (CInt)
+import Foreign.Ptr
+import System.IO.Unsafe
+import Z.Data.Builder as B
+import Z.Data.Vector as V
+import Z.Data.Text.Builder (ToText, toBuilder)
+import Z.IO.UV.FFI
+import Z.IO.UV.Manager
+import Z.IO.UV.Errno
+import Z.IO.Exception
+import Z.IO.Buffered
+import Z.Foreign
+
+-- | Standard input and output streams
+--
+-- We support both regular file and TTY based streams, when initialized
+-- 'uv_guess_handle' is called to decide which type of devices are connected
+-- to standard streams.
+--
+-- Note 'StdStream' is not thread safe, you shouldn't use them without lock.
+-- For the same reason you shouldn't use stderr directly, use `Z.IO.Logger` module instead.
+
+data StdStream
+    = StdTTY {-# UNPACK #-}!(Ptr UVHandle) {-# UNPACK #-}!UVSlot UVManager -- similar to UVStream
+    | StdFile {-# UNPACK #-}!UVFD                                          -- similar to UVFile
+
+isStdStreamTTY :: StdStream -> Bool
+isStdStreamTTY (StdTTY _ _ _) = True
+isStdStreamTTY _              = False
+
+instance Input StdStream where
+    {-# INLINE readInput #-}
+    readInput (StdTTY hdl slot uvm) buf len = mask_ $ do
+        pokeBufferTable uvm slot buf len
+        m <- getBlockMVar uvm slot
+        _ <- tryTakeMVar m
+        throwUVIfMinus_ $ withUVManager' uvm (hs_uv_read_start hdl)
+        -- since we are inside mask, this is the only place
+        -- async exceptions could possibly kick in, and we should stop reading
+        r <- takeMVar m `onException` (do
+                throwUVIfMinus_ $ withUVManager' uvm (uv_read_stop hdl)
+                void (tryTakeMVar m))
+        if  | r > 0  -> return r
+            -- r == 0 should be impossible, since we guard this situation in c side
+            | r == fromIntegral UV_EOF -> return 0
+            | r < 0 ->  throwUVIfMinus (return r)
+    readInput (StdFile fd) buf len =
+        throwUVIfMinus $ hs_uv_fs_read fd buf len (-1)
+
+instance Output StdStream where
+    {-# INLINE writeOutput #-}
+    writeOutput (StdTTY hdl _ uvm) buf len = mask_ $ do
+        m <- withUVManager' uvm $ do
+            reqSlot <- getUVSlot uvm (hs_uv_write hdl buf len)
+            m <- getBlockMVar uvm reqSlot
+            _ <- tryTakeMVar m
+            return m
+        throwUVIfMinus_ (uninterruptibleMask_ $ takeMVar m)
+    writeOutput (StdFile fd) buf len = go buf len
+      where
+        go !b !bufSiz = do
+            written <- throwUVIfMinus
+                (hs_uv_fs_write fd b bufSiz (-1))
+            when (written < bufSiz)
+                (go (b `plusPtr` written) (bufSiz-written))
+
+-- | The global stdin stream.
+stdin :: StdStream
+{-# NOINLINE stdin #-}
+stdin = unsafePerformIO (makeStdStream 0)
+
+-- | The global stdout stream.
+--
+-- | If you want to write logs, don't use 'stdout' directly, use 'Z.IO.Logger' instead.
+stdout :: StdStream
+{-# NOINLINE stdout #-}
+stdout = unsafePerformIO (makeStdStream 1)
+
+-- | The global stderr stream.
+--
+-- | If you want to write logs, don't use 'stderr' directly, use 'Z.IO.Logger' instead.
+stderr :: StdStream
+{-# NOINLINE stderr #-}
+stderr = unsafePerformIO (makeStdStream 2)
+
+-- |  A global buffered stdin stream protected by 'MVar'.
+stdinBuf :: MVar (BufferedInput StdStream)
+{-# NOINLINE stdinBuf #-}
+stdinBuf = unsafePerformIO (newBufferedInput defaultChunkSize stdin >>= newMVar)
+
+-- |  A global buffered stdout stream protected by 'MVar'.
+--
+-- | If you want to write logs, don't use 'stdoutBuf' directly, use 'Z.IO.Logger' instead.
+stdoutBuf :: MVar (BufferedOutput StdStream)
+{-# NOINLINE stdoutBuf #-}
+stdoutBuf = unsafePerformIO (newBufferedOutput defaultChunkSize stdout >>= newMVar)
+
+-- |  A global buffered stderr stream protected by 'MVar'.
+--
+-- | If you want to write logs, don't use 'stderrBuf' directly, use 'Z.IO.Logger' instead.
+stderrBuf :: MVar (BufferedOutput StdStream)
+{-# NOINLINE stderrBuf #-}
+stderrBuf = unsafePerformIO (newBufferedOutput defaultChunkSize stderr >>= newMVar)
+
+makeStdStream :: UVFD -> IO StdStream
+makeStdStream fd = do
+    typ <- uv_guess_handle fd
+    if typ == UV_TTY
+    then do
+        uvm <- getUVManager
+        withUVManager uvm $ \ loop -> do
+            hdl <- hs_uv_handle_alloc loop
+            slot <- getUVSlot uvm (peekUVHandleData hdl)
+            _ <- tryTakeMVar =<< getBlockMVar uvm slot   -- clear the parking spot
+            throwUVIfMinus_ (uv_tty_init loop hdl (fromIntegral fd))
+                `onException` hs_uv_handle_free hdl
+            return (StdTTY hdl slot uvm)
+    else return (StdFile fd)
+
+-- | Change terminal's mode if stdin is connected to a terminal.
+setStdinTTYMode :: UVTTYMode -> IO ()
+setStdinTTYMode mode = case stdin of
+    StdTTY hdl _ uvm ->
+        withUVManager' uvm . throwUVIfMinus_ $ uv_tty_set_mode hdl mode
+    _ -> return ()
+
+-- | Get terminal's output window size in (width, height) format,
+-- return (-1, -1) if stdout is a file.
+getStdoutWinSize :: IO (CInt, CInt)
+getStdoutWinSize = case stdout of
+    StdTTY hdl _ uvm ->
+        withUVManager' uvm $ do
+            (w, (h, ())) <- allocPrimUnsafe $ \ w ->
+                allocPrimUnsafe $ \ h -> throwUVIfMinus_ $ uv_tty_get_winsize hdl w h
+            return (w, h)
+    _ -> return (-1, -1)
+
+--------------------------------------------------------------------------------
+
+-- | print a 'ToText' and flush to stdout.
+printStd :: ToText a => a -> IO ()
+printStd s = putStd (toBuilder s)
+
+-- | print a 'Builder' and flush to stdout.
+putStd :: Builder a -> IO ()
+putStd b = withMVar stdoutBuf $ \ o -> do
+    writeBuilder o b
+    flushBuffer o
+
+-- | print a 'Builder' and flush to stdout, with a linefeed.
+putLineStd :: Builder a -> IO ()
+putLineStd b = withMVar stdoutBuf $ \ o -> do
+    writeBuilder o (b >> B.char8 '\n')
+    flushBuffer o
+
+-- | read a line from stdin
+readLineStd :: IO V.Bytes
+readLineStd = withMVar stdinBuf readLine
diff --git a/Z/IO/UV/Errno.hsc b/Z/IO/UV/Errno.hsc
new file mode 100644
--- /dev/null
+++ b/Z/IO/UV/Errno.hsc
@@ -0,0 +1,256 @@
+{-# LANGUAGE PatternSynonyms #-}
+{-|
+Module      : Z.IO.UVErrno
+Description : Errno provided by libuv
+Copyright   : (c) Winterland, 2017-2018
+License     : BSD
+Maintainer  : drkoster@qq.com
+Stability   : experimental
+Portability : non-portable
+
+INTERNAL MODULE, provides all libuv errno.
+
+-}
+
+module Z.IO.UV.Errno where
+
+import Foreign.C.Types
+import Foreign.C.String
+
+#include "hs_uv.h"
+
+uvStdError :: CInt -> IO String
+uvStdError errno = peekCString =<< uv_strerror errno
+
+foreign import ccall unsafe uv_strerror :: CInt -> IO CString
+
+uvErrName :: CInt -> IO String
+uvErrName errno = peekCString =<< uv_err_name errno
+
+foreign import ccall unsafe uv_err_name :: CInt -> IO CString
+
+-- | argument list too long
+pattern UV_E2BIG           :: CInt
+pattern UV_E2BIG           = #{const UV_E2BIG          }
+-- | permission denied
+pattern UV_EACCES          :: CInt
+pattern UV_EACCES          = #{const UV_EACCES         }
+-- | address already in use
+pattern UV_EADDRINUSE      :: CInt
+pattern UV_EADDRINUSE      = #{const UV_EADDRINUSE     }
+-- | address not available
+pattern UV_EADDRNOTAVAIL   :: CInt
+pattern UV_EADDRNOTAVAIL   = #{const UV_EADDRNOTAVAIL  }
+-- | address family not supported
+pattern UV_EAFNOSUPPORT    :: CInt
+pattern UV_EAFNOSUPPORT    = #{const UV_EAFNOSUPPORT   }
+-- | resource temporarily unavailable
+pattern UV_EAGAIN          :: CInt
+pattern UV_EAGAIN          = #{const UV_EAGAIN         }
+-- | address family not supported
+pattern UV_EAI_ADDRFAMILY  :: CInt
+pattern UV_EAI_ADDRFAMILY  = #{const UV_EAI_ADDRFAMILY }
+-- | temporary failure
+pattern UV_EAI_AGAIN       :: CInt
+pattern UV_EAI_AGAIN       = #{const UV_EAI_AGAIN      }
+-- | bad ai_flags value
+pattern UV_EAI_BADFLAGS    :: CInt
+pattern UV_EAI_BADFLAGS    = #{const UV_EAI_BADFLAGS   }
+-- | invalid value for hints
+pattern UV_EAI_BADHINTS    :: CInt
+pattern UV_EAI_BADHINTS    = #{const UV_EAI_BADHINTS   }
+-- | request canceled
+pattern UV_EAI_CANCELED    :: CInt
+pattern UV_EAI_CANCELED    = #{const UV_EAI_CANCELED   }
+-- | permanent failure
+pattern UV_EAI_FAIL        :: CInt
+pattern UV_EAI_FAIL        = #{const UV_EAI_FAIL       }
+-- | ai_family not supported
+pattern UV_EAI_FAMILY      :: CInt
+pattern UV_EAI_FAMILY      = #{const UV_EAI_FAMILY     }
+-- | out of memory
+pattern UV_EAI_MEMORY      :: CInt
+pattern UV_EAI_MEMORY      = #{const UV_EAI_MEMORY     }
+-- | no address
+pattern UV_EAI_NODATA      :: CInt
+pattern UV_EAI_NODATA      = #{const UV_EAI_NODATA     }
+-- | unknown node or service
+pattern UV_EAI_NONAME      :: CInt
+pattern UV_EAI_NONAME      = #{const UV_EAI_NONAME     }
+-- | argument buffer overflow
+pattern UV_EAI_OVERFLOW    :: CInt
+pattern UV_EAI_OVERFLOW    = #{const UV_EAI_OVERFLOW   }
+-- | resolved protocol is unknown
+pattern UV_EAI_PROTOCOL    :: CInt
+pattern UV_EAI_PROTOCOL    = #{const UV_EAI_PROTOCOL   }
+-- | service not available for socket type
+pattern UV_EAI_SERVICE     :: CInt
+pattern UV_EAI_SERVICE     = #{const UV_EAI_SERVICE    }
+-- | socket type not supported
+pattern UV_EAI_SOCKTYPE    :: CInt
+pattern UV_EAI_SOCKTYPE    = #{const UV_EAI_SOCKTYPE   }
+-- | connection already in progress
+pattern UV_EALREADY        :: CInt
+pattern UV_EALREADY        = #{const UV_EALREADY       }
+-- | bad file descriptor
+pattern UV_EBADF           :: CInt
+pattern UV_EBADF           = #{const UV_EBADF          }
+-- | resource busy or locked
+pattern UV_EBUSY           :: CInt
+pattern UV_EBUSY           = #{const UV_EBUSY          }
+-- | operation canceled
+pattern UV_ECANCELED       :: CInt
+pattern UV_ECANCELED       = #{const UV_ECANCELED      }
+-- | invalid Unicode character
+pattern UV_ECHARSET        :: CInt
+pattern UV_ECHARSET        = #{const UV_ECHARSET       }
+-- | software caused connection abort
+pattern UV_ECONNABORTED    :: CInt
+pattern UV_ECONNABORTED    = #{const UV_ECONNABORTED   }
+-- | connection refused
+pattern UV_ECONNREFUSED    :: CInt
+pattern UV_ECONNREFUSED    = #{const UV_ECONNREFUSED   }
+-- | connection reset by peer
+pattern UV_ECONNRESET      :: CInt
+pattern UV_ECONNRESET      = #{const UV_ECONNRESET     }
+-- | destination address required
+pattern UV_EDESTADDRREQ    :: CInt
+pattern UV_EDESTADDRREQ    = #{const UV_EDESTADDRREQ   }
+-- | file already exists
+pattern UV_EEXIST          :: CInt
+pattern UV_EEXIST          = #{const UV_EEXIST         }
+-- | bad address in system call argument
+pattern UV_EFAULT          :: CInt
+pattern UV_EFAULT          = #{const UV_EFAULT         }
+-- | file too large
+pattern UV_EFBIG           :: CInt
+pattern UV_EFBIG           = #{const UV_EFBIG          }
+-- | host is unreachable
+pattern UV_EHOSTUNREACH    :: CInt
+pattern UV_EHOSTUNREACH    = #{const UV_EHOSTUNREACH   }
+-- | interrupted system call
+pattern UV_EINTR           :: CInt
+pattern UV_EINTR           = #{const UV_EINTR          }
+-- | invalid argument
+pattern UV_EINVAL          :: CInt
+pattern UV_EINVAL          = #{const UV_EINVAL         }
+-- | i/o error
+pattern UV_EIO             :: CInt
+pattern UV_EIO             = #{const UV_EIO            }
+-- | socket is already connected
+pattern UV_EISCONN         :: CInt
+pattern UV_EISCONN         = #{const UV_EISCONN        }
+-- | illegal operation on a directory
+pattern UV_EISDIR          :: CInt
+pattern UV_EISDIR          = #{const UV_EISDIR         }
+-- | too many symbolic links encountered
+pattern UV_ELOOP           :: CInt
+pattern UV_ELOOP           = #{const UV_ELOOP          }
+-- | too many open files
+pattern UV_EMFILE          :: CInt
+pattern UV_EMFILE          = #{const UV_EMFILE         }
+-- | message too long
+pattern UV_EMSGSIZE        :: CInt
+pattern UV_EMSGSIZE        = #{const UV_EMSGSIZE       }
+-- | name too long
+pattern UV_ENAMETOOLONG    :: CInt
+pattern UV_ENAMETOOLONG    = #{const UV_ENAMETOOLONG   }
+-- | network is down
+pattern UV_ENETDOWN        :: CInt
+pattern UV_ENETDOWN        = #{const UV_ENETDOWN       }
+-- | network is unreachable
+pattern UV_ENETUNREACH     :: CInt
+pattern UV_ENETUNREACH     = #{const UV_ENETUNREACH    }
+-- | file table overflow
+pattern UV_ENFILE          :: CInt
+pattern UV_ENFILE          = #{const UV_ENFILE         }
+-- | no buffer space available
+pattern UV_ENOBUFS         :: CInt
+pattern UV_ENOBUFS         = #{const UV_ENOBUFS        }
+-- | no such device
+pattern UV_ENODEV          :: CInt
+pattern UV_ENODEV          = #{const UV_ENODEV         }
+-- | no such file or directory
+pattern UV_ENOENT          :: CInt
+pattern UV_ENOENT          = #{const UV_ENOENT         }
+-- | not enough memory
+pattern UV_ENOMEM          :: CInt
+pattern UV_ENOMEM          = #{const UV_ENOMEM         }
+-- | machine is not on the network
+pattern UV_ENONET          :: CInt
+pattern UV_ENONET          = #{const UV_ENONET         }
+-- | protocol not available
+pattern UV_ENOPROTOOPT     :: CInt
+pattern UV_ENOPROTOOPT     = #{const UV_ENOPROTOOPT    }
+-- | no space left on device
+pattern UV_ENOSPC          :: CInt
+pattern UV_ENOSPC          = #{const UV_ENOSPC         }
+-- | function not implemented
+pattern UV_ENOSYS          :: CInt
+pattern UV_ENOSYS          = #{const UV_ENOSYS         }
+-- | socket is not connected
+pattern UV_ENOTCONN        :: CInt
+pattern UV_ENOTCONN        = #{const UV_ENOTCONN       }
+-- | not a directory
+pattern UV_ENOTDIR         :: CInt
+pattern UV_ENOTDIR         = #{const UV_ENOTDIR        }
+-- | directory not empty
+pattern UV_ENOTEMPTY       :: CInt
+pattern UV_ENOTEMPTY       = #{const UV_ENOTEMPTY      }
+-- | socket operation on non-socket
+pattern UV_ENOTSOCK        :: CInt
+pattern UV_ENOTSOCK        = #{const UV_ENOTSOCK       }
+-- | operation not supported on socket
+pattern UV_ENOTSUP         :: CInt
+pattern UV_ENOTSUP         = #{const UV_ENOTSUP        }
+-- | operation not permitted
+pattern UV_EPERM           :: CInt
+pattern UV_EPERM           = #{const UV_EPERM          }
+-- | broken pipe
+pattern UV_EPIPE           :: CInt
+pattern UV_EPIPE           = #{const UV_EPIPE          }
+-- | protocol error
+pattern UV_EPROTO          :: CInt
+pattern UV_EPROTO          = #{const UV_EPROTO         }
+-- | protocol not supported
+pattern UV_EPROTONOSUPPORT :: CInt
+pattern UV_EPROTONOSUPPORT = #{const UV_EPROTONOSUPPORT}
+-- | protocol wrong type for socket
+pattern UV_EPROTOTYPE      :: CInt
+pattern UV_EPROTOTYPE      = #{const UV_EPROTOTYPE     }
+-- | result too large
+pattern UV_ERANGE          :: CInt
+pattern UV_ERANGE          = #{const UV_ERANGE         }
+-- | read-only file system
+pattern UV_EROFS           :: CInt
+pattern UV_EROFS           = #{const UV_EROFS          }
+-- | cannot send after transport endpoint shutdown
+pattern UV_ESHUTDOWN       :: CInt
+pattern UV_ESHUTDOWN       = #{const UV_ESHUTDOWN      }
+-- | invalid seek
+pattern UV_ESPIPE          :: CInt
+pattern UV_ESPIPE          = #{const UV_ESPIPE         }
+-- | no such process
+pattern UV_ESRCH           :: CInt
+pattern UV_ESRCH           = #{const UV_ESRCH          }
+-- | connection timed out
+pattern UV_ETIMEDOUT       :: CInt
+pattern UV_ETIMEDOUT       = #{const UV_ETIMEDOUT      }
+-- | text file is busy
+pattern UV_ETXTBSY         :: CInt
+pattern UV_ETXTBSY         = #{const UV_ETXTBSY        }
+-- | cross-device link not permitted
+pattern UV_EXDEV           :: CInt
+pattern UV_EXDEV           = #{const UV_EXDEV          }
+-- | unknown error
+pattern UV_UNKNOWN         :: CInt
+pattern UV_UNKNOWN         = #{const UV_UNKNOWN        }
+-- | end of file
+pattern UV_EOF             :: CInt
+pattern UV_EOF             = #{const UV_EOF            }
+-- | no such device or address
+pattern UV_ENXIO           :: CInt
+pattern UV_ENXIO           = #{const UV_ENXIO          }
+-- | too many links
+pattern UV_EMLINK          :: CInt
+pattern UV_EMLINK          = #{const UV_EMLINK         }
diff --git a/Z/IO/UV/FFI.hsc b/Z/IO/UV/FFI.hsc
new file mode 100644
--- /dev/null
+++ b/Z/IO/UV/FFI.hsc
@@ -0,0 +1,709 @@
+{-# LANGUAGE CPP                        #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MagicHash                  #-}
+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE PatternSynonyms            #-}
+{-# LANGUAGE UnliftedFFITypes           #-}
+
+{-|
+Module      : Z.IO.UV
+Description : libuv operations
+Copyright   : (c) Winterland, 2017-2018
+License     : BSD
+Maintainer  : drkoster@qq.com
+Stability   : experimental
+Portability : non-portable
+
+INTERNAL MODULE, provides all libuv side operations.
+
+-}
+
+module Z.IO.UV.FFI where
+
+import           Data.Bits
+import           Data.Int
+import           Data.Word
+import           Foreign.C.String
+import           Foreign.C.Types
+import           Foreign.Ptr
+import           Foreign.Storable
+import           Z.Foreign
+import           Z.IO.Network.SocketAddr    (SocketAddr)
+import           System.Posix.Types (CSsize (..))
+import           GHC.Generics
+
+#include "hs_uv.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+--------------------------------------------------------------------------------
+-- libuv version
+foreign import ccall unsafe uv_version :: IO CUInt
+foreign import ccall unsafe uv_version_string :: IO CString
+
+--------------------------------------------------------------------------------
+-- Type alias
+type UVSlot = Int
+-- | UVSlotUnSafe wrap a slot which may not have a 'MVar' in blocking table, 
+--   i.e. the blocking table need to be resized.
+newtype UVSlotUnSafe = UVSlotUnSafe { unsafeGetSlot :: UVSlot }
+type UVFD = Int32
+
+--------------------------------------------------------------------------------
+-- CONSTANT
+
+pattern ACCEPT_BUFFER_SIZE :: Int
+pattern ACCEPT_BUFFER_SIZE = #const ACCEPT_BUFFER_SIZE
+pattern SO_REUSEPORT_LOAD_BALANCE :: Int
+pattern SO_REUSEPORT_LOAD_BALANCE = #const SO_REUSEPORT_LOAD_BALANCE
+pattern INIT_LOOP_SIZE :: Int
+pattern INIT_LOOP_SIZE = #const INIT_LOOP_SIZE
+
+--------------------------------------------------------------------------------
+-- loop
+data UVLoop
+data UVLoopData
+
+peekUVEventQueue :: Ptr UVLoopData -> IO (Int, Ptr Int)
+peekUVEventQueue p = (,)
+    <$> (#{peek hs_loop_data, event_counter          } p)
+    <*> (#{peek hs_loop_data, event_queue            } p)
+
+clearUVEventCounter :: Ptr UVLoopData -> IO ()
+clearUVEventCounter p = do
+    #{poke hs_loop_data, event_counter          } p $ (0 :: Int)
+
+peekUVBufferTable :: Ptr UVLoopData -> IO (Ptr (Ptr Word8), Ptr CSsize)
+peekUVBufferTable p = (,)
+    <$> (#{peek hs_loop_data, buffer_table          } p)
+    <*> (#{peek hs_loop_data, buffer_size_table     } p)
+
+newtype UVRunMode = UVRunMode CInt 
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+pattern UV_RUN_DEFAULT :: UVRunMode
+pattern UV_RUN_DEFAULT = UVRunMode #{const UV_RUN_DEFAULT}
+pattern UV_RUN_ONCE :: UVRunMode
+pattern UV_RUN_ONCE    = UVRunMode #{const UV_RUN_ONCE}
+pattern UV_RUN_NOWAIT :: UVRunMode
+pattern UV_RUN_NOWAIT  = UVRunMode #{const UV_RUN_NOWAIT}
+
+-- | Peek loop data pointer from uv loop  pointer.
+peekUVLoopData :: Ptr UVLoop -> IO (Ptr UVLoopData)
+peekUVLoopData p = #{peek uv_loop_t, data} p
+
+foreign import ccall unsafe hs_uv_loop_init      :: Int -> IO (Ptr UVLoop)
+foreign import ccall unsafe hs_uv_loop_close     :: Ptr UVLoop -> IO ()
+
+-- | uv_run with usafe FFI.
+foreign import ccall unsafe "hs_uv_run" uv_run    :: Ptr UVLoop -> UVRunMode -> IO CInt
+
+-- | uv_run with safe FFI.
+foreign import ccall safe "hs_uv_run" uv_run_safe :: Ptr UVLoop -> UVRunMode -> IO CInt
+
+foreign import ccall unsafe uv_loop_alive :: Ptr UVLoop -> IO CInt
+
+--------------------------------------------------------------------------------
+-- thread safe wake up
+
+foreign import ccall unsafe hs_uv_wake_up_timer :: Ptr UVLoopData -> IO CInt
+foreign import ccall unsafe hs_uv_wake_up_async :: Ptr UVLoopData -> IO CInt
+
+--------------------------------------------------------------------------------
+-- handle
+data UVHandle
+
+peekUVHandleData :: Ptr UVHandle -> IO UVSlotUnSafe
+peekUVHandleData p =  UVSlotUnSafe <$> (#{peek uv_handle_t, data} p :: IO Int)
+
+foreign import ccall unsafe hs_uv_fileno :: Ptr UVHandle -> IO UVFD
+foreign import ccall unsafe hs_uv_handle_alloc :: Ptr UVLoop -> IO (Ptr UVHandle)
+foreign import ccall unsafe hs_uv_handle_free  :: Ptr UVHandle -> IO ()
+foreign import ccall unsafe hs_uv_handle_close :: Ptr UVHandle -> IO ()
+
+--------------------------------------------------------------------------------
+-- request
+
+foreign import ccall unsafe hs_uv_cancel :: Ptr UVLoop -> UVSlot -> IO ()
+
+--------------------------------------------------------------------------------
+-- stream
+
+foreign import ccall unsafe hs_uv_listen  :: Ptr UVHandle -> CInt -> IO CInt
+foreign import ccall unsafe hs_uv_listen_resume :: Ptr UVHandle -> IO ()
+
+foreign import ccall unsafe hs_uv_read_start :: Ptr UVHandle -> IO CInt
+foreign import ccall unsafe uv_read_stop :: Ptr UVHandle -> IO CInt
+foreign import ccall unsafe hs_uv_write :: Ptr UVHandle -> Ptr Word8 -> Int -> IO UVSlotUnSafe
+
+foreign import ccall unsafe hs_uv_accept_check_alloc :: Ptr UVHandle -> IO (Ptr UVHandle)
+foreign import ccall unsafe hs_uv_accept_check_init :: Ptr UVHandle -> IO CInt
+foreign import ccall unsafe hs_uv_accept_check_close :: Ptr UVHandle -> IO ()
+
+--------------------------------------------------------------------------------
+-- tcp & pipe
+foreign import ccall unsafe hs_uv_tcp_open :: Ptr UVHandle -> UVFD -> IO CInt
+foreign import ccall unsafe uv_tcp_init :: Ptr UVLoop -> Ptr UVHandle -> IO CInt
+foreign import ccall unsafe uv_tcp_init_ex :: Ptr UVLoop -> Ptr UVHandle -> CUInt -> IO CInt
+foreign import ccall unsafe uv_tcp_nodelay :: Ptr UVHandle -> CInt -> IO CInt
+foreign import ccall unsafe uv_tcp_keepalive :: Ptr UVHandle -> CInt -> CUInt -> IO CInt
+
+uV_TCP_IPV6ONLY :: CUInt
+uV_TCP_IPV6ONLY = #{const UV_TCP_IPV6ONLY}
+
+foreign import ccall unsafe uv_tcp_bind :: Ptr UVHandle -> Ptr SocketAddr -> CUInt -> IO CInt
+foreign import ccall unsafe hs_uv_tcp_connect :: Ptr UVHandle -> Ptr SocketAddr -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_set_socket_reuse :: Ptr UVHandle -> IO CInt
+
+foreign import ccall unsafe hs_uv_pipe_open :: Ptr UVHandle -> UVFD -> IO CInt
+foreign import ccall unsafe uv_pipe_init :: Ptr UVLoop -> Ptr UVHandle -> CInt -> IO CInt
+foreign import ccall unsafe uv_pipe_bind :: Ptr UVHandle -> CString -> IO CInt
+foreign import ccall unsafe hs_uv_pipe_connect :: Ptr UVHandle -> CString -> IO UVSlotUnSafe
+
+--------------------------------------------------------------------------------
+-- udp
+foreign import ccall unsafe uv_udp_init :: Ptr UVLoop -> Ptr UVHandle -> IO CInt
+foreign import ccall unsafe uv_udp_init_ex :: Ptr UVLoop -> Ptr UVHandle -> CUInt -> IO CInt
+foreign import ccall unsafe uv_udp_open :: Ptr UVHandle -> UVFD -> IO CInt
+foreign import ccall unsafe uv_udp_bind :: Ptr UVHandle -> Ptr SocketAddr -> UVUDPFlag -> IO CInt
+
+newtype UVMembership = UVMembership CInt deriving (Show, Eq, Ord)
+
+pattern UV_LEAVE_GROUP :: UVMembership
+pattern UV_LEAVE_GROUP = UVMembership #{const UV_LEAVE_GROUP}
+pattern UV_JOIN_GROUP :: UVMembership
+pattern UV_JOIN_GROUP = UVMembership #{const UV_JOIN_GROUP}
+
+newtype UVUDPFlag = UVUDPFlag CInt deriving (Show, Eq, Ord, Storable, Bits, FiniteBits, Num)
+
+pattern UV_UDP_DEFAULT :: UVUDPFlag
+pattern UV_UDP_DEFAULT = UVUDPFlag 0
+pattern UV_UDP_IPV6ONLY :: UVUDPFlag
+pattern UV_UDP_IPV6ONLY = UVUDPFlag #{const UV_UDP_IPV6ONLY}
+pattern UV_UDP_REUSEADDR :: UVUDPFlag
+pattern UV_UDP_REUSEADDR = UVUDPFlag #{const UV_UDP_REUSEADDR}
+
+pattern UV_UDP_PARTIAL :: Int32
+pattern UV_UDP_PARTIAL = #{const UV_UDP_PARTIAL}
+
+foreign import ccall unsafe uv_udp_connect
+    :: Ptr UVHandle -> Ptr SocketAddr -> IO CInt
+
+foreign import ccall unsafe uv_udp_set_membership ::
+    Ptr UVHandle -> CString -> CString -> UVMembership -> IO CInt
+foreign import ccall unsafe uv_udp_set_source_membership ::
+    Ptr UVHandle -> CString -> CString -> CString -> UVMembership -> IO CInt
+
+foreign import ccall unsafe uv_udp_set_multicast_loop :: Ptr UVHandle -> CInt -> IO CInt
+foreign import ccall unsafe uv_udp_set_multicast_ttl :: Ptr UVHandle -> CInt -> IO CInt
+foreign import ccall unsafe uv_udp_set_multicast_interface :: Ptr UVHandle -> CString -> IO CInt
+foreign import ccall unsafe uv_udp_set_broadcast :: Ptr UVHandle -> CInt -> IO CInt
+foreign import ccall unsafe uv_udp_set_ttl :: Ptr UVHandle -> CInt -> IO CInt
+
+foreign import ccall unsafe hs_uv_udp_recv_start :: Ptr UVHandle -> IO CInt
+foreign import ccall unsafe uv_udp_recv_stop :: Ptr UVHandle -> IO CInt
+foreign import ccall unsafe hs_uv_udp_send 
+    :: Ptr UVHandle -> Ptr SocketAddr -> Ptr Word8 -> Int -> IO UVSlotUnSafe
+foreign import ccall unsafe uv_udp_getsockname 
+    :: Ptr UVHandle -> Ptr SocketAddr -> MBA## CInt -> IO CInt
+foreign import ccall unsafe uv_udp_getpeername
+    :: Ptr UVHandle -> Ptr SocketAddr -> MBA## CInt -> IO CInt
+
+
+--------------------------------------------------------------------------------
+-- tty
+
+-- | Terminal mode.
+--
+-- When in 'UV_TTY_MODE_RAW' mode, input is always available character-by-character,
+-- not including modifiers. Additionally, all special processing of characters by the terminal is disabled, 
+-- including echoing input characters. Note that CTRL+C will no longer cause a SIGINT when in this mode.
+newtype UVTTYMode = UVTTYMode CInt
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+pattern UV_TTY_MODE_NORMAL :: UVTTYMode
+pattern UV_TTY_MODE_NORMAL = UVTTYMode #{const UV_TTY_MODE_NORMAL}
+pattern UV_TTY_MODE_RAW :: UVTTYMode
+pattern UV_TTY_MODE_RAW = UVTTYMode #{const UV_TTY_MODE_RAW}
+pattern UV_TTY_MODE_IO :: UVTTYMode
+pattern UV_TTY_MODE_IO = UVTTYMode #{const UV_TTY_MODE_IO}
+
+foreign import ccall unsafe uv_tty_init :: Ptr UVLoop -> Ptr UVHandle -> CInt -> IO CInt
+foreign import ccall unsafe uv_tty_set_mode :: Ptr UVHandle -> UVTTYMode -> IO CInt
+foreign import ccall unsafe uv_tty_get_winsize :: Ptr UVHandle -> MBA## CInt -> MBA## CInt -> IO CInt
+
+--------------------------------------------------------------------------------
+-- fs
+
+newtype FileMode = FileMode CInt
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+-- | 00700 user (file owner) has read, write and execute permission
+pattern S_IRWXU :: FileMode
+pattern S_IRWXU = FileMode #{const S_IRWXU}
+
+-- | 00400 user has read permission
+pattern S_IRUSR :: FileMode
+pattern S_IRUSR = FileMode #{const S_IRUSR}
+
+-- | 00200 user has write permission
+pattern S_IWUSR :: FileMode
+pattern S_IWUSR = FileMode #{const S_IWUSR}
+
+-- | 00100 user has execute permission
+pattern S_IXUSR :: FileMode
+pattern S_IXUSR = FileMode #{const S_IXUSR}
+
+-- | 00070 group has read, write and execute permission
+pattern S_IRWXG :: FileMode
+pattern S_IRWXG = FileMode #{const S_IRWXG}
+
+-- | 00040 group has read permission
+pattern S_IRGRP :: FileMode
+pattern S_IRGRP = FileMode #{const S_IRGRP}
+
+-- | 00020 group has write permission
+pattern S_IWGRP :: FileMode
+pattern S_IWGRP = FileMode #{const S_IWGRP}
+
+-- | 00010 group has execute permission
+pattern S_IXGRP :: FileMode
+pattern S_IXGRP = FileMode #{const S_IXGRP}
+
+-- | 00007 others have read, write and execute permission
+pattern S_IRWXO :: FileMode
+pattern S_IRWXO = FileMode #{const S_IRWXO}
+
+-- | 00004 others have read permission
+pattern S_IROTH :: FileMode
+pattern S_IROTH = FileMode #{const S_IROTH}
+
+-- | 00002 others have write permission
+pattern S_IWOTH :: FileMode
+pattern S_IWOTH = FileMode #{const S_IWOTH}
+
+-- | 00001 others have execute permission
+pattern S_IXOTH :: FileMode
+pattern S_IXOTH = FileMode #{const S_IXOTH}
+
+-- | Default mode for open, 0o666(readable and writable).
+pattern DEFAULT_MODE :: FileMode
+pattern DEFAULT_MODE = FileMode 0o666
+
+-- non-threaded functions
+foreign import ccall unsafe hs_uv_fs_open    :: CString -> FileFlag -> FileMode -> IO UVFD
+foreign import ccall unsafe hs_uv_fs_close   :: UVFD -> IO Int
+foreign import ccall unsafe hs_uv_fs_read    :: UVFD -> Ptr Word8 -> Int -> Int64 -> IO Int
+foreign import ccall unsafe hs_uv_fs_write   :: UVFD -> Ptr Word8 -> Int -> Int64 -> IO Int
+foreign import ccall unsafe hs_uv_fs_unlink  :: CString -> IO Int
+foreign import ccall unsafe hs_uv_fs_mkdir   :: CString -> FileMode -> IO Int
+foreign import ccall unsafe hs_uv_fs_rmdir   :: CString -> IO Int
+foreign import ccall unsafe hs_uv_fs_mkdtemp :: CString -> Int -> CString -> IO Int
+
+-- threaded functions
+foreign import ccall unsafe hs_uv_fs_open_threaded 
+    :: CString -> FileFlag -> FileMode -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_close_threaded 
+    :: UVFD -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_read_threaded  
+    :: UVFD -> Ptr Word8 -> Int -> Int64 -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_write_threaded 
+    :: UVFD -> Ptr Word8 -> Int -> Int64 -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_unlink_threaded
+    :: CString -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_mkdir_threaded 
+    :: CString -> FileMode -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_rmdir_threaded 
+    :: CString -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_mkdtemp_threaded 
+    :: CString -> Int -> CString -> Ptr UVLoop -> IO UVSlotUnSafe
+
+newtype FileFlag = FileFlag CInt
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+-- | The file is opened in append mode. Before each write, the file offset is positioned at the end of the file.
+pattern O_APPEND :: FileFlag
+pattern O_APPEND = FileFlag #{const UV_FS_O_APPEND}
+
+-- | The file is created if it does not already exist.
+pattern O_CREAT :: FileFlag
+pattern O_CREAT = FileFlag #{const UV_FS_O_CREAT}
+
+-- | File IO is done directly to and from user-space buffers, which must be aligned. Buffer size and address should be a multiple of the physical sector size of the block device, (DO NOT USE WITH Z-IO's @BufferedIO@)
+pattern O_DIRECT :: FileFlag
+pattern O_DIRECT = FileFlag #{const UV_FS_O_DIRECT}
+
+-- | If the path is not a directory, fail the open. (Not useful on regular file)
+--
+-- Note 'o_DIRECTORY' is not supported on Windows.
+pattern O_DIRECTORY :: FileFlag
+pattern O_DIRECTORY = FileFlag #{const UV_FS_O_DIRECTORY}
+
+-- |The file is opened for synchronous IO. Write operations will complete once all data and a minimum of metadata are flushed to disk.
+--
+-- Note 'o_DSYNC' is supported on Windows via @FILE_FLAG_WRITE_THROUGH@.
+pattern O_DSYNC :: FileFlag
+pattern O_DSYNC = FileFlag #{const UV_FS_O_DSYNC}
+
+-- | If the 'o_CREAT' flag is set and the file already exists, fail the open.
+--
+-- Note In general, the behavior of 'o_EXCL' is undefined if it is used without 'o_CREAT'. There is one exception: on 
+-- Linux 2.6 and later, 'o_EXCL' can be used without 'o_CREAT' if pathname refers to a block device. If the block 
+-- device is in use by the system (e.g., mounted), the open will fail with the error @EBUSY@.
+pattern O_EXCL :: FileFlag
+pattern O_EXCL = FileFlag #{const UV_FS_O_EXCL}
+
+-- | Atomically obtain an exclusive lock.
+--
+-- Note UV_FS_O_EXLOCK is only supported on macOS and Windows.
+-- (libuv: Changed in version 1.17.0: support is added for Windows.)
+pattern O_EXLOCK :: FileFlag
+pattern O_EXLOCK = FileFlag #{const UV_FS_O_EXLOCK}
+
+-- | Do not update the file access time when the file is read.
+-- 
+-- Note 'o_NOATIME' is not supported on Windows.
+pattern O_NOATIME :: FileFlag
+pattern O_NOATIME = FileFlag #{const UV_FS_O_NOATIME}
+
+-- | If the path identifies a terminal device, opening the path will not cause that terminal to become the controlling terminal for the process (if the process does not already have one). (Not sure if this flag is useful)
+--
+-- Note 'o_NOCTTY' is not supported on Windows.
+pattern O_NOCTTY :: FileFlag
+pattern O_NOCTTY = FileFlag #{const UV_FS_O_NOCTTY}
+
+-- | If the path is a symbolic link, fail the open.
+--
+-- Note 'o_NOFOLLOW' is not supported on Windows.
+pattern O_NOFOLLOW :: FileFlag
+pattern O_NOFOLLOW = FileFlag #{const UV_FS_O_NOFOLLOW}
+
+-- | Open the file in nonblocking mode if possible. (Definitely not useful in Z-IO)
+--
+-- Note 'o_NONBLOCK' is not supported on Windows. (Not useful on regular file anyway)
+pattern O_NONBLOCK :: FileFlag
+pattern O_NONBLOCK = FileFlag #{const UV_FS_O_NONBLOCK}
+
+-- | Access is intended to be random. The system can use this as a hint to optimize file caching.
+-- 
+-- Note 'o_RANDOM' is only supported on Windows via @FILE_FLAG_RANDOM_ACCESS@.
+pattern O_RANDOM :: FileFlag
+pattern O_RANDOM = FileFlag #{const UV_FS_O_RANDOM}
+
+-- | Open the file for read-only access.
+pattern O_RDONLY :: FileFlag
+pattern O_RDONLY = FileFlag #{const UV_FS_O_RDONLY}
+
+-- | Open the file for read-write access.
+pattern O_RDWR :: FileFlag
+pattern O_RDWR = FileFlag #{const UV_FS_O_RDWR}
+
+
+-- | Access is intended to be sequential from beginning to end. The system can use this as a hint to optimize file caching.
+-- 
+-- Note 'o_SEQUENTIAL' is only supported on Windows via @FILE_FLAG_SEQUENTIAL_SCAN@.
+pattern O_SEQUENTIAL :: FileFlag
+pattern O_SEQUENTIAL = FileFlag #{const UV_FS_O_SEQUENTIAL}
+
+-- | The file is temporary and should not be flushed to disk if possible.
+--
+-- Note 'o_SHORT_LIVED' is only supported on Windows via @FILE_ATTRIBUTE_TEMPORARY@.
+pattern O_SHORT_LIVED :: FileFlag
+pattern O_SHORT_LIVED = FileFlag #{const UV_FS_O_SHORT_LIVED}
+
+-- | Open the symbolic link itself rather than the resource it points to.
+pattern O_SYMLINK :: FileFlag
+pattern O_SYMLINK = FileFlag #{const UV_FS_O_SYMLINK}
+
+-- | The file is opened for synchronous IO. Write operations will complete once all data and all metadata are flushed to disk.
+--
+-- Note 'o_SYNC' is supported on Windows via @FILE_FLAG_WRITE_THROUGH@.
+pattern O_SYNC :: FileFlag
+pattern O_SYNC = FileFlag #{const UV_FS_O_SYNC}
+
+-- | The file is temporary and should not be flushed to disk if possible.
+--
+-- Note 'o_TEMPORARY' is only supported on Windows via @FILE_ATTRIBUTE_TEMPORARY@.
+pattern O_TEMPORARY :: FileFlag
+pattern O_TEMPORARY = FileFlag #{const UV_FS_O_TEMPORARY}
+
+-- | If the file exists and is a regular file, and the file is opened successfully for write access, its length shall be truncated to zero.
+pattern O_TRUNC :: FileFlag
+pattern O_TRUNC = FileFlag #{const UV_FS_O_TRUNC}
+
+-- | Open the file for write-only access.
+pattern O_WRONLY :: FileFlag
+pattern O_WRONLY = FileFlag #{const UV_FS_O_WRONLY}
+
+#if defined(_WIN32)
+newtype UVDirEntType = UVDirEntType CInt
+#else
+newtype UVDirEntType = UVDirEntType CChar
+#endif
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+data DirEntType
+    = DirEntUnknown
+    | DirEntFile
+    | DirEntDir
+    | DirEntLink
+    | DirEntFIFO
+    | DirEntSocket
+    | DirEntChar
+    | DirEntBlock
+  deriving (Read, Show, Eq, Ord, Generic)
+
+fromUVDirEntType :: UVDirEntType -> DirEntType
+fromUVDirEntType t
+    | t == uV__DT_FILE   = DirEntFile
+    | t == uV__DT_DIR    = DirEntDir
+    | t == uV__DT_LINK   = DirEntLink
+    | t == uV__DT_FIFO   = DirEntFIFO
+    | t == uV__DT_SOCKET = DirEntSocket
+    | t == uV__DT_CHAR   = DirEntChar
+    | t == uV__DT_BLOCK  = DirEntBlock
+    | otherwise          = DirEntUnknown
+
+#{enum UVDirEntType, UVDirEntType,
+    uV__DT_FILE    = UV__DT_FILE,
+    uV__DT_DIR     = UV__DT_DIR,
+    uV__DT_LINK    = UV__DT_LINK,
+    uV__DT_FIFO    = UV__DT_FIFO,
+    uV__DT_SOCKET  = UV__DT_SOCKET,
+    uV__DT_CHAR    = UV__DT_CHAR,
+    uV__DT_BLOCK   = UV__DT_BLOCK}
+
+data UVDirEnt
+
+peekUVDirEnt :: Ptr UVDirEnt -> IO (CString, UVDirEntType)
+#ifdef HAVE_DIRENT_TYPES
+peekUVDirEnt p = (,) (#{ptr hs_uv__dirent_t, d_name } p) <$> (#{peek hs_uv__dirent_t, d_type } p)
+#else
+peekUVDirEnt p = return ((#{ptr hs_uv__dirent_t,  d_name } p), #{const DT_UNKNOWN})
+#endif
+
+foreign import ccall unsafe hs_uv_fs_scandir_cleanup
+    :: Ptr (Ptr UVDirEnt) -> Int -> IO ()
+foreign import ccall unsafe hs_uv_fs_scandir
+    :: CString -> MBA## (Ptr UVDirEnt) -> IO Int
+foreign import ccall unsafe hs_uv_fs_scandir_extra_cleanup 
+    :: Ptr (Ptr (Ptr UVDirEnt)) -> Int -> IO ()
+foreign import ccall unsafe hs_uv_fs_scandir_threaded
+    :: CString -> Ptr (Ptr (Ptr UVDirEnt)) -> Ptr UVLoop -> IO UVSlotUnSafe
+
+data UVTimeSpec = UVTimeSpec 
+    { uvtSecond     :: {-# UNPACK #-} !CLong
+    , uvtNanoSecond :: {-# UNPACK #-} !CLong
+    } deriving (Show, Read, Eq, Ord, Generic)
+
+instance Storable UVTimeSpec where
+    sizeOf _  = #{size uv_timespec_t}
+    alignment _ = #{alignment uv_timespec_t}
+    peek p = UVTimeSpec <$> (#{peek uv_timespec_t, tv_sec } p)
+                        <*> (#{peek uv_timespec_t, tv_nsec } p)
+    poke p (UVTimeSpec sec nsec) = do
+        (#{poke uv_timespec_t, tv_sec  } p sec)
+        (#{poke uv_timespec_t, tv_nsec } p nsec)
+
+data UVStat = UVStat
+    { stDev      :: {-# UNPACK #-} !Word64
+    , stMode     :: {-# UNPACK #-} !Word64
+    , stNlink    :: {-# UNPACK #-} !Word64
+    , stUid      :: {-# UNPACK #-} !Word64
+    , stGid      :: {-# UNPACK #-} !Word64
+    , stRdev     :: {-# UNPACK #-} !Word64
+    , stIno      :: {-# UNPACK #-} !Word64
+    , stSize     :: {-# UNPACK #-} !Word64
+    , stBlksize  :: {-# UNPACK #-} !Word64
+    , stBlocks   :: {-# UNPACK #-} !Word64
+    , stFlags    :: {-# UNPACK #-} !Word64
+    , stGen      :: {-# UNPACK #-} !Word64
+    , stAtim     :: {-# UNPACK #-} !UVTimeSpec
+    , stMtim     :: {-# UNPACK #-} !UVTimeSpec
+    , stCtim     :: {-# UNPACK #-} !UVTimeSpec
+    , stBirthtim :: {-# UNPACK #-} !UVTimeSpec
+    } deriving (Show, Read, Eq, Ord, Generic)
+
+uvStatSize :: Int
+uvStatSize = #{size uv_stat_t}
+
+peekUVStat :: Ptr UVStat -> IO UVStat
+peekUVStat p = UVStat
+    <$> (#{peek uv_stat_t, st_dev          } p)
+    <*> (#{peek uv_stat_t, st_mode         } p)
+    <*> (#{peek uv_stat_t, st_nlink        } p)
+    <*> (#{peek uv_stat_t, st_uid          } p)
+    <*> (#{peek uv_stat_t, st_gid          } p)
+    <*> (#{peek uv_stat_t, st_rdev         } p)
+    <*> (#{peek uv_stat_t, st_ino          } p)
+    <*> (#{peek uv_stat_t, st_size         } p)
+    <*> (#{peek uv_stat_t, st_blksize      } p)
+    <*> (#{peek uv_stat_t, st_blocks       } p)
+    <*> (#{peek uv_stat_t, st_flags        } p)
+    <*> (#{peek uv_stat_t, st_gen          } p)
+    <*> (#{peek uv_stat_t, st_atim         } p)
+    <*> (#{peek uv_stat_t, st_mtim         } p)
+    <*> (#{peek uv_stat_t, st_ctim         } p)
+    <*> (#{peek uv_stat_t, st_birthtim     } p)
+
+foreign import ccall unsafe hs_uv_fs_stat :: CString -> Ptr UVStat -> IO Int
+foreign import ccall unsafe hs_uv_fs_fstat :: UVFD -> Ptr UVStat -> IO Int
+foreign import ccall unsafe hs_uv_fs_lstat :: CString -> Ptr UVStat -> IO Int
+foreign import ccall unsafe hs_uv_fs_rename :: CString -> CString -> IO Int
+foreign import ccall unsafe hs_uv_fs_fsync :: UVFD -> IO Int
+foreign import ccall unsafe hs_uv_fs_fdatasync :: UVFD -> IO Int
+foreign import ccall unsafe hs_uv_fs_ftruncate :: UVFD -> Int64 -> IO Int
+
+foreign import ccall unsafe hs_uv_fs_stat_threaded
+    :: CString -> Ptr UVStat -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_fstat_threaded
+    :: UVFD -> Ptr UVStat -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_lstat_threaded
+    :: CString -> Ptr UVStat -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_rename_threaded
+    :: CString -> CString -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_fsync_threaded
+    :: UVFD -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_fdatasync_threaded
+    :: UVFD -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_ftruncate_threaded 
+    :: UVFD -> Int64 -> Ptr UVLoop -> IO UVSlotUnSafe
+
+-- | Flags control copying.
+-- 
+--   * 'COPYFILE_EXCL': If present, uv_fs_copyfile() will fail with UV_EEXIST if the destination path already exists. The default behavior is to overwrite the destination if it exists.
+--   * 'COPYFILE_FICLONE': If present, uv_fs_copyfile() will attempt to create a copy-on-write reflink. If the underlying platform does not support copy-on-write, then a fallback copy mechanism is used.
+-- 
+newtype UVCopyFileFlag = UVCopyFileFlag CInt
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+pattern COPYFILE_DEFAULT :: UVCopyFileFlag
+pattern COPYFILE_DEFAULT = UVCopyFileFlag 0
+
+pattern COPYFILE_EXCL :: UVCopyFileFlag
+pattern COPYFILE_EXCL = UVCopyFileFlag #{const UV_FS_COPYFILE_EXCL}
+
+pattern COPYFILE_FICLONE :: UVCopyFileFlag
+#ifdef UV_FS_COPYFILE_FICLONE
+pattern COPYFILE_FICLONE = UVCopyFileFlag #{const UV_FS_COPYFILE_FICLONE}
+#else
+pattern COPYFILE_FICLONE = UVCopyFileFlag 0   -- fallback to normal copy.
+#endif
+
+foreign import ccall unsafe hs_uv_fs_copyfile :: CString -> CString -> UVCopyFileFlag -> IO Int
+foreign import ccall unsafe hs_uv_fs_copyfile_threaded
+    :: CString -> CString -> UVCopyFileFlag -> Ptr UVLoop -> IO UVSlotUnSafe
+
+newtype UVAccessMode = UVAccessMode CInt
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+pattern F_OK :: UVAccessMode
+pattern F_OK = UVAccessMode #{const F_OK}
+pattern R_OK :: UVAccessMode
+pattern R_OK = UVAccessMode #{const R_OK}
+pattern W_OK :: UVAccessMode
+pattern W_OK = UVAccessMode #{const W_OK}
+pattern X_OK :: UVAccessMode
+pattern X_OK = UVAccessMode #{const X_OK}
+
+data AccessResult = NoExistence | NoPermission | AccessOK deriving (Show, Eq, Ord)
+
+foreign import ccall unsafe hs_uv_fs_access :: CString -> UVAccessMode -> IO Int
+foreign import ccall unsafe hs_uv_fs_access_threaded
+    :: CString -> UVAccessMode -> Ptr UVLoop -> IO UVSlotUnSafe
+
+foreign import ccall unsafe hs_uv_fs_chmod :: CString -> FileMode -> IO Int
+foreign import ccall unsafe hs_uv_fs_chmod_threaded
+    :: CString -> FileMode -> Ptr UVLoop -> IO UVSlotUnSafe
+
+foreign import ccall unsafe hs_uv_fs_fchmod :: UVFD -> FileMode -> IO Int
+foreign import ccall unsafe hs_uv_fs_fchmod_threaded
+    :: UVFD -> FileMode -> Ptr UVLoop -> IO UVSlotUnSafe
+
+foreign import ccall unsafe hs_uv_fs_utime :: CString -> Double -> Double -> IO Int
+foreign import ccall unsafe hs_uv_fs_utime_threaded
+    :: CString -> Double -> Double -> Ptr UVLoop -> IO UVSlotUnSafe
+
+foreign import ccall unsafe hs_uv_fs_futime :: UVFD -> Double -> Double -> IO Int
+foreign import ccall unsafe hs_uv_fs_futime_threaded
+    :: UVFD -> Double -> Double -> Ptr UVLoop -> IO UVSlotUnSafe
+
+newtype UVSymlinkFlag = UVSymlinkFlag CInt
+    deriving (Eq, Ord, Read, Show, FiniteBits, Bits, Storable, Num)
+
+pattern SYMLINK_DEFAULT :: UVSymlinkFlag
+pattern SYMLINK_DEFAULT = UVSymlinkFlag 0
+
+pattern SYMLINK_DIR :: UVSymlinkFlag
+pattern SYMLINK_DIR = UVSymlinkFlag #{const UV_FS_SYMLINK_DIR}
+
+pattern SYMLINK_JUNCTION :: UVSymlinkFlag
+pattern SYMLINK_JUNCTION = UVSymlinkFlag #{const UV_FS_SYMLINK_JUNCTION}
+
+foreign import ccall unsafe hs_uv_fs_link :: CString -> CString -> IO Int
+foreign import ccall unsafe hs_uv_fs_link_threaded
+    :: CString -> CString -> Ptr UVLoop -> IO UVSlotUnSafe
+
+foreign import ccall unsafe hs_uv_fs_symlink :: CString -> CString -> UVSymlinkFlag -> IO Int
+foreign import ccall unsafe hs_uv_fs_symlink_threaded
+    :: CString -> CString -> UVSymlinkFlag -> Ptr UVLoop -> IO UVSlotUnSafe
+
+-- readlink and realpath share the same cleanup and callback
+foreign import ccall unsafe hs_uv_fs_readlink_cleanup
+    :: CString -> IO ()
+foreign import ccall unsafe hs_uv_fs_readlink
+    :: CString -> MBA## CString -> IO Int
+foreign import ccall unsafe hs_uv_fs_realpath
+    :: CString -> MBA## CString -> IO Int
+foreign import ccall unsafe hs_uv_fs_readlink_extra_cleanup 
+    :: Ptr CString -> IO ()
+foreign import ccall unsafe hs_uv_fs_readlink_threaded
+    :: CString -> Ptr CString -> Ptr UVLoop -> IO UVSlotUnSafe
+foreign import ccall unsafe hs_uv_fs_realpath_threaded
+    :: CString -> Ptr CString -> Ptr UVLoop -> IO UVSlotUnSafe
+
+--------------------------------------------------------------------------------
+-- misc
+
+newtype UVHandleType = UVHandleType CInt deriving (Eq, Ord, Read, Show, Storable)
+
+pattern UV_UNKNOWN_HANDLE :: UVHandleType
+pattern UV_UNKNOWN_HANDLE = UVHandleType #{const UV_UNKNOWN_HANDLE}
+pattern UV_ASYNC :: UVHandleType
+pattern UV_ASYNC = UVHandleType #{const UV_ASYNC}
+pattern UV_CHECK :: UVHandleType
+pattern UV_CHECK = UVHandleType #{const UV_CHECK}
+pattern UV_FS_EVENT :: UVHandleType
+pattern UV_FS_EVENT = UVHandleType #{const UV_FS_EVENT}
+pattern UV_FS_POLL :: UVHandleType
+pattern UV_FS_POLL = UVHandleType #{const UV_FS_POLL}
+pattern UV_HANDLE :: UVHandleType
+pattern UV_HANDLE = UVHandleType #{const UV_HANDLE}
+pattern UV_IDLE :: UVHandleType
+pattern UV_IDLE = UVHandleType #{const UV_IDLE}
+pattern UV_NAMED_PIPE :: UVHandleType
+pattern UV_NAMED_PIPE = UVHandleType #{const UV_NAMED_PIPE}
+pattern UV_POLL :: UVHandleType
+pattern UV_POLL = UVHandleType #{const UV_POLL}
+pattern UV_PREPARE :: UVHandleType
+pattern UV_PREPARE = UVHandleType #{const UV_PREPARE}
+pattern UV_PROCESS :: UVHandleType
+pattern UV_PROCESS = UVHandleType #{const UV_PROCESS}
+pattern UV_STREAM :: UVHandleType
+pattern UV_STREAM = UVHandleType #{const UV_STREAM}
+pattern UV_TCP :: UVHandleType
+pattern UV_TCP = UVHandleType #{const UV_TCP}
+pattern UV_TIMER :: UVHandleType
+pattern UV_TIMER = UVHandleType #{const UV_TIMER}
+pattern UV_TTY :: UVHandleType
+pattern UV_TTY = UVHandleType #{const UV_TTY}
+pattern UV_UDP :: UVHandleType
+pattern UV_UDP = UVHandleType #{const UV_UDP}
+pattern UV_SIGNAL :: UVHandleType
+pattern UV_SIGNAL = UVHandleType #{const UV_SIGNAL}
+pattern UV_FILE :: UVHandleType
+pattern UV_FILE = UVHandleType #{const UV_FILE}
+
+foreign import ccall unsafe uv_guess_handle :: UVFD -> IO UVHandleType
diff --git a/Z/IO/UV/Manager.hs b/Z/IO/UV/Manager.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/UV/Manager.hs
@@ -0,0 +1,466 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE MultiWayIf #-}
+
+{-|
+Module      : Z.IO.UV.Manager
+Description : IO manager based on libuv
+Copyright   : (c) Dong Han, 2017-2018
+License     : BSD
+Maintainer  : winterland1989@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+This module provide IO manager which bridge libuv's async interface with ghc's light weight thread.
+
+The main procedures for doing event IO is:
+
+  * Allocate a slot number using 'allocSlot'.
+  * Prepare you IO buffer and write them to uv loop with 'pokeBufferTable'(both read and write).
+  * Block your thread with a 'MVar', using 'getBlockMVar' to get it.
+  * Read the result with 'getResult', for read it's the read bytes number, for write it will be zero.
+    Use 'E.throwIfError' to guard error situations.
+  * Return the slot back uv manager with 'freeSlot'.
+
+Usually slots are cache in the IO device so that you don't have to allocate new one before each IO operation.
+Check "System.IO.Socket.TCP" as an example.
+
+-}
+
+module Z.IO.UV.Manager
+  ( UVManager
+  , getUVManager
+  , getBlockMVar
+  , peekBufferTable
+  , pokeBufferTable
+  , withUVManager
+  , withUVManager'
+  , getUVSlot
+  -- * request based async function helper
+  , withUVRequest
+  , withUVRequest_
+  , withUVRequest'
+  , withUVRequestEx
+  -- * uv_stream abstraction
+  , initUVStream
+  , UVStream(..)
+  -- * concurrent helpers
+  , forkBa
+  ) where
+
+import           Control.Concurrent
+import           Control.Monad
+import           Control.Monad.IO.Class
+import           Data.IORef
+import           Data.Bits (shiftL)
+import           Data.Word
+import           Foreign.Ptr
+import           Foreign.Storable
+import           GHC.Conc.Sync            (labelThread)
+import           System.IO.Unsafe
+import           Z.Data.Array
+import           Z.Data.PrimRef.PrimIORef
+import           Z.IO.Buffered
+import           Z.IO.Exception
+import           Z.IO.UV.Errno
+import           Z.IO.Resource
+import           Z.IO.UV.FFI
+
+#define IDLE_LIMIT 20
+
+--------------------------------------------------------------------------------
+
+data UVManager = UVManager
+    { uvmBlockTable :: {-# UNPACK #-} !(IORef (UnliftedArray (MVar Int))) -- a array to store threads blocked on async IO.
+
+    , uvmLoop       :: {-# UNPACK #-} !(Ptr UVLoop)        -- the uv loop refrerence
+
+    , uvmLoopData   :: {-# UNPACK #-} !(Ptr UVLoopData)    -- cached pointer to uv_loop_t's data field
+
+    , uvmRunning    :: {-# UNPACK #-} !(MVar Bool)     -- only uv manager thread will modify this value.
+                                                        -- 'True' druing uv_run and 'False' otherwise.
+                                                        --
+                                                        -- unlike epoll/ONESHOT, uv loop are NOT thread safe,
+                                                        -- we have to wake up the loop before mutating uv_loop's
+                                                        -- state.
+    , uvmCap        ::  {-# UNPACK #-} !Int                -- the capability uv manager run on.
+    }
+
+instance Show UVManager where
+    show uvm = "UVManager on capability " ++ show (uvmCap uvm)
+
+instance Eq UVManager where
+    uvm == uvm' =
+        uvmCap uvm == uvmCap uvm'
+
+uvManagerArray :: IORef (Array UVManager)
+{-# NOINLINE uvManagerArray #-}
+uvManagerArray = unsafePerformIO $ do
+    numCaps <- getNumCapabilities
+    uvmArray <- newArr numCaps
+    s <- newQSemN 0
+    forM_ [0..numCaps-1] $ \ i -> do
+        -- fork uv manager thread
+        forkOn i . withResource (initUVManager INIT_LOOP_SIZE i) $ \ m -> do
+            myThreadId >>= (`labelThread` ("uv manager on " ++ show i))
+            writeArr uvmArray i m
+            signalQSemN s 1
+            startUVManager m
+    waitQSemN s numCaps
+    iuvmArray <- unsafeFreezeArr uvmArray
+    newIORef iuvmArray
+
+-- | Get 'UVManager' runing on the same capability.
+--
+getUVManager :: IO UVManager
+{-# INLINABLE getUVManager #-}
+getUVManager = do
+    (cap, _) <- threadCapability =<< myThreadId
+    uvmArray <- readIORef uvManagerArray
+    indexArrM uvmArray (cap `rem` sizeofArr uvmArray)
+
+-- | Get 'MVar' from blocking table with given slot.
+--
+getBlockMVar :: UVManager -> UVSlot -> IO (MVar Int)
+{-# INLINABLE getBlockMVar #-}
+getBlockMVar uvm slot = do
+    blockTable <- readIORef (uvmBlockTable uvm)
+    indexArrM blockTable slot
+
+-- | Poke a prepared buffer and size into loop data under given slot.
+--
+-- NOTE, this action is not protected with 'withUVManager' for effcient reason, you should merge this action
+-- with other uv action and put them together inside a 'withUVManager' or 'withUVManager\''. for example:
+--
+-- @
+--    ...
+--    withUVManager' uvm $ do
+--        pokeBufferTable uvm slot buf len
+--        uvReadStart handle
+--    ...
+-- @
+--
+pokeBufferTable :: UVManager    -- ^ uv manager
+                -> UVSlot       -- ^ uv slot
+                -> Ptr Word8    -- ^ buffer pointer
+                -> Int          -- ^ buffer length
+                -> IO ()
+{-# INLINABLE pokeBufferTable #-}
+pokeBufferTable uvm slot buf bufSiz = do
+    (bufTable, bufSizTable) <- peekUVBufferTable (uvmLoopData uvm)
+    pokeElemOff bufTable slot buf
+    pokeElemOff bufSizTable slot (fromIntegral bufSiz)
+
+peekBufferTable :: UVManager -> UVSlot -> IO Int
+{-# INLINABLE peekBufferTable #-}
+peekBufferTable uvm slot = do
+    (_, bufSizTable) <- peekUVBufferTable (uvmLoopData uvm)
+    fromIntegral <$> peekElemOff bufSizTable slot
+
+initUVManager :: HasCallStack => Int -> Int -> Resource UVManager
+initUVManager siz cap = do
+    loop  <- initResource
+                (throwOOMIfNull $ hs_uv_loop_init (fromIntegral siz))
+                hs_uv_loop_close
+    liftIO $ do
+        mblockTable <- newArr siz
+        forM_ [0..siz-1] $ \ i -> writeArr mblockTable i =<< newEmptyMVar
+        blockTable <- unsafeFreezeArr mblockTable
+        blockTableRef <- newIORef blockTable
+        loopData <- peekUVLoopData loop
+        runningLock <- newMVar False
+        return (UVManager blockTableRef loop loopData runningLock cap)
+
+-- | Lock an uv mananger, so that we can safely mutate its uv_loop's state.
+--
+-- libuv is not thread safe, use this function to perform any action which will mutate uv_loop's state.
+--
+withUVManager :: HasCallStack => UVManager -> (Ptr UVLoop -> IO a) -> IO a
+withUVManager (UVManager _ loop loopData runningLock _) f = go
+  where
+    go = do
+        r <- withMVar runningLock $ \ running ->
+            if running
+            then do
+                -- if uv_run is running, it will stop
+                -- if uv_run is not running, next running won't block
+                throwUVIfMinus_ (hs_uv_wake_up_async loopData)
+                return Nothing
+            else do
+                r <- f loop
+                return (Just r)
+        case r of
+            Just r' -> return r'
+            _       -> yield >> go -- we yield here, because uv_run is probably not finished yet
+
+-- | Lock an uv mananger, so that we can safely mutate its uv_loop's state.
+--
+-- Some action did not request uv_loop pointer explicitly, but will mutate uv_loop underhood, for example:
+-- @uv_read_start@. These actions have to be protected by locking the uv_loop.
+--
+-- In fact most of the libuv's functions are not thread safe, so watch out!
+--
+withUVManager' :: HasCallStack => UVManager -> IO a -> IO a
+withUVManager' uvm f = withUVManager uvm (\ _ -> f)
+
+-- | Start the uv loop
+--
+startUVManager :: HasCallStack => UVManager -> IO ()
+startUVManager uvm@(UVManager _ _ _ runningLock _) = poll -- use a closure capture uvm in case of stack memory leaking
+  where
+    -- we borrow mio's non-blocking/blocking poll strategy here
+    poll = do
+        e <- withMVar runningLock $ \ _ -> step uvm False
+        if e > 0                                        -- first we do a non-blocking poll, if we got events
+        then yield >> poll                              -- we yield here, to let other threads do actual work
+        else do                                         -- otherwise we still yield once
+            yield                                       -- in case other threads can still progress
+            e' <- withMVar runningLock $ \ _ -> step uvm False   -- now we do another non-blocking poll to make sure
+            if e' > 0 then yield >> poll             -- if we got events somehow, we yield and go back
+            else do                                 -- if there's still no events, we directly jump to safe blocking poll
+                _ <- swapMVar runningLock True          -- after swap this lock, other thread can wake up us
+                _ <- step uvm True                  -- by send async handler, and it's thread safe
+                _ <- swapMVar runningLock False
+
+                yield                               -- we yield here, to let other threads do actual work
+                poll
+
+    -- call uv_run, return the event number
+    step :: UVManager -> Bool -> IO Int
+    step (UVManager blockTableRef loop loopData _ _) block = do
+            blockTable <- readIORef blockTableRef
+            clearUVEventCounter loopData        -- clean event counter
+
+            if block
+            then if rtsSupportsBoundThreads
+                then throwUVIfMinus_ $ uv_run_safe loop UV_RUN_ONCE
+                else do
+                    -- use a 1ms timeout blocking poll on non-threaded rts
+                    throwUVIfMinus_ (hs_uv_wake_up_timer loopData)
+                    throwUVIfMinus_ (uv_run loop UV_RUN_ONCE)
+            else throwUVIfMinus_ (uv_run loop UV_RUN_NOWAIT)
+
+            (c, q) <- peekUVEventQueue loopData
+            forM_ [0..c-1] $ \ i -> do
+                slot <- peekElemOff q i
+                lock <- indexArrM blockTable slot
+                -- It's important to read the buffer size table inside running lock and
+                -- unlock ghc thread with the result, where 'tryPutMVar' will mutate waiting
+                -- thread's stack to ensure it will receive the result after get resumed.
+                --
+                -- After step finished, other threads are free to take the same slot,
+                -- thus can overwrite the buffer size table, i.e. the previous result.
+                --
+                r <- peekBufferTable uvm slot
+                tryPutMVar lock r
+            return c
+
+-- | Run a libuv FFI to get a 'UVSlotUnSafe' (which may exceed block table size),
+-- resize the block table in that case, so that the returned slot always has an
+-- accompanying 'MVar' in block table.
+--
+-- Always use this function to turn an 'UVSlotUnsafe' into 'UVSlot', so that the block
+-- table size synchronize with libuv side's slot table.
+getUVSlot :: HasCallStack => UVManager -> IO UVSlotUnSafe -> IO UVSlot
+{-# INLINE getUVSlot #-}
+getUVSlot (UVManager blockTableRef _ _ _ _) f = do
+    slot <- throwUVIfMinus (unsafeGetSlot <$> f)
+    blockTable <- readIORef blockTableRef
+    let oldSiz = sizeofArr blockTable
+    when (slot == oldSiz) $ do
+        let newSiz = oldSiz `shiftL` 2
+        blockTable' <- newArr newSiz
+        copyArr blockTable' 0 blockTable 0 oldSiz
+        forM_ [oldSiz..newSiz-1] $ \ i ->
+            writeArr blockTable' i =<< newEmptyMVar
+        !iBlockTable' <- unsafeFreezeArr blockTable'
+        writeIORef blockTableRef iBlockTable'
+    return slot
+
+--------------------------------------------------------------------------------
+
+-- | Cancel uv async function (actions which can be cancelled with 'uv_cancel') with
+-- best effort, if the action is already performed, run an extra clean up action.
+cancelUVReq :: UVManager -> UVSlot -> (Int -> IO ()) -> IO ()
+cancelUVReq uvm slot extra_cleanup = withUVManager uvm $ \ loop -> do
+    m <- getBlockMVar uvm slot
+    r <- tryTakeMVar m
+    case r of
+        Just r' -> extra_cleanup r'             -- It's too late
+        _ -> do
+            pokeBufferTable uvm slot nullPtr 0  -- doing this let libuv side knows that
+                                                -- we won't keep buffer alive in callbacks
+            hs_uv_cancel loop slot              -- then we cancel the io with best efforts
+
+-- | Exception safe uv request helper
+--
+-- This helper will run a libuv's async function, which will return a
+-- libuv side's slot, then we will accommodate a 'MVar' in block table and
+-- wait on that 'MVar', until the async function finished or an exception
+-- is received, in later case we will call 'cancelUVReq' to cancel the on-going
+-- async function with best efforts,
+withUVRequest :: HasCallStack
+              => UVManager -> (Ptr UVLoop -> IO UVSlotUnSafe) -> IO Int
+withUVRequest uvm f = do
+    (slot, m) <- withUVManager uvm $ \ loop -> mask_ $ do
+        slot <- getUVSlot uvm (f loop)
+        m <- getBlockMVar uvm slot
+        _ <- tryTakeMVar m
+        return (slot, m)
+    throwUVIfMinus (takeMVar m `onException` cancelUVReq uvm slot no_extra_cleanup)
+  where no_extra_cleanup = const $ return ()
+
+-- | Same with 'withUVRequest' but disgard the result.
+withUVRequest_ :: HasCallStack
+               => UVManager -> (Ptr UVLoop -> IO UVSlotUnSafe) -> IO ()
+withUVRequest_ uvm f = void (withUVRequest uvm f)
+
+-- | Same with 'withUVRequest' but apply an convert function to result.
+--
+-- The convert function have all access to the returned value including
+-- negative ones, it's convert funtions's responsiblity to throw an exception
+-- if appropriate.
+withUVRequest' :: HasCallStack
+               => UVManager
+               -> (Ptr UVLoop -> IO UVSlotUnSafe)
+               -> (Int -> IO b)     -- ^ convert function
+               -> IO b
+withUVRequest' uvm f g = do
+    (slot, m) <- withUVManager uvm $ \ loop -> mask_ $ do
+        slot <- getUVSlot uvm (f loop)
+        m <- getBlockMVar uvm slot
+        _ <- tryTakeMVar m
+        return (slot, m)
+    g =<< (takeMVar m `onException` cancelUVReq uvm slot no_extra_cleanup)
+  where no_extra_cleanup = const $ return ()
+
+-- | Same with 'withUVRequest', but will also run an extra cleanup function
+-- if async exception hit this thread but the async action is already successfully performed,
+-- e.g. release result memory.
+withUVRequestEx :: HasCallStack
+                => UVManager -> (Ptr UVLoop -> IO UVSlotUnSafe) -> (Int -> IO ()) -> IO Int
+withUVRequestEx uvm f extra_cleanup = do
+    (slot, m) <- withUVManager uvm $ \ loop -> mask_ $ do
+        slot <- getUVSlot uvm (f loop)
+        m <- getBlockMVar uvm slot
+        _ <- tryTakeMVar m
+        return (slot, m)
+    throwUVIfMinus (takeMVar m `onException` cancelUVReq uvm slot extra_cleanup)
+
+--------------------------------------------------------------------------------
+
+-- | Fork a new GHC thread with active load-balancing.
+--
+-- Using libuv based IO solution has a disadvantage that file handlers are bound to certain
+-- uv_loop, thus certain uv mananger/capability. Worker threads that migrate to other capability
+-- will lead contention since various APIs here is protected by manager's lock, this makes GHC's
+-- work-stealing strategy unsuitable for certain workload, such as a webserver.
+-- we solve this problem with simple round-robin load-balancing: forkBa will automatically
+-- distribute new threads to all capabilities in round-robin manner. Thus its name forkBa(lance).
+forkBa :: IO () -> IO ThreadId
+forkBa io = do
+    i <- atomicAddCounter counter 1
+    forkOn i io
+  where
+    counter :: Counter
+    {-# NOINLINE counter #-}
+    counter = unsafePerformIO $ newCounter 0
+
+--------------------------------------------------------------------------------
+-- UVStream
+
+-- | A haskell data type wrap an @uv_stream_t@ inside
+--
+-- 'UVStream' DO NOT provide thread safety! Use 'UVStream' concurrently in multiple
+-- threads will lead to undefined behavior.
+data UVStream = UVStream
+    { uvsHandle  :: {-# UNPACK #-} !(Ptr UVHandle)
+    , uvsSlot    :: {-# UNPACK #-} !UVSlot
+    , uvsManager :: UVManager
+    , uvsClosed  :: {-# UNPACK #-} !(IORef Bool)    -- We have no thread-safe guarantee,
+                                                    -- so no need to use atomic read&write
+    }
+
+instance Show UVStream where
+    show (UVStream hdl slot uvm _) =
+        "UVStream{uvsHandle=" ++ show hdl ++
+                ",uvsSlot=" ++ show slot ++
+                ",uvsManager=" ++ show uvm ++ "}"
+
+-- | Safely lock an uv manager and perform uv_handle initialization.
+--
+-- Initialization an UV stream usually take two step:
+--
+--   * allocate an uv_stream struct with proper size
+--   * lock a particular uv_loop from a uv manager, and perform custom initialization, such as @uv_tcp_init@.
+--
+-- And this is what 'initUVStream' do, all you need to do is to provide the manager you want to hook the handle
+-- onto(usually the one on the same capability, i.e. the one obtained by 'getUVManager'),
+-- and provide a custom initialization function (which should throw an exception if failed).
+--
+initUVStream :: HasCallStack
+             => (Ptr UVLoop -> Ptr UVHandle -> IO ())
+             -> UVManager
+             -> Resource UVStream
+initUVStream f uvm = initResource
+    (withUVManager uvm $ \ loop -> do
+        hdl <- hs_uv_handle_alloc loop
+        slot <- getUVSlot uvm (peekUVHandleData hdl)
+        _ <- tryTakeMVar =<< getBlockMVar uvm slot   -- clear the parking spot
+        f loop hdl `onException` hs_uv_handle_free hdl
+        closed <- newIORef False
+        return (UVStream hdl slot uvm closed))
+    closeUVStream
+
+closeUVStream :: UVStream -> IO ()
+closeUVStream (UVStream hdl _ uvm closed) = withUVManager' uvm $ do
+    c <- readIORef closed
+    -- hs_uv_handle_close won't return error
+    unless c $ writeIORef closed True >> hs_uv_handle_close hdl
+
+instance Input UVStream where
+    -- readInput :: HasCallStack => UVStream -> Ptr Word8 ->  Int -> IO Int
+    readInput (UVStream hdl slot uvm closed) buf len = mask_ $ do
+        c <- readIORef closed
+        when c throwECLOSED
+        -- set up buffer
+        pokeBufferTable uvm slot buf len
+        m <- getBlockMVar uvm slot
+        -- clean up
+        _ <- tryTakeMVar m
+
+        throwUVIfMinus_ $ withUVManager' uvm (hs_uv_read_start hdl)
+        -- since we are inside mask, this is the only place
+        -- async exceptions could possibly kick in, and we should stop reading
+        r <- takeMVar m `onException` (do
+                -- normally we call 'uv_read_stop' in C read callback
+                -- but when exception raise, here's the place to stop
+                throwUVIfMinus_ $ withUVManager' uvm (uv_read_stop hdl)
+                void (tryTakeMVar m))
+
+        if  | r > 0  -> return r
+            -- r == 0 should be impossible, since we guard this situation in c side
+            | r == fromIntegral UV_EOF -> return 0
+            | r < 0 ->  throwUVIfMinus (return r)
+
+
+instance Output UVStream where
+    -- writeOutput :: HasCallStack => UVStream -> Ptr Word8 -> Int -> IO ()
+    writeOutput (UVStream hdl _ uvm closed) buf len = mask_ $ do
+        c <- readIORef closed
+        when c throwECLOSED
+        m <- withUVManager' uvm $ do
+            reqSlot <- getUVSlot uvm (hs_uv_write hdl buf len)
+            m <- getBlockMVar uvm reqSlot
+            _ <- tryTakeMVar m
+            return m
+        -- we can't cancel uv_write_t with current libuv,
+        -- otherwise disaster will happen if buffer got collected.
+        -- so we have to turn to uninterruptibleMask_'s help.
+        -- i.e. writing UVStream is an uninterruptible operation.
+        -- OS will guarantee writing TTY and socket will not
+        -- hang forever anyway.
+        throwUVIfMinus_  (uninterruptibleMask_ $ takeMVar m)
+
+--------------------------------------------------------------------------------
diff --git a/Z/IO/UV/Win.hs b/Z/IO/UV/Win.hs
new file mode 100644
--- /dev/null
+++ b/Z/IO/UV/Win.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE CPP #-}
+{-|
+Module      : Z.IO.UV.Win
+Description : Special code on windows
+Copyright   : (c) Winterland, 2017-2018
+License     : BSD
+Maintainer  : drkoster@qq.com
+Stability   : experimental
+Portability : non-portable
+-}
+module Z.IO.UV.Win where
+
+#if defined(mingw32_HOST_OS)
+import System.IO.Unsafe
+import Control.Exception
+#endif
+
+-- | 'withUVInitDo' is necessary for some socket code because on windows WSAStartup has to be called
+-- before use sockets.
+--
+-- This functions will run 'uv__once_init' once if not run before,
+--
+withUVInitDo :: IO a -> IO a
+{-# INLINE withUVInitDo #-}
+
+#if defined(mingw32_HOST_OS)
+
+withUVInitDo act = evaluate withUVInit >> act
+
+{-# NOINLINE withUVInit #-}
+withUVInit :: ()
+-- Use a CAF to make forcing it do initialisation once, but subsequent forces will be cheap
+withUVInit = unsafePerformIO $ uv__once_init
+
+foreign import ccall unsafe "uv__once_init" uv__once_init :: IO ()
+#else
+
+withUVInitDo x = x
+#endif
diff --git a/cbits/hs_uv_base.c b/cbits/hs_uv_base.c
new file mode 100644
--- /dev/null
+++ b/cbits/hs_uv_base.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2017-2018 Dong Han
+ *
+ * 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.
+ * 3. Neither the names of the authors or the names of any contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE 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.
+ */
+
+#include <hs_uv.h>
+#include <stdio.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// loop
+//
+// initialize a loop with its data to give slot size. return NULL on fail.
+uv_loop_t* hs_uv_loop_init(HsInt siz){
+    int r; 
+    HsInt i;
+
+    uv_loop_t* loop = malloc(sizeof(uv_loop_t));
+    r = uv_loop_init(loop);
+
+    if (r < 0) { 
+        free(loop);
+        return NULL;
+    }
+
+    hs_loop_data* loop_data = malloc(sizeof(hs_loop_data));
+        
+    HsInt* event_queue = malloc(siz*sizeof(HsInt));
+    char** buffer_table = malloc(siz*sizeof(char*));
+    HsInt* buffer_size_table = malloc(siz*sizeof(HsInt));
+    HsInt* slot_table = malloc(siz*sizeof(HsInt));
+    HsInt* free_slot_queue = malloc(siz*sizeof(HsInt));
+    hs_uv_struct** uv_struct_table = malloc(sizeof(void*));
+    hs_uv_struct* uv_struct_table_block = malloc(siz*sizeof(hs_uv_struct));
+
+    uv_async_t* async = malloc(sizeof(uv_async_t));
+    uv_timer_t* timer = malloc(sizeof(uv_timer_t));
+
+    if (loop_data == NULL || event_queue == NULL || buffer_table == NULL ||
+            buffer_size_table == NULL || slot_table == NULL || free_slot_queue == NULL ||
+                uv_struct_table == NULL || uv_struct_table_block == NULL ||
+                    async == NULL || timer == NULL ||
+                        uv_timer_init(loop, timer) < 0 ||
+                            uv_async_init(loop, async, NULL) < 0){
+        free(event_queue);
+        free(loop_data);
+        free(buffer_table);
+        free(buffer_size_table);
+        free(slot_table);
+        free(free_slot_queue);
+        free(uv_struct_table);
+        free(uv_struct_table_block);
+
+        free(async);
+        free(timer);
+
+        uv_loop_close(loop);
+        free(loop);
+        free(loop_data);
+        return NULL;    // before return NULL, free all structs
+    } else {
+        // initialize slot table
+        for (i = 0; i < siz; i++) {
+            slot_table[i] = i+1;
+        }
+        loop_data->event_queue          = event_queue;
+        loop_data->buffer_table         = buffer_table;
+        loop_data->buffer_size_table    = buffer_size_table;
+        loop_data->slot_table           = slot_table;
+        loop_data->free_slot            = 0;
+        loop_data->free_slot_queue      = free_slot_queue;
+        loop_data->free_slot_counter    = 0;
+        loop_data->uv_struct_table      = uv_struct_table;
+        uv_struct_table[0]              = uv_struct_table_block;
+        loop_data->resize               = 0;
+        loop_data->size                 = siz;
+        loop_data->async                = async;
+        loop_data->timer                = timer;
+        loop->data = loop_data;
+        return loop;
+    }
+}
+
+// resize a loop's data to given slot size, return NULL on fail.
+hs_loop_data* hs_uv_loop_resize(hs_loop_data* loop_data, HsInt siz){
+    HsInt i;
+    loop_data->resize += 1;
+    HsInt* event_queue_new       = realloc(loop_data->event_queue, (siz*sizeof(HsInt)));
+    char** buffer_table_new       = realloc(loop_data->buffer_table, (siz*sizeof(char*)));
+    HsInt* buffer_size_table_new = realloc(loop_data->buffer_size_table, (siz*sizeof(HsInt)));
+    HsInt* slot_table_new = realloc(loop_data->slot_table, (siz*sizeof(HsInt)));
+    HsInt* free_slot_queue_new = realloc(loop_data->free_slot_queue, (siz*sizeof(HsInt)));
+    hs_uv_struct** uv_struct_table_new = realloc(loop_data->uv_struct_table, (loop_data->resize+1)*sizeof(void*));
+    hs_uv_struct* uv_struct_table_block = malloc((loop_data->size)*sizeof(hs_uv_struct));
+
+    if (event_queue_new == NULL || buffer_table_new == NULL ||
+            buffer_size_table_new == NULL || slot_table_new == NULL || free_slot_queue_new == NULL ||
+                uv_struct_table_new == NULL || uv_struct_table_block == NULL){
+        // release new memory
+        if (event_queue_new != loop_data->event_queue) free(event_queue_new);
+        if (buffer_table_new != loop_data->buffer_table) free(buffer_table_new);
+        if (buffer_size_table_new != loop_data->buffer_size_table) free(buffer_size_table_new);
+        if (slot_table_new != loop_data->slot_table) free(slot_table_new);
+        if (free_slot_queue_new != loop_data->free_slot_queue) free(free_slot_queue_new);
+        if (uv_struct_table_new != loop_data->uv_struct_table) free(uv_struct_table_new);
+        free(uv_struct_table_block);
+        return NULL;
+    } else {
+        for (i = loop_data->size; i < siz; i++) {
+            slot_table_new[i] = i+1;
+        }
+        loop_data->event_queue        = event_queue_new;
+        loop_data->buffer_table       = buffer_table_new;
+        loop_data->buffer_size_table  = buffer_size_table_new;
+        loop_data->slot_table         = slot_table_new;
+        loop_data->free_slot          = loop_data->size;
+        loop_data->free_slot_queue    = free_slot_queue_new;
+        loop_data->uv_struct_table    = uv_struct_table_new;
+        uv_struct_table_new[loop_data->resize] = uv_struct_table_block;
+        loop_data->size               = siz;
+        return loop_data;
+    }
+}
+
+// allocate free slot, resize loop data if neccessary 
+// return -1 on resize failure, slot otherwise.
+HsInt alloc_slot(hs_loop_data* loop_data){
+    HsInt r = loop_data->free_slot;
+    loop_data->free_slot = loop_data->slot_table[r];
+    // the slot exceed range, we should resize
+    if (r == loop_data->size-1 &&
+        hs_uv_loop_resize(loop_data, (loop_data->size) << 1) == NULL) {
+        return -1;
+    }
+    return r;
+}
+
+void free_slot(hs_loop_data* loop_data, HsInt slot){
+    loop_data->free_slot_queue[loop_data->free_slot_counter] = slot;
+    loop_data->free_slot_counter++;
+}
+
+int hs_uv_run(uv_loop_t* loop, uv_run_mode mode){
+    hs_loop_data* loop_data = loop->data;
+    HsInt* q = loop_data->free_slot_queue;
+    HsInt i = loop_data->free_slot_counter;
+    HsInt slot;
+    // do the real slot release, see notes on slot allocation in hs_uv.h
+    for (i--; i >= 0; i--){
+        slot = q[i];
+        loop_data->slot_table[slot] = loop_data->free_slot;
+        loop_data->free_slot = slot;
+    }
+    loop_data->free_slot_counter = 0;
+    return uv_run(loop, mode);
+}
+
+hs_uv_struct* fetch_uv_struct(hs_loop_data* loop_data, HsInt slot){
+    int bits = 0;
+    HsInt slot2 = slot >> INIT_LOOP_SIZE_BIT;
+    // __builtin_clz may be a good idea
+    while (slot2 > 0){ bits += 1; slot2 = slot2 >> 1; }
+    if (bits == 0)
+        return loop_data->uv_struct_table[bits] + slot;
+    else
+        return loop_data->uv_struct_table[bits] + 
+            slot - (1<<(INIT_LOOP_SIZE_BIT+bits-1));
+}
+
+void hs_uv_walk_close_cb(uv_handle_t* handle, void* arg){
+    if (uv_is_closing(handle) == 0) uv_close(handle, hs_uv_handle_free);
+}
+
+// This function close all the handles live on that loop and the loop itself,
+// then release all the memory.
+//
+// https://stackoverflow.com/questions/25615340/closing-libuv-handles-correctly
+//
+void hs_uv_loop_close(uv_loop_t* loop){
+    uv_stop(loop);
+    uv_walk(loop, hs_uv_walk_close_cb, NULL);
+    uv_run(loop, UV_RUN_NOWAIT);
+    while(uv_loop_close(loop) == UV_EBUSY);
+
+    hs_loop_data* loop_data = loop->data;
+    free(loop);
+    free(loop_data->event_queue);
+    free(loop_data->buffer_table);
+    free(loop_data->buffer_size_table);
+    free(loop_data->slot_table);
+    free(loop_data->uv_struct_table);
+
+    free(loop_data->async);
+    free(loop_data->timer);
+
+    free(loop_data);
+    free(loop);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// thread-safe wake up
+
+void uv_timer_wake_cb(uv_timer_t* timer){ uv_timer_stop(timer); }
+
+int hs_uv_wake_up_timer(hs_loop_data* loop_data){
+    return uv_timer_start(loop_data->timer, uv_timer_wake_cb, 1, 1);
+}
+int hs_uv_wake_up_async(hs_loop_data* loop_data){
+    return uv_async_send(loop_data->async);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// handle
+//
+// Get handle's OS file
+int32_t hs_uv_fileno(uv_handle_t* handle){
+    uv_os_fd_t fd;
+    int r;
+    r = uv_fileno(handle, &fd);
+    if (r < 0) { return (int32_t)r; } else { return (int32_t)fd; }
+}
+
+// Initialize a uv_handle_t, with data field set to an unique slot
+uv_handle_t* hs_uv_handle_alloc(uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return NULL;
+    uv_handle_t* handle = 
+        (uv_handle_t*)fetch_uv_struct(loop_data, slot);
+    handle->loop = loop;
+    handle->data = (void*)slot;
+    return handle;
+}
+
+// Free uv_handle_t only, used when handle initialization failed.
+void hs_uv_handle_free(uv_handle_t* handle){
+    uv_loop_t* loop = handle->loop;
+    free_slot(loop->data, (HsInt)handle->data);
+}
+
+// Close and free uv_handle_t
+void hs_uv_handle_close(uv_handle_t* handle){
+    uv_close(handle, hs_uv_handle_free);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// request
+
+// Cancel an ongoing uv request, so that the slot can be freed ASAP.
+void hs_uv_cancel(uv_loop_t* loop, HsInt slot){
+    hs_loop_data* loop_data = loop->data;
+    uv_req_t* req = 
+        (uv_req_t*)fetch_uv_struct(loop_data, slot);
+    switch (req->type) {
+        case UV_CONNECT:
+            hs_uv_handle_close((uv_handle_t*)((uv_connect_t*)req)->handle);
+            break;
+        case UV_WRITE:
+            hs_uv_handle_close((uv_handle_t*)((uv_write_t*)req)->handle);
+            break;
+        case UV_SHUTDOWN:
+            hs_uv_handle_close((uv_handle_t*)((uv_shutdown_t*)req)->handle);
+            break;
+        case UV_UDP_SEND:
+            hs_uv_handle_close((uv_handle_t*)((uv_udp_send_t*)req)->handle);
+            break;
+        default:
+            // we do it in best effort basis
+            uv_cancel(req);
+    }
+}
diff --git a/cbits/hs_uv_dns.c b/cbits/hs_uv_dns.c
new file mode 100644
--- /dev/null
+++ b/cbits/hs_uv_dns.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020-2020 Dong Han
+ *
+ * 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.
+ * 3. Neither the names of the authors or the names of any contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE 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.
+ */
+
+#include <hs_uv.h>
+
+HsInt hs_getaddrinfo(const char *node, const char *service,
+                     const struct addrinfo *hints,
+                     struct addrinfo **res){
+    int err;
+    err = getaddrinfo(node, service, hints, res);
+
+    return (HsInt)uv__getaddrinfo_translate_error(err);
+}
+
+HsInt hs_getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
+                     char *host, socklen_t hostlen,
+                     char *serv, socklen_t servlen, int flags){
+    int err;
+    err = getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags);
+    return (HsInt)uv__getaddrinfo_translate_error(err);
+}
diff --git a/cbits/hs_uv_file.c b/cbits/hs_uv_file.c
new file mode 100644
--- /dev/null
+++ b/cbits/hs_uv_file.c
@@ -0,0 +1,739 @@
+/*
+ * Copyright (c) 2017-2018 Dong Han
+ *
+ * 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.
+ * 3. Neither the names of the authors or the names of any contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE 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.
+ */
+
+#include <hs_uv.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// fs, none thread pool version
+//
+// we wrappered non-threaded pool version functions, so that we can move the allocation
+// of uv_fs_t to stack, most of the functions can be optimized in this way.
+// in none thread pool version, req->result is directly returned.
+
+int32_t hs_uv_fs_open(const char* path, int flags, int mode){
+    uv_fs_t req;
+    uv_fs_open(NULL, &req, path, flags, mode, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (int32_t)req.result;
+}
+
+HsInt hs_uv_fs_close(int32_t file){
+    uv_fs_t req;
+    uv_fs_close(NULL, &req, (uv_file)file, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_read(int32_t file, char* buffer, HsInt buffer_size, int64_t offset){
+    uv_fs_t req;
+    uv_buf_t buf = { .base = buffer, .len = (size_t)buffer_size };
+    uv_fs_read(NULL, &req, (uv_file)file, &buf, 1, offset, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_write(int32_t file, char* buffer, HsInt buffer_size, int64_t offset){
+    uv_fs_t req;
+    uv_buf_t buf = { .base = buffer, .len = (size_t)buffer_size };
+    uv_fs_write(NULL, &req, (uv_file)file, &buf, 1, offset, NULL);
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_unlink(const char* path){
+    uv_fs_t req;
+    uv_fs_unlink(NULL, &req, path, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_mkdir(const char* path, int mode){
+    uv_fs_t req;
+    uv_fs_mkdir(NULL, &req, path, mode, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_mkdtemp(const char* tpl, HsInt tpl_size, char* temp_path){
+    uv_fs_t req;
+    strcpy(temp_path, tpl);
+    strcpy(temp_path + tpl_size, "XXXXXX");
+    uv_fs_mkdtemp(NULL, &req, temp_path, NULL);
+    strcpy(temp_path, req.path);    // save the temp path
+    uv_fs_req_cleanup(&req);        // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_rmdir(const char* path){
+    uv_fs_t req;
+    uv_fs_rmdir(NULL, &req, path, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+void hs_uv_fs_scandir_cleanup(uv_dirent_t** dents, HsInt n){
+    int i;
+    for (i=0; i<n; i++){
+        uv__fs_scandir_free(dents[i]);
+    }
+    uv__fs_scandir_free(dents);
+}
+
+HsInt hs_uv_fs_scandir(const char* path, uv_dirent_t*** dents){
+    uv_fs_t req;
+    uv_fs_scandir(NULL, &req, path, 0, NULL);
+    *dents = req.ptr;
+#if defined(_WIN32)
+    uv__free(req.file.pathw);      //  we clean up dents later
+#endif
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_stat(const char* path, uv_stat_t* stat){
+    uv_fs_t req;
+    uv_fs_stat(NULL, &req, path, NULL);
+    memcpy(stat, &req.statbuf, sizeof(uv_stat_t));
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_fstat(int32_t file, uv_stat_t* stat){
+    uv_fs_t req;
+    uv_fs_fstat(NULL, &req, file, NULL);
+    memcpy(stat, &req.statbuf, sizeof(uv_stat_t));
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_lstat(const char* path, uv_stat_t* stat){
+    uv_fs_t req;
+    uv_fs_lstat(NULL, &req, path, NULL);
+    memcpy(stat, &req.statbuf, sizeof(uv_stat_t));
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_rename(const char* path, const char* path2){
+    uv_fs_t req;
+    uv_fs_rename(NULL, &req, path, path2, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_fsync(int32_t file){
+    uv_fs_t req;
+    uv_fs_fsync(NULL, &req, file, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_fdatasync(int32_t file){
+    uv_fs_t req;
+    uv_fs_fdatasync(NULL, &req, file, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_ftruncate(int32_t file, int64_t off){
+    uv_fs_t req;
+    uv_fs_ftruncate(NULL, &req, file, off, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_copyfile(const char* path, const char* path2, int flag){
+    uv_fs_t req;
+    uv_fs_copyfile(NULL, &req, path, path2, flag, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_access(const char* path, int mode){
+    uv_fs_t req;
+    uv_fs_access(NULL, &req, path, mode, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_chmod(const char* path, int mode){
+    uv_fs_t req;
+    uv_fs_chmod(NULL, &req, path, mode, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_fchmod(int32_t file, int mode){
+    uv_fs_t req;
+    uv_fs_fchmod(NULL, &req, file, mode, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_utime(const char* path, double atime, double mtime){
+    uv_fs_t req;
+    uv_fs_utime(NULL, &req, path, atime, mtime, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_futime(int32_t file, double atime, double mtime){
+    uv_fs_t req;
+    uv_fs_futime(NULL, &req, file, atime, mtime, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_link(const char* path, const char* path2){
+    uv_fs_t req;
+    uv_fs_link(NULL, &req, path, path2, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_symlink(const char* path, const char* path2, int flag){
+    uv_fs_t req;
+    uv_fs_symlink(NULL, &req, path, path2, flag, NULL);
+    uv_fs_req_cleanup(&req);    // maybe not neccessary
+    return (HsInt)req.result;
+}
+
+void hs_uv_fs_readlink_cleanup(char* path){
+    if (path != NULL) uv__free(path);
+}
+
+HsInt hs_uv_fs_readlink(const char* path, char** result_path){
+    uv_fs_t req;
+    uv_fs_readlink(NULL, &req, path, NULL);
+    *result_path = req.ptr;
+#if defined(_WIN32)
+    uv__free(req.file.pathw);      //  we clean up result_path later
+#endif
+    return (HsInt)req.result;
+}
+
+HsInt hs_uv_fs_realpath(const char* path, char** result_path){
+    uv_fs_t req;
+    uv_fs_realpath(NULL, &req, path, NULL);
+    *result_path = req.ptr;
+#if defined(_WIN32)
+    uv__free(req.file.pathw);      //  we clean up result_path later
+#endif
+    return (HsInt)req.result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// fs, thread pool version
+//
+void hs_uv_fs_callback(uv_fs_t* req){
+    uv_loop_t* loop = req->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = (HsInt)req->data; 
+    // push the slot to event queue
+    loop_data->buffer_size_table[slot] = (HsInt)req->result;
+    loop_data->event_queue[loop_data->event_counter] = slot;
+    loop_data->event_counter += 1;
+    uv_fs_req_cleanup(req);
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+HsInt hs_uv_fs_open_threaded(const char* path, int flags, int mode, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+
+    int r = uv_fs_open(loop, req, path, flags, mode, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_close_threaded(int32_t file, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_close(loop, req, (uv_file)file, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_read_threaded(int32_t file, char* buffer, HsInt buffer_size, int64_t offset, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    uv_buf_t buf = { .base = buffer, .len = (size_t)buffer_size };
+    int r = uv_fs_read(loop, req, (uv_file)file, &buf, 1, offset, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_write_threaded(int32_t file, char* buffer, HsInt buffer_size, int64_t offset, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    uv_buf_t buf = { .base = buffer, .len = (size_t)buffer_size };
+    int r = uv_fs_write(loop, req, (uv_file)file, &buf, 1, offset, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_unlink_threaded(const char* path, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_unlink(loop, req, path, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_mkdir_threaded(const char* path, int mode, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_mkdir(loop, req, path, mode, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+void hs_uv_fs_mkdtemp_callback(uv_fs_t* req){
+    uv_loop_t* loop = req->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = (HsInt)req->data; 
+    char* path = loop_data->buffer_table[slot];
+    if (path != NULL) {
+        // push the slot to event queue
+        loop_data->buffer_size_table[slot] = (HsInt)req->result;
+        loop_data->event_queue[loop_data->event_counter] = slot;
+        loop_data->event_counter += 1;
+        strcpy(path, req->path);  // save the temp path
+    }
+    uv_fs_req_cleanup(req);
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+HsInt hs_uv_fs_mkdtemp_threaded(const char* tpl, HsInt tpl_size, char* temp_path, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    strcpy(temp_path, tpl);
+    strcpy(temp_path + tpl_size, "XXXXXX");
+    loop_data->buffer_table[slot] = temp_path;
+    int r = uv_fs_mkdtemp(loop, req, temp_path, hs_uv_fs_mkdtemp_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_rmdir_threaded(const char* path, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_rmdir(loop, req, path, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+void hs_uv_fs_scandir_callback(uv_fs_t* req){
+    uv_loop_t* loop = req->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = (HsInt)req->data; 
+    uv_dirent_t*** dents = (uv_dirent_t***)loop_data->buffer_table[slot];
+    if (dents != NULL) {
+        *dents = req->ptr;
+        // save the dent struct array pointer
+        // push the slot to event queue
+        loop_data->buffer_size_table[slot] = (HsInt)req->result;
+        loop_data->event_queue[loop_data->event_counter] = slot;
+        loop_data->event_counter += 1;
+    }
+    //  we can't cleanup request here, because doing that will
+    //  destory our dents array, which we haven't copied in Haskell yet. 
+    //  so we manually break down uv_fs_req_cleanup here:
+    //  we free path buffer first, then clean up dents later using
+    //  hs_uv_fs_scandir_cleanup, or hs_uv_fs_scandir_extra_cleanup 
+    //  in case of async exception.
+#if defined(_WIN32)
+    if (req->file.pathw != NULL)
+        uv__free(req->file.pathw);  
+#else
+    if (req->path != NULL)
+        uv__free((void*) req->path);
+#endif
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+void hs_uv_fs_scandir_extra_cleanup(uv_dirent_t*** dents_p, HsInt n){
+    int i;
+    uv_dirent_t** dents = *dents_p;
+    if (dents != NULL) {
+        for (i=0; i<n; i++){
+            uv__fs_scandir_free(dents[i]);
+        }
+        uv__fs_scandir_free(dents);
+    }
+}
+
+HsInt hs_uv_fs_scandir_threaded(const char* path, uv_dirent_t*** dents, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    loop_data->buffer_table[slot] = (char*)dents;
+    int r = uv_fs_scandir(loop, req, path, 0, hs_uv_fs_scandir_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+void hs_uv_fs_stat_callback(uv_fs_t* req){
+    uv_loop_t* loop = req->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = (HsInt)req->data; 
+    uv_stat_t* stat = (uv_stat_t*)loop_data->buffer_table[slot];
+    if (stat != NULL) {
+        // push the slot to event queue
+        loop_data->buffer_size_table[slot] = (HsInt)req->result;
+        loop_data->event_queue[loop_data->event_counter] = slot;
+        loop_data->event_counter += 1;
+        memcpy(stat, &req->statbuf, sizeof(uv_stat_t));  // save the temp path
+    }
+    uv_fs_req_cleanup(req);
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+HsInt hs_uv_fs_stat_threaded(const char* path, uv_stat_t* stat, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    loop_data->buffer_table[slot] = (char*)stat;
+    int r = uv_fs_stat(loop, req, path, hs_uv_fs_stat_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_fstat_threaded(int32_t file, uv_stat_t* stat, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    loop_data->buffer_table[slot] = (char*)stat;
+    int r = uv_fs_fstat(loop, req, file, hs_uv_fs_stat_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_lstat_threaded(const char* path, uv_stat_t* stat, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    loop_data->buffer_table[slot] = (char*)stat;
+    int r = uv_fs_lstat(loop, req, path, hs_uv_fs_stat_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_rename_threaded(const char* path, const char* path2, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_rename(loop, req, path, path2, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_fsync_threaded(int32_t file, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_fsync(loop, req, file, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_fdatasync_threaded(int32_t file, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_fdatasync(loop, req, file, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_ftruncate_threaded(int32_t file, int64_t off, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_ftruncate(loop, req, file, off, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_copyfile_threaded(const char* path, const char* path2, int flag, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_copyfile(loop, req, path, path2, flag, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_access_threaded(const char* path, int mode, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_access(loop, req, path, mode, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_chmod_threaded(const char* path, int mode, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_chmod(loop, req, path, mode, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_fchmod_threaded(int32_t file, int mode, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_fchmod(loop, req, file, mode, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_utime_threaded(const char* path, double atime, double mtime, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_utime(loop, req, path, atime, mtime, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_futime_threaded(int32_t file, double atime, double mtime, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_futime(loop, req, file, atime, mtime, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_link_threaded(const char* path, const char* path2, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_link(loop, req, path, path2, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+HsInt hs_uv_fs_symlink_threaded(const char* path, const char* path2, int flag, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_fs_symlink(loop, req, path, path2, flag, hs_uv_fs_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+void hs_uv_fs_readlink_callback(uv_fs_t* req){
+    uv_loop_t* loop = req->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = (HsInt)req->data; 
+    char** path = (char**)loop_data->buffer_table[slot];
+    if (path != NULL) {
+        *path = req->ptr;  // save the result path
+        // push the slot to event queue
+        loop_data->buffer_size_table[slot] = (HsInt)req->result;
+        loop_data->event_queue[loop_data->event_counter] = slot;
+        loop_data->event_counter += 1;
+    }
+    //  for the same reason with 'scandir', we can't cleanup request here
+#if defined(_WIN32)
+    if (req->file.pathw != NULL)
+        uv__free(req->file.pathw);  
+#else
+    if (req->path != NULL)
+        uv__free((void*) req->path);
+#endif
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+void hs_uv_fs_readlink_extra_cleanup(char** path){
+    if (*path != NULL) uv__free(*path);
+}
+
+HsInt hs_uv_fs_readlink_threaded(const char* path, char** result_path, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    loop_data->buffer_table[slot] = (char*)result_path;
+    int r = uv_fs_readlink(loop, req, path, hs_uv_fs_readlink_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
+
+// share cleanup and callback with readlink
+HsInt hs_uv_fs_realpath_threaded(const char* path, char** result_path, uv_loop_t* loop){
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_fs_t* req = 
+        (uv_fs_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    loop_data->buffer_table[slot] = (char*)result_path;
+    int r = uv_fs_realpath(loop, req, path, hs_uv_fs_readlink_callback);
+    if (r < 0) {
+        free_slot(loop_data, slot);
+        return (HsInt)r;
+    } else return slot;
+}
diff --git a/cbits/hs_uv_stream.c b/cbits/hs_uv_stream.c
new file mode 100644
--- /dev/null
+++ b/cbits/hs_uv_stream.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2017-2018 Dong Han
+ *
+ * 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.
+ * 3. Neither the names of the authors or the names of any contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE 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.
+ */
+
+#include <hs_uv.h>
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// stream
+//
+// We reuse buffer_size_table as the result table, i.e. after haskell threads
+// are unblocked, they should peek result(length, errcode..) from buffer_size_table
+
+// This callback simply copy buffer from buffer table and buffer size table
+void hs_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf){
+    HsInt slot = (HsInt)handle->data;
+    hs_loop_data* loop_data = handle->loop->data;
+    buf->base = loop_data->buffer_table[slot];      // fetch buffer_table from buffer_table table
+    buf->len = loop_data->buffer_size_table[slot];  // we ignore suggested_size completely
+}
+
+// We only do single read per uv_run with uv_read_stop
+void hs_read_cb (uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf){
+    HsInt slot = (HsInt)stream->data;
+    hs_loop_data* loop_data = stream->loop->data;
+    if (nread != 0) {
+        loop_data->buffer_size_table[slot] = nread;
+        loop_data->event_queue[loop_data->event_counter] = slot; // push the slot to event queue
+        loop_data->event_counter += 1;
+        uv_read_stop(stream);
+    }
+}
+
+int hs_uv_read_start(uv_stream_t* stream){
+    return uv_read_start(stream, hs_alloc_cb, hs_read_cb);
+}
+
+void hs_write_cb(uv_write_t* req, int status){
+    HsInt slot = (HsInt)req->data;
+    uv_loop_t* loop = req->handle->loop;
+    hs_loop_data* loop_data = loop->data;
+    loop_data->buffer_size_table[slot] = (HsInt)status;      // 0 in case of success, < 0 otherwise.
+    loop_data->event_queue[loop_data->event_counter] = slot;   // push the slot to event queue
+    loop_data->event_counter += 1;
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+HsInt hs_uv_write(uv_stream_t* handle, char* buf, HsInt buf_siz){
+    uv_loop_t* loop = handle->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_write_t* req = 
+        (uv_write_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+
+    // on windows this struct is captured by WSASend
+    // on unix this struct is copied by libuv's uv_write
+    // so it's safe to allocate it on stack
+    uv_buf_t buf_t = { .base = buf, .len = (size_t)buf_siz };
+    
+    int r = uv_write(req, handle, &buf_t, 1, hs_write_cb); // we never use writev: we do our own
+                                                           // user-space buffering in haskell.
+    if (r < 0) {
+        free_slot(loop_data, slot);  // free the uv_req_t, the callback won't fired
+        return (HsInt)r;
+    } else return slot;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// tcp
+
+/* on windows uv_tcp_open doesn't work propery for sockets that are not
+ * connected or accepted by libuv because the lack of some state initialization,
+ * so we do it by manually set those flags
+ *
+ * referenes:   https://github.com/libuv/libuv/issues/397
+ *              https://github.com/libuv/libuv/pull/1150
+ */
+#if defined(_WIN32)
+void hs_uv_connection_init(uv_stream_t* handle){
+  handle->flags |= UV_HANDLE_CONNECTION;
+  handle->stream.conn.write_reqs_pending = 0;
+  (&handle->read_req)->type = UV_READ;                                                        \
+  (&handle->read_req)->u.io.overlapped.Internal = 0;  /* SET_REQ_SUCCESS() */ 
+  handle->read_req.event_handle = NULL;
+  handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+  handle->read_req.data = handle;
+  handle->stream.conn.shutdown_req = NULL;
+}
+
+int hs_uv_tcp_open(uv_tcp_t* handle, int32_t sock) {
+  int r = uv_tcp_open(handle, (uv_os_sock_t)sock);
+  if (r == 0) {
+    hs_uv_connection_init((uv_stream_t*)handle);
+    handle->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+  }
+  return r;
+}
+
+int hs_uv_pipe_open(uv_pipe_t* handle, int32_t file) {
+  int r = uv_pipe_open(handle, (uv_file_t)file);
+  if (r == 0) {
+    hs_uv_connection_init((uv_stream_t*)handle);
+    handle->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+  }
+  return r;
+}
+#else
+int hs_uv_tcp_open(uv_tcp_t* handle, int32_t sock) {
+  return uv_tcp_open(handle, (uv_os_sock_t)sock);
+}
+int hs_uv_pipe_open(uv_pipe_t* handle, int32_t sock) {
+  return uv_pipe_open(handle, (uv_os_sock_t)sock);
+}
+#endif
+
+void hs_connect_cb(uv_connect_t* req, int status){
+    HsInt slot = (HsInt)req->data;
+    uv_loop_t* loop = req->handle->loop;
+    hs_loop_data* loop_data = loop->data;  // uv_connect_t has handle field
+    loop_data->buffer_size_table[slot] = status;             // 0 in case of success, < 0 otherwise.
+    loop_data->event_queue[loop_data->event_counter] = slot; // push the slot to event queue
+    loop_data->event_counter += 1;
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+HsInt hs_uv_tcp_connect(uv_tcp_t* handle, const struct sockaddr* addr){
+    uv_loop_t* loop = handle->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_connect_t* req = 
+        (uv_connect_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    int r = uv_tcp_connect(req, handle, addr, hs_connect_cb);
+    if (r < 0) {
+        free_slot(loop_data, slot);  // free the uv_req_t, the callback won't fired
+        return r;
+    } else return slot;
+}
+
+HsInt hs_uv_pipe_connect(uv_pipe_t* handle, const char* name){
+    uv_loop_t* loop = handle->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_connect_t* req = 
+        (uv_connect_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+    uv_pipe_connect(req, handle, name, hs_connect_cb);
+    return slot;
+}
+
+// When libuv listen's callback is called, client is actually already accepted, 
+// so our customized accept function just return the fd directly, Following code
+// doesn't support IPC for now.
+//
+// TODO research on accepting fds sent by IPC pipes.
+//
+#if defined(_WIN32)
+int32_t hs_uv_tcp_accept(uv_tcp_t* server) {
+  int32_t fd = -1;
+
+  uv_tcp_accept_t* req = server->tcp.serv.pending_accepts;
+
+  if (!req) {
+    /* No valid connections found, so we error out. */
+    return WSAEWOULDBLOCK;
+  }
+
+  if (req->accept_socket == INVALID_SOCKET) {
+    return WSAENOTCONN;
+  }
+
+  fd = (int32_t)req->accept_socket;
+
+  /* Prepare the req to pick up a new connection */
+
+  server->tcp.serv.pending_accepts = req->next_pending;
+  req->next_pending = NULL;
+  req->accept_socket = INVALID_SOCKET;
+
+  if (!(server->flags & UV__HANDLE_CLOSING)) {
+    /* Check if we're in a middle of changing the number of pending accepts. */
+    if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) {
+      uv_tcp_queue_accept(server, req);
+    } else {
+      /* We better be switching to a single pending accept. */
+      assert(server->flags & UV_HANDLE_TCP_SINGLE_ACCEPT);
+      server->tcp.serv.processed_accepts++;
+      if (server->tcp.serv.processed_accepts >= uv_simultaneous_server_accepts) {
+        server->tcp.serv.processed_accepts = 0;
+        /*
+         * All previously queued accept requests are now processed.
+         * We now switch to queueing just a single accept.
+         */
+        uv_tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]);
+        server->flags &= ~UV_HANDLE_TCP_ACCEPT_STATE_CHANGING;
+        server->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
+      }
+    }
+  }
+  return fd;
+}
+int32_t hs_uv_pipe_accept(uv_pipe_t* server) {
+    int32_t fd = -1;
+
+    uv_loop_t* loop = server->loop;
+    uv_pipe_accept_t* req;
+    req = server->pipe.serv.pending_accepts;
+    if (!req) {
+      /* No valid connections found, so we error out. */
+      return WSAEWOULDBLOCK;
+    }
+
+    fd = (int32_t)req->pipeHandle;
+
+    /* Prepare the req to pick up a new connection */
+    server->pipe.serv.pending_accepts = req->next_pending;
+    req->next_pending = NULL;
+    req->pipeHandle = INVALID_HANDLE_VALUE;
+    if (!(server->flags & UV__HANDLE_CLOSING)) {
+        uv_pipe_queue_accept(loop, server, req, FALSE);
+    }
+    return fd;
+}
+int32_t hs_uv_accept(uv_stream_t* server) {
+    int32_t fd;
+    switch (server->type) {
+        case UV_TCP:
+            fd = hs_uv_tcp_accept((uv_tcp_t*)server);
+            break;
+        case UV_NAMED_PIPE:
+            fd = hs_uv_pipe_accept((uv_pipe_t*)server);
+            break;
+        default:
+            assert(0);
+    }
+    return fd;
+}
+#else
+int32_t hs_uv_accept(uv_stream_t* server) {
+    int32_t fd = (int32_t)server->accepted_fd;
+    server->accepted_fd = -1;
+    return fd;
+}
+#endif
+
+void hs_listen_cb(uv_stream_t* server, int status){
+    HsInt slot = (HsInt)server->data;
+    hs_loop_data* loop_data = server->loop->data;
+
+    // fetch accept buffer from buffer_table table
+    int32_t* accept_buf = (int32_t*)loop_data->buffer_table[slot];     
+    HsInt accepted_number = loop_data->buffer_size_table[slot];
+    loop_data->buffer_size_table[slot] = accepted_number - 1;
+
+    if (status == 0) {
+        accept_buf[accepted_number] = hs_uv_accept(server);       
+    } else {
+        accept_buf[accepted_number] = (int32_t)status;
+    } 
+
+    if (accepted_number == 0) {
+#if defined(_WIN32)
+        // we have no way to deal with this situation on windows, since 
+        // we can't stop accepting after request has been inserted
+        // but this should not happen on windows anyway,
+        // since on windows simultaneous_accepts is small, e.g. pending accept
+        // requests' number is small.
+        // It must takes many uv_run without copying accept buffer on haskell side
+        // which is very unlikely to happen.
+        closesocket(hs_uv_accept(server)); 
+#else
+        // on unix, we can stop accepting using uv__io_stop, this is
+        // important because libuv will loop accepting until EAGAIN/EWOULDBLOCK,
+        // If we return to accept thread too slow in haskell side, the 
+        // accept buffer may not be able to hold all the clients queued in backlog.
+        // And this is very likely to happen under high load. Thus we
+        // must stop accepting when the buffer is full.
+        //
+        // Limit this number may also be good for stop a non-block uv_run from
+        // running too long, which will affect haskell's GC.
+        //
+        // do last accept without clearing server->accepted_fd
+        // libuv will take this as a no accepting, thus call uv__io_stop for us.
+        // set back accepted_fd so that libuv break from accept loop
+        // upon next resuming, we clear this accepted_fd with -1 and call uv__io_start
+        server->accepted_fd = accept_buf[accepted_number];
+#endif
+    }
+
+}
+
+int hs_uv_listen(uv_stream_t* stream, int backlog){
+    return uv_listen(stream, backlog, hs_listen_cb);
+}
+
+// on windows we don't need to do anything, since we didn't and can't stopped. 
+void hs_uv_listen_resume(uv_stream_t* server){
+#if !defined(_WIN32)
+    server->accepted_fd = -1;
+    uv__io_start(server->loop, &server->io_watcher, POLLIN);
+#endif
+}
+
+// Check if the socket's accept buffer is filled with FDs, if so, unlock the accept thread
+//
+void hs_accept_check_cb(uv_check_t* check){
+    uv_stream_t* server=(uv_stream_t*)check->data;
+    HsInt slot = (HsInt)server->data;
+    hs_loop_data* loop_data = server->loop->data;
+    // This relays on GHC ByteArray# memory layout
+    HsInt* buffer_ptr = (HsInt*)loop_data->buffer_table[slot];
+    HsInt backlog = *(buffer_ptr-1);
+    backlog = backlog / 4; // int32_t fd
+
+    if (loop_data->buffer_size_table[slot] < backlog-1){
+        loop_data->event_queue[loop_data->event_counter] = slot; // push the slot to event queue
+        loop_data->event_counter += 1;
+    }
+}
+
+// It's hard to arrange accepting notification without check handler, we can't
+// do it in listen's callback, since it'll be called multiple times during uv_run.
+uv_check_t* hs_uv_accept_check_alloc(uv_stream_t* server){
+    uv_check_t* check = malloc(sizeof(uv_check_t));
+    if (check == NULL) return NULL;
+    check->data = (void*)server;    // we link server to check's data field
+    return check;
+}
+
+int hs_uv_accept_check_init(uv_check_t* check){
+    uv_stream_t* server = check->data;
+    int r = uv_check_init(server->loop, check);
+    if (r < 0) return r;
+    return uv_check_start(check, hs_accept_check_cb);
+}
+
+void hs_uv_accept_check_close(uv_check_t* check){
+    uv_close((uv_handle_t*)check, (uv_close_cb)free);
+}
+
+int hs_set_socket_reuse(uv_stream_t* server) {
+#if (SO_REUSEPORT_LOAD_BALANCE == 1)
+    int yes = 1;
+    if (setsockopt(server->io_watcher.fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
+        return uv_translate_sys_error(errno);
+    return 0;
+#else
+    return 0;
+#endif
+}
diff --git a/cbits/hs_uv_udp.c b/cbits/hs_uv_udp.c
new file mode 100644
--- /dev/null
+++ b/cbits/hs_uv_udp.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2017-2019 Dong Han
+ *
+ * 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.
+ * 3. Neither the names of the authors or the names of any contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE 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.
+ */
+
+#include <hs_uv.h>
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// udp
+
+// We do batch read per uv_run, the buffer index keep decreasing until hit zero
+// then we call uv_udp_recv_stop to stop receiving.
+void hs_udp_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf){
+    HsInt slot = (HsInt)handle->data;
+    hs_loop_data* loop_data = handle->loop->data;
+    // fetch buffer_table from buffer_table table
+    // the first 12 + 128 bytes is reserved for sockaddr and flag
+    char** buffer_array = (char**)loop_data->buffer_table[slot];
+    (loop_data->buffer_size_table[slot])--;
+    ssize_t buffer_index = loop_data->buffer_size_table[slot];
+    if (buffer_index < 0) {
+        uv_udp_recv_stop((uv_udp_t*)handle);
+        buf->base = NULL;
+        buf->len  = 0;
+    } else {
+        buf->base = (char*)buffer_array[buffer_index] + 140;
+        buf->len  = *((int32_t*)buffer_array[buffer_index]);
+    }
+}
+
+void hs_udp_recv_cb (uv_udp_t* udp, ssize_t nread, const uv_buf_t* _buf
+    , const struct sockaddr* addr, unsigned flags){
+    if (nread ==0 && addr == NULL) return;
+    HsInt slot = (HsInt)udp->data;
+    hs_loop_data* loop_data = udp->loop->data;
+
+    char* buf = (char*)(_buf->base)-140;  
+    struct sockaddr* addr_buf =  (struct sockaddr*)(buf+12);
+    // result
+    *(int32_t*)buf = (int32_t)nread;
+    // flag
+    *(int32_t*)(buf+4) = (int32_t)flags;
+
+    if (addr == NULL) {
+        // set sockaddr flag
+        *(int32_t*)(buf+8) = 0;
+    } else {
+        // set sockaddr flag
+        *(int32_t*)(buf+8) = 1;
+        // copy sockaddr
+        if (addr->sa_family == AF_INET){
+            memcpy(addr_buf, addr, sizeof(struct sockaddr_in));
+        } else if (addr->sa_family == AF_INET6){
+            memcpy(addr_buf, addr, sizeof(struct sockaddr_in6));
+        } else {
+            memcpy(addr_buf, addr, sizeof(struct sockaddr));
+        }
+    }
+    if (nread != 0) {
+        loop_data->event_queue[loop_data->event_counter] = slot; // push the slot to event queue
+        loop_data->event_counter += 1;
+        uv_udp_recv_stop(udp);
+    }
+}
+
+int hs_uv_udp_recv_start(uv_udp_t* handle){
+    return uv_udp_recv_start(handle, hs_udp_alloc_cb, hs_udp_recv_cb);
+}
+
+void hs_uv_udp_send_cb(uv_udp_send_t* req, int status){
+    HsInt slot = (HsInt)req->data;
+    uv_loop_t* loop = req->handle->loop;
+    hs_loop_data* loop_data = loop->data;
+    loop_data->buffer_size_table[slot] = (HsInt)status;      // 0 in case of success, < 0 otherwise.
+    loop_data->event_queue[loop_data->event_counter] = slot;   // push the slot to event queue
+    loop_data->event_counter += 1;
+    free_slot(loop_data, slot);  // free the uv_req_t
+}
+
+HsInt hs_uv_udp_send(uv_udp_t* handle, const struct sockaddr* addr, char* buf, HsInt buf_siz){
+    uv_loop_t* loop = handle->loop;
+    hs_loop_data* loop_data = loop->data;
+    HsInt slot = alloc_slot(loop_data);
+    if (slot < 0) return UV_ENOMEM;
+    uv_udp_send_t* req = 
+        (uv_udp_send_t*)fetch_uv_struct(loop_data, slot);
+    req->data = (void*)slot;
+
+    // on windows this struct is captured by WSASend
+    // on unix this struct is copied by libuv's uv_udp_send
+    // so it's safe to allocate it on stack
+    uv_buf_t buf_t = { .base = buf, .len = (size_t)buf_siz };
+    
+    int r = uv_udp_send(req, handle, &buf_t, 1, addr, hs_uv_udp_send_cb);
+                                                        // we never use writev: we do our own
+                                                        // user-space buffering in haskell.
+    if (r < 0) {
+        free_slot(loop_data, slot);  // free the uv_req_t, the callback won't fired
+        return (HsInt)r;
+    } else return slot;
+
+}
diff --git a/include/hs_uv.h b/include/hs_uv.h
new file mode 100644
--- /dev/null
+++ b/include/hs_uv.h
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 2017-2018 Dong Han
+ *
+ * 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.
+ * 3. Neither the names of the authors or the names of any contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE 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.
+ */
+
+#include <uv.h>
+#include <assert.h>
+#include <HsFFI.h>  // for HsInt
+#include <stdlib.h> // for malloc, free, etc.
+#include <string.h> // for fs path
+
+#if !defined(_WIN32)
+
+#if defined(__sun)
+# include <sys/port.h>
+# include <port.h>
+#endif /* __sun */
+
+#if defined(_AIX)
+# define reqevents events
+# define rtnevents revents
+# include <sys/poll.h>
+#else
+# include <poll.h>
+#endif /* _AIX */
+
+#include <dirent.h>
+
+#endif /* _WIN32 */
+
+////////////////////////////////////////////////////////////////////////////////
+// CONSTANT
+#define ACCEPT_BUFFER_SIZE 1020
+#define INIT_LOOP_SIZE 128
+#define INIT_LOOP_SIZE_BIT 7
+
+#if defined(__linux__) && defined(SO_REUSEPORT)
+#define SO_REUSEPORT_LOAD_BALANCE 1
+#else
+#define SO_REUSEPORT_LOAD_BALANCE 0
+#endif
+
+// this function will be a noop when SO_REUSEPORT_LOAD_BALANCE == 0
+int hs_set_socket_reuse(uv_stream_t* server);
+
+////////////////////////////////////////////////////////////////////////////////
+// error handling
+int uv_translate_sys_error(int sys_errno);
+
+////////////////////////////////////////////////////////////////////////////////
+// loop
+//
+// the memory pool item struct, we use the largest possible value,
+// except uv_getnameinfo_t, which is too large than average :(
+//
+// following size data is from libuv v1.12, in bytes
+//                         win  unix
+// uv_timer_t        : 160  152
+// uv_prepare_t      : 120  120
+// uv_check_t        : 120  120
+// uv_idle_t         : 120  120
+// uv_async_t        : 224  128
+// uv_poll_t         : 416  160
+// uv_signal_t       : 264  152
+// uv_process_t      : 264  136
+// uv_tcp_t          : 320  248
+// uv_pipe_t         : 576  264
+// uv_tty_t          : 344  312
+// uv_udp_t          : 424  216
+// uv_fs_event_t     : 272  136
+// uv_fs_poll_t      : 104  104
+// uv_req_t          : 112  64
+// uv_getaddrinfo_t  : 216  160
+// uv_getnameinfo_t  : 1368 1320  !too large
+// uv_shutdown_t     : 128  80
+// uv_write_t        : 176  192
+// uv_connect_t      : 128  96
+// uv_udp_send_t     : 128  320
+// uv_fs_t           : 456  440
+// uv_work_t         : 176  128
+typedef union {
+    uv_timer_t          timer_t      ;
+    uv_prepare_t        prepare_t    ;
+    uv_check_t          check_t      ;
+    uv_idle_t           idle_t       ;
+    uv_async_t          async_t      ;
+    uv_poll_t           poll_t       ;
+    uv_signal_t         signal_t     ;
+    uv_process_t        process_t    ;
+    uv_tcp_t            tcp_t        ;
+    uv_pipe_t           pipe_t       ;
+    uv_tty_t            tty_t        ;
+    uv_udp_t            udp_t        ;
+    uv_fs_event_t       fs_event_t   ;
+    uv_fs_poll_t        fs_poll_t    ;
+    uv_req_t            req_t        ;
+    uv_getaddrinfo_t    getaddrinfo_t;
+//   uv_getnameinfo_t    getnameinfo_t; !too large
+    uv_shutdown_t       shutdown_t   ;
+    uv_write_t          write_t      ;
+    uv_connect_t        connect_t    ;
+    uv_udp_send_t       udp_send_t   ;
+    uv_fs_t             fs_t         ;
+    uv_work_t           work_t       ;
+} hs_uv_struct;
+
+typedef struct {
+    // following two fields record events during uv_run, inside callback which
+    // wants to record a event, push the handler's slot into the queue
+    HsInt   event_counter;
+    HsInt*  event_queue;
+    // following two fields provide buffers allocated in haskell to uv_alloc_cb,
+    // the buffer_size_table are also used to record operation's result
+    char**  buffer_table;
+    HsInt*  buffer_size_table;
+    // following fields are used to implemented a stable slot allocator
+    // see note below
+    HsInt*  slot_table;
+    HsInt   free_slot;
+    HsInt*  free_slot_queue;
+    HsInt   free_slot_counter;
+    // uv_struct_table field is a memory pool for uv_handle_t / uv_req_t struct,
+    // see note below
+    hs_uv_struct**  uv_struct_table;
+    HsInt   size;
+    size_t  resize;
+    // following fields are handlers used to wake up event loop under threaded and
+    // non-threaded RTS respectively.
+    uv_async_t* async;
+    uv_timer_t* timer;
+} hs_loop_data;
+// Note: the slot allocator
+//
+// we used to do slot allocation in haskell, but doing it in C allow us to free slot
+// in the right place, e.g. uv_close_cb. The allocator use the same algorithm with
+// ghc's stable pointer table. When initialized, it's looked like:
+//
+//  slot_table->[0][1][2][3]...[INIT_LOOP_SIZE-1]
+//               |  |  |  |
+//              =1 =2 =3 =4 ...
+//  free_slot = 0
+//
+// Every time we allocate a slot, we return current free_slot, and set the free_slot to
+// slot_table[free_slot], so after 3 allocation, it's looked like
+//
+//  slot_table->[0][1][2][3]...[INIT_LOOP_SIZE-1]
+//               |  |  |  |
+//              =1 =2 =3 =4 ...
+//  free_slot = 3
+//
+// When we free slot x, we set slot_table[x] to current free_slot, and free_slot
+// to x. Now let's say we return slot 1, it will be looked like
+//
+//  slot_table->[0][1][2][3]...[INIT_LOOP_SIZE-1]
+//               |  |  |  |
+//              =1 =3 =3 =4 ...
+//  free_slot = 1
+//
+// Next time allocation will give 1 back to us, and free_slot will continue point to 3.
+//
+// But in practice, we never directly return a free slot back to slot_table, because
+// the haskell thread allocating slot may be paused by RTS before its takeMVar parking its
+// TSO since parking itself need an allocation. Now if uv_run fired its callback and 
+// free the slot, next registration will got the same slot, and mess up with previous
+// haskell thread. In order to solve this race condition, we free a slot in two steps:
+//
+// 1. free_slot will first push slot to free_slot_queue, and increase free_slot_counter.
+// 2. before uv_run we loop through this queue and put these free slots back to slot_table.
+//
+// With this scheme, slots will be held one scheduler loop longer, thus ensure the haskell
+// thread can successfully wait on its 'MVar', and in turn make sure it will be resumed.
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// Note: uv_handle_t/uv_req_t memory pool
+//
+// The reasons to use a memory pool for uv structs are:
+//
+// 1. A malloc/free per request is inefficient.
+// 2. It's diffcult to manange if we use haskell heap(pinned).
+// 3. uv structs are small mostly.
+//
+// The memory pool is grow on demand, when initialized, it's looked like:
+//
+// uv_struct_table [0]
+//                  |
+//                  V
+//      struct_block[INIT_LOOP_SIZE]
+//
+// After several loop data resize, it will be looked like:
+//
+// uv_struct_table [0]                [1]                [2] ...
+//                  |                  |                  |
+//                  V                  |                  |
+//      struct_block[INIT_LOOP_SIZE]   V                  |
+//                        struct_block[INIT_LOOP_SIZE]    V
+//                                            struct_block[INIT_LOOP_SIZE*2]
+//                                                                            .
+//                                                                            .
+//                                                                            .
+// That is, we keep the total number of items sync with loop->data->size, while
+// not touching previously allocated blocks, so that the structs are never moved.
+//
+// Check fetch_uv_struct below to see how do we get struct's address by slot.
+
+uv_loop_t* hs_uv_loop_init(HsInt siz);
+void hs_uv_loop_close(uv_loop_t* loop);
+HsInt alloc_slot(hs_loop_data* loop_data);
+void free_slot(hs_loop_data* loop_data, HsInt slot);
+hs_uv_struct* fetch_uv_struct(hs_loop_data* loop_data, HsInt slot);
+int hs_uv_run(uv_loop_t* loop, uv_run_mode mode);
+
+////////////////////////////////////////////////////////////////////////////////
+// wake up
+int hs_uv_wake_up_timer(hs_loop_data* loop_data);
+int hs_uv_wake_up_async(hs_loop_data* loop_data);
+
+////////////////////////////////////////////////////////////////////////////////
+// handle
+uv_handle_t* hs_uv_handle_alloc(uv_loop_t* loop);
+void hs_uv_handle_free(uv_handle_t* handle);
+void hs_uv_handle_close(uv_handle_t* handle);
+
+////////////////////////////////////////////////////////////////////////////////
+// request
+void hs_uv_cancel(uv_loop_t* loop, HsInt slot);
+
+////////////////////////////////////////////////////////////////////////////////
+// stream
+int hs_uv_listen(uv_stream_t* stream, int backlog);
+void hs_uv_listen_resume(uv_stream_t* server);
+int hs_uv_read_start(uv_stream_t* handle);
+HsInt hs_uv_write(uv_stream_t* handle, char* buf, HsInt buf_size);
+uv_check_t* hs_uv_accept_check_alloc(uv_stream_t* server);
+int hs_uv_accept_check_init(uv_check_t* check);
+void hs_uv_accept_check_close(uv_check_t* check);
+
+////////////////////////////////////////////////////////////////////////////////
+// tcp and pipe
+int hs_uv_tcp_open(uv_tcp_t* handle, int32_t sock);
+HsInt hs_uv_tcp_connect(uv_tcp_t* handle, const struct sockaddr* addr);
+
+int hs_uv_pipe_open(uv_pipe_t* handle, int32_t sock);
+HsInt hs_uv_pipe_connect(uv_pipe_t* handle, const char* name);
+
+#if defined(_WIN32)
+#define UV_HANDLE_READING                       0x00000100
+#define UV_HANDLE_BOUND                         0x00000200
+#define UV_HANDLE_LISTENING                     0x00000800
+#define UV_HANDLE_CONNECTION                    0x00001000
+#define UV_HANDLE_READABLE                      0x00008000
+#define UV_HANDLE_WRITABLE                      0x00010000
+enum {
+  UV__SIGNAL_ONE_SHOT = 0x80000,  /* On signal reception remove sighandler */
+  UV__HANDLE_INTERNAL = 0x8000,
+  UV__HANDLE_ACTIVE   = 0x4000,
+  UV__HANDLE_REF      = 0x2000,
+  UV__HANDLE_CLOSING  = 0 /* no-op on unix */
+};
+#define UV_HANDLE_TCP_SINGLE_ACCEPT             0x08000000
+#define UV_HANDLE_TCP_ACCEPT_STATE_CHANGING     0x10000000
+extern unsigned int uv_simultaneous_server_accepts;
+extern void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req);
+#else
+void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// udp
+
+int hs_uv_udp_recv_start(uv_udp_t* handle);
+HsInt hs_uv_upd_send(uv_udp_t* handle, const struct sockaddr* addr, char* buf, HsInt buf_siz);
+
+////////////////////////////////////////////////////////////////////////////////
+// fs
+
+// we define file open flag here for compatibility on libuv < v1.16
+// see https://github.com/libuv/libuv/commit/4b666bd2d82a51f1c809b2703a91679789c1ec01#diff-a5e63f9b16ca783355e2d83941c3eafb
+
+#if defined(_WIN32)
+
+/* fs open() flags supported on this platform: */
+#ifndef UV_FS_O_APPEND
+#define UV_FS_O_APPEND       _O_APPEND
+#endif
+#ifndef UV_FS_O_CREAT
+#define UV_FS_O_CREAT        _O_CREAT
+#endif
+#ifndef UV_FS_O_EXCL
+#define UV_FS_O_EXCL         _O_EXCL
+#endif
+#ifndef UV_FS_O_RANDOM
+#define UV_FS_O_RANDOM       _O_RANDOM
+#endif
+#ifndef UV_FS_O_RDONLY
+#define UV_FS_O_RDONLY       _O_RDONLY
+#endif
+#ifndef UV_FS_O_RDWR
+#define UV_FS_O_RDWR         _O_RDWR
+#endif
+#ifndef UV_FS_O_SEQUENTIAL
+#define UV_FS_O_SEQUENTIAL   _O_SEQUENTIAL
+#endif
+#ifndef UV_FS_O_SHORT_LIVED
+#define UV_FS_O_SHORT_LIVED  _O_SHORT_LIVED
+#endif
+#ifndef UV_FS_O_TEMPORARY
+#define UV_FS_O_TEMPORARY    _O_TEMPORARY
+#endif
+#ifndef UV_FS_O_TRUNC
+#define UV_FS_O_TRUNC        _O_TRUNC
+#endif
+#ifndef UV_FS_O_WRONLY
+#define UV_FS_O_WRONLY       _O_WRONLY
+#endif
+
+/* fs open() flags supported on other platforms (or mapped on this platform): */
+#ifndef UV_FS_O_DIRECT
+#define UV_FS_O_DIRECT       0x2000000 /* FILE_FLAG_NO_BUFFERING */
+#endif
+#ifndef UV_FS_O_DIRECTORY
+#define UV_FS_O_DIRECTORY    0
+#endif
+#ifndef UV_FS_O_DSYNC
+#define UV_FS_O_DSYNC        0x4000000 /* FILE_FLAG_WRITE_THROUGH */
+#endif
+#ifndef UV_FS_O_EXLOCK
+#define UV_FS_O_EXLOCK       0
+#endif
+#ifndef UV_FS_O_NOATIME
+#define UV_FS_O_NOATIME      0
+#endif
+#ifndef UV_FS_O_NOCTTY
+#define UV_FS_O_NOCTTY       0
+#endif
+#ifndef UV_FS_O_NOFOLLOW
+#define UV_FS_O_NOFOLLOW     0
+#endif
+#ifndef UV_FS_O_NONBLOCK
+#define UV_FS_O_NONBLOCK     0
+#endif
+#ifndef UV_FS_O_SYMLINK
+#define UV_FS_O_SYMLINK      0
+#endif
+#ifndef UV_FS_O_SYNC
+#define UV_FS_O_SYNC         0x8000000 /* FILE_FLAG_WRITE_THROUGH */
+#endif
+
+#else /* _WIN32 */
+
+#ifndef UV_FS_O_APPEND
+#if defined(O_APPEND)
+# define UV_FS_O_APPEND       O_APPEND
+#else
+# define UV_FS_O_APPEND       0
+#endif
+#endif
+#ifndef UV_FS_O_CREAT
+#if defined(O_CREAT)
+# define UV_FS_O_CREAT        O_CREAT
+#else
+# define UV_FS_O_CREAT        0
+#endif
+#endif
+#ifndef UV_FS_O_DIRECT
+#if defined(O_DIRECT)
+# define UV_FS_O_DIRECT       O_DIRECT
+#else
+# define UV_FS_O_DIRECT       0
+#endif
+#endif
+#ifndef UV_FS_O_DIRECTORY
+#if defined(O_DIRECTORY)
+# define UV_FS_O_DIRECTORY    O_DIRECTORY
+#else
+# define UV_FS_O_DIRECTORY    0
+#endif
+#endif
+#ifndef UV_FS_O_DSYNC
+#if defined(O_DSYNC)
+# define UV_FS_O_DSYNC        O_DSYNC
+#else
+# define UV_FS_O_DSYNC        0
+#endif
+#endif
+#ifndef UV_FS_O_EXCL
+#if defined(O_EXCL)
+# define UV_FS_O_EXCL         O_EXCL
+#else
+# define UV_FS_O_EXCL         0
+#endif
+#endif
+#ifndef UV_FS_O_EXLOCK
+#if defined(O_EXLOCK)
+# define UV_FS_O_EXLOCK       O_EXLOCK
+#else
+# define UV_FS_O_EXLOCK       0
+#endif
+#endif
+#ifndef UV_FS_O_NOATIME
+#if defined(O_NOATIME)
+# define UV_FS_O_NOATIME      O_NOATIME
+#else
+# define UV_FS_O_NOATIME      0
+#endif
+#endif
+#ifndef UV_FS_O_NOCTTY
+#if defined(O_NOCTTY)
+# define UV_FS_O_NOCTTY       O_NOCTTY
+#else
+# define UV_FS_O_NOCTTY       0
+#endif
+#endif
+#ifndef UV_FS_O_NOFOLLOW
+#if defined(O_NOFOLLOW)
+# define UV_FS_O_NOFOLLOW     O_NOFOLLOW
+#else
+# define UV_FS_O_NOFOLLOW     0
+#endif
+#endif
+#ifndef UV_FS_O_NONBLOCK
+#if defined(O_NONBLOCK)
+# define UV_FS_O_NONBLOCK     O_NONBLOCK
+#else
+# define UV_FS_O_NONBLOCK     0
+#endif
+#endif
+#ifndef UV_FS_O_RDONLY
+#if defined(O_RDONLY)
+# define UV_FS_O_RDONLY       O_RDONLY
+#else
+# define UV_FS_O_RDONLY       0
+#endif
+#endif
+#ifndef UV_FS_O_RDWR
+#if defined(O_RDWR)
+# define UV_FS_O_RDWR         O_RDWR
+#else
+# define UV_FS_O_RDWR         0
+#endif
+#endif
+#ifndef UV_FS_O_SYMLINK
+#if defined(O_SYMLINK)
+# define UV_FS_O_SYMLINK      O_SYMLINK
+#else
+# define UV_FS_O_SYMLINK      0
+#endif
+#endif
+#ifndef UV_FS_O_SYNC
+#if defined(O_SYNC)
+# define UV_FS_O_SYNC         O_SYNC
+#else
+# define UV_FS_O_SYNC         0
+#endif
+#endif
+#ifndef UV_FS_O_TRUNC
+#if defined(O_TRUNC)
+# define UV_FS_O_TRUNC        O_TRUNC
+#else
+# define UV_FS_O_TRUNC        0
+#endif
+#endif
+#ifndef UV_FS_O_WRONLY
+#if defined(O_WRONLY)
+# define UV_FS_O_WRONLY       O_WRONLY
+#else
+# define UV_FS_O_WRONLY       0
+#endif
+#endif
+
+/* fs open() flags supported on other platforms: */
+#ifndef UV_FS_O_RANDOM
+#define UV_FS_O_RANDOM        0
+#endif
+#ifndef UV_FS_O_SHORT_LIVED
+#define UV_FS_O_SHORT_LIVED   0
+#endif
+#ifndef UV_FS_O_SEQUENTIAL
+#define UV_FS_O_SEQUENTIAL    0
+#endif
+#ifndef UV_FS_O_TEMPORARY
+#define UV_FS_O_TEMPORARY     0
+#endif
+
+#endif /* _WIN32 */
+
+typedef uv__dirent_t hs_uv__dirent_t;
+
+#if defined(_WIN32)
+
+#define HAVE_DIRENT_TYPES
+#define UV__DT_DIR     UV_DIRENT_DIR
+#define UV__DT_FILE    UV_DIRENT_FILE
+#define UV__DT_LINK    UV_DIRENT_LINK
+#define UV__DT_FIFO    UV_DIRENT_FIFO
+#define UV__DT_SOCKET  UV_DIRENT_SOCKET
+#define UV__DT_CHAR    UV_DIRENT_CHAR
+#define UV__DT_BLOCK   UV_DIRENT_BLOCK
+
+#else /* _WIN32 */
+
+#if defined(DT_UNKNOWN)
+# define HAVE_DIRENT_TYPES
+# if defined(DT_REG)
+#  define UV__DT_FILE DT_REG
+# else
+#  define UV__DT_FILE -1
+# endif
+# if defined(DT_DIR)
+#  define UV__DT_DIR DT_DIR
+# else
+#  define UV__DT_DIR -2
+# endif
+# if defined(DT_LNK)
+#  define UV__DT_LINK DT_LNK
+# else
+#  define UV__DT_LINK -3
+# endif
+# if defined(DT_FIFO)
+#  define UV__DT_FIFO DT_FIFO
+# else
+#  define UV__DT_FIFO -4
+# endif
+# if defined(DT_SOCK)
+#  define UV__DT_SOCKET DT_SOCK
+# else
+#  define UV__DT_SOCKET -5
+# endif
+# if defined(DT_CHR)
+#  define UV__DT_CHAR DT_CHR
+# else
+#  define UV__DT_CHAR -6
+# endif
+# if defined(DT_BLK)
+#  define UV__DT_BLOCK DT_BLK
+# else
+#  define UV__DT_BLOCK -7
+# endif
+#endif /* DT_UNKNOWN */
+
+#endif /* _WIN32 */
+
+
+void uv__free(void* p);
+#if defined(_WIN32)
+# define uv__fs_scandir_free uv__free
+#else /* _WIN32 */
+# define uv__fs_scandir_free free
+// uv__once_init will run WSAStartup which is required to run socket code on WIN32
+void uv__once_init(void);
+#endif /* _WIN32 */
+
+void hs_uv_fs_scandir_cleanup(uv_dirent_t** dents, HsInt n);
+void hs_uv_fs_scandir_extra_cleanup(uv_dirent_t*** dents_p, HsInt n);
+void hs_uv_fs_readlink_extra_cleanup(char** path);
+void hs_uv_fs_readlink_cleanup(char* path);
+
+////////////////////////////////////////////////////////////////////////////////
+// fs, none thread pool version
+int32_t hs_uv_fs_open(const char* path, int flags, int mode);
+HsInt hs_uv_fs_close(int32_t file);
+HsInt hs_uv_fs_read(int32_t file, char* buffer, HsInt buffer_size, int64_t offset);
+HsInt hs_uv_fs_write(int32_t file, char* buffer, HsInt buffer_size, int64_t offset);
+HsInt hs_uv_fs_unlink(const char* path);
+HsInt hs_uv_fs_mkdir(const char* path, int mode);
+HsInt hs_uv_fs_mkdtemp(const char* tpl, HsInt tpl_size, char* temp_path);
+HsInt hs_uv_fs_rmdir(const char* path);
+HsInt hs_uv_fs_scandir(const char* path, uv_dirent_t*** dents);
+HsInt hs_uv_fs_stat(const char* path, uv_stat_t* stat);
+HsInt hs_uv_fs_fstat(int32_t file, uv_stat_t* stat);
+HsInt hs_uv_fs_lstat(const char* path, uv_stat_t* stat);
+HsInt hs_uv_fs_rename(const char* path, const char* path2);
+HsInt hs_uv_fs_fsync(int32_t file);
+HsInt hs_uv_fs_fdatasync(int32_t file);
+HsInt hs_uv_fs_ftruncate(int32_t file, int64_t off);
+HsInt hs_uv_fs_copyfile(const char* path, const char* path2, int flag);
+HsInt hs_uv_fs_access(const char* path, int mode);
+HsInt hs_uv_fs_chmod(const char* path, int mode);
+HsInt hs_uv_fs_fchmod(int32_t file, int mode);
+HsInt hs_uv_fs_utime(const char* path, double atime, double mtime);
+HsInt hs_uv_fs_futime(int32_t file, double atime, double mtime);
+HsInt hs_uv_fs_link(const char* path, const char* path2);
+HsInt hs_uv_fs_symlink(const char* path, const char* path2, int flag);
+HsInt hs_uv_fs_readlink(const char* path, char** result_path);
+HsInt hs_uv_fs_realpath(const char* path, char** result_path);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// fs, thread pool version
+HsInt hs_uv_fs_open_threaded(const char* path, int flags, int mode, uv_loop_t* loop);
+HsInt hs_uv_fs_close_threaded(int32_t file, uv_loop_t* loop);
+HsInt hs_uv_fs_read_threaded(int32_t file, char* buffer, HsInt buffer_size, int64_t offset, uv_loop_t* loop);
+HsInt hs_uv_fs_write_threaded(int32_t file, char* buffer, HsInt buffer_size, int64_t offset, uv_loop_t* loop);
+HsInt hs_uv_fs_unlink_threaded(const char* path, uv_loop_t* loop);
+HsInt hs_uv_fs_mkdir_threaded(const char* path, int mode, uv_loop_t* loop);
+HsInt hs_uv_fs_mkdtemp_threaded(const char* tpl, HsInt tpl_size, char* temp_path, uv_loop_t* loop);
+HsInt hs_uv_fs_rmdir_threaded(const char* path, uv_loop_t* loop);
+HsInt hs_uv_fs_scandir_threaded(const char* path, uv_dirent_t*** dents, uv_loop_t* loop);
+HsInt hs_uv_fs_stat_threaded(const char* path, uv_stat_t* stat, uv_loop_t* loop);
+HsInt hs_uv_fs_fstat_threaded(int32_t file, uv_stat_t* stat, uv_loop_t* loop);
+HsInt hs_uv_fs_lstat_threaded(const char* path, uv_stat_t* stat, uv_loop_t* loop);
+HsInt hs_uv_fs_rename_threaded(const char* path, const char* path2, uv_loop_t* loop);
+HsInt hs_uv_fs_fsync_threaded(int32_t file, uv_loop_t* loop);
+HsInt hs_uv_fs_fdatasync_threaded(int32_t file, uv_loop_t* loop);
+HsInt hs_uv_fs_ftruncate_threaded(int32_t file, int64_t off, uv_loop_t* loop);
+HsInt hs_uv_fs_copyfile_threaded(const char* path, const char* path2, int flag, uv_loop_t* loop);
+HsInt hs_uv_fs_access_threaded(const char* path, int mode, uv_loop_t* loop);
+HsInt hs_uv_fs_chmod_threaded(const char* path, int mode, uv_loop_t* loop);
+HsInt hs_uv_fs_fchmod_threaded(int32_t file, int mode, uv_loop_t* loop);
+HsInt hs_uv_fs_utime_threaded(const char* path, double atime, double mtime, uv_loop_t* loop);
+HsInt hs_uv_fs_futime_threaded(int32_t file, double atime, double mtime, uv_loop_t* loop);
+HsInt hs_uv_fs_link_threaded(const char* path, const char* path2, uv_loop_t* loop);
+HsInt hs_uv_fs_symlink_threaded(const char* path, const char* path2, int flag, uv_loop_t* loop);
+HsInt hs_uv_fs_readlink_threaded(const char* path, char** result_path, uv_loop_t* loop);
+HsInt hs_uv_fs_realpath_threaded(const char* path, char** result_path, uv_loop_t* loop);
+
+////////////////////////////////////////////////////////////////////////////////
+// dns
+int uv__getaddrinfo_translate_error(int sys_err);
+HsInt hs_getaddrinfo(const char *node, const char *service,
+                     const struct addrinfo *hints,
+                     struct addrinfo **res);
+HsInt hs_getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
+                     char *host, socklen_t hostlen,
+                     char *serv, socklen_t servlen, int flags);
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+-- file test/Spec.hs
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/test/Z/IO/FileSystem/ThreadedSpec.hs b/test/Z/IO/FileSystem/ThreadedSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/IO/FileSystem/ThreadedSpec.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Z.IO.FileSystem.ThreadedSpec where
+
+import           Control.Concurrent.MVar (readMVar)
+import           Control.Monad
+import           Data.Bits
+import           Z.Data.Vector         as V
+import           Z.Data.Vector.Base    as V
+import           Data.List               as List
+import           Foreign.Marshal.Array
+import           Foreign.Ptr
+import           Z.IO.Buffered
+import           Z.IO.Exception
+import           Z.IO.FileSystem.Threaded
+import           Z.IO.Resource
+import           Z.IO.UV.Manager
+import           Test.Hspec
+import           Test.HUnit
+
+spec :: Spec
+spec = describe "filesystem (threadpool version) operations" $ do
+
+        let content = "Hello world!"
+            content2 = V.cycleN 1024 "quick fox jumps over the lazy dog, 世界你好!\n"
+            size = V.length content
+            size2 = V.length content2
+
+        it "create a temp dir" $ do
+            tempdir <- mkdtemp "stdio-filesystem-unit"
+            dirs <- scandir "./"
+            rmdir tempdir
+            List.lookup tempdir dirs @?= Just DirEntDir
+
+
+        let flags = O_RDWR .|. O_CREAT
+            mode = DEFAULT_MODE
+            filename = "test-file"
+
+        it "Opens and writes a file" $ do
+            withResource (initFileT filename flags mode) $ \ file -> do
+                o <- newBufferedOutput 4096 file
+                writeBuffer o content
+                flushBuffer o
+
+            unlink filename
+
+        it "Opens and writes a file II" $ do
+            withResource (initFileT filename flags mode) $ \ file -> do
+                o <- newBufferedOutput 4096 file
+                writeBuffer o content2
+                flushBuffer o
+
+            withResource (initFileT filename flags mode) $ \ file -> do
+                i <- newBufferedInput 4096 file
+                written <- readExactly size2 i
+                written @=? content2
+
+            withResource (initFileT filename flags mode) $ \ file -> do
+                i <- newBufferedInput 4096 file
+                firstLine <- readLine i
+                firstLine  @=? fst (V.break (== V.c2w '\n') content2)
+
+            unlink filename
+
+
+        it "create and remove dir" $ do
+            tempdir <- mkdtemp "stdio-filesystem-unit"
+            let dirname  = tempdir <> "/test-dir"
+            mkdir dirname mode
+            dirs <- scandir tempdir
+            print dirs
+            rmdir dirname
+            rmdir tempdir
+            List.lookup "test-dir" dirs @?= Just DirEntDir
+
+        let linkname  = "test-link"
+            symlinkname  = "test-symlink"
+            symlinkname2  = "test-symlink2"
+
+        it "link stat should be equal to target file" $ do
+
+            withResource (initFileT filename flags mode) $ \ file -> return ()
+
+            s0 <- stat filename
+
+            link filename linkname
+            symlink "test-link" symlinkname SYMLINK_DEFAULT
+
+            absfp <- realpath filename
+            symlink absfp symlinkname2 SYMLINK_DEFAULT  -- the second way to create a proper symlink
+
+            s1 <- stat linkname
+            s2 <- stat symlinkname
+            s2' <- stat symlinkname2
+
+            s0 @?= s1 {stNlink = 1} -- update hard link number
+            s0 @?= s2 {stNlink = 1}
+            s0 @?= s2' {stNlink = 1}
+
+            withResource (initFileT filename flags mode) $ \ file -> do
+                s4 <- fstat file
+                s0 @?= s4 {stNlink = 1}
+
+            unlink filename
+            unlink linkname
+            unlink symlinkname
+            unlink symlinkname2
+
+        it "utime result in stat change" $ do
+            withResource (initFileT filename flags mode) $ \ file -> return ()
+            utime filename 1000.2000 3000.4000
+            s <- stat filename
+            print s
+            uvtSecond (stAtim s) @?= 1000
+            uvtNanoSecond (stAtim s) @?= 200000000
+            uvtSecond (stMtim s) @?= 3000
+            uvtNanoSecond (stMtim s) @?= 400000000
+            unlink filename
+
+        it "futime result in fstat change" $ do
+            withResource (initFileT filename flags mode) $ \ file -> do
+                futime file 5000.6000 7000.8000
+                s <- fstat file
+                print s
+                uvtSecond (stAtim s) @?= 5000
+                uvtNanoSecond (stAtim s) @?= 600000000
+                uvtSecond (stMtim s) @?= 7000
+                uvtNanoSecond (stMtim s) @?= 800000000
+            unlink filename
+
diff --git a/test/Z/IO/FileSystemSpec.hs b/test/Z/IO/FileSystemSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/IO/FileSystemSpec.hs
@@ -0,0 +1,138 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Z.IO.FileSystemSpec where
+
+import           Control.Concurrent.MVar (readMVar)
+import           Control.Monad
+import           Data.Bits
+import           Z.Data.Vector         as V
+import           Z.Data.Vector.Base    as V
+import           Data.List               as List
+import           Foreign.Marshal.Array
+import           Foreign.Ptr
+import           Z.IO.Buffered
+import           Z.IO.Exception
+import           Z.IO.FileSystem
+import           Z.IO.Resource
+import           Z.IO.UV.Manager
+import           Test.Hspec
+import           Test.HUnit
+
+spec :: Spec
+spec = describe "filesystem operations" $ do
+
+        let content = "Hello world!"
+            content2 = V.cycleN 1024 "quick fox jumps over the lazy dog, 世界你好!\n"
+            size = V.length content
+            size2 = V.length content2
+
+
+        it "create a temp dir" $ do
+            tempdir <- mkdtemp "stdio-filesystem-unit"
+            dirs <- scandir "./"
+            rmdir tempdir
+            List.lookup tempdir dirs @?= Just DirEntDir
+
+
+        let flags = O_RDWR .|. O_CREAT
+            mode = DEFAULT_MODE
+            filename = "test-file"
+
+        it "Opens and writes a file" $ do
+            withResource (initFile filename flags mode) $ \ file -> do
+                o <- newBufferedOutput 4096 file
+                writeBuffer o content
+                flushBuffer o
+
+            withResource (initFile filename flags mode) $ \ file -> do
+                i <- newBufferedInput 4096 file
+                written <- readExactly size i
+                written @=? content
+
+
+            unlink filename
+
+        it "Opens and writes a file II" $ do
+            withResource (initFile filename flags mode) $ \ file -> do
+                o <- newBufferedOutput 4096 file
+                writeBuffer o content2
+                flushBuffer o
+
+            withResource (initFile filename flags mode) $ \ file -> do
+                i <- newBufferedInput 4096 file
+                written <- readExactly size2 i
+                written @=? content2
+
+            withResource (initFile filename flags mode) $ \ file -> do
+                i <- newBufferedInput 4096 file
+                firstLine <- readLine i
+                firstLine  @=? fst (V.break (== V.c2w '\n') content2)
+
+            unlink filename
+
+
+        it "create and remove dir" $ do
+            tempdir <- mkdtemp "stdio-filesystem-unit"
+            let dirname  = tempdir <> "/test-dir"
+            mkdir dirname mode
+            dirs <- scandir tempdir
+            print dirs
+            rmdir dirname
+            rmdir tempdir
+            List.lookup "test-dir" dirs @?= Just DirEntDir
+
+        let linkname  = "test-link"
+            symlinkname  = "test-symlink"
+            symlinkname2  = "test-symlink2"
+
+        it "link stat should be equal to target file" $ do
+
+            withResource (initFile filename flags mode) $ \ file -> return ()
+
+            s0 <- stat filename
+
+            link filename linkname
+            symlink "test-link" symlinkname SYMLINK_DEFAULT
+
+            absfp <- realpath filename
+            symlink absfp symlinkname2 SYMLINK_DEFAULT  -- the second way to create a proper symlink
+
+            s1 <- stat linkname
+            s2 <- stat symlinkname
+            s2' <- stat symlinkname2
+
+            s0 @?= s1 {stNlink = 1} -- update hard link number
+            s0 @?= s2 {stNlink = 1}
+            s0 @?= s2' {stNlink = 1}
+
+            withResource (initFile filename flags mode) $ \ file -> do
+                s4 <- fstat file
+                s0 @?= s4 {stNlink = 1}
+
+            unlink filename
+            unlink linkname
+            unlink symlinkname
+            unlink symlinkname2
+
+        it "utime result in stat change" $ do
+            withResource (initFile filename flags mode) $ \ file -> return ()
+            utime filename 1000.2000 3000.4000
+            s <- stat filename
+            print s
+            uvtSecond (stAtim s) @?= 1000
+            uvtNanoSecond (stAtim s) @?= 200000000
+            uvtSecond (stMtim s) @?= 3000
+            uvtNanoSecond (stMtim s) @?= 400000000
+            unlink filename
+
+        it "futime result in fstat change" $ do
+            withResource (initFile filename flags mode) $ \ file -> do
+                futime file 5000.6000 7000.8000
+                s <- fstat file
+                print s
+                uvtSecond (stAtim s) @?= 5000
+                uvtNanoSecond (stAtim s) @?= 600000000
+                uvtSecond (stMtim s) @?= 7000
+                uvtNanoSecond (stMtim s) @?= 800000000
+            unlink filename
+
diff --git a/test/Z/IO/LowResTimerSpec.hs b/test/Z/IO/LowResTimerSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/IO/LowResTimerSpec.hs
@@ -0,0 +1,52 @@
+module Z.IO.LowResTimerSpec where
+
+import           Control.Concurrent
+import           Control.Monad
+import           Control.Monad.IO.Class
+import           Z.Data.PrimRef.PrimIORef
+import           Z.IO.LowResTimer
+import           Test.Hspec
+import           Test.HUnit
+
+spec :: Spec
+spec = describe "low resolution timers" $ do
+    it "timers registration should not be missed" $ do
+        c <- newCounter 0
+        replicateM_ 10000 $ do
+            forM_ [1..10] $ \ i -> do
+                registerLowResTimer i (atomicAddCounter_ c 1)
+
+        threadDelay 1000
+        lrtm <- getLowResTimerManager
+        running <- isLowResTimerManagerRunning lrtm
+        assertEqual "timer manager should start" True running
+
+        threadDelay 1200000 -- make sure all timers are fired
+        c' <- readPrimIORef c
+        assertEqual "timers registration counter" 100000 c'
+
+        threadDelay 100000  -- another 0.1s
+
+        lrtm <- getLowResTimerManager
+        running <- isLowResTimerManagerRunning lrtm
+        assertEqual "timer manager should stopped" False running
+
+    it "throttle" $ do
+        c <- newCounter 0
+        throttledAdd <- throttle 10 (atomicAddCounter_ c 1)
+        forkIO . replicateM_ 100 $ do
+            throttledAdd
+            threadDelay 50000
+        threadDelay 10000000  -- wait 10s here
+        c' <- readPrimIORef c
+        assertBool "throttled add" (6  <= c' && c' <= 7)
+
+    it "throttleTrailing" $ do
+        c <- newCounter 0
+        throttledAdd <- throttleTrailing_ 10 (atomicAddCounter_ c 1)
+        forkIO . replicateM_ 100 $ do
+            throttledAdd
+            threadDelay 50000
+        threadDelay 10000000  -- wait 10s here
+        c' <- readPrimIORef c
+        assertBool "throttled add" (5  <= c' && c' <= 6)
diff --git a/test/Z/IO/ResourceSpec.hs b/test/Z/IO/ResourceSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/IO/ResourceSpec.hs
@@ -0,0 +1,121 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Z.IO.ResourceSpec where
+
+import           Control.Concurrent
+import           Control.Exception
+import           Control.Monad
+import           Z.Data.PrimRef.PrimIORef
+import           Data.Typeable
+import           Z.IO.Resource          as R
+import           Test.Hspec
+import           Test.HUnit
+
+data WorkerException = WorkerException deriving (Typeable, Show)
+
+instance Exception WorkerException
+
+spec :: Spec
+spec = describe "resource tests" $ do
+    it "resource pool" $ do
+        resCounter <- newCounter 0
+        workerCounter <- newCounter 0
+        let res = initResource (atomicAddCounter_ resCounter 1)
+                               (\ _ -> atomicSubCounter_ resCounter 1)
+            resPool = initPool res 100 1
+        R.withResource resPool $ \ pool -> do
+            let res = initInPool pool
+            replicateM_ 300 . forkIO. R.withResource res $ \ _ -> do
+                atomicAddCounter_ workerCounter 1
+                r <- readPrimIORef resCounter
+                threadDelay 1000000
+                assertEqual "pool should limit max usage" True (r <= 100)
+
+            threadDelay 4000000 -- first 100 worker quickly get resources
+                                -- then hold for 1s, rest 100 worker have to wait, and so on
+                                -- so here we wait for 4s to make sure every worker got a resource
+                                -- we used to use replicateConcurrently_ from async, but it's
+                                -- not really neccessary
+
+            w <- readPrimIORef workerCounter
+            assertEqual "worker should be able to get resource" 300 w
+
+            r <- readPrimIORef resCounter
+            assertEqual "pool should keep returned resources alive" 100 r
+
+            s <- statPool pool
+            assertEqual "pool should be scanning returned resources" PoolScanning s
+
+            threadDelay 1200000  -- another 1.2s
+
+            r <- readPrimIORef resCounter
+            assertEqual "pool should reap unused resources" 0 r
+
+            threadDelay 1200000  -- another 1.2s
+
+            s <- statPool pool
+            assertEqual "pool should stop scanning returned resources" PoolEmpty s
+
+            -- Let's test again
+
+            writePrimIORef workerCounter 0
+
+            replicateM_ 300 . forkIO. R.withResource res $ \ _ -> do
+                atomicAddCounter_ workerCounter 1
+                r <- readPrimIORef resCounter
+                threadDelay 1000000
+                assertEqual "pool should limit max usage" True (r <= 100)
+
+            threadDelay 4000000
+
+            w <- readPrimIORef workerCounter
+            assertEqual "worker should be able to get resource" 300 w
+
+            r <- readPrimIORef resCounter
+            assertEqual "pool should keep returned resources alive" 100 r
+
+            s <- statPool pool
+            assertEqual "pool should be scanning returned resources" PoolScanning s
+
+            threadDelay 1200000  -- another 1.2s
+
+            r <- readPrimIORef resCounter
+            assertEqual "pool should reap unused resources" 0 r
+
+            threadDelay 1200000  -- another 1.2s
+
+            s <- statPool pool
+            assertEqual "pool should stop scanning returned resources" PoolEmpty s
+
+    it "resource pool under exceptions" $ do
+        resCounter <- newCounter 0
+        let res = initResource (atomicAddCounter' resCounter 1)
+                               (\ _ -> atomicSubCounter_ resCounter 1)
+            resPool = initPool res 100 1
+        R.withResource resPool $ \ pool -> do
+            let res = initInPool pool
+            handle (\ (e :: WorkerException) -> return ()) .
+                    replicateM_ 300 . forkIO. R.withResource res $ \ i -> do
+                        r <- readPrimIORef resCounter
+                        threadDelay 1000000
+                        when (even i) (throwIO WorkerException)
+                        assertEqual "pool should limit max usage" True (r <= 100)
+
+            threadDelay 4000000
+
+            r <- readPrimIORef resCounter
+            assertEqual "pool should keep returned resources alive" 100 r
+
+            s <- statPool pool
+            assertEqual "pool should be scanning returned resources" PoolScanning s
+
+            threadDelay 1200000  -- another 1.2s
+
+            r <- readPrimIORef resCounter
+            assertEqual "pool should reap unused resources" 0 r
+
+            threadDelay 1200000  -- another 1.2s
+
+            s <- statPool pool
+            assertEqual "pool should stop scanning returned resources" PoolEmpty s
diff --git a/test/Z/IO/UDPSpec.hs b/test/Z/IO/UDPSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Z/IO/UDPSpec.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Z.IO.UDPSpec where
+
+import           Control.Concurrent
+import           Control.Monad
+import           Data.Bits
+import           Z.Data.Vector         as V
+import           Z.Data.Vector.Base    as V
+import           Data.List               as List
+import           Foreign.Marshal.Array
+import           Foreign.Ptr
+import           Z.IO.Exception
+import           Z.IO.Resource
+import           Z.IO.Network.SocketAddr
+import           Z.IO.Network.UDP
+import           Test.Hspec
+import           Test.HUnit
+
+spec :: Spec
+spec = describe "UDP operations" $ do
+    it "roundtrip test" $ do
+        let testMsg = V.replicate 256 48
+            longMsg = V.replicate 2048 48
+            addr = SocketAddrInet 12345 inetLoopback
+        withResource (initUDP defaultUDPConfig{udpSendMsgSize = 2048}) $ \ c ->
+            withResource (initUDP defaultUDPConfig{udpLocalAddr = Just (addr,UV_UDP_DEFAULT)}) $ \ s -> do
+                forkIO $ sendUDP c addr testMsg
+                [(_, partial, rcvMsg)]<- recvUDP defaultUDPRecvConfig s
+                partial @=? False
+                rcvMsg @=? testMsg
+
+                threadDelay 100000
+
+                forkIO $ sendUDP c addr longMsg
+                [(_, partial, rcvMsg)]<- recvUDP defaultUDPRecvConfig s
+                partial @=? True
+
+    it "UDP sending addr test" $ do
+        let testMsg = V.replicate 256 48
+            addr = SocketAddrInet 12346 inetLoopback
+            addr' = SocketAddrInet 12347 inetLoopback
+        withResource (initUDP defaultUDPConfig{udpLocalAddr = Just (addr,UV_UDP_DEFAULT)}) $ \ c ->
+            withResource (initUDP defaultUDPConfig{udpLocalAddr = Just (addr',UV_UDP_DEFAULT)}) $ \ s -> do
+                forkIO $ sendUDP c addr' testMsg
+                [(rcvAddr, _, _)]<- recvUDP defaultUDPRecvConfig s
+                Just addr @=? rcvAddr
+
+    it "overlong message exception" $ do
+        let testMsg = V.replicate 4096 48
+            addr = SocketAddrInet 12348 inetLoopback
+        withResource (initUDP defaultUDPConfig) $ \ c ->
+            withResource (initUDP defaultUDPConfig) $ \ s -> do
+                sendUDP c addr testMsg `shouldThrow` anyException
diff --git a/third_party/libuv/include/uv.h b/third_party/libuv/include/uv.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/include/uv.h
@@ -0,0 +1,1817 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* See https://github.com/libuv/libuv#documentation for documentation. */
+
+#ifndef UV_H
+#define UV_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(BUILDING_UV_SHARED) && defined(USING_UV_SHARED)
+#error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both."
+#endif
+
+#ifdef _WIN32
+  /* Windows - set up dll import/export decorators. */
+# if defined(BUILDING_UV_SHARED)
+    /* Building shared library. */
+#   define UV_EXTERN __declspec(dllexport)
+# elif defined(USING_UV_SHARED)
+    /* Using shared library. */
+#   define UV_EXTERN __declspec(dllimport)
+# else
+    /* Building static library. */
+#   define UV_EXTERN /* nothing */
+# endif
+#elif __GNUC__ >= 4
+# define UV_EXTERN __attribute__((visibility("default")))
+#else
+# define UV_EXTERN /* nothing */
+#endif
+
+#include "uv/errno.h"
+#include "uv/version.h"
+#include <stddef.h>
+#include <stdio.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#if defined(_WIN32)
+# include "uv/win.h"
+#else
+# include "uv/unix.h"
+#endif
+
+/* Expand this list if necessary. */
+#define UV_ERRNO_MAP(XX)                                                      \
+  XX(E2BIG, "argument list too long")                                         \
+  XX(EACCES, "permission denied")                                             \
+  XX(EADDRINUSE, "address already in use")                                    \
+  XX(EADDRNOTAVAIL, "address not available")                                  \
+  XX(EAFNOSUPPORT, "address family not supported")                            \
+  XX(EAGAIN, "resource temporarily unavailable")                              \
+  XX(EAI_ADDRFAMILY, "address family not supported")                          \
+  XX(EAI_AGAIN, "temporary failure")                                          \
+  XX(EAI_BADFLAGS, "bad ai_flags value")                                      \
+  XX(EAI_BADHINTS, "invalid value for hints")                                 \
+  XX(EAI_CANCELED, "request canceled")                                        \
+  XX(EAI_FAIL, "permanent failure")                                           \
+  XX(EAI_FAMILY, "ai_family not supported")                                   \
+  XX(EAI_MEMORY, "out of memory")                                             \
+  XX(EAI_NODATA, "no address")                                                \
+  XX(EAI_NONAME, "unknown node or service")                                   \
+  XX(EAI_OVERFLOW, "argument buffer overflow")                                \
+  XX(EAI_PROTOCOL, "resolved protocol is unknown")                            \
+  XX(EAI_SERVICE, "service not available for socket type")                    \
+  XX(EAI_SOCKTYPE, "socket type not supported")                               \
+  XX(EALREADY, "connection already in progress")                              \
+  XX(EBADF, "bad file descriptor")                                            \
+  XX(EBUSY, "resource busy or locked")                                        \
+  XX(ECANCELED, "operation canceled")                                         \
+  XX(ECHARSET, "invalid Unicode character")                                   \
+  XX(ECONNABORTED, "software caused connection abort")                        \
+  XX(ECONNREFUSED, "connection refused")                                      \
+  XX(ECONNRESET, "connection reset by peer")                                  \
+  XX(EDESTADDRREQ, "destination address required")                            \
+  XX(EEXIST, "file already exists")                                           \
+  XX(EFAULT, "bad address in system call argument")                           \
+  XX(EFBIG, "file too large")                                                 \
+  XX(EHOSTUNREACH, "host is unreachable")                                     \
+  XX(EINTR, "interrupted system call")                                        \
+  XX(EINVAL, "invalid argument")                                              \
+  XX(EIO, "i/o error")                                                        \
+  XX(EISCONN, "socket is already connected")                                  \
+  XX(EISDIR, "illegal operation on a directory")                              \
+  XX(ELOOP, "too many symbolic links encountered")                            \
+  XX(EMFILE, "too many open files")                                           \
+  XX(EMSGSIZE, "message too long")                                            \
+  XX(ENAMETOOLONG, "name too long")                                           \
+  XX(ENETDOWN, "network is down")                                             \
+  XX(ENETUNREACH, "network is unreachable")                                   \
+  XX(ENFILE, "file table overflow")                                           \
+  XX(ENOBUFS, "no buffer space available")                                    \
+  XX(ENODEV, "no such device")                                                \
+  XX(ENOENT, "no such file or directory")                                     \
+  XX(ENOMEM, "not enough memory")                                             \
+  XX(ENONET, "machine is not on the network")                                 \
+  XX(ENOPROTOOPT, "protocol not available")                                   \
+  XX(ENOSPC, "no space left on device")                                       \
+  XX(ENOSYS, "function not implemented")                                      \
+  XX(ENOTCONN, "socket is not connected")                                     \
+  XX(ENOTDIR, "not a directory")                                              \
+  XX(ENOTEMPTY, "directory not empty")                                        \
+  XX(ENOTSOCK, "socket operation on non-socket")                              \
+  XX(ENOTSUP, "operation not supported on socket")                            \
+  XX(EPERM, "operation not permitted")                                        \
+  XX(EPIPE, "broken pipe")                                                    \
+  XX(EPROTO, "protocol error")                                                \
+  XX(EPROTONOSUPPORT, "protocol not supported")                               \
+  XX(EPROTOTYPE, "protocol wrong type for socket")                            \
+  XX(ERANGE, "result too large")                                              \
+  XX(EROFS, "read-only file system")                                          \
+  XX(ESHUTDOWN, "cannot send after transport endpoint shutdown")              \
+  XX(ESPIPE, "invalid seek")                                                  \
+  XX(ESRCH, "no such process")                                                \
+  XX(ETIMEDOUT, "connection timed out")                                       \
+  XX(ETXTBSY, "text file is busy")                                            \
+  XX(EXDEV, "cross-device link not permitted")                                \
+  XX(UNKNOWN, "unknown error")                                                \
+  XX(EOF, "end of file")                                                      \
+  XX(ENXIO, "no such device or address")                                      \
+  XX(EMLINK, "too many links")                                                \
+  XX(EHOSTDOWN, "host is down")                                               \
+  XX(EREMOTEIO, "remote I/O error")                                           \
+  XX(ENOTTY, "inappropriate ioctl for device")                                \
+  XX(EFTYPE, "inappropriate file type or format")                             \
+  XX(EILSEQ, "illegal byte sequence")                                         \
+
+#define UV_HANDLE_TYPE_MAP(XX)                                                \
+  XX(ASYNC, async)                                                            \
+  XX(CHECK, check)                                                            \
+  XX(FS_EVENT, fs_event)                                                      \
+  XX(FS_POLL, fs_poll)                                                        \
+  XX(HANDLE, handle)                                                          \
+  XX(IDLE, idle)                                                              \
+  XX(NAMED_PIPE, pipe)                                                        \
+  XX(POLL, poll)                                                              \
+  XX(PREPARE, prepare)                                                        \
+  XX(PROCESS, process)                                                        \
+  XX(STREAM, stream)                                                          \
+  XX(TCP, tcp)                                                                \
+  XX(TIMER, timer)                                                            \
+  XX(TTY, tty)                                                                \
+  XX(UDP, udp)                                                                \
+  XX(SIGNAL, signal)                                                          \
+
+#define UV_REQ_TYPE_MAP(XX)                                                   \
+  XX(REQ, req)                                                                \
+  XX(CONNECT, connect)                                                        \
+  XX(WRITE, write)                                                            \
+  XX(SHUTDOWN, shutdown)                                                      \
+  XX(UDP_SEND, udp_send)                                                      \
+  XX(FS, fs)                                                                  \
+  XX(WORK, work)                                                              \
+  XX(GETADDRINFO, getaddrinfo)                                                \
+  XX(GETNAMEINFO, getnameinfo)                                                \
+  XX(RANDOM, random)                                                          \
+
+typedef enum {
+#define XX(code, _) UV_ ## code = UV__ ## code,
+  UV_ERRNO_MAP(XX)
+#undef XX
+  UV_ERRNO_MAX = UV__EOF - 1
+} uv_errno_t;
+
+typedef enum {
+  UV_UNKNOWN_HANDLE = 0,
+#define XX(uc, lc) UV_##uc,
+  UV_HANDLE_TYPE_MAP(XX)
+#undef XX
+  UV_FILE,
+  UV_HANDLE_TYPE_MAX
+} uv_handle_type;
+
+typedef enum {
+  UV_UNKNOWN_REQ = 0,
+#define XX(uc, lc) UV_##uc,
+  UV_REQ_TYPE_MAP(XX)
+#undef XX
+  UV_REQ_TYPE_PRIVATE
+  UV_REQ_TYPE_MAX
+} uv_req_type;
+
+
+/* Handle types. */
+typedef struct uv_loop_s uv_loop_t;
+typedef struct uv_handle_s uv_handle_t;
+typedef struct uv_dir_s uv_dir_t;
+typedef struct uv_stream_s uv_stream_t;
+typedef struct uv_tcp_s uv_tcp_t;
+typedef struct uv_udp_s uv_udp_t;
+typedef struct uv_pipe_s uv_pipe_t;
+typedef struct uv_tty_s uv_tty_t;
+typedef struct uv_poll_s uv_poll_t;
+typedef struct uv_timer_s uv_timer_t;
+typedef struct uv_prepare_s uv_prepare_t;
+typedef struct uv_check_s uv_check_t;
+typedef struct uv_idle_s uv_idle_t;
+typedef struct uv_async_s uv_async_t;
+typedef struct uv_process_s uv_process_t;
+typedef struct uv_fs_event_s uv_fs_event_t;
+typedef struct uv_fs_poll_s uv_fs_poll_t;
+typedef struct uv_signal_s uv_signal_t;
+
+/* Request types. */
+typedef struct uv_req_s uv_req_t;
+typedef struct uv_getaddrinfo_s uv_getaddrinfo_t;
+typedef struct uv_getnameinfo_s uv_getnameinfo_t;
+typedef struct uv_shutdown_s uv_shutdown_t;
+typedef struct uv_write_s uv_write_t;
+typedef struct uv_connect_s uv_connect_t;
+typedef struct uv_udp_send_s uv_udp_send_t;
+typedef struct uv_fs_s uv_fs_t;
+typedef struct uv_work_s uv_work_t;
+typedef struct uv_random_s uv_random_t;
+
+/* None of the above. */
+typedef struct uv_env_item_s uv_env_item_t;
+typedef struct uv_cpu_info_s uv_cpu_info_t;
+typedef struct uv_interface_address_s uv_interface_address_t;
+typedef struct uv_dirent_s uv_dirent_t;
+typedef struct uv_passwd_s uv_passwd_t;
+typedef struct uv_utsname_s uv_utsname_t;
+typedef struct uv_statfs_s uv_statfs_t;
+
+typedef enum {
+  UV_LOOP_BLOCK_SIGNAL = 0,
+  UV_METRICS_IDLE_TIME
+} uv_loop_option;
+
+typedef enum {
+  UV_RUN_DEFAULT = 0,
+  UV_RUN_ONCE,
+  UV_RUN_NOWAIT
+} uv_run_mode;
+
+
+UV_EXTERN unsigned int uv_version(void);
+UV_EXTERN const char* uv_version_string(void);
+
+typedef void* (*uv_malloc_func)(size_t size);
+typedef void* (*uv_realloc_func)(void* ptr, size_t size);
+typedef void* (*uv_calloc_func)(size_t count, size_t size);
+typedef void (*uv_free_func)(void* ptr);
+
+UV_EXTERN void uv_library_shutdown(void);
+
+UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func,
+                                   uv_realloc_func realloc_func,
+                                   uv_calloc_func calloc_func,
+                                   uv_free_func free_func);
+
+UV_EXTERN uv_loop_t* uv_default_loop(void);
+UV_EXTERN int uv_loop_init(uv_loop_t* loop);
+UV_EXTERN int uv_loop_close(uv_loop_t* loop);
+/*
+ * NOTE:
+ *  This function is DEPRECATED (to be removed after 0.12), users should
+ *  allocate the loop manually and use uv_loop_init instead.
+ */
+UV_EXTERN uv_loop_t* uv_loop_new(void);
+/*
+ * NOTE:
+ *  This function is DEPRECATED (to be removed after 0.12). Users should use
+ *  uv_loop_close and free the memory manually instead.
+ */
+UV_EXTERN void uv_loop_delete(uv_loop_t*);
+UV_EXTERN size_t uv_loop_size(void);
+UV_EXTERN int uv_loop_alive(const uv_loop_t* loop);
+UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...);
+UV_EXTERN int uv_loop_fork(uv_loop_t* loop);
+
+UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode);
+UV_EXTERN void uv_stop(uv_loop_t*);
+
+UV_EXTERN void uv_ref(uv_handle_t*);
+UV_EXTERN void uv_unref(uv_handle_t*);
+UV_EXTERN int uv_has_ref(const uv_handle_t*);
+
+UV_EXTERN void uv_update_time(uv_loop_t*);
+UV_EXTERN uint64_t uv_now(const uv_loop_t*);
+
+UV_EXTERN int uv_backend_fd(const uv_loop_t*);
+UV_EXTERN int uv_backend_timeout(const uv_loop_t*);
+
+typedef void (*uv_alloc_cb)(uv_handle_t* handle,
+                            size_t suggested_size,
+                            uv_buf_t* buf);
+typedef void (*uv_read_cb)(uv_stream_t* stream,
+                           ssize_t nread,
+                           const uv_buf_t* buf);
+typedef void (*uv_write_cb)(uv_write_t* req, int status);
+typedef void (*uv_connect_cb)(uv_connect_t* req, int status);
+typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status);
+typedef void (*uv_connection_cb)(uv_stream_t* server, int status);
+typedef void (*uv_close_cb)(uv_handle_t* handle);
+typedef void (*uv_poll_cb)(uv_poll_t* handle, int status, int events);
+typedef void (*uv_timer_cb)(uv_timer_t* handle);
+typedef void (*uv_async_cb)(uv_async_t* handle);
+typedef void (*uv_prepare_cb)(uv_prepare_t* handle);
+typedef void (*uv_check_cb)(uv_check_t* handle);
+typedef void (*uv_idle_cb)(uv_idle_t* handle);
+typedef void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal);
+typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
+typedef void (*uv_fs_cb)(uv_fs_t* req);
+typedef void (*uv_work_cb)(uv_work_t* req);
+typedef void (*uv_after_work_cb)(uv_work_t* req, int status);
+typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req,
+                                  int status,
+                                  struct addrinfo* res);
+typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req,
+                                  int status,
+                                  const char* hostname,
+                                  const char* service);
+typedef void (*uv_random_cb)(uv_random_t* req,
+                             int status,
+                             void* buf,
+                             size_t buflen);
+
+typedef struct {
+  long tv_sec;
+  long tv_nsec;
+} uv_timespec_t;
+
+
+typedef struct {
+  uint64_t st_dev;
+  uint64_t st_mode;
+  uint64_t st_nlink;
+  uint64_t st_uid;
+  uint64_t st_gid;
+  uint64_t st_rdev;
+  uint64_t st_ino;
+  uint64_t st_size;
+  uint64_t st_blksize;
+  uint64_t st_blocks;
+  uint64_t st_flags;
+  uint64_t st_gen;
+  uv_timespec_t st_atim;
+  uv_timespec_t st_mtim;
+  uv_timespec_t st_ctim;
+  uv_timespec_t st_birthtim;
+} uv_stat_t;
+
+
+typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle,
+                               const char* filename,
+                               int events,
+                               int status);
+
+typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle,
+                              int status,
+                              const uv_stat_t* prev,
+                              const uv_stat_t* curr);
+
+typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum);
+
+
+typedef enum {
+  UV_LEAVE_GROUP = 0,
+  UV_JOIN_GROUP
+} uv_membership;
+
+
+UV_EXTERN int uv_translate_sys_error(int sys_errno);
+
+UV_EXTERN const char* uv_strerror(int err);
+UV_EXTERN char* uv_strerror_r(int err, char* buf, size_t buflen);
+
+UV_EXTERN const char* uv_err_name(int err);
+UV_EXTERN char* uv_err_name_r(int err, char* buf, size_t buflen);
+
+
+#define UV_REQ_FIELDS                                                         \
+  /* public */                                                                \
+  void* data;                                                                 \
+  /* read-only */                                                             \
+  uv_req_type type;                                                           \
+  /* private */                                                               \
+  void* reserved[6];                                                          \
+  UV_REQ_PRIVATE_FIELDS                                                       \
+
+/* Abstract base class of all requests. */
+struct uv_req_s {
+  UV_REQ_FIELDS
+};
+
+
+/* Platform-specific request types. */
+UV_PRIVATE_REQ_TYPES
+
+
+UV_EXTERN int uv_shutdown(uv_shutdown_t* req,
+                          uv_stream_t* handle,
+                          uv_shutdown_cb cb);
+
+struct uv_shutdown_s {
+  UV_REQ_FIELDS
+  uv_stream_t* handle;
+  uv_shutdown_cb cb;
+  UV_SHUTDOWN_PRIVATE_FIELDS
+};
+
+
+#define UV_HANDLE_FIELDS                                                      \
+  /* public */                                                                \
+  void* data;                                                                 \
+  /* read-only */                                                             \
+  uv_loop_t* loop;                                                            \
+  uv_handle_type type;                                                        \
+  /* private */                                                               \
+  uv_close_cb close_cb;                                                       \
+  void* handle_queue[2];                                                      \
+  union {                                                                     \
+    int fd;                                                                   \
+    void* reserved[4];                                                        \
+  } u;                                                                        \
+  UV_HANDLE_PRIVATE_FIELDS                                                    \
+
+/* The abstract base class of all handles. */
+struct uv_handle_s {
+  UV_HANDLE_FIELDS
+};
+
+UV_EXTERN size_t uv_handle_size(uv_handle_type type);
+UV_EXTERN uv_handle_type uv_handle_get_type(const uv_handle_t* handle);
+UV_EXTERN const char* uv_handle_type_name(uv_handle_type type);
+UV_EXTERN void* uv_handle_get_data(const uv_handle_t* handle);
+UV_EXTERN uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle);
+UV_EXTERN void uv_handle_set_data(uv_handle_t* handle, void* data);
+
+UV_EXTERN size_t uv_req_size(uv_req_type type);
+UV_EXTERN void* uv_req_get_data(const uv_req_t* req);
+UV_EXTERN void uv_req_set_data(uv_req_t* req, void* data);
+UV_EXTERN uv_req_type uv_req_get_type(const uv_req_t* req);
+UV_EXTERN const char* uv_req_type_name(uv_req_type type);
+
+UV_EXTERN int uv_is_active(const uv_handle_t* handle);
+
+UV_EXTERN void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg);
+
+/* Helpers for ad hoc debugging, no API/ABI stability guaranteed. */
+UV_EXTERN void uv_print_all_handles(uv_loop_t* loop, FILE* stream);
+UV_EXTERN void uv_print_active_handles(uv_loop_t* loop, FILE* stream);
+
+UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
+
+UV_EXTERN int uv_send_buffer_size(uv_handle_t* handle, int* value);
+UV_EXTERN int uv_recv_buffer_size(uv_handle_t* handle, int* value);
+
+UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd);
+
+UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len);
+
+
+#define UV_STREAM_FIELDS                                                      \
+  /* number of bytes queued for writing */                                    \
+  size_t write_queue_size;                                                    \
+  uv_alloc_cb alloc_cb;                                                       \
+  uv_read_cb read_cb;                                                         \
+  /* private */                                                               \
+  UV_STREAM_PRIVATE_FIELDS
+
+/*
+ * uv_stream_t is a subclass of uv_handle_t.
+ *
+ * uv_stream is an abstract class.
+ *
+ * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t and uv_tty_t.
+ */
+struct uv_stream_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+};
+
+UV_EXTERN size_t uv_stream_get_write_queue_size(const uv_stream_t* stream);
+
+UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb);
+UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client);
+
+UV_EXTERN int uv_read_start(uv_stream_t*,
+                            uv_alloc_cb alloc_cb,
+                            uv_read_cb read_cb);
+UV_EXTERN int uv_read_stop(uv_stream_t*);
+
+UV_EXTERN int uv_write(uv_write_t* req,
+                       uv_stream_t* handle,
+                       const uv_buf_t bufs[],
+                       unsigned int nbufs,
+                       uv_write_cb cb);
+UV_EXTERN int uv_write2(uv_write_t* req,
+                        uv_stream_t* handle,
+                        const uv_buf_t bufs[],
+                        unsigned int nbufs,
+                        uv_stream_t* send_handle,
+                        uv_write_cb cb);
+UV_EXTERN int uv_try_write(uv_stream_t* handle,
+                           const uv_buf_t bufs[],
+                           unsigned int nbufs);
+
+/* uv_write_t is a subclass of uv_req_t. */
+struct uv_write_s {
+  UV_REQ_FIELDS
+  uv_write_cb cb;
+  uv_stream_t* send_handle; /* TODO: make private and unix-only in v2.x. */
+  uv_stream_t* handle;
+  UV_WRITE_PRIVATE_FIELDS
+};
+
+
+UV_EXTERN int uv_is_readable(const uv_stream_t* handle);
+UV_EXTERN int uv_is_writable(const uv_stream_t* handle);
+
+UV_EXTERN int uv_stream_set_blocking(uv_stream_t* handle, int blocking);
+
+UV_EXTERN int uv_is_closing(const uv_handle_t* handle);
+
+
+/*
+ * uv_tcp_t is a subclass of uv_stream_t.
+ *
+ * Represents a TCP stream or TCP server.
+ */
+struct uv_tcp_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+  UV_TCP_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle);
+UV_EXTERN int uv_tcp_init_ex(uv_loop_t*, uv_tcp_t* handle, unsigned int flags);
+UV_EXTERN int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock);
+UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable);
+UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle,
+                               int enable,
+                               unsigned int delay);
+UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
+
+enum uv_tcp_flags {
+  /* Used with uv_tcp_bind, when an IPv6 address is used. */
+  UV_TCP_IPV6ONLY = 1
+};
+
+UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle,
+                          const struct sockaddr* addr,
+                          unsigned int flags);
+UV_EXTERN int uv_tcp_getsockname(const uv_tcp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_tcp_getpeername(const uv_tcp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb);
+UV_EXTERN int uv_tcp_connect(uv_connect_t* req,
+                             uv_tcp_t* handle,
+                             const struct sockaddr* addr,
+                             uv_connect_cb cb);
+
+/* uv_connect_t is a subclass of uv_req_t. */
+struct uv_connect_s {
+  UV_REQ_FIELDS
+  uv_connect_cb cb;
+  uv_stream_t* handle;
+  UV_CONNECT_PRIVATE_FIELDS
+};
+
+
+/*
+ * UDP support.
+ */
+
+enum uv_udp_flags {
+  /* Disables dual stack mode. */
+  UV_UDP_IPV6ONLY = 1,
+  /*
+   * Indicates message was truncated because read buffer was too small. The
+   * remainder was discarded by the OS. Used in uv_udp_recv_cb.
+   */
+  UV_UDP_PARTIAL = 2,
+  /*
+   * Indicates if SO_REUSEADDR will be set when binding the handle.
+   * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
+   * Unix platforms, it sets the SO_REUSEADDR flag.  What that means is that
+   * multiple threads or processes can bind to the same address without error
+   * (provided they all set the flag) but only the last one to bind will receive
+   * any traffic, in effect "stealing" the port from the previous listener.
+   */
+  UV_UDP_REUSEADDR = 4,
+  /*
+   * Indicates that the message was received by recvmmsg, so the buffer provided
+   * must not be freed by the recv_cb callback.
+   */
+  UV_UDP_MMSG_CHUNK = 8,
+
+  /*
+   * Indicates that recvmmsg should be used, if available.
+   */
+  UV_UDP_RECVMMSG = 256
+};
+
+typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status);
+typedef void (*uv_udp_recv_cb)(uv_udp_t* handle,
+                               ssize_t nread,
+                               const uv_buf_t* buf,
+                               const struct sockaddr* addr,
+                               unsigned flags);
+
+/* uv_udp_t is a subclass of uv_handle_t. */
+struct uv_udp_s {
+  UV_HANDLE_FIELDS
+  /* read-only */
+  /*
+   * Number of bytes queued for sending. This field strictly shows how much
+   * information is currently queued.
+   */
+  size_t send_queue_size;
+  /*
+   * Number of send requests currently in the queue awaiting to be processed.
+   */
+  size_t send_queue_count;
+  UV_UDP_PRIVATE_FIELDS
+};
+
+/* uv_udp_send_t is a subclass of uv_req_t. */
+struct uv_udp_send_s {
+  UV_REQ_FIELDS
+  uv_udp_t* handle;
+  uv_udp_send_cb cb;
+  UV_UDP_SEND_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle);
+UV_EXTERN int uv_udp_init_ex(uv_loop_t*, uv_udp_t* handle, unsigned int flags);
+UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
+UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
+                          const struct sockaddr* addr,
+                          unsigned int flags);
+UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr);
+
+UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
+UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle,
+                                    const char* multicast_addr,
+                                    const char* interface_addr,
+                                    uv_membership membership);
+UV_EXTERN int uv_udp_set_source_membership(uv_udp_t* handle,
+                                           const char* multicast_addr,
+                                           const char* interface_addr,
+                                           const char* source_addr,
+                                           uv_membership membership);
+UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on);
+UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl);
+UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle,
+                                             const char* interface_addr);
+UV_EXTERN int uv_udp_set_broadcast(uv_udp_t* handle, int on);
+UV_EXTERN int uv_udp_set_ttl(uv_udp_t* handle, int ttl);
+UV_EXTERN int uv_udp_send(uv_udp_send_t* req,
+                          uv_udp_t* handle,
+                          const uv_buf_t bufs[],
+                          unsigned int nbufs,
+                          const struct sockaddr* addr,
+                          uv_udp_send_cb send_cb);
+UV_EXTERN int uv_udp_try_send(uv_udp_t* handle,
+                              const uv_buf_t bufs[],
+                              unsigned int nbufs,
+                              const struct sockaddr* addr);
+UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle,
+                                uv_alloc_cb alloc_cb,
+                                uv_udp_recv_cb recv_cb);
+UV_EXTERN int uv_udp_using_recvmmsg(const uv_udp_t* handle);
+UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle);
+UV_EXTERN size_t uv_udp_get_send_queue_size(const uv_udp_t* handle);
+UV_EXTERN size_t uv_udp_get_send_queue_count(const uv_udp_t* handle);
+
+
+/*
+ * uv_tty_t is a subclass of uv_stream_t.
+ *
+ * Representing a stream for the console.
+ */
+struct uv_tty_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+  UV_TTY_PRIVATE_FIELDS
+};
+
+typedef enum {
+  /* Initial/normal terminal mode */
+  UV_TTY_MODE_NORMAL,
+  /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
+  UV_TTY_MODE_RAW,
+  /* Binary-safe I/O mode for IPC (Unix-only) */
+  UV_TTY_MODE_IO
+} uv_tty_mode_t;
+
+typedef enum {
+  /*
+   * The console supports handling of virtual terminal sequences
+   * (Windows10 new console, ConEmu)
+   */
+  UV_TTY_SUPPORTED,
+  /* The console cannot process the virtual terminal sequence.  (Legacy
+   * console)
+   */
+  UV_TTY_UNSUPPORTED
+} uv_tty_vtermstate_t;
+
+
+UV_EXTERN int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable);
+UV_EXTERN int uv_tty_set_mode(uv_tty_t*, uv_tty_mode_t mode);
+UV_EXTERN int uv_tty_reset_mode(void);
+UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height);
+UV_EXTERN void uv_tty_set_vterm_state(uv_tty_vtermstate_t state);
+UV_EXTERN int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state);
+
+#ifdef __cplusplus
+extern "C++" {
+
+inline int uv_tty_set_mode(uv_tty_t* handle, int mode) {
+  return uv_tty_set_mode(handle, static_cast<uv_tty_mode_t>(mode));
+}
+
+}
+#endif
+
+UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);
+
+/*
+ * uv_pipe_t is a subclass of uv_stream_t.
+ *
+ * Representing a pipe stream or pipe server. On Windows this is a Named
+ * Pipe. On Unix this is a Unix domain socket.
+ */
+struct uv_pipe_s {
+  UV_HANDLE_FIELDS
+  UV_STREAM_FIELDS
+  int ipc; /* non-zero if this pipe is used for passing handles */
+  UV_PIPE_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc);
+UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file);
+UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name);
+UV_EXTERN void uv_pipe_connect(uv_connect_t* req,
+                               uv_pipe_t* handle,
+                               const char* name,
+                               uv_connect_cb cb);
+UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle,
+                                  char* buffer,
+                                  size_t* size);
+UV_EXTERN int uv_pipe_getpeername(const uv_pipe_t* handle,
+                                  char* buffer,
+                                  size_t* size);
+UV_EXTERN void uv_pipe_pending_instances(uv_pipe_t* handle, int count);
+UV_EXTERN int uv_pipe_pending_count(uv_pipe_t* handle);
+UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle);
+UV_EXTERN int uv_pipe_chmod(uv_pipe_t* handle, int flags);
+
+
+struct uv_poll_s {
+  UV_HANDLE_FIELDS
+  uv_poll_cb poll_cb;
+  UV_POLL_PRIVATE_FIELDS
+};
+
+enum uv_poll_event {
+  UV_READABLE = 1,
+  UV_WRITABLE = 2,
+  UV_DISCONNECT = 4,
+  UV_PRIORITIZED = 8
+};
+
+UV_EXTERN int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd);
+UV_EXTERN int uv_poll_init_socket(uv_loop_t* loop,
+                                  uv_poll_t* handle,
+                                  uv_os_sock_t socket);
+UV_EXTERN int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb);
+UV_EXTERN int uv_poll_stop(uv_poll_t* handle);
+
+
+struct uv_prepare_s {
+  UV_HANDLE_FIELDS
+  UV_PREPARE_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_prepare_init(uv_loop_t*, uv_prepare_t* prepare);
+UV_EXTERN int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb);
+UV_EXTERN int uv_prepare_stop(uv_prepare_t* prepare);
+
+
+struct uv_check_s {
+  UV_HANDLE_FIELDS
+  UV_CHECK_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_check_init(uv_loop_t*, uv_check_t* check);
+UV_EXTERN int uv_check_start(uv_check_t* check, uv_check_cb cb);
+UV_EXTERN int uv_check_stop(uv_check_t* check);
+
+
+struct uv_idle_s {
+  UV_HANDLE_FIELDS
+  UV_IDLE_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_idle_init(uv_loop_t*, uv_idle_t* idle);
+UV_EXTERN int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb);
+UV_EXTERN int uv_idle_stop(uv_idle_t* idle);
+
+
+struct uv_async_s {
+  UV_HANDLE_FIELDS
+  UV_ASYNC_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_async_init(uv_loop_t*,
+                            uv_async_t* async,
+                            uv_async_cb async_cb);
+UV_EXTERN int uv_async_send(uv_async_t* async);
+
+
+/*
+ * uv_timer_t is a subclass of uv_handle_t.
+ *
+ * Used to get woken up at a specified time in the future.
+ */
+struct uv_timer_s {
+  UV_HANDLE_FIELDS
+  UV_TIMER_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_timer_init(uv_loop_t*, uv_timer_t* handle);
+UV_EXTERN int uv_timer_start(uv_timer_t* handle,
+                             uv_timer_cb cb,
+                             uint64_t timeout,
+                             uint64_t repeat);
+UV_EXTERN int uv_timer_stop(uv_timer_t* handle);
+UV_EXTERN int uv_timer_again(uv_timer_t* handle);
+UV_EXTERN void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat);
+UV_EXTERN uint64_t uv_timer_get_repeat(const uv_timer_t* handle);
+
+
+/*
+ * uv_getaddrinfo_t is a subclass of uv_req_t.
+ *
+ * Request object for uv_getaddrinfo.
+ */
+struct uv_getaddrinfo_s {
+  UV_REQ_FIELDS
+  /* read-only */
+  uv_loop_t* loop;
+  /* struct addrinfo* addrinfo is marked as private, but it really isn't. */
+  UV_GETADDRINFO_PRIVATE_FIELDS
+};
+
+
+UV_EXTERN int uv_getaddrinfo(uv_loop_t* loop,
+                             uv_getaddrinfo_t* req,
+                             uv_getaddrinfo_cb getaddrinfo_cb,
+                             const char* node,
+                             const char* service,
+                             const struct addrinfo* hints);
+UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai);
+
+
+/*
+* uv_getnameinfo_t is a subclass of uv_req_t.
+*
+* Request object for uv_getnameinfo.
+*/
+struct uv_getnameinfo_s {
+  UV_REQ_FIELDS
+  /* read-only */
+  uv_loop_t* loop;
+  /* host and service are marked as private, but they really aren't. */
+  UV_GETNAMEINFO_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_getnameinfo(uv_loop_t* loop,
+                             uv_getnameinfo_t* req,
+                             uv_getnameinfo_cb getnameinfo_cb,
+                             const struct sockaddr* addr,
+                             int flags);
+
+
+/* uv_spawn() options. */
+typedef enum {
+  UV_IGNORE         = 0x00,
+  UV_CREATE_PIPE    = 0x01,
+  UV_INHERIT_FD     = 0x02,
+  UV_INHERIT_STREAM = 0x04,
+
+  /*
+   * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE
+   * determine the direction of flow, from the child process' perspective. Both
+   * flags may be specified to create a duplex data stream.
+   */
+  UV_READABLE_PIPE  = 0x10,
+  UV_WRITABLE_PIPE  = 0x20,
+
+  /*
+   * Open the child pipe handle in overlapped mode on Windows.
+   * On Unix it is silently ignored.
+   */
+  UV_OVERLAPPED_PIPE = 0x40
+} uv_stdio_flags;
+
+typedef struct uv_stdio_container_s {
+  uv_stdio_flags flags;
+
+  union {
+    uv_stream_t* stream;
+    int fd;
+  } data;
+} uv_stdio_container_t;
+
+typedef struct uv_process_options_s {
+  uv_exit_cb exit_cb; /* Called after the process exits. */
+  const char* file;   /* Path to program to execute. */
+  /*
+   * Command line arguments. args[0] should be the path to the program. On
+   * Windows this uses CreateProcess which concatenates the arguments into a
+   * string this can cause some strange errors. See the note at
+   * windows_verbatim_arguments.
+   */
+  char** args;
+  /*
+   * This will be set as the environ variable in the subprocess. If this is
+   * NULL then the parents environ will be used.
+   */
+  char** env;
+  /*
+   * If non-null this represents a directory the subprocess should execute
+   * in. Stands for current working directory.
+   */
+  const char* cwd;
+  /*
+   * Various flags that control how uv_spawn() behaves. See the definition of
+   * `enum uv_process_flags` below.
+   */
+  unsigned int flags;
+  /*
+   * The `stdio` field points to an array of uv_stdio_container_t structs that
+   * describe the file descriptors that will be made available to the child
+   * process. The convention is that stdio[0] points to stdin, fd 1 is used for
+   * stdout, and fd 2 is stderr.
+   *
+   * Note that on windows file descriptors greater than 2 are available to the
+   * child process only if the child processes uses the MSVCRT runtime.
+   */
+  int stdio_count;
+  uv_stdio_container_t* stdio;
+  /*
+   * Libuv can change the child process' user/group id. This happens only when
+   * the appropriate bits are set in the flags fields. This is not supported on
+   * windows; uv_spawn() will fail and set the error to UV_ENOTSUP.
+   */
+  uv_uid_t uid;
+  uv_gid_t gid;
+} uv_process_options_t;
+
+/*
+ * These are the flags that can be used for the uv_process_options.flags field.
+ */
+enum uv_process_flags {
+  /*
+   * Set the child process' user id. The user id is supplied in the `uid` field
+   * of the options struct. This does not work on windows; setting this flag
+   * will cause uv_spawn() to fail.
+   */
+  UV_PROCESS_SETUID = (1 << 0),
+  /*
+   * Set the child process' group id. The user id is supplied in the `gid`
+   * field of the options struct. This does not work on windows; setting this
+   * flag will cause uv_spawn() to fail.
+   */
+  UV_PROCESS_SETGID = (1 << 1),
+  /*
+   * Do not wrap any arguments in quotes, or perform any other escaping, when
+   * converting the argument list into a command line string. This option is
+   * only meaningful on Windows systems. On Unix it is silently ignored.
+   */
+  UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2),
+  /*
+   * Spawn the child process in a detached state - this will make it a process
+   * group leader, and will effectively enable the child to keep running after
+   * the parent exits.  Note that the child process will still keep the
+   * parent's event loop alive unless the parent process calls uv_unref() on
+   * the child's process handle.
+   */
+  UV_PROCESS_DETACHED = (1 << 3),
+  /*
+   * Hide the subprocess window that would normally be created. This option is
+   * only meaningful on Windows systems. On Unix it is silently ignored.
+   */
+  UV_PROCESS_WINDOWS_HIDE = (1 << 4),
+  /*
+   * Hide the subprocess console window that would normally be created. This
+   * option is only meaningful on Windows systems. On Unix it is silently
+   * ignored.
+   */
+  UV_PROCESS_WINDOWS_HIDE_CONSOLE = (1 << 5),
+  /*
+   * Hide the subprocess GUI window that would normally be created. This
+   * option is only meaningful on Windows systems. On Unix it is silently
+   * ignored.
+   */
+  UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6)
+};
+
+/*
+ * uv_process_t is a subclass of uv_handle_t.
+ */
+struct uv_process_s {
+  UV_HANDLE_FIELDS
+  uv_exit_cb exit_cb;
+  int pid;
+  UV_PROCESS_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_spawn(uv_loop_t* loop,
+                       uv_process_t* handle,
+                       const uv_process_options_t* options);
+UV_EXTERN int uv_process_kill(uv_process_t*, int signum);
+UV_EXTERN int uv_kill(int pid, int signum);
+UV_EXTERN uv_pid_t uv_process_get_pid(const uv_process_t*);
+
+
+/*
+ * uv_work_t is a subclass of uv_req_t.
+ */
+struct uv_work_s {
+  UV_REQ_FIELDS
+  uv_loop_t* loop;
+  uv_work_cb work_cb;
+  uv_after_work_cb after_work_cb;
+  UV_WORK_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_queue_work(uv_loop_t* loop,
+                            uv_work_t* req,
+                            uv_work_cb work_cb,
+                            uv_after_work_cb after_work_cb);
+
+UV_EXTERN int uv_cancel(uv_req_t* req);
+
+
+struct uv_cpu_times_s {
+  uint64_t user; /* milliseconds */
+  uint64_t nice; /* milliseconds */
+  uint64_t sys; /* milliseconds */
+  uint64_t idle; /* milliseconds */
+  uint64_t irq; /* milliseconds */
+};
+
+struct uv_cpu_info_s {
+  char* model;
+  int speed;
+  struct uv_cpu_times_s cpu_times;
+};
+
+struct uv_interface_address_s {
+  char* name;
+  char phys_addr[6];
+  int is_internal;
+  union {
+    struct sockaddr_in address4;
+    struct sockaddr_in6 address6;
+  } address;
+  union {
+    struct sockaddr_in netmask4;
+    struct sockaddr_in6 netmask6;
+  } netmask;
+};
+
+struct uv_passwd_s {
+  char* username;
+  long uid;
+  long gid;
+  char* shell;
+  char* homedir;
+};
+
+struct uv_utsname_s {
+  char sysname[256];
+  char release[256];
+  char version[256];
+  char machine[256];
+  /* This struct does not contain the nodename and domainname fields present in
+     the utsname type. domainname is a GNU extension. Both fields are referred
+     to as meaningless in the docs. */
+};
+
+struct uv_statfs_s {
+  uint64_t f_type;
+  uint64_t f_bsize;
+  uint64_t f_blocks;
+  uint64_t f_bfree;
+  uint64_t f_bavail;
+  uint64_t f_files;
+  uint64_t f_ffree;
+  uint64_t f_spare[4];
+};
+
+typedef enum {
+  UV_DIRENT_UNKNOWN,
+  UV_DIRENT_FILE,
+  UV_DIRENT_DIR,
+  UV_DIRENT_LINK,
+  UV_DIRENT_FIFO,
+  UV_DIRENT_SOCKET,
+  UV_DIRENT_CHAR,
+  UV_DIRENT_BLOCK
+} uv_dirent_type_t;
+
+struct uv_dirent_s {
+  const char* name;
+  uv_dirent_type_t type;
+};
+
+UV_EXTERN char** uv_setup_args(int argc, char** argv);
+UV_EXTERN int uv_get_process_title(char* buffer, size_t size);
+UV_EXTERN int uv_set_process_title(const char* title);
+UV_EXTERN int uv_resident_set_memory(size_t* rss);
+UV_EXTERN int uv_uptime(double* uptime);
+UV_EXTERN uv_os_fd_t uv_get_osfhandle(int fd);
+UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd);
+
+typedef struct {
+  long tv_sec;
+  long tv_usec;
+} uv_timeval_t;
+
+typedef struct {
+  int64_t tv_sec;
+  int32_t tv_usec;
+} uv_timeval64_t;
+
+typedef struct {
+   uv_timeval_t ru_utime; /* user CPU time used */
+   uv_timeval_t ru_stime; /* system CPU time used */
+   uint64_t ru_maxrss;    /* maximum resident set size */
+   uint64_t ru_ixrss;     /* integral shared memory size */
+   uint64_t ru_idrss;     /* integral unshared data size */
+   uint64_t ru_isrss;     /* integral unshared stack size */
+   uint64_t ru_minflt;    /* page reclaims (soft page faults) */
+   uint64_t ru_majflt;    /* page faults (hard page faults) */
+   uint64_t ru_nswap;     /* swaps */
+   uint64_t ru_inblock;   /* block input operations */
+   uint64_t ru_oublock;   /* block output operations */
+   uint64_t ru_msgsnd;    /* IPC messages sent */
+   uint64_t ru_msgrcv;    /* IPC messages received */
+   uint64_t ru_nsignals;  /* signals received */
+   uint64_t ru_nvcsw;     /* voluntary context switches */
+   uint64_t ru_nivcsw;    /* involuntary context switches */
+} uv_rusage_t;
+
+UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
+
+UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
+UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
+UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd);
+UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd);
+UV_EXTERN uv_pid_t uv_os_getpid(void);
+UV_EXTERN uv_pid_t uv_os_getppid(void);
+
+#if defined(__PASE__)
+/* On IBM i PASE, the highest process priority is -10 */
+# define UV_PRIORITY_LOW 39          /* RUNPTY(99) */
+# define UV_PRIORITY_BELOW_NORMAL 15 /* RUNPTY(50) */
+# define UV_PRIORITY_NORMAL 0        /* RUNPTY(20) */
+# define UV_PRIORITY_ABOVE_NORMAL -4 /* RUNTY(12) */
+# define UV_PRIORITY_HIGH -7         /* RUNPTY(6) */
+# define UV_PRIORITY_HIGHEST -10     /* RUNPTY(1) */
+#else
+# define UV_PRIORITY_LOW 19
+# define UV_PRIORITY_BELOW_NORMAL 10
+# define UV_PRIORITY_NORMAL 0
+# define UV_PRIORITY_ABOVE_NORMAL -7
+# define UV_PRIORITY_HIGH -14
+# define UV_PRIORITY_HIGHEST -20
+#endif
+
+UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority);
+UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority);
+
+UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
+UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
+
+UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses,
+                                     int* count);
+UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses,
+                                           int count);
+
+struct uv_env_item_s {
+  char* name;
+  char* value;
+};
+
+UV_EXTERN int uv_os_environ(uv_env_item_t** envitems, int* count);
+UV_EXTERN void uv_os_free_environ(uv_env_item_t* envitems, int count);
+UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size);
+UV_EXTERN int uv_os_setenv(const char* name, const char* value);
+UV_EXTERN int uv_os_unsetenv(const char* name);
+
+#ifdef MAXHOSTNAMELEN
+# define UV_MAXHOSTNAMESIZE (MAXHOSTNAMELEN + 1)
+#else
+  /*
+    Fallback for the maximum hostname size, including the null terminator. The
+    Windows gethostname() documentation states that 256 bytes will always be
+    large enough to hold the null-terminated hostname.
+  */
+# define UV_MAXHOSTNAMESIZE 256
+#endif
+
+UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size);
+
+UV_EXTERN int uv_os_uname(uv_utsname_t* buffer);
+
+UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop);
+
+typedef enum {
+  UV_FS_UNKNOWN = -1,
+  UV_FS_CUSTOM,
+  UV_FS_OPEN,
+  UV_FS_CLOSE,
+  UV_FS_READ,
+  UV_FS_WRITE,
+  UV_FS_SENDFILE,
+  UV_FS_STAT,
+  UV_FS_LSTAT,
+  UV_FS_FSTAT,
+  UV_FS_FTRUNCATE,
+  UV_FS_UTIME,
+  UV_FS_FUTIME,
+  UV_FS_ACCESS,
+  UV_FS_CHMOD,
+  UV_FS_FCHMOD,
+  UV_FS_FSYNC,
+  UV_FS_FDATASYNC,
+  UV_FS_UNLINK,
+  UV_FS_RMDIR,
+  UV_FS_MKDIR,
+  UV_FS_MKDTEMP,
+  UV_FS_RENAME,
+  UV_FS_SCANDIR,
+  UV_FS_LINK,
+  UV_FS_SYMLINK,
+  UV_FS_READLINK,
+  UV_FS_CHOWN,
+  UV_FS_FCHOWN,
+  UV_FS_REALPATH,
+  UV_FS_COPYFILE,
+  UV_FS_LCHOWN,
+  UV_FS_OPENDIR,
+  UV_FS_READDIR,
+  UV_FS_CLOSEDIR,
+  UV_FS_STATFS,
+  UV_FS_MKSTEMP,
+  UV_FS_LUTIME
+} uv_fs_type;
+
+struct uv_dir_s {
+  uv_dirent_t* dirents;
+  size_t nentries;
+  void* reserved[4];
+  UV_DIR_PRIVATE_FIELDS
+};
+
+/* uv_fs_t is a subclass of uv_req_t. */
+struct uv_fs_s {
+  UV_REQ_FIELDS
+  uv_fs_type fs_type;
+  uv_loop_t* loop;
+  uv_fs_cb cb;
+  ssize_t result;
+  void* ptr;
+  const char* path;
+  uv_stat_t statbuf;  /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */
+  UV_FS_PRIVATE_FIELDS
+};
+
+UV_EXTERN uv_fs_type uv_fs_get_type(const uv_fs_t*);
+UV_EXTERN ssize_t uv_fs_get_result(const uv_fs_t*);
+UV_EXTERN int uv_fs_get_system_error(const uv_fs_t*);
+UV_EXTERN void* uv_fs_get_ptr(const uv_fs_t*);
+UV_EXTERN const char* uv_fs_get_path(const uv_fs_t*);
+UV_EXTERN uv_stat_t* uv_fs_get_statbuf(uv_fs_t*);
+
+UV_EXTERN void uv_fs_req_cleanup(uv_fs_t* req);
+UV_EXTERN int uv_fs_close(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_open(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         const char* path,
+                         int flags,
+                         int mode,
+                         uv_fs_cb cb);
+UV_EXTERN int uv_fs_read(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         uv_file file,
+                         const uv_buf_t bufs[],
+                         unsigned int nbufs,
+                         int64_t offset,
+                         uv_fs_cb cb);
+UV_EXTERN int uv_fs_unlink(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_write(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          const uv_buf_t bufs[],
+                          unsigned int nbufs,
+                          int64_t offset,
+                          uv_fs_cb cb);
+/*
+ * This flag can be used with uv_fs_copyfile() to return an error if the
+ * destination already exists.
+ */
+#define UV_FS_COPYFILE_EXCL   0x0001
+
+/*
+ * This flag can be used with uv_fs_copyfile() to attempt to create a reflink.
+ * If copy-on-write is not supported, a fallback copy mechanism is used.
+ */
+#define UV_FS_COPYFILE_FICLONE 0x0002
+
+/*
+ * This flag can be used with uv_fs_copyfile() to attempt to create a reflink.
+ * If copy-on-write is not supported, an error is returned.
+ */
+#define UV_FS_COPYFILE_FICLONE_FORCE 0x0004
+
+UV_EXTERN int uv_fs_copyfile(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             const char* path,
+                             const char* new_path,
+                             int flags,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          int mode,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* tpl,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_mkstemp(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* tpl,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_scandir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            int flags,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req,
+                                 uv_dirent_t* ent);
+UV_EXTERN int uv_fs_opendir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_readdir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            uv_dir_t* dir,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_closedir(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             uv_dir_t* dir,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_stat(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         const char* path,
+                         uv_fs_cb cb);
+UV_EXTERN int uv_fs_fstat(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_rename(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           const char* new_path,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_fsync(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          uv_file file,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_fdatasync(uv_loop_t* loop,
+                              uv_fs_t* req,
+                              uv_file file,
+                              uv_fs_cb cb);
+UV_EXTERN int uv_fs_ftruncate(uv_loop_t* loop,
+                              uv_fs_t* req,
+                              uv_file file,
+                              int64_t offset,
+                              uv_fs_cb cb);
+UV_EXTERN int uv_fs_sendfile(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             uv_file out_fd,
+                             uv_file in_fd,
+                             int64_t in_offset,
+                             size_t length,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_access(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           int mode,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_chmod(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          int mode,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_utime(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          double atime,
+                          double mtime,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_futime(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           uv_file file,
+                           double atime,
+                           double mtime,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_lutime(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           double atime,
+                           double mtime,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_lstat(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_link(uv_loop_t* loop,
+                         uv_fs_t* req,
+                         const char* path,
+                         const char* new_path,
+                         uv_fs_cb cb);
+
+/*
+ * This flag can be used with uv_fs_symlink() on Windows to specify whether
+ * path argument points to a directory.
+ */
+#define UV_FS_SYMLINK_DIR          0x0001
+
+/*
+ * This flag can be used with uv_fs_symlink() on Windows to specify whether
+ * the symlink is to be created using junction points.
+ */
+#define UV_FS_SYMLINK_JUNCTION     0x0002
+
+UV_EXTERN int uv_fs_symlink(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            const char* new_path,
+                            int flags,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_readlink(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             const char* path,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_realpath(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             const char* path,
+                             uv_fs_cb cb);
+UV_EXTERN int uv_fs_fchmod(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           uv_file file,
+                           int mode,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_chown(uv_loop_t* loop,
+                          uv_fs_t* req,
+                          const char* path,
+                          uv_uid_t uid,
+                          uv_gid_t gid,
+                          uv_fs_cb cb);
+UV_EXTERN int uv_fs_fchown(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           uv_file file,
+                           uv_uid_t uid,
+                           uv_gid_t gid,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_lchown(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           uv_uid_t uid,
+                           uv_gid_t gid,
+                           uv_fs_cb cb);
+UV_EXTERN int uv_fs_statfs(uv_loop_t* loop,
+                           uv_fs_t* req,
+                           const char* path,
+                           uv_fs_cb cb);
+
+
+enum uv_fs_event {
+  UV_RENAME = 1,
+  UV_CHANGE = 2
+};
+
+
+struct uv_fs_event_s {
+  UV_HANDLE_FIELDS
+  /* private */
+  char* path;
+  UV_FS_EVENT_PRIVATE_FIELDS
+};
+
+
+/*
+ * uv_fs_stat() based polling file watcher.
+ */
+struct uv_fs_poll_s {
+  UV_HANDLE_FIELDS
+  /* Private, don't touch. */
+  void* poll_ctx;
+};
+
+UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle);
+UV_EXTERN int uv_fs_poll_start(uv_fs_poll_t* handle,
+                               uv_fs_poll_cb poll_cb,
+                               const char* path,
+                               unsigned int interval);
+UV_EXTERN int uv_fs_poll_stop(uv_fs_poll_t* handle);
+UV_EXTERN int uv_fs_poll_getpath(uv_fs_poll_t* handle,
+                                 char* buffer,
+                                 size_t* size);
+
+
+struct uv_signal_s {
+  UV_HANDLE_FIELDS
+  uv_signal_cb signal_cb;
+  int signum;
+  UV_SIGNAL_PRIVATE_FIELDS
+};
+
+UV_EXTERN int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle);
+UV_EXTERN int uv_signal_start(uv_signal_t* handle,
+                              uv_signal_cb signal_cb,
+                              int signum);
+UV_EXTERN int uv_signal_start_oneshot(uv_signal_t* handle,
+                                      uv_signal_cb signal_cb,
+                                      int signum);
+UV_EXTERN int uv_signal_stop(uv_signal_t* handle);
+
+UV_EXTERN void uv_loadavg(double avg[3]);
+
+
+/*
+ * Flags to be passed to uv_fs_event_start().
+ */
+enum uv_fs_event_flags {
+  /*
+   * By default, if the fs event watcher is given a directory name, we will
+   * watch for all events in that directory. This flags overrides this behavior
+   * and makes fs_event report only changes to the directory entry itself. This
+   * flag does not affect individual files watched.
+   * This flag is currently not implemented yet on any backend.
+   */
+  UV_FS_EVENT_WATCH_ENTRY = 1,
+
+  /*
+   * By default uv_fs_event will try to use a kernel interface such as inotify
+   * or kqueue to detect events. This may not work on remote filesystems such
+   * as NFS mounts. This flag makes fs_event fall back to calling stat() on a
+   * regular interval.
+   * This flag is currently not implemented yet on any backend.
+   */
+  UV_FS_EVENT_STAT = 2,
+
+  /*
+   * By default, event watcher, when watching directory, is not registering
+   * (is ignoring) changes in it's subdirectories.
+   * This flag will override this behaviour on platforms that support it.
+   */
+  UV_FS_EVENT_RECURSIVE = 4
+};
+
+
+UV_EXTERN int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle);
+UV_EXTERN int uv_fs_event_start(uv_fs_event_t* handle,
+                                uv_fs_event_cb cb,
+                                const char* path,
+                                unsigned int flags);
+UV_EXTERN int uv_fs_event_stop(uv_fs_event_t* handle);
+UV_EXTERN int uv_fs_event_getpath(uv_fs_event_t* handle,
+                                  char* buffer,
+                                  size_t* size);
+
+UV_EXTERN int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr);
+UV_EXTERN int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr);
+
+UV_EXTERN int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
+UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size);
+
+UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size);
+UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst);
+
+
+struct uv_random_s {
+  UV_REQ_FIELDS
+  /* read-only */
+  uv_loop_t* loop;
+  /* private */
+  int status;
+  void* buf;
+  size_t buflen;
+  uv_random_cb cb;
+  struct uv__work work_req;
+};
+
+UV_EXTERN int uv_random(uv_loop_t* loop,
+                        uv_random_t* req,
+                        void *buf,
+                        size_t buflen,
+                        unsigned flags,  /* For future extension; must be 0. */
+                        uv_random_cb cb);
+
+#if defined(IF_NAMESIZE)
+# define UV_IF_NAMESIZE (IF_NAMESIZE + 1)
+#elif defined(IFNAMSIZ)
+# define UV_IF_NAMESIZE (IFNAMSIZ + 1)
+#else
+# define UV_IF_NAMESIZE (16 + 1)
+#endif
+
+UV_EXTERN int uv_if_indextoname(unsigned int ifindex,
+                                char* buffer,
+                                size_t* size);
+UV_EXTERN int uv_if_indextoiid(unsigned int ifindex,
+                               char* buffer,
+                               size_t* size);
+
+UV_EXTERN int uv_exepath(char* buffer, size_t* size);
+
+UV_EXTERN int uv_cwd(char* buffer, size_t* size);
+
+UV_EXTERN int uv_chdir(const char* dir);
+
+UV_EXTERN uint64_t uv_get_free_memory(void);
+UV_EXTERN uint64_t uv_get_total_memory(void);
+UV_EXTERN uint64_t uv_get_constrained_memory(void);
+
+UV_EXTERN uint64_t uv_hrtime(void);
+UV_EXTERN void uv_sleep(unsigned int msec);
+
+UV_EXTERN void uv_disable_stdio_inheritance(void);
+
+UV_EXTERN int uv_dlopen(const char* filename, uv_lib_t* lib);
+UV_EXTERN void uv_dlclose(uv_lib_t* lib);
+UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr);
+UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib);
+
+UV_EXTERN int uv_mutex_init(uv_mutex_t* handle);
+UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle);
+UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle);
+UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle);
+UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle);
+UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle);
+
+UV_EXTERN int uv_rwlock_init(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_destroy(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_rdlock(uv_rwlock_t* rwlock);
+UV_EXTERN int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_rdunlock(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock);
+UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock);
+UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock);
+
+UV_EXTERN int uv_sem_init(uv_sem_t* sem, unsigned int value);
+UV_EXTERN void uv_sem_destroy(uv_sem_t* sem);
+UV_EXTERN void uv_sem_post(uv_sem_t* sem);
+UV_EXTERN void uv_sem_wait(uv_sem_t* sem);
+UV_EXTERN int uv_sem_trywait(uv_sem_t* sem);
+
+UV_EXTERN int uv_cond_init(uv_cond_t* cond);
+UV_EXTERN void uv_cond_destroy(uv_cond_t* cond);
+UV_EXTERN void uv_cond_signal(uv_cond_t* cond);
+UV_EXTERN void uv_cond_broadcast(uv_cond_t* cond);
+
+UV_EXTERN int uv_barrier_init(uv_barrier_t* barrier, unsigned int count);
+UV_EXTERN void uv_barrier_destroy(uv_barrier_t* barrier);
+UV_EXTERN int uv_barrier_wait(uv_barrier_t* barrier);
+
+UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex);
+UV_EXTERN int uv_cond_timedwait(uv_cond_t* cond,
+                                uv_mutex_t* mutex,
+                                uint64_t timeout);
+
+UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void));
+
+UV_EXTERN int uv_key_create(uv_key_t* key);
+UV_EXTERN void uv_key_delete(uv_key_t* key);
+UV_EXTERN void* uv_key_get(uv_key_t* key);
+UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
+
+UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
+
+typedef void (*uv_thread_cb)(void* arg);
+
+UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
+
+typedef enum {
+  UV_THREAD_NO_FLAGS = 0x00,
+  UV_THREAD_HAS_STACK_SIZE = 0x01
+} uv_thread_create_flags;
+
+struct uv_thread_options_s {
+  unsigned int flags;
+  size_t stack_size;
+  /* More fields may be added at any time. */
+};
+
+typedef struct uv_thread_options_s uv_thread_options_t;
+
+UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid,
+                                  const uv_thread_options_t* params,
+                                  uv_thread_cb entry,
+                                  void* arg);
+UV_EXTERN uv_thread_t uv_thread_self(void);
+UV_EXTERN int uv_thread_join(uv_thread_t *tid);
+UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
+
+/* The presence of these unions force similar struct layout. */
+#define XX(_, name) uv_ ## name ## _t name;
+union uv_any_handle {
+  UV_HANDLE_TYPE_MAP(XX)
+};
+
+union uv_any_req {
+  UV_REQ_TYPE_MAP(XX)
+};
+#undef XX
+
+
+struct uv_loop_s {
+  /* User data - use this for whatever. */
+  void* data;
+  /* Loop reference counting. */
+  unsigned int active_handles;
+  void* handle_queue[2];
+  union {
+    void* unused;
+    unsigned int count;
+  } active_reqs;
+  /* Internal storage for future extensions. */
+  void* internal_fields;
+  /* Internal flag to signal loop stop. */
+  unsigned int stop_flag;
+  UV_LOOP_PRIVATE_FIELDS
+};
+
+UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
+UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
+
+/* Don't export the private CPP symbols. */
+#undef UV_HANDLE_TYPE_PRIVATE
+#undef UV_REQ_TYPE_PRIVATE
+#undef UV_REQ_PRIVATE_FIELDS
+#undef UV_STREAM_PRIVATE_FIELDS
+#undef UV_TCP_PRIVATE_FIELDS
+#undef UV_PREPARE_PRIVATE_FIELDS
+#undef UV_CHECK_PRIVATE_FIELDS
+#undef UV_IDLE_PRIVATE_FIELDS
+#undef UV_ASYNC_PRIVATE_FIELDS
+#undef UV_TIMER_PRIVATE_FIELDS
+#undef UV_GETADDRINFO_PRIVATE_FIELDS
+#undef UV_GETNAMEINFO_PRIVATE_FIELDS
+#undef UV_FS_REQ_PRIVATE_FIELDS
+#undef UV_WORK_PRIVATE_FIELDS
+#undef UV_FS_EVENT_PRIVATE_FIELDS
+#undef UV_SIGNAL_PRIVATE_FIELDS
+#undef UV_LOOP_PRIVATE_FIELDS
+#undef UV_LOOP_PRIVATE_PLATFORM_FIELDS
+#undef UV__ERR
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* UV_H */
diff --git a/third_party/libuv/include/uv/errno.h b/third_party/libuv/include/uv/errno.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/include/uv/errno.h
@@ -0,0 +1,448 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_ERRNO_H_
+#define UV_ERRNO_H_
+
+#include <errno.h>
+#if EDOM > 0
+# define UV__ERR(x) (-(x))
+#else
+# define UV__ERR(x) (x)
+#endif
+
+#define UV__EOF     (-4095)
+#define UV__UNKNOWN (-4094)
+
+#define UV__EAI_ADDRFAMILY  (-3000)
+#define UV__EAI_AGAIN       (-3001)
+#define UV__EAI_BADFLAGS    (-3002)
+#define UV__EAI_CANCELED    (-3003)
+#define UV__EAI_FAIL        (-3004)
+#define UV__EAI_FAMILY      (-3005)
+#define UV__EAI_MEMORY      (-3006)
+#define UV__EAI_NODATA      (-3007)
+#define UV__EAI_NONAME      (-3008)
+#define UV__EAI_OVERFLOW    (-3009)
+#define UV__EAI_SERVICE     (-3010)
+#define UV__EAI_SOCKTYPE    (-3011)
+#define UV__EAI_BADHINTS    (-3013)
+#define UV__EAI_PROTOCOL    (-3014)
+
+/* Only map to the system errno on non-Windows platforms. It's apparently
+ * a fairly common practice for Windows programmers to redefine errno codes.
+ */
+#if defined(E2BIG) && !defined(_WIN32)
+# define UV__E2BIG UV__ERR(E2BIG)
+#else
+# define UV__E2BIG (-4093)
+#endif
+
+#if defined(EACCES) && !defined(_WIN32)
+# define UV__EACCES UV__ERR(EACCES)
+#else
+# define UV__EACCES (-4092)
+#endif
+
+#if defined(EADDRINUSE) && !defined(_WIN32)
+# define UV__EADDRINUSE UV__ERR(EADDRINUSE)
+#else
+# define UV__EADDRINUSE (-4091)
+#endif
+
+#if defined(EADDRNOTAVAIL) && !defined(_WIN32)
+# define UV__EADDRNOTAVAIL UV__ERR(EADDRNOTAVAIL)
+#else
+# define UV__EADDRNOTAVAIL (-4090)
+#endif
+
+#if defined(EAFNOSUPPORT) && !defined(_WIN32)
+# define UV__EAFNOSUPPORT UV__ERR(EAFNOSUPPORT)
+#else
+# define UV__EAFNOSUPPORT (-4089)
+#endif
+
+#if defined(EAGAIN) && !defined(_WIN32)
+# define UV__EAGAIN UV__ERR(EAGAIN)
+#else
+# define UV__EAGAIN (-4088)
+#endif
+
+#if defined(EALREADY) && !defined(_WIN32)
+# define UV__EALREADY UV__ERR(EALREADY)
+#else
+# define UV__EALREADY (-4084)
+#endif
+
+#if defined(EBADF) && !defined(_WIN32)
+# define UV__EBADF UV__ERR(EBADF)
+#else
+# define UV__EBADF (-4083)
+#endif
+
+#if defined(EBUSY) && !defined(_WIN32)
+# define UV__EBUSY UV__ERR(EBUSY)
+#else
+# define UV__EBUSY (-4082)
+#endif
+
+#if defined(ECANCELED) && !defined(_WIN32)
+# define UV__ECANCELED UV__ERR(ECANCELED)
+#else
+# define UV__ECANCELED (-4081)
+#endif
+
+#if defined(ECHARSET) && !defined(_WIN32)
+# define UV__ECHARSET UV__ERR(ECHARSET)
+#else
+# define UV__ECHARSET (-4080)
+#endif
+
+#if defined(ECONNABORTED) && !defined(_WIN32)
+# define UV__ECONNABORTED UV__ERR(ECONNABORTED)
+#else
+# define UV__ECONNABORTED (-4079)
+#endif
+
+#if defined(ECONNREFUSED) && !defined(_WIN32)
+# define UV__ECONNREFUSED UV__ERR(ECONNREFUSED)
+#else
+# define UV__ECONNREFUSED (-4078)
+#endif
+
+#if defined(ECONNRESET) && !defined(_WIN32)
+# define UV__ECONNRESET UV__ERR(ECONNRESET)
+#else
+# define UV__ECONNRESET (-4077)
+#endif
+
+#if defined(EDESTADDRREQ) && !defined(_WIN32)
+# define UV__EDESTADDRREQ UV__ERR(EDESTADDRREQ)
+#else
+# define UV__EDESTADDRREQ (-4076)
+#endif
+
+#if defined(EEXIST) && !defined(_WIN32)
+# define UV__EEXIST UV__ERR(EEXIST)
+#else
+# define UV__EEXIST (-4075)
+#endif
+
+#if defined(EFAULT) && !defined(_WIN32)
+# define UV__EFAULT UV__ERR(EFAULT)
+#else
+# define UV__EFAULT (-4074)
+#endif
+
+#if defined(EHOSTUNREACH) && !defined(_WIN32)
+# define UV__EHOSTUNREACH UV__ERR(EHOSTUNREACH)
+#else
+# define UV__EHOSTUNREACH (-4073)
+#endif
+
+#if defined(EINTR) && !defined(_WIN32)
+# define UV__EINTR UV__ERR(EINTR)
+#else
+# define UV__EINTR (-4072)
+#endif
+
+#if defined(EINVAL) && !defined(_WIN32)
+# define UV__EINVAL UV__ERR(EINVAL)
+#else
+# define UV__EINVAL (-4071)
+#endif
+
+#if defined(EIO) && !defined(_WIN32)
+# define UV__EIO UV__ERR(EIO)
+#else
+# define UV__EIO (-4070)
+#endif
+
+#if defined(EISCONN) && !defined(_WIN32)
+# define UV__EISCONN UV__ERR(EISCONN)
+#else
+# define UV__EISCONN (-4069)
+#endif
+
+#if defined(EISDIR) && !defined(_WIN32)
+# define UV__EISDIR UV__ERR(EISDIR)
+#else
+# define UV__EISDIR (-4068)
+#endif
+
+#if defined(ELOOP) && !defined(_WIN32)
+# define UV__ELOOP UV__ERR(ELOOP)
+#else
+# define UV__ELOOP (-4067)
+#endif
+
+#if defined(EMFILE) && !defined(_WIN32)
+# define UV__EMFILE UV__ERR(EMFILE)
+#else
+# define UV__EMFILE (-4066)
+#endif
+
+#if defined(EMSGSIZE) && !defined(_WIN32)
+# define UV__EMSGSIZE UV__ERR(EMSGSIZE)
+#else
+# define UV__EMSGSIZE (-4065)
+#endif
+
+#if defined(ENAMETOOLONG) && !defined(_WIN32)
+# define UV__ENAMETOOLONG UV__ERR(ENAMETOOLONG)
+#else
+# define UV__ENAMETOOLONG (-4064)
+#endif
+
+#if defined(ENETDOWN) && !defined(_WIN32)
+# define UV__ENETDOWN UV__ERR(ENETDOWN)
+#else
+# define UV__ENETDOWN (-4063)
+#endif
+
+#if defined(ENETUNREACH) && !defined(_WIN32)
+# define UV__ENETUNREACH UV__ERR(ENETUNREACH)
+#else
+# define UV__ENETUNREACH (-4062)
+#endif
+
+#if defined(ENFILE) && !defined(_WIN32)
+# define UV__ENFILE UV__ERR(ENFILE)
+#else
+# define UV__ENFILE (-4061)
+#endif
+
+#if defined(ENOBUFS) && !defined(_WIN32)
+# define UV__ENOBUFS UV__ERR(ENOBUFS)
+#else
+# define UV__ENOBUFS (-4060)
+#endif
+
+#if defined(ENODEV) && !defined(_WIN32)
+# define UV__ENODEV UV__ERR(ENODEV)
+#else
+# define UV__ENODEV (-4059)
+#endif
+
+#if defined(ENOENT) && !defined(_WIN32)
+# define UV__ENOENT UV__ERR(ENOENT)
+#else
+# define UV__ENOENT (-4058)
+#endif
+
+#if defined(ENOMEM) && !defined(_WIN32)
+# define UV__ENOMEM UV__ERR(ENOMEM)
+#else
+# define UV__ENOMEM (-4057)
+#endif
+
+#if defined(ENONET) && !defined(_WIN32)
+# define UV__ENONET UV__ERR(ENONET)
+#else
+# define UV__ENONET (-4056)
+#endif
+
+#if defined(ENOSPC) && !defined(_WIN32)
+# define UV__ENOSPC UV__ERR(ENOSPC)
+#else
+# define UV__ENOSPC (-4055)
+#endif
+
+#if defined(ENOSYS) && !defined(_WIN32)
+# define UV__ENOSYS UV__ERR(ENOSYS)
+#else
+# define UV__ENOSYS (-4054)
+#endif
+
+#if defined(ENOTCONN) && !defined(_WIN32)
+# define UV__ENOTCONN UV__ERR(ENOTCONN)
+#else
+# define UV__ENOTCONN (-4053)
+#endif
+
+#if defined(ENOTDIR) && !defined(_WIN32)
+# define UV__ENOTDIR UV__ERR(ENOTDIR)
+#else
+# define UV__ENOTDIR (-4052)
+#endif
+
+#if defined(ENOTEMPTY) && !defined(_WIN32)
+# define UV__ENOTEMPTY UV__ERR(ENOTEMPTY)
+#else
+# define UV__ENOTEMPTY (-4051)
+#endif
+
+#if defined(ENOTSOCK) && !defined(_WIN32)
+# define UV__ENOTSOCK UV__ERR(ENOTSOCK)
+#else
+# define UV__ENOTSOCK (-4050)
+#endif
+
+#if defined(ENOTSUP) && !defined(_WIN32)
+# define UV__ENOTSUP UV__ERR(ENOTSUP)
+#else
+# define UV__ENOTSUP (-4049)
+#endif
+
+#if defined(EPERM) && !defined(_WIN32)
+# define UV__EPERM UV__ERR(EPERM)
+#else
+# define UV__EPERM (-4048)
+#endif
+
+#if defined(EPIPE) && !defined(_WIN32)
+# define UV__EPIPE UV__ERR(EPIPE)
+#else
+# define UV__EPIPE (-4047)
+#endif
+
+#if defined(EPROTO) && !defined(_WIN32)
+# define UV__EPROTO UV__ERR(EPROTO)
+#else
+# define UV__EPROTO UV__ERR(4046)
+#endif
+
+#if defined(EPROTONOSUPPORT) && !defined(_WIN32)
+# define UV__EPROTONOSUPPORT UV__ERR(EPROTONOSUPPORT)
+#else
+# define UV__EPROTONOSUPPORT (-4045)
+#endif
+
+#if defined(EPROTOTYPE) && !defined(_WIN32)
+# define UV__EPROTOTYPE UV__ERR(EPROTOTYPE)
+#else
+# define UV__EPROTOTYPE (-4044)
+#endif
+
+#if defined(EROFS) && !defined(_WIN32)
+# define UV__EROFS UV__ERR(EROFS)
+#else
+# define UV__EROFS (-4043)
+#endif
+
+#if defined(ESHUTDOWN) && !defined(_WIN32)
+# define UV__ESHUTDOWN UV__ERR(ESHUTDOWN)
+#else
+# define UV__ESHUTDOWN (-4042)
+#endif
+
+#if defined(ESPIPE) && !defined(_WIN32)
+# define UV__ESPIPE UV__ERR(ESPIPE)
+#else
+# define UV__ESPIPE (-4041)
+#endif
+
+#if defined(ESRCH) && !defined(_WIN32)
+# define UV__ESRCH UV__ERR(ESRCH)
+#else
+# define UV__ESRCH (-4040)
+#endif
+
+#if defined(ETIMEDOUT) && !defined(_WIN32)
+# define UV__ETIMEDOUT UV__ERR(ETIMEDOUT)
+#else
+# define UV__ETIMEDOUT (-4039)
+#endif
+
+#if defined(ETXTBSY) && !defined(_WIN32)
+# define UV__ETXTBSY UV__ERR(ETXTBSY)
+#else
+# define UV__ETXTBSY (-4038)
+#endif
+
+#if defined(EXDEV) && !defined(_WIN32)
+# define UV__EXDEV UV__ERR(EXDEV)
+#else
+# define UV__EXDEV (-4037)
+#endif
+
+#if defined(EFBIG) && !defined(_WIN32)
+# define UV__EFBIG UV__ERR(EFBIG)
+#else
+# define UV__EFBIG (-4036)
+#endif
+
+#if defined(ENOPROTOOPT) && !defined(_WIN32)
+# define UV__ENOPROTOOPT UV__ERR(ENOPROTOOPT)
+#else
+# define UV__ENOPROTOOPT (-4035)
+#endif
+
+#if defined(ERANGE) && !defined(_WIN32)
+# define UV__ERANGE UV__ERR(ERANGE)
+#else
+# define UV__ERANGE (-4034)
+#endif
+
+#if defined(ENXIO) && !defined(_WIN32)
+# define UV__ENXIO UV__ERR(ENXIO)
+#else
+# define UV__ENXIO (-4033)
+#endif
+
+#if defined(EMLINK) && !defined(_WIN32)
+# define UV__EMLINK UV__ERR(EMLINK)
+#else
+# define UV__EMLINK (-4032)
+#endif
+
+/* EHOSTDOWN is not visible on BSD-like systems when _POSIX_C_SOURCE is
+ * defined. Fortunately, its value is always 64 so it's possible albeit
+ * icky to hard-code it.
+ */
+#if defined(EHOSTDOWN) && !defined(_WIN32)
+# define UV__EHOSTDOWN UV__ERR(EHOSTDOWN)
+#elif defined(__APPLE__) || \
+      defined(__DragonFly__) || \
+      defined(__FreeBSD__) || \
+      defined(__FreeBSD_kernel__) || \
+      defined(__NetBSD__) || \
+      defined(__OpenBSD__)
+# define UV__EHOSTDOWN (-64)
+#else
+# define UV__EHOSTDOWN (-4031)
+#endif
+
+#if defined(EREMOTEIO) && !defined(_WIN32)
+# define UV__EREMOTEIO UV__ERR(EREMOTEIO)
+#else
+# define UV__EREMOTEIO (-4030)
+#endif
+
+#if defined(ENOTTY) && !defined(_WIN32)
+# define UV__ENOTTY UV__ERR(ENOTTY)
+#else
+# define UV__ENOTTY (-4029)
+#endif
+
+#if defined(EFTYPE) && !defined(_WIN32)
+# define UV__EFTYPE UV__ERR(EFTYPE)
+#else
+# define UV__EFTYPE (-4028)
+#endif
+
+#if defined(EILSEQ) && !defined(_WIN32)
+# define UV__EILSEQ UV__ERR(EILSEQ)
+#else
+# define UV__EILSEQ (-4027)
+#endif
+
+#endif /* UV_ERRNO_H_ */
diff --git a/third_party/libuv/include/uv/stdint-msvc2008.h b/third_party/libuv/include/uv/stdint-msvc2008.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/include/uv/stdint-msvc2008.h
@@ -0,0 +1,247 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2008 Alexander Chemeris
+// 
+// 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.
+// 
+//   3. The name of the author may be used to endorse or promote products
+//      derived from this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+#  include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#     define _W64 __w64
+#  else
+#     define _W64
+#  endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+   typedef signed char       int8_t;
+   typedef signed short      int16_t;
+   typedef signed int        int32_t;
+   typedef unsigned char     uint8_t;
+   typedef unsigned short    uint16_t;
+   typedef unsigned int      uint32_t;
+#else
+   typedef signed __int8     int8_t;
+   typedef signed __int16    int16_t;
+   typedef signed __int32    int32_t;
+   typedef unsigned __int8   uint8_t;
+   typedef unsigned __int16  uint16_t;
+   typedef unsigned __int32  uint32_t;
+#endif
+typedef signed __int64       int64_t;
+typedef unsigned __int64     uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef signed __int64    intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef _W64 signed int   intptr_t;
+   typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C   INT64_C
+#define UINTMAX_C  UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/third_party/libuv/include/uv/threadpool.h b/third_party/libuv/include/uv/threadpool.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/include/uv/threadpool.h
@@ -0,0 +1,37 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * This file is private to libuv. It provides common functionality to both
+ * Windows and Unix backends.
+ */
+
+#ifndef UV_THREADPOOL_H_
+#define UV_THREADPOOL_H_
+
+struct uv__work {
+  void (*work)(struct uv__work *w);
+  void (*done)(struct uv__work *w, int status);
+  struct uv_loop_s* loop;
+  void* wq[2];
+};
+
+#endif /* UV_THREADPOOL_H_ */
diff --git a/third_party/libuv/include/uv/tree.h b/third_party/libuv/include/uv/tree.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/include/uv/tree.h
@@ -0,0 +1,768 @@
+/*-
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef  UV_TREE_H_
+#define  UV_TREE_H_
+
+#ifndef UV__UNUSED
+# if __GNUC__
+#  define UV__UNUSED __attribute__((unused))
+# else
+#  define UV__UNUSED
+# endif
+#endif
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *  - every search path from the root to a leaf consists of the
+ *    same number of black nodes,
+ *  - each red node (except for the root) has a black parent,
+ *  - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type)                                                \
+struct name {                                                                 \
+  struct type *sph_root; /* root of the tree */                               \
+}
+
+#define SPLAY_INITIALIZER(root)                                               \
+  { NULL }
+
+#define SPLAY_INIT(root) do {                                                 \
+  (root)->sph_root = NULL;                                                    \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ENTRY(type)                                                     \
+struct {                                                                      \
+  struct type *spe_left;          /* left element */                          \
+  struct type *spe_right;         /* right element */                         \
+}
+
+#define SPLAY_LEFT(elm, field)    (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)   (elm)->field.spe_right
+#define SPLAY_ROOT(head)          (head)->sph_root
+#define SPLAY_EMPTY(head)         (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {                             \
+  SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);              \
+  SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                 \
+  (head)->sph_root = tmp;                                                     \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {                              \
+  SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);              \
+  SPLAY_LEFT(tmp, field) = (head)->sph_root;                                  \
+  (head)->sph_root = tmp;                                                     \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {                                 \
+  SPLAY_LEFT(tmp, field) = (head)->sph_root;                                  \
+  tmp = (head)->sph_root;                                                     \
+  (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                     \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {                                \
+  SPLAY_RIGHT(tmp, field) = (head)->sph_root;                                 \
+  tmp = (head)->sph_root;                                                     \
+  (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                    \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {                   \
+  SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);             \
+  SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);            \
+  SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);             \
+  SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);             \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)                               \
+void name##_SPLAY(struct name *, struct type *);                              \
+void name##_SPLAY_MINMAX(struct name *, int);                                 \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);               \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);               \
+                                                                              \
+/* Finds the node with the same key as elm */                                 \
+static __inline struct type *                                                 \
+name##_SPLAY_FIND(struct name *head, struct type *elm)                        \
+{                                                                             \
+  if (SPLAY_EMPTY(head))                                                      \
+    return(NULL);                                                             \
+  name##_SPLAY(head, elm);                                                    \
+  if ((cmp)(elm, (head)->sph_root) == 0)                                      \
+    return (head->sph_root);                                                  \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+static __inline struct type *                                                 \
+name##_SPLAY_NEXT(struct name *head, struct type *elm)                        \
+{                                                                             \
+  name##_SPLAY(head, elm);                                                    \
+  if (SPLAY_RIGHT(elm, field) != NULL) {                                      \
+    elm = SPLAY_RIGHT(elm, field);                                            \
+    while (SPLAY_LEFT(elm, field) != NULL) {                                  \
+      elm = SPLAY_LEFT(elm, field);                                           \
+    }                                                                         \
+  } else                                                                      \
+    elm = NULL;                                                               \
+  return (elm);                                                               \
+}                                                                             \
+                                                                              \
+static __inline struct type *                                                 \
+name##_SPLAY_MIN_MAX(struct name *head, int val)                              \
+{                                                                             \
+  name##_SPLAY_MINMAX(head, val);                                             \
+  return (SPLAY_ROOT(head));                                                  \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)                                \
+struct type *                                                                 \
+name##_SPLAY_INSERT(struct name *head, struct type *elm)                      \
+{                                                                             \
+    if (SPLAY_EMPTY(head)) {                                                  \
+      SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;                \
+    } else {                                                                  \
+      int __comp;                                                             \
+      name##_SPLAY(head, elm);                                                \
+      __comp = (cmp)(elm, (head)->sph_root);                                  \
+      if(__comp < 0) {                                                        \
+        SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);         \
+        SPLAY_RIGHT(elm, field) = (head)->sph_root;                           \
+        SPLAY_LEFT((head)->sph_root, field) = NULL;                           \
+      } else if (__comp > 0) {                                                \
+        SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);       \
+        SPLAY_LEFT(elm, field) = (head)->sph_root;                            \
+        SPLAY_RIGHT((head)->sph_root, field) = NULL;                          \
+      } else                                                                  \
+        return ((head)->sph_root);                                            \
+    }                                                                         \
+    (head)->sph_root = (elm);                                                 \
+    return (NULL);                                                            \
+}                                                                             \
+                                                                              \
+struct type *                                                                 \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)                      \
+{                                                                             \
+  struct type *__tmp;                                                         \
+  if (SPLAY_EMPTY(head))                                                      \
+    return (NULL);                                                            \
+  name##_SPLAY(head, elm);                                                    \
+  if ((cmp)(elm, (head)->sph_root) == 0) {                                    \
+    if (SPLAY_LEFT((head)->sph_root, field) == NULL) {                        \
+      (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);                \
+    } else {                                                                  \
+      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
+      (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);                 \
+      name##_SPLAY(head, elm);                                                \
+      SPLAY_RIGHT((head)->sph_root, field) = __tmp;                           \
+    }                                                                         \
+    return (elm);                                                             \
+  }                                                                           \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+void                                                                          \
+name##_SPLAY(struct name *head, struct type *elm)                             \
+{                                                                             \
+  struct type __node, *__left, *__right, *__tmp;                              \
+  int __comp;                                                                 \
+                                                                              \
+  SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;            \
+  __left = __right = &__node;                                                 \
+                                                                              \
+  while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {                      \
+    if (__comp < 0) {                                                         \
+      __tmp = SPLAY_LEFT((head)->sph_root, field);                            \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if ((cmp)(elm, __tmp) < 0){                                             \
+        SPLAY_ROTATE_RIGHT(head, __tmp, field);                               \
+        if (SPLAY_LEFT((head)->sph_root, field) == NULL)                      \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKLEFT(head, __right, field);                                   \
+    } else if (__comp > 0) {                                                  \
+      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if ((cmp)(elm, __tmp) > 0){                                             \
+        SPLAY_ROTATE_LEFT(head, __tmp, field);                                \
+        if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                     \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKRIGHT(head, __left, field);                                   \
+    }                                                                         \
+  }                                                                           \
+  SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                      \
+}                                                                             \
+                                                                              \
+/* Splay with either the minimum or the maximum element                       \
+ * Used to find minimum or maximum element in tree.                           \
+ */                                                                           \
+void name##_SPLAY_MINMAX(struct name *head, int __comp)                       \
+{                                                                             \
+  struct type __node, *__left, *__right, *__tmp;                              \
+                                                                              \
+  SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;            \
+  __left = __right = &__node;                                                 \
+                                                                              \
+  while (1) {                                                                 \
+    if (__comp < 0) {                                                         \
+      __tmp = SPLAY_LEFT((head)->sph_root, field);                            \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if (__comp < 0){                                                        \
+        SPLAY_ROTATE_RIGHT(head, __tmp, field);                               \
+        if (SPLAY_LEFT((head)->sph_root, field) == NULL)                      \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKLEFT(head, __right, field);                                   \
+    } else if (__comp > 0) {                                                  \
+      __tmp = SPLAY_RIGHT((head)->sph_root, field);                           \
+      if (__tmp == NULL)                                                      \
+        break;                                                                \
+      if (__comp > 0) {                                                       \
+        SPLAY_ROTATE_LEFT(head, __tmp, field);                                \
+        if (SPLAY_RIGHT((head)->sph_root, field) == NULL)                     \
+          break;                                                              \
+      }                                                                       \
+      SPLAY_LINKRIGHT(head, __left, field);                                   \
+    }                                                                         \
+  }                                                                           \
+  SPLAY_ASSEMBLE(head, &__node, __left, __right, field);                      \
+}
+
+#define SPLAY_NEGINF  -1
+#define SPLAY_INF     1
+
+#define SPLAY_INSERT(name, x, y)  name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)  name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)    name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)    name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)        (SPLAY_EMPTY(x) ? NULL                      \
+                                  : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)        (SPLAY_EMPTY(x) ? NULL                      \
+                                  : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)                                          \
+  for ((x) = SPLAY_MIN(name, head);                                           \
+       (x) != NULL;                                                           \
+       (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type)                                                   \
+struct name {                                                                 \
+  struct type *rbh_root; /* root of the tree */                               \
+}
+
+#define RB_INITIALIZER(root)                                                  \
+  { NULL }
+
+#define RB_INIT(root) do {                                                    \
+  (root)->rbh_root = NULL;                                                    \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_BLACK  0
+#define RB_RED    1
+#define RB_ENTRY(type)                                                        \
+struct {                                                                      \
+  struct type *rbe_left;        /* left element */                            \
+  struct type *rbe_right;       /* right element */                           \
+  struct type *rbe_parent;      /* parent element */                          \
+  int rbe_color;                /* node color */                              \
+}
+
+#define RB_LEFT(elm, field)     (elm)->field.rbe_left
+#define RB_RIGHT(elm, field)    (elm)->field.rbe_right
+#define RB_PARENT(elm, field)   (elm)->field.rbe_parent
+#define RB_COLOR(elm, field)    (elm)->field.rbe_color
+#define RB_ROOT(head)           (head)->rbh_root
+#define RB_EMPTY(head)          (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {                                       \
+  RB_PARENT(elm, field) = parent;                                             \
+  RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;                          \
+  RB_COLOR(elm, field) = RB_RED;                                              \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_SET_BLACKRED(black, red, field) do {                               \
+  RB_COLOR(black, field) = RB_BLACK;                                          \
+  RB_COLOR(red, field) = RB_RED;                                              \
+} while (/*CONSTCOND*/ 0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)  do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {                            \
+  (tmp) = RB_RIGHT(elm, field);                                               \
+  if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {                 \
+    RB_PARENT(RB_LEFT(tmp, field), field) = (elm);                            \
+  }                                                                           \
+  RB_AUGMENT(elm);                                                            \
+  if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {              \
+    if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))                       \
+      RB_LEFT(RB_PARENT(elm, field), field) = (tmp);                          \
+    else                                                                      \
+      RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);                         \
+  } else                                                                      \
+    (head)->rbh_root = (tmp);                                                 \
+  RB_LEFT(tmp, field) = (elm);                                                \
+  RB_PARENT(elm, field) = (tmp);                                              \
+  RB_AUGMENT(tmp);                                                            \
+  if ((RB_PARENT(tmp, field)))                                                \
+    RB_AUGMENT(RB_PARENT(tmp, field));                                        \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {                           \
+  (tmp) = RB_LEFT(elm, field);                                                \
+  if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {                 \
+    RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);                           \
+  }                                                                           \
+  RB_AUGMENT(elm);                                                            \
+  if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {              \
+    if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))                       \
+      RB_LEFT(RB_PARENT(elm, field), field) = (tmp);                          \
+    else                                                                      \
+      RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);                         \
+  } else                                                                      \
+    (head)->rbh_root = (tmp);                                                 \
+  RB_RIGHT(tmp, field) = (elm);                                               \
+  RB_PARENT(elm, field) = (tmp);                                              \
+  RB_AUGMENT(tmp);                                                            \
+  if ((RB_PARENT(tmp, field)))                                                \
+    RB_AUGMENT(RB_PARENT(tmp, field));                                        \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+#define  RB_PROTOTYPE(name, type, field, cmp)                                 \
+  RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define  RB_PROTOTYPE_STATIC(name, type, field, cmp)                          \
+  RB_PROTOTYPE_INTERNAL(name, type, field, cmp, UV__UNUSED static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)                   \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *);               \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *);             \
+attr struct type *name##_RB_INSERT(struct name *, struct type *);             \
+attr struct type *name##_RB_FIND(struct name *, struct type *);               \
+attr struct type *name##_RB_NFIND(struct name *, struct type *);              \
+attr struct type *name##_RB_NEXT(struct type *);                              \
+attr struct type *name##_RB_PREV(struct type *);                              \
+attr struct type *name##_RB_MINMAX(struct name *, int);                       \
+                                                                              \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define  RB_GENERATE(name, type, field, cmp)                                  \
+  RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define  RB_GENERATE_STATIC(name, type, field, cmp)                           \
+  RB_GENERATE_INTERNAL(name, type, field, cmp, UV__UNUSED static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)                    \
+attr void                                                                     \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)                   \
+{                                                                             \
+  struct type *parent, *gparent, *tmp;                                        \
+  while ((parent = RB_PARENT(elm, field)) != NULL &&                          \
+      RB_COLOR(parent, field) == RB_RED) {                                    \
+    gparent = RB_PARENT(parent, field);                                       \
+    if (parent == RB_LEFT(gparent, field)) {                                  \
+      tmp = RB_RIGHT(gparent, field);                                         \
+      if (tmp && RB_COLOR(tmp, field) == RB_RED) {                            \
+        RB_COLOR(tmp, field) = RB_BLACK;                                      \
+        RB_SET_BLACKRED(parent, gparent, field);                              \
+        elm = gparent;                                                        \
+        continue;                                                             \
+      }                                                                       \
+      if (RB_RIGHT(parent, field) == elm) {                                   \
+        RB_ROTATE_LEFT(head, parent, tmp, field);                             \
+        tmp = parent;                                                         \
+        parent = elm;                                                         \
+        elm = tmp;                                                            \
+      }                                                                       \
+      RB_SET_BLACKRED(parent, gparent, field);                                \
+      RB_ROTATE_RIGHT(head, gparent, tmp, field);                             \
+    } else {                                                                  \
+      tmp = RB_LEFT(gparent, field);                                          \
+      if (tmp && RB_COLOR(tmp, field) == RB_RED) {                            \
+        RB_COLOR(tmp, field) = RB_BLACK;                                      \
+        RB_SET_BLACKRED(parent, gparent, field);                              \
+        elm = gparent;                                                        \
+        continue;                                                             \
+      }                                                                       \
+      if (RB_LEFT(parent, field) == elm) {                                    \
+        RB_ROTATE_RIGHT(head, parent, tmp, field);                            \
+        tmp = parent;                                                         \
+        parent = elm;                                                         \
+        elm = tmp;                                                            \
+      }                                                                       \
+      RB_SET_BLACKRED(parent, gparent, field);                                \
+      RB_ROTATE_LEFT(head, gparent, tmp, field);                              \
+    }                                                                         \
+  }                                                                           \
+  RB_COLOR(head->rbh_root, field) = RB_BLACK;                                 \
+}                                                                             \
+                                                                              \
+attr void                                                                     \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent,                \
+    struct type *elm)                                                         \
+{                                                                             \
+  struct type *tmp;                                                           \
+  while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&                 \
+      elm != RB_ROOT(head)) {                                                 \
+    if (RB_LEFT(parent, field) == elm) {                                      \
+      tmp = RB_RIGHT(parent, field);                                          \
+      if (RB_COLOR(tmp, field) == RB_RED) {                                   \
+        RB_SET_BLACKRED(tmp, parent, field);                                  \
+        RB_ROTATE_LEFT(head, parent, tmp, field);                             \
+        tmp = RB_RIGHT(parent, field);                                        \
+      }                                                                       \
+      if ((RB_LEFT(tmp, field) == NULL ||                                     \
+          RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&                \
+          (RB_RIGHT(tmp, field) == NULL ||                                    \
+          RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {               \
+        RB_COLOR(tmp, field) = RB_RED;                                        \
+        elm = parent;                                                         \
+        parent = RB_PARENT(elm, field);                                       \
+      } else {                                                                \
+        if (RB_RIGHT(tmp, field) == NULL ||                                   \
+            RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {              \
+          struct type *oleft;                                                 \
+          if ((oleft = RB_LEFT(tmp, field))                                   \
+              != NULL)                                                        \
+            RB_COLOR(oleft, field) = RB_BLACK;                                \
+          RB_COLOR(tmp, field) = RB_RED;                                      \
+          RB_ROTATE_RIGHT(head, tmp, oleft, field);                           \
+          tmp = RB_RIGHT(parent, field);                                      \
+        }                                                                     \
+        RB_COLOR(tmp, field) = RB_COLOR(parent, field);                       \
+        RB_COLOR(parent, field) = RB_BLACK;                                   \
+        if (RB_RIGHT(tmp, field))                                             \
+          RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;                   \
+        RB_ROTATE_LEFT(head, parent, tmp, field);                             \
+        elm = RB_ROOT(head);                                                  \
+        break;                                                                \
+      }                                                                       \
+    } else {                                                                  \
+      tmp = RB_LEFT(parent, field);                                           \
+      if (RB_COLOR(tmp, field) == RB_RED) {                                   \
+        RB_SET_BLACKRED(tmp, parent, field);                                  \
+        RB_ROTATE_RIGHT(head, parent, tmp, field);                            \
+        tmp = RB_LEFT(parent, field);                                         \
+      }                                                                       \
+      if ((RB_LEFT(tmp, field) == NULL ||                                     \
+          RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&                \
+          (RB_RIGHT(tmp, field) == NULL ||                                    \
+          RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {               \
+        RB_COLOR(tmp, field) = RB_RED;                                        \
+        elm = parent;                                                         \
+        parent = RB_PARENT(elm, field);                                       \
+      } else {                                                                \
+        if (RB_LEFT(tmp, field) == NULL ||                                    \
+            RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {               \
+          struct type *oright;                                                \
+          if ((oright = RB_RIGHT(tmp, field))                                 \
+              != NULL)                                                        \
+            RB_COLOR(oright, field) = RB_BLACK;                               \
+          RB_COLOR(tmp, field) = RB_RED;                                      \
+          RB_ROTATE_LEFT(head, tmp, oright, field);                           \
+          tmp = RB_LEFT(parent, field);                                       \
+        }                                                                     \
+        RB_COLOR(tmp, field) = RB_COLOR(parent, field);                       \
+        RB_COLOR(parent, field) = RB_BLACK;                                   \
+        if (RB_LEFT(tmp, field))                                              \
+          RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;                    \
+        RB_ROTATE_RIGHT(head, parent, tmp, field);                            \
+        elm = RB_ROOT(head);                                                  \
+        break;                                                                \
+      }                                                                       \
+    }                                                                         \
+  }                                                                           \
+  if (elm)                                                                    \
+    RB_COLOR(elm, field) = RB_BLACK;                                          \
+}                                                                             \
+                                                                              \
+attr struct type *                                                            \
+name##_RB_REMOVE(struct name *head, struct type *elm)                         \
+{                                                                             \
+  struct type *child, *parent, *old = elm;                                    \
+  int color;                                                                  \
+  if (RB_LEFT(elm, field) == NULL)                                            \
+    child = RB_RIGHT(elm, field);                                             \
+  else if (RB_RIGHT(elm, field) == NULL)                                      \
+    child = RB_LEFT(elm, field);                                              \
+  else {                                                                      \
+    struct type *left;                                                        \
+    elm = RB_RIGHT(elm, field);                                               \
+    while ((left = RB_LEFT(elm, field)) != NULL)                              \
+      elm = left;                                                             \
+    child = RB_RIGHT(elm, field);                                             \
+    parent = RB_PARENT(elm, field);                                           \
+    color = RB_COLOR(elm, field);                                             \
+    if (child)                                                                \
+      RB_PARENT(child, field) = parent;                                       \
+    if (parent) {                                                             \
+      if (RB_LEFT(parent, field) == elm)                                      \
+        RB_LEFT(parent, field) = child;                                       \
+      else                                                                    \
+        RB_RIGHT(parent, field) = child;                                      \
+      RB_AUGMENT(parent);                                                     \
+    } else                                                                    \
+      RB_ROOT(head) = child;                                                  \
+    if (RB_PARENT(elm, field) == old)                                         \
+      parent = elm;                                                           \
+    (elm)->field = (old)->field;                                              \
+    if (RB_PARENT(old, field)) {                                              \
+      if (RB_LEFT(RB_PARENT(old, field), field) == old)                       \
+        RB_LEFT(RB_PARENT(old, field), field) = elm;                          \
+      else                                                                    \
+        RB_RIGHT(RB_PARENT(old, field), field) = elm;                         \
+      RB_AUGMENT(RB_PARENT(old, field));                                      \
+    } else                                                                    \
+      RB_ROOT(head) = elm;                                                    \
+    RB_PARENT(RB_LEFT(old, field), field) = elm;                              \
+    if (RB_RIGHT(old, field))                                                 \
+      RB_PARENT(RB_RIGHT(old, field), field) = elm;                           \
+    if (parent) {                                                             \
+      left = parent;                                                          \
+      do {                                                                    \
+        RB_AUGMENT(left);                                                     \
+      } while ((left = RB_PARENT(left, field)) != NULL);                      \
+    }                                                                         \
+    goto color;                                                               \
+  }                                                                           \
+  parent = RB_PARENT(elm, field);                                             \
+  color = RB_COLOR(elm, field);                                               \
+  if (child)                                                                  \
+    RB_PARENT(child, field) = parent;                                         \
+  if (parent) {                                                               \
+    if (RB_LEFT(parent, field) == elm)                                        \
+      RB_LEFT(parent, field) = child;                                         \
+    else                                                                      \
+      RB_RIGHT(parent, field) = child;                                        \
+    RB_AUGMENT(parent);                                                       \
+  } else                                                                      \
+    RB_ROOT(head) = child;                                                    \
+color:                                                                        \
+  if (color == RB_BLACK)                                                      \
+    name##_RB_REMOVE_COLOR(head, parent, child);                              \
+  return (old);                                                               \
+}                                                                             \
+                                                                              \
+/* Inserts a node into the RB tree */                                         \
+attr struct type *                                                            \
+name##_RB_INSERT(struct name *head, struct type *elm)                         \
+{                                                                             \
+  struct type *tmp;                                                           \
+  struct type *parent = NULL;                                                 \
+  int comp = 0;                                                               \
+  tmp = RB_ROOT(head);                                                        \
+  while (tmp) {                                                               \
+    parent = tmp;                                                             \
+    comp = (cmp)(elm, parent);                                                \
+    if (comp < 0)                                                             \
+      tmp = RB_LEFT(tmp, field);                                              \
+    else if (comp > 0)                                                        \
+      tmp = RB_RIGHT(tmp, field);                                             \
+    else                                                                      \
+      return (tmp);                                                           \
+  }                                                                           \
+  RB_SET(elm, parent, field);                                                 \
+  if (parent != NULL) {                                                       \
+    if (comp < 0)                                                             \
+      RB_LEFT(parent, field) = elm;                                           \
+    else                                                                      \
+      RB_RIGHT(parent, field) = elm;                                          \
+    RB_AUGMENT(parent);                                                       \
+  } else                                                                      \
+    RB_ROOT(head) = elm;                                                      \
+  name##_RB_INSERT_COLOR(head, elm);                                          \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+/* Finds the node with the same key as elm */                                 \
+attr struct type *                                                            \
+name##_RB_FIND(struct name *head, struct type *elm)                           \
+{                                                                             \
+  struct type *tmp = RB_ROOT(head);                                           \
+  int comp;                                                                   \
+  while (tmp) {                                                               \
+    comp = cmp(elm, tmp);                                                     \
+    if (comp < 0)                                                             \
+      tmp = RB_LEFT(tmp, field);                                              \
+    else if (comp > 0)                                                        \
+      tmp = RB_RIGHT(tmp, field);                                             \
+    else                                                                      \
+      return (tmp);                                                           \
+  }                                                                           \
+  return (NULL);                                                              \
+}                                                                             \
+                                                                              \
+/* Finds the first node greater than or equal to the search key */            \
+attr struct type *                                                            \
+name##_RB_NFIND(struct name *head, struct type *elm)                          \
+{                                                                             \
+  struct type *tmp = RB_ROOT(head);                                           \
+  struct type *res = NULL;                                                    \
+  int comp;                                                                   \
+  while (tmp) {                                                               \
+    comp = cmp(elm, tmp);                                                     \
+    if (comp < 0) {                                                           \
+      res = tmp;                                                              \
+      tmp = RB_LEFT(tmp, field);                                              \
+    }                                                                         \
+    else if (comp > 0)                                                        \
+      tmp = RB_RIGHT(tmp, field);                                             \
+    else                                                                      \
+      return (tmp);                                                           \
+  }                                                                           \
+  return (res);                                                               \
+}                                                                             \
+                                                                              \
+/* ARGSUSED */                                                                \
+attr struct type *                                                            \
+name##_RB_NEXT(struct type *elm)                                              \
+{                                                                             \
+  if (RB_RIGHT(elm, field)) {                                                 \
+    elm = RB_RIGHT(elm, field);                                               \
+    while (RB_LEFT(elm, field))                                               \
+      elm = RB_LEFT(elm, field);                                              \
+  } else {                                                                    \
+    if (RB_PARENT(elm, field) &&                                              \
+        (elm == RB_LEFT(RB_PARENT(elm, field), field)))                       \
+      elm = RB_PARENT(elm, field);                                            \
+    else {                                                                    \
+      while (RB_PARENT(elm, field) &&                                         \
+          (elm == RB_RIGHT(RB_PARENT(elm, field), field)))                    \
+        elm = RB_PARENT(elm, field);                                          \
+      elm = RB_PARENT(elm, field);                                            \
+    }                                                                         \
+  }                                                                           \
+  return (elm);                                                               \
+}                                                                             \
+                                                                              \
+/* ARGSUSED */                                                                \
+attr struct type *                                                            \
+name##_RB_PREV(struct type *elm)                                              \
+{                                                                             \
+  if (RB_LEFT(elm, field)) {                                                  \
+    elm = RB_LEFT(elm, field);                                                \
+    while (RB_RIGHT(elm, field))                                              \
+      elm = RB_RIGHT(elm, field);                                             \
+  } else {                                                                    \
+    if (RB_PARENT(elm, field) &&                                              \
+        (elm == RB_RIGHT(RB_PARENT(elm, field), field)))                      \
+      elm = RB_PARENT(elm, field);                                            \
+    else {                                                                    \
+      while (RB_PARENT(elm, field) &&                                         \
+          (elm == RB_LEFT(RB_PARENT(elm, field), field)))                     \
+        elm = RB_PARENT(elm, field);                                          \
+      elm = RB_PARENT(elm, field);                                            \
+    }                                                                         \
+  }                                                                           \
+  return (elm);                                                               \
+}                                                                             \
+                                                                              \
+attr struct type *                                                            \
+name##_RB_MINMAX(struct name *head, int val)                                  \
+{                                                                             \
+  struct type *tmp = RB_ROOT(head);                                           \
+  struct type *parent = NULL;                                                 \
+  while (tmp) {                                                               \
+    parent = tmp;                                                             \
+    if (val < 0)                                                              \
+      tmp = RB_LEFT(tmp, field);                                              \
+    else                                                                      \
+      tmp = RB_RIGHT(tmp, field);                                             \
+  }                                                                           \
+  return (parent);                                                            \
+}
+
+#define RB_NEGINF   -1
+#define RB_INF      1
+
+#define RB_INSERT(name, x, y)   name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)   name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)     name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y)    name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y)     name##_RB_NEXT(y)
+#define RB_PREV(name, x, y)     name##_RB_PREV(y)
+#define RB_MIN(name, x)         name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)         name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)                                             \
+  for ((x) = RB_MIN(name, head);                                              \
+       (x) != NULL;                                                           \
+       (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_FROM(x, name, y)                                           \
+  for ((x) = (y);                                                             \
+      ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);                \
+       (x) = (y))
+
+#define RB_FOREACH_SAFE(x, name, head, y)                                     \
+  for ((x) = RB_MIN(name, head);                                              \
+      ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);                \
+       (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head)                                     \
+  for ((x) = RB_MAX(name, head);                                              \
+       (x) != NULL;                                                           \
+       (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_FROM(x, name, y)                                   \
+  for ((x) = (y);                                                             \
+      ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);                \
+       (x) = (y))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)                             \
+  for ((x) = RB_MAX(name, head);                                              \
+      ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);                \
+       (x) = (y))
+
+#endif  /* UV_TREE_H_ */
diff --git a/third_party/libuv/include/uv/version.h b/third_party/libuv/include/uv/version.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/include/uv/version.h
@@ -0,0 +1,43 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_VERSION_H
+#define UV_VERSION_H
+
+ /*
+ * Versions with the same major number are ABI stable. API is allowed to
+ * evolve between minor releases, but only in a backwards compatible way.
+ * Make sure you update the -soname directives in configure.ac
+ * whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but
+ * not UV_VERSION_PATCH.)
+ */
+
+#define UV_VERSION_MAJOR 1
+#define UV_VERSION_MINOR 39
+#define UV_VERSION_PATCH 1
+#define UV_VERSION_IS_RELEASE 0
+#define UV_VERSION_SUFFIX "dev"
+
+#define UV_VERSION_HEX  ((UV_VERSION_MAJOR << 16) | \
+                         (UV_VERSION_MINOR <<  8) | \
+                         (UV_VERSION_PATCH))
+
+#endif /* UV_VERSION_H */
diff --git a/third_party/libuv/include/uv/win.h b/third_party/libuv/include/uv/win.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/include/uv/win.h
@@ -0,0 +1,691 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT   0x0600
+#endif
+
+#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
+typedef intptr_t ssize_t;
+# define SSIZE_MAX INTPTR_MAX
+# define _SSIZE_T_
+# define _SSIZE_T_DEFINED
+#endif
+
+#include <winsock2.h>
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+typedef struct pollfd {
+  SOCKET fd;
+  short  events;
+  short  revents;
+} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD;
+#endif
+
+#ifndef LOCALE_INVARIANT
+# define LOCALE_INVARIANT 0x007f
+#endif
+
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+
+#include <process.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#include "uv/tree.h"
+#include "uv/threadpool.h"
+
+#define MAX_PIPENAME_LEN 256
+
+#ifndef S_IFLNK
+# define S_IFLNK 0xA000
+#endif
+
+/* Additional signals supported by uv_signal and or uv_kill. The CRT defines
+ * the following signals already:
+ *
+ *   #define SIGINT           2
+ *   #define SIGILL           4
+ *   #define SIGABRT_COMPAT   6
+ *   #define SIGFPE           8
+ *   #define SIGSEGV         11
+ *   #define SIGTERM         15
+ *   #define SIGBREAK        21
+ *   #define SIGABRT         22
+ *
+ * The additional signals have values that are common on other Unix
+ * variants (Linux and Darwin)
+ */
+#define SIGHUP                1
+#define SIGKILL               9
+#define SIGWINCH             28
+
+/* Redefine NSIG to take SIGWINCH into consideration */
+#if defined(NSIG) && NSIG <= SIGWINCH
+# undef NSIG
+#endif
+#ifndef NSIG
+# define NSIG SIGWINCH + 1
+#endif
+
+/* The CRT defines SIGABRT_COMPAT as 6, which equals SIGABRT on many unix-like
+ * platforms. However MinGW doesn't define it, so we do. */
+#ifndef SIGABRT_COMPAT
+# define SIGABRT_COMPAT       6
+#endif
+
+/*
+ * Guids and typedefs for winsock extension functions
+ * Mingw32 doesn't have these :-(
+ */
+#ifndef WSAID_ACCEPTEX
+# define WSAID_ACCEPTEX                                                       \
+         {0xb5367df1, 0xcbac, 0x11cf,                                         \
+         {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+
+# define WSAID_CONNECTEX                                                      \
+         {0x25a207b9, 0xddf3, 0x4660,                                         \
+         {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}}
+
+# define WSAID_GETACCEPTEXSOCKADDRS                                           \
+         {0xb5367df2, 0xcbac, 0x11cf,                                         \
+         {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+
+# define WSAID_DISCONNECTEX                                                   \
+         {0x7fda2e11, 0x8630, 0x436f,                                         \
+         {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}}
+
+# define WSAID_TRANSMITFILE                                                   \
+         {0xb5367df0, 0xcbac, 0x11cf,                                         \
+         {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+
+  typedef BOOL (PASCAL *LPFN_ACCEPTEX)
+                      (SOCKET sListenSocket,
+                       SOCKET sAcceptSocket,
+                       PVOID lpOutputBuffer,
+                       DWORD dwReceiveDataLength,
+                       DWORD dwLocalAddressLength,
+                       DWORD dwRemoteAddressLength,
+                       LPDWORD lpdwBytesReceived,
+                       LPOVERLAPPED lpOverlapped);
+
+  typedef BOOL (PASCAL *LPFN_CONNECTEX)
+                      (SOCKET s,
+                       const struct sockaddr* name,
+                       int namelen,
+                       PVOID lpSendBuffer,
+                       DWORD dwSendDataLength,
+                       LPDWORD lpdwBytesSent,
+                       LPOVERLAPPED lpOverlapped);
+
+  typedef void (PASCAL *LPFN_GETACCEPTEXSOCKADDRS)
+                      (PVOID lpOutputBuffer,
+                       DWORD dwReceiveDataLength,
+                       DWORD dwLocalAddressLength,
+                       DWORD dwRemoteAddressLength,
+                       LPSOCKADDR* LocalSockaddr,
+                       LPINT LocalSockaddrLength,
+                       LPSOCKADDR* RemoteSockaddr,
+                       LPINT RemoteSockaddrLength);
+
+  typedef BOOL (PASCAL *LPFN_DISCONNECTEX)
+                      (SOCKET hSocket,
+                       LPOVERLAPPED lpOverlapped,
+                       DWORD dwFlags,
+                       DWORD reserved);
+
+  typedef BOOL (PASCAL *LPFN_TRANSMITFILE)
+                      (SOCKET hSocket,
+                       HANDLE hFile,
+                       DWORD nNumberOfBytesToWrite,
+                       DWORD nNumberOfBytesPerSend,
+                       LPOVERLAPPED lpOverlapped,
+                       LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
+                       DWORD dwFlags);
+
+  typedef PVOID RTL_SRWLOCK;
+  typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
+#endif
+
+typedef int (WSAAPI* LPFN_WSARECV)
+            (SOCKET socket,
+             LPWSABUF buffers,
+             DWORD buffer_count,
+             LPDWORD bytes,
+             LPDWORD flags,
+             LPWSAOVERLAPPED overlapped,
+             LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+
+typedef int (WSAAPI* LPFN_WSARECVFROM)
+            (SOCKET socket,
+             LPWSABUF buffers,
+             DWORD buffer_count,
+             LPDWORD bytes,
+             LPDWORD flags,
+             struct sockaddr* addr,
+             LPINT addr_len,
+             LPWSAOVERLAPPED overlapped,
+             LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+
+#ifndef _NTDEF_
+  typedef LONG NTSTATUS;
+  typedef NTSTATUS *PNTSTATUS;
+#endif
+
+#ifndef RTL_CONDITION_VARIABLE_INIT
+  typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE;
+#endif
+
+typedef struct _AFD_POLL_HANDLE_INFO {
+  HANDLE Handle;
+  ULONG Events;
+  NTSTATUS Status;
+} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO;
+
+typedef struct _AFD_POLL_INFO {
+  LARGE_INTEGER Timeout;
+  ULONG NumberOfHandles;
+  ULONG Exclusive;
+  AFD_POLL_HANDLE_INFO Handles[1];
+} AFD_POLL_INFO, *PAFD_POLL_INFO;
+
+#define UV_MSAFD_PROVIDER_COUNT 3
+
+
+/**
+ * It should be possible to cast uv_buf_t[] to WSABUF[]
+ * see http://msdn.microsoft.com/en-us/library/ms741542(v=vs.85).aspx
+ */
+typedef struct uv_buf_t {
+  ULONG len;
+  char* base;
+} uv_buf_t;
+
+typedef int uv_file;
+typedef SOCKET uv_os_sock_t;
+typedef HANDLE uv_os_fd_t;
+typedef int uv_pid_t;
+
+typedef HANDLE uv_thread_t;
+
+typedef HANDLE uv_sem_t;
+
+typedef CRITICAL_SECTION uv_mutex_t;
+
+/* This condition variable implementation is based on the SetEvent solution
+ * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+ * We could not use the SignalObjectAndWait solution (section 3.4) because
+ * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and
+ * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs.
+ */
+
+typedef union {
+  CONDITION_VARIABLE cond_var;
+  struct {
+    unsigned int waiters_count;
+    CRITICAL_SECTION waiters_count_lock;
+    HANDLE signal_event;
+    HANDLE broadcast_event;
+  } unused_; /* TODO: retained for ABI compatibility; remove me in v2.x. */
+} uv_cond_t;
+
+typedef union {
+  struct {
+    unsigned int num_readers_;
+    CRITICAL_SECTION num_readers_lock_;
+    HANDLE write_semaphore_;
+  } state_;
+  /* TODO: remove me in v2.x. */
+  struct {
+    SRWLOCK unused_;
+  } unused1_;
+  /* TODO: remove me in v2.x. */
+  struct {
+    uv_mutex_t unused1_;
+    uv_mutex_t unused2_;
+  } unused2_;
+} uv_rwlock_t;
+
+typedef struct {
+  unsigned int n;
+  unsigned int count;
+  uv_mutex_t mutex;
+  uv_sem_t turnstile1;
+  uv_sem_t turnstile2;
+} uv_barrier_t;
+
+typedef struct {
+  DWORD tls_index;
+} uv_key_t;
+
+#define UV_ONCE_INIT { 0, NULL }
+
+typedef struct uv_once_s {
+  unsigned char ran;
+  HANDLE event;
+} uv_once_t;
+
+/* Platform-specific definitions for uv_spawn support. */
+typedef unsigned char uv_uid_t;
+typedef unsigned char uv_gid_t;
+
+typedef struct uv__dirent_s {
+  int d_type;
+  char d_name[1];
+} uv__dirent_t;
+
+#define UV_DIR_PRIVATE_FIELDS \
+  HANDLE dir_handle;          \
+  WIN32_FIND_DATAW find_data; \
+  BOOL need_find_call;
+
+#define HAVE_DIRENT_TYPES
+#define UV__DT_DIR     UV_DIRENT_DIR
+#define UV__DT_FILE    UV_DIRENT_FILE
+#define UV__DT_LINK    UV_DIRENT_LINK
+#define UV__DT_FIFO    UV_DIRENT_FIFO
+#define UV__DT_SOCKET  UV_DIRENT_SOCKET
+#define UV__DT_CHAR    UV_DIRENT_CHAR
+#define UV__DT_BLOCK   UV_DIRENT_BLOCK
+
+/* Platform-specific definitions for uv_dlopen support. */
+#define UV_DYNAMIC FAR WINAPI
+typedef struct {
+  HMODULE handle;
+  char* errmsg;
+} uv_lib_t;
+
+#define UV_LOOP_PRIVATE_FIELDS                                                \
+    /* The loop's I/O completion port */                                      \
+  HANDLE iocp;                                                                \
+  /* The current time according to the event loop. in msecs. */               \
+  uint64_t time;                                                              \
+  /* Tail of a single-linked circular queue of pending reqs. If the queue */  \
+  /* is empty, tail_ is NULL. If there is only one item, */                   \
+  /* tail_->next_req == tail_ */                                              \
+  uv_req_t* pending_reqs_tail;                                                \
+  /* Head of a single-linked list of closed handles */                        \
+  uv_handle_t* endgame_handles;                                               \
+  /* TODO(bnoordhuis) Stop heap-allocating |timer_heap| in libuv v2.x. */     \
+  void* timer_heap;                                                           \
+    /* Lists of active loop (prepare / check / idle) watchers */              \
+  uv_prepare_t* prepare_handles;                                              \
+  uv_check_t* check_handles;                                                  \
+  uv_idle_t* idle_handles;                                                    \
+  /* This pointer will refer to the prepare/check/idle handle whose */        \
+  /* callback is scheduled to be called next. This is needed to allow */      \
+  /* safe removal from one of the lists above while that list being */        \
+  /* iterated over. */                                                        \
+  uv_prepare_t* next_prepare_handle;                                          \
+  uv_check_t* next_check_handle;                                              \
+  uv_idle_t* next_idle_handle;                                                \
+  /* This handle holds the peer sockets for the fast variant of uv_poll_t */  \
+  SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT];                          \
+  /* Counter to keep track of active tcp streams */                           \
+  unsigned int active_tcp_streams;                                            \
+  /* Counter to keep track of active udp streams */                           \
+  unsigned int active_udp_streams;                                            \
+  /* Counter to started timer */                                              \
+  uint64_t timer_counter;                                                     \
+  /* Threadpool */                                                            \
+  void* wq[2];                                                                \
+  uv_mutex_t wq_mutex;                                                        \
+  uv_async_t wq_async;
+
+#define UV_REQ_TYPE_PRIVATE                                                   \
+  /* TODO: remove the req suffix */                                           \
+  UV_ACCEPT,                                                                  \
+  UV_FS_EVENT_REQ,                                                            \
+  UV_POLL_REQ,                                                                \
+  UV_PROCESS_EXIT,                                                            \
+  UV_READ,                                                                    \
+  UV_UDP_RECV,                                                                \
+  UV_WAKEUP,                                                                  \
+  UV_SIGNAL_REQ,
+
+#define UV_REQ_PRIVATE_FIELDS                                                 \
+  union {                                                                     \
+    /* Used by I/O operations */                                              \
+    struct {                                                                  \
+      OVERLAPPED overlapped;                                                  \
+      size_t queued_bytes;                                                    \
+    } io;                                                                     \
+  } u;                                                                        \
+  struct uv_req_s* next_req;
+
+#define UV_WRITE_PRIVATE_FIELDS \
+  int coalesced;                \
+  uv_buf_t write_buffer;        \
+  HANDLE event_handle;          \
+  HANDLE wait_handle;
+
+#define UV_CONNECT_PRIVATE_FIELDS                                             \
+  /* empty */
+
+#define UV_SHUTDOWN_PRIVATE_FIELDS                                            \
+  /* empty */
+
+#define UV_UDP_SEND_PRIVATE_FIELDS                                            \
+  /* empty */
+
+#define UV_PRIVATE_REQ_TYPES                                                  \
+  typedef struct uv_pipe_accept_s {                                           \
+    UV_REQ_FIELDS                                                             \
+    HANDLE pipeHandle;                                                        \
+    struct uv_pipe_accept_s* next_pending;                                    \
+  } uv_pipe_accept_t;                                                         \
+                                                                              \
+  typedef struct uv_tcp_accept_s {                                            \
+    UV_REQ_FIELDS                                                             \
+    SOCKET accept_socket;                                                     \
+    char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32];             \
+    HANDLE event_handle;                                                      \
+    HANDLE wait_handle;                                                       \
+    struct uv_tcp_accept_s* next_pending;                                     \
+  } uv_tcp_accept_t;                                                          \
+                                                                              \
+  typedef struct uv_read_s {                                                  \
+    UV_REQ_FIELDS                                                             \
+    HANDLE event_handle;                                                      \
+    HANDLE wait_handle;                                                       \
+  } uv_read_t;
+
+#define uv_stream_connection_fields                                           \
+  unsigned int write_reqs_pending;                                            \
+  uv_shutdown_t* shutdown_req;
+
+#define uv_stream_server_fields                                               \
+  uv_connection_cb connection_cb;
+
+#define UV_STREAM_PRIVATE_FIELDS                                              \
+  unsigned int reqs_pending;                                                  \
+  int activecnt;                                                              \
+  uv_read_t read_req;                                                         \
+  union {                                                                     \
+    struct { uv_stream_connection_fields } conn;                              \
+    struct { uv_stream_server_fields     } serv;                              \
+  } stream;
+
+#define uv_tcp_server_fields                                                  \
+  uv_tcp_accept_t* accept_reqs;                                               \
+  unsigned int processed_accepts;                                             \
+  uv_tcp_accept_t* pending_accepts;                                           \
+  LPFN_ACCEPTEX func_acceptex;
+
+#define uv_tcp_connection_fields                                              \
+  uv_buf_t read_buffer;                                                       \
+  LPFN_CONNECTEX func_connectex;
+
+#define UV_TCP_PRIVATE_FIELDS                                                 \
+  SOCKET socket;                                                              \
+  int delayed_error;                                                          \
+  union {                                                                     \
+    struct { uv_tcp_server_fields } serv;                                     \
+    struct { uv_tcp_connection_fields } conn;                                 \
+  } tcp;
+
+#define UV_UDP_PRIVATE_FIELDS                                                 \
+  SOCKET socket;                                                              \
+  unsigned int reqs_pending;                                                  \
+  int activecnt;                                                              \
+  uv_req_t recv_req;                                                          \
+  uv_buf_t recv_buffer;                                                       \
+  struct sockaddr_storage recv_from;                                          \
+  int recv_from_len;                                                          \
+  uv_udp_recv_cb recv_cb;                                                     \
+  uv_alloc_cb alloc_cb;                                                       \
+  LPFN_WSARECV func_wsarecv;                                                  \
+  LPFN_WSARECVFROM func_wsarecvfrom;
+
+#define uv_pipe_server_fields                                                 \
+  int pending_instances;                                                      \
+  uv_pipe_accept_t* accept_reqs;                                              \
+  uv_pipe_accept_t* pending_accepts;
+
+#define uv_pipe_connection_fields                                             \
+  uv_timer_t* eof_timer;                                                      \
+  uv_write_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \
+  DWORD ipc_remote_pid;                                                       \
+  union {                                                                     \
+    uint32_t payload_remaining;                                               \
+    uint64_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \
+  } ipc_data_frame;                                                           \
+  void* ipc_xfer_queue[2];                                                    \
+  int ipc_xfer_queue_length;                                                  \
+  uv_write_t* non_overlapped_writes_tail;                                     \
+  CRITICAL_SECTION readfile_thread_lock;                                      \
+  volatile HANDLE readfile_thread_handle;
+
+#define UV_PIPE_PRIVATE_FIELDS                                                \
+  HANDLE handle;                                                              \
+  WCHAR* name;                                                                \
+  union {                                                                     \
+    struct { uv_pipe_server_fields } serv;                                    \
+    struct { uv_pipe_connection_fields } conn;                                \
+  } pipe;
+
+/* TODO: put the parser states in an union - TTY handles are always half-duplex
+ * so read-state can safely overlap write-state. */
+#define UV_TTY_PRIVATE_FIELDS                                                 \
+  HANDLE handle;                                                              \
+  union {                                                                     \
+    struct {                                                                  \
+      /* Used for readable TTY handles */                                     \
+      /* TODO: remove me in v2.x. */                                          \
+      HANDLE unused_;                                                         \
+      uv_buf_t read_line_buffer;                                              \
+      HANDLE read_raw_wait;                                                   \
+      /* Fields used for translating win keystrokes into vt100 characters */  \
+      char last_key[8];                                                       \
+      unsigned char last_key_offset;                                          \
+      unsigned char last_key_len;                                             \
+      WCHAR last_utf16_high_surrogate;                                        \
+      INPUT_RECORD last_input_record;                                         \
+    } rd;                                                                     \
+    struct {                                                                  \
+      /* Used for writable TTY handles */                                     \
+      /* utf8-to-utf16 conversion state */                                    \
+      unsigned int utf8_codepoint;                                            \
+      unsigned char utf8_bytes_left;                                          \
+      /* eol conversion state */                                              \
+      unsigned char previous_eol;                                             \
+      /* ansi parser state */                                                 \
+      unsigned short ansi_parser_state;                                       \
+      unsigned char ansi_csi_argc;                                            \
+      unsigned short ansi_csi_argv[4];                                        \
+      COORD saved_position;                                                   \
+      WORD saved_attributes;                                                  \
+    } wr;                                                                     \
+  } tty;
+
+#define UV_POLL_PRIVATE_FIELDS                                                \
+  SOCKET socket;                                                              \
+  /* Used in fast mode */                                                     \
+  SOCKET peer_socket;                                                         \
+  AFD_POLL_INFO afd_poll_info_1;                                              \
+  AFD_POLL_INFO afd_poll_info_2;                                              \
+  /* Used in fast and slow mode. */                                           \
+  uv_req_t poll_req_1;                                                        \
+  uv_req_t poll_req_2;                                                        \
+  unsigned char submitted_events_1;                                           \
+  unsigned char submitted_events_2;                                           \
+  unsigned char mask_events_1;                                                \
+  unsigned char mask_events_2;                                                \
+  unsigned char events;
+
+#define UV_TIMER_PRIVATE_FIELDS                                               \
+  void* heap_node[3];                                                         \
+  int unused;                                                                 \
+  uint64_t timeout;                                                           \
+  uint64_t repeat;                                                            \
+  uint64_t start_id;                                                          \
+  uv_timer_cb timer_cb;
+
+#define UV_ASYNC_PRIVATE_FIELDS                                               \
+  struct uv_req_s async_req;                                                  \
+  uv_async_cb async_cb;                                                       \
+  /* char to avoid alignment issues */                                        \
+  char volatile async_sent;
+
+#define UV_PREPARE_PRIVATE_FIELDS                                             \
+  uv_prepare_t* prepare_prev;                                                 \
+  uv_prepare_t* prepare_next;                                                 \
+  uv_prepare_cb prepare_cb;
+
+#define UV_CHECK_PRIVATE_FIELDS                                               \
+  uv_check_t* check_prev;                                                     \
+  uv_check_t* check_next;                                                     \
+  uv_check_cb check_cb;
+
+#define UV_IDLE_PRIVATE_FIELDS                                                \
+  uv_idle_t* idle_prev;                                                       \
+  uv_idle_t* idle_next;                                                       \
+  uv_idle_cb idle_cb;
+
+#define UV_HANDLE_PRIVATE_FIELDS                                              \
+  uv_handle_t* endgame_next;                                                  \
+  unsigned int flags;
+
+#define UV_GETADDRINFO_PRIVATE_FIELDS                                         \
+  struct uv__work work_req;                                                   \
+  uv_getaddrinfo_cb getaddrinfo_cb;                                           \
+  void* alloc;                                                                \
+  WCHAR* node;                                                                \
+  WCHAR* service;                                                             \
+  /* The addrinfoW field is used to store a pointer to the hints, and    */   \
+  /* later on to store the result of GetAddrInfoW. The final result will */   \
+  /* be converted to struct addrinfo* and stored in the addrinfo field.  */   \
+  struct addrinfoW* addrinfow;                                                \
+  struct addrinfo* addrinfo;                                                  \
+  int retcode;
+
+#define UV_GETNAMEINFO_PRIVATE_FIELDS                                         \
+  struct uv__work work_req;                                                   \
+  uv_getnameinfo_cb getnameinfo_cb;                                           \
+  struct sockaddr_storage storage;                                            \
+  int flags;                                                                  \
+  char host[NI_MAXHOST];                                                      \
+  char service[NI_MAXSERV];                                                   \
+  int retcode;
+
+#define UV_PROCESS_PRIVATE_FIELDS                                             \
+  struct uv_process_exit_s {                                                  \
+    UV_REQ_FIELDS                                                             \
+  } exit_req;                                                                 \
+  BYTE* child_stdio_buffer;                                                   \
+  int exit_signal;                                                            \
+  HANDLE wait_handle;                                                         \
+  HANDLE process_handle;                                                      \
+  volatile char exit_cb_pending;
+
+#define UV_FS_PRIVATE_FIELDS                                                  \
+  struct uv__work work_req;                                                   \
+  int flags;                                                                  \
+  DWORD sys_errno_;                                                           \
+  union {                                                                     \
+    /* TODO: remove me in 0.9. */                                             \
+    WCHAR* pathw;                                                             \
+    int fd;                                                                   \
+  } file;                                                                     \
+  union {                                                                     \
+    struct {                                                                  \
+      int mode;                                                               \
+      WCHAR* new_pathw;                                                       \
+      int file_flags;                                                         \
+      int fd_out;                                                             \
+      unsigned int nbufs;                                                     \
+      uv_buf_t* bufs;                                                         \
+      int64_t offset;                                                         \
+      uv_buf_t bufsml[4];                                                     \
+    } info;                                                                   \
+    struct {                                                                  \
+      double atime;                                                           \
+      double mtime;                                                           \
+    } time;                                                                   \
+  } fs;
+
+#define UV_WORK_PRIVATE_FIELDS                                                \
+  struct uv__work work_req;
+
+#define UV_FS_EVENT_PRIVATE_FIELDS                                            \
+  struct uv_fs_event_req_s {                                                  \
+    UV_REQ_FIELDS                                                             \
+  } req;                                                                      \
+  HANDLE dir_handle;                                                          \
+  int req_pending;                                                            \
+  uv_fs_event_cb cb;                                                          \
+  WCHAR* filew;                                                               \
+  WCHAR* short_filew;                                                         \
+  WCHAR* dirw;                                                                \
+  char* buffer;
+
+#define UV_SIGNAL_PRIVATE_FIELDS                                              \
+  RB_ENTRY(uv_signal_s) tree_entry;                                           \
+  struct uv_req_s signal_req;                                                 \
+  unsigned long pending_signum;
+
+#ifndef F_OK
+#define F_OK 0
+#endif
+#ifndef R_OK
+#define R_OK 4
+#endif
+#ifndef W_OK
+#define W_OK 2
+#endif
+#ifndef X_OK
+#define X_OK 1
+#endif
+
+/* fs open() flags supported on this platform: */
+#define UV_FS_O_APPEND       _O_APPEND
+#define UV_FS_O_CREAT        _O_CREAT
+#define UV_FS_O_EXCL         _O_EXCL
+#define UV_FS_O_FILEMAP      0x20000000
+#define UV_FS_O_RANDOM       _O_RANDOM
+#define UV_FS_O_RDONLY       _O_RDONLY
+#define UV_FS_O_RDWR         _O_RDWR
+#define UV_FS_O_SEQUENTIAL   _O_SEQUENTIAL
+#define UV_FS_O_SHORT_LIVED  _O_SHORT_LIVED
+#define UV_FS_O_TEMPORARY    _O_TEMPORARY
+#define UV_FS_O_TRUNC        _O_TRUNC
+#define UV_FS_O_WRONLY       _O_WRONLY
+
+/* fs open() flags supported on other platforms (or mapped on this platform): */
+#define UV_FS_O_DIRECT       0x02000000 /* FILE_FLAG_NO_BUFFERING */
+#define UV_FS_O_DIRECTORY    0
+#define UV_FS_O_DSYNC        0x04000000 /* FILE_FLAG_WRITE_THROUGH */
+#define UV_FS_O_EXLOCK       0x10000000 /* EXCLUSIVE SHARING MODE */
+#define UV_FS_O_NOATIME      0
+#define UV_FS_O_NOCTTY       0
+#define UV_FS_O_NOFOLLOW     0
+#define UV_FS_O_NONBLOCK     0
+#define UV_FS_O_SYMLINK      0
+#define UV_FS_O_SYNC         0x08000000 /* FILE_FLAG_WRITE_THROUGH */
diff --git a/third_party/libuv/src/fs-poll.c b/third_party/libuv/src/fs-poll.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/fs-poll.c
@@ -0,0 +1,287 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv-common.h"
+
+#ifdef _WIN32
+#include "win/internal.h"
+#include "win/handle-inl.h"
+#define uv__make_close_pending(h) uv_want_endgame((h)->loop, (h))
+#else
+#include "unix/internal.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct poll_ctx {
+  uv_fs_poll_t* parent_handle;
+  int busy_polling;
+  unsigned int interval;
+  uint64_t start_time;
+  uv_loop_t* loop;
+  uv_fs_poll_cb poll_cb;
+  uv_timer_t timer_handle;
+  uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
+  uv_stat_t statbuf;
+  struct poll_ctx* previous; /* context from previous start()..stop() period */
+  char path[1]; /* variable length */
+};
+
+static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b);
+static void poll_cb(uv_fs_t* req);
+static void timer_cb(uv_timer_t* timer);
+static void timer_close_cb(uv_handle_t* handle);
+
+static uv_stat_t zero_statbuf;
+
+
+int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
+  handle->poll_ctx = NULL;
+  return 0;
+}
+
+
+int uv_fs_poll_start(uv_fs_poll_t* handle,
+                     uv_fs_poll_cb cb,
+                     const char* path,
+                     unsigned int interval) {
+  struct poll_ctx* ctx;
+  uv_loop_t* loop;
+  size_t len;
+  int err;
+
+  if (uv_is_active((uv_handle_t*)handle))
+    return 0;
+
+  loop = handle->loop;
+  len = strlen(path);
+  ctx = uv__calloc(1, sizeof(*ctx) + len);
+
+  if (ctx == NULL)
+    return UV_ENOMEM;
+
+  ctx->loop = loop;
+  ctx->poll_cb = cb;
+  ctx->interval = interval ? interval : 1;
+  ctx->start_time = uv_now(loop);
+  ctx->parent_handle = handle;
+  memcpy(ctx->path, path, len + 1);
+
+  err = uv_timer_init(loop, &ctx->timer_handle);
+  if (err < 0)
+    goto error;
+
+  ctx->timer_handle.flags |= UV_HANDLE_INTERNAL;
+  uv__handle_unref(&ctx->timer_handle);
+
+  err = uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb);
+  if (err < 0)
+    goto error;
+
+  if (handle->poll_ctx != NULL)
+    ctx->previous = handle->poll_ctx;
+  handle->poll_ctx = ctx;
+  uv__handle_start(handle);
+
+  return 0;
+
+error:
+  uv__free(ctx);
+  return err;
+}
+
+
+int uv_fs_poll_stop(uv_fs_poll_t* handle) {
+  struct poll_ctx* ctx;
+
+  if (!uv_is_active((uv_handle_t*)handle))
+    return 0;
+
+  ctx = handle->poll_ctx;
+  assert(ctx != NULL);
+  assert(ctx->parent_handle == handle);
+
+  /* Close the timer if it's active. If it's inactive, there's a stat request
+   * in progress and poll_cb will take care of the cleanup.
+   */
+  if (uv_is_active((uv_handle_t*)&ctx->timer_handle))
+    uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
+
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
+  struct poll_ctx* ctx;
+  size_t required_len;
+
+  if (!uv_is_active((uv_handle_t*)handle)) {
+    *size = 0;
+    return UV_EINVAL;
+  }
+
+  ctx = handle->poll_ctx;
+  assert(ctx != NULL);
+
+  required_len = strlen(ctx->path);
+  if (required_len >= *size) {
+    *size = required_len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, ctx->path, required_len);
+  *size = required_len;
+  buffer[required_len] = '\0';
+
+  return 0;
+}
+
+
+void uv__fs_poll_close(uv_fs_poll_t* handle) {
+  uv_fs_poll_stop(handle);
+
+  if (handle->poll_ctx == NULL)
+    uv__make_close_pending((uv_handle_t*)handle);
+}
+
+
+static void timer_cb(uv_timer_t* timer) {
+  struct poll_ctx* ctx;
+
+  ctx = container_of(timer, struct poll_ctx, timer_handle);
+  assert(ctx->parent_handle != NULL);
+  assert(ctx->parent_handle->poll_ctx == ctx);
+  ctx->start_time = uv_now(ctx->loop);
+
+  if (uv_fs_stat(ctx->loop, &ctx->fs_req, ctx->path, poll_cb))
+    abort();
+}
+
+
+static void poll_cb(uv_fs_t* req) {
+  uv_stat_t* statbuf;
+  struct poll_ctx* ctx;
+  uint64_t interval;
+  uv_fs_poll_t* handle;
+
+  ctx = container_of(req, struct poll_ctx, fs_req);
+  handle = ctx->parent_handle;
+
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle))
+    goto out;
+
+  if (req->result != 0) {
+    if (ctx->busy_polling != req->result) {
+      ctx->poll_cb(ctx->parent_handle,
+                   req->result,
+                   &ctx->statbuf,
+                   &zero_statbuf);
+      ctx->busy_polling = req->result;
+    }
+    goto out;
+  }
+
+  statbuf = &req->statbuf;
+
+  if (ctx->busy_polling != 0)
+    if (ctx->busy_polling < 0 || !statbuf_eq(&ctx->statbuf, statbuf))
+      ctx->poll_cb(ctx->parent_handle, 0, &ctx->statbuf, statbuf);
+
+  ctx->statbuf = *statbuf;
+  ctx->busy_polling = 1;
+
+out:
+  uv_fs_req_cleanup(req);
+
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) {
+    uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
+    return;
+  }
+
+  /* Reschedule timer, subtract the delay from doing the stat(). */
+  interval = ctx->interval;
+  interval -= (uv_now(ctx->loop) - ctx->start_time) % interval;
+
+  if (uv_timer_start(&ctx->timer_handle, timer_cb, interval, 0))
+    abort();
+}
+
+
+static void timer_close_cb(uv_handle_t* timer) {
+  struct poll_ctx* ctx;
+  struct poll_ctx* it;
+  struct poll_ctx* last;
+  uv_fs_poll_t* handle;
+
+  ctx = container_of(timer, struct poll_ctx, timer_handle);
+  handle = ctx->parent_handle;
+  if (ctx == handle->poll_ctx) {
+    handle->poll_ctx = ctx->previous;
+    if (handle->poll_ctx == NULL && uv__is_closing(handle))
+      uv__make_close_pending((uv_handle_t*)handle);
+  } else {
+    for (last = handle->poll_ctx, it = last->previous;
+         it != ctx;
+         last = it, it = it->previous) {
+      assert(last->previous != NULL);
+    }
+    last->previous = ctx->previous;
+  }
+  uv__free(ctx);
+}
+
+
+static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) {
+  return a->st_ctim.tv_nsec == b->st_ctim.tv_nsec
+      && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec
+      && a->st_birthtim.tv_nsec == b->st_birthtim.tv_nsec
+      && a->st_ctim.tv_sec == b->st_ctim.tv_sec
+      && a->st_mtim.tv_sec == b->st_mtim.tv_sec
+      && a->st_birthtim.tv_sec == b->st_birthtim.tv_sec
+      && a->st_size == b->st_size
+      && a->st_mode == b->st_mode
+      && a->st_uid == b->st_uid
+      && a->st_gid == b->st_gid
+      && a->st_ino == b->st_ino
+      && a->st_dev == b->st_dev
+      && a->st_flags == b->st_flags
+      && a->st_gen == b->st_gen;
+}
+
+
+#if defined(_WIN32)
+
+#include "win/internal.h"
+#include "win/handle-inl.h"
+
+void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  uv__handle_close(handle);
+}
+
+#endif /* _WIN32 */
diff --git a/third_party/libuv/src/heap-inl.h b/third_party/libuv/src/heap-inl.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/heap-inl.h
@@ -0,0 +1,245 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UV_SRC_HEAP_H_
+#define UV_SRC_HEAP_H_
+
+#include <stddef.h>  /* NULL */
+
+#if defined(__GNUC__)
+# define HEAP_EXPORT(declaration) __attribute__((unused)) static declaration
+#else
+# define HEAP_EXPORT(declaration) static declaration
+#endif
+
+struct heap_node {
+  struct heap_node* left;
+  struct heap_node* right;
+  struct heap_node* parent;
+};
+
+/* A binary min heap.  The usual properties hold: the root is the lowest
+ * element in the set, the height of the tree is at most log2(nodes) and
+ * it's always a complete binary tree.
+ *
+ * The heap function try hard to detect corrupted tree nodes at the cost
+ * of a minor reduction in performance.  Compile with -DNDEBUG to disable.
+ */
+struct heap {
+  struct heap_node* min;
+  unsigned int nelts;
+};
+
+/* Return non-zero if a < b. */
+typedef int (*heap_compare_fn)(const struct heap_node* a,
+                               const struct heap_node* b);
+
+/* Public functions. */
+HEAP_EXPORT(void heap_init(struct heap* heap));
+HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap));
+HEAP_EXPORT(void heap_insert(struct heap* heap,
+                             struct heap_node* newnode,
+                             heap_compare_fn less_than));
+HEAP_EXPORT(void heap_remove(struct heap* heap,
+                             struct heap_node* node,
+                             heap_compare_fn less_than));
+HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than));
+
+/* Implementation follows. */
+
+HEAP_EXPORT(void heap_init(struct heap* heap)) {
+  heap->min = NULL;
+  heap->nelts = 0;
+}
+
+HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)) {
+  return heap->min;
+}
+
+/* Swap parent with child. Child moves closer to the root, parent moves away. */
+static void heap_node_swap(struct heap* heap,
+                           struct heap_node* parent,
+                           struct heap_node* child) {
+  struct heap_node* sibling;
+  struct heap_node t;
+
+  t = *parent;
+  *parent = *child;
+  *child = t;
+
+  parent->parent = child;
+  if (child->left == child) {
+    child->left = parent;
+    sibling = child->right;
+  } else {
+    child->right = parent;
+    sibling = child->left;
+  }
+  if (sibling != NULL)
+    sibling->parent = child;
+
+  if (parent->left != NULL)
+    parent->left->parent = parent;
+  if (parent->right != NULL)
+    parent->right->parent = parent;
+
+  if (child->parent == NULL)
+    heap->min = child;
+  else if (child->parent->left == parent)
+    child->parent->left = child;
+  else
+    child->parent->right = child;
+}
+
+HEAP_EXPORT(void heap_insert(struct heap* heap,
+                             struct heap_node* newnode,
+                             heap_compare_fn less_than)) {
+  struct heap_node** parent;
+  struct heap_node** child;
+  unsigned int path;
+  unsigned int n;
+  unsigned int k;
+
+  newnode->left = NULL;
+  newnode->right = NULL;
+  newnode->parent = NULL;
+
+  /* Calculate the path from the root to the insertion point.  This is a min
+   * heap so we always insert at the left-most free node of the bottom row.
+   */
+  path = 0;
+  for (k = 0, n = 1 + heap->nelts; n >= 2; k += 1, n /= 2)
+    path = (path << 1) | (n & 1);
+
+  /* Now traverse the heap using the path we calculated in the previous step. */
+  parent = child = &heap->min;
+  while (k > 0) {
+    parent = child;
+    if (path & 1)
+      child = &(*child)->right;
+    else
+      child = &(*child)->left;
+    path >>= 1;
+    k -= 1;
+  }
+
+  /* Insert the new node. */
+  newnode->parent = *parent;
+  *child = newnode;
+  heap->nelts += 1;
+
+  /* Walk up the tree and check at each node if the heap property holds.
+   * It's a min heap so parent < child must be true.
+   */
+  while (newnode->parent != NULL && less_than(newnode, newnode->parent))
+    heap_node_swap(heap, newnode->parent, newnode);
+}
+
+HEAP_EXPORT(void heap_remove(struct heap* heap,
+                             struct heap_node* node,
+                             heap_compare_fn less_than)) {
+  struct heap_node* smallest;
+  struct heap_node** max;
+  struct heap_node* child;
+  unsigned int path;
+  unsigned int k;
+  unsigned int n;
+
+  if (heap->nelts == 0)
+    return;
+
+  /* Calculate the path from the min (the root) to the max, the left-most node
+   * of the bottom row.
+   */
+  path = 0;
+  for (k = 0, n = heap->nelts; n >= 2; k += 1, n /= 2)
+    path = (path << 1) | (n & 1);
+
+  /* Now traverse the heap using the path we calculated in the previous step. */
+  max = &heap->min;
+  while (k > 0) {
+    if (path & 1)
+      max = &(*max)->right;
+    else
+      max = &(*max)->left;
+    path >>= 1;
+    k -= 1;
+  }
+
+  heap->nelts -= 1;
+
+  /* Unlink the max node. */
+  child = *max;
+  *max = NULL;
+
+  if (child == node) {
+    /* We're removing either the max or the last node in the tree. */
+    if (child == heap->min) {
+      heap->min = NULL;
+    }
+    return;
+  }
+
+  /* Replace the to be deleted node with the max node. */
+  child->left = node->left;
+  child->right = node->right;
+  child->parent = node->parent;
+
+  if (child->left != NULL) {
+    child->left->parent = child;
+  }
+
+  if (child->right != NULL) {
+    child->right->parent = child;
+  }
+
+  if (node->parent == NULL) {
+    heap->min = child;
+  } else if (node->parent->left == node) {
+    node->parent->left = child;
+  } else {
+    node->parent->right = child;
+  }
+
+  /* Walk down the subtree and check at each node if the heap property holds.
+   * It's a min heap so parent < child must be true.  If the parent is bigger,
+   * swap it with the smallest child.
+   */
+  for (;;) {
+    smallest = child;
+    if (child->left != NULL && less_than(child->left, smallest))
+      smallest = child->left;
+    if (child->right != NULL && less_than(child->right, smallest))
+      smallest = child->right;
+    if (smallest == child)
+      break;
+    heap_node_swap(heap, child, smallest);
+  }
+
+  /* Walk up the subtree and check that each parent is less than the node
+   * this is required, because `max` node is not guaranteed to be the
+   * actual maximum in tree
+   */
+  while (child->parent != NULL && less_than(child, child->parent))
+    heap_node_swap(heap, child->parent, child);
+}
+
+HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)) {
+  heap_remove(heap, heap->min, less_than);
+}
+
+#undef HEAP_EXPORT
+
+#endif  /* UV_SRC_HEAP_H_ */
diff --git a/third_party/libuv/src/idna.c b/third_party/libuv/src/idna.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/idna.c
@@ -0,0 +1,291 @@
+/* Copyright (c) 2011, 2018 Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Derived from https://github.com/bnoordhuis/punycode
+ * but updated to support IDNA 2008.
+ */
+
+#include "uv.h"
+#include "idna.h"
+#include <string.h>
+
+static unsigned uv__utf8_decode1_slow(const char** p,
+                                      const char* pe,
+                                      unsigned a) {
+  unsigned b;
+  unsigned c;
+  unsigned d;
+  unsigned min;
+
+  if (a > 0xF7)
+    return -1;
+
+  switch (*p - pe) {
+  default:
+    if (a > 0xEF) {
+      min = 0x10000;
+      a = a & 7;
+      b = (unsigned char) *(*p)++;
+      c = (unsigned char) *(*p)++;
+      d = (unsigned char) *(*p)++;
+      break;
+    }
+    /* Fall through. */
+  case 2:
+    if (a > 0xDF) {
+      min = 0x800;
+      b = 0x80 | (a & 15);
+      c = (unsigned char) *(*p)++;
+      d = (unsigned char) *(*p)++;
+      a = 0;
+      break;
+    }
+    /* Fall through. */
+  case 1:
+    if (a > 0xBF) {
+      min = 0x80;
+      b = 0x80;
+      c = 0x80 | (a & 31);
+      d = (unsigned char) *(*p)++;
+      a = 0;
+      break;
+    }
+    return -1;  /* Invalid continuation byte. */
+  }
+
+  if (0x80 != (0xC0 & (b ^ c ^ d)))
+    return -1;  /* Invalid sequence. */
+
+  b &= 63;
+  c &= 63;
+  d &= 63;
+  a = (a << 18) | (b << 12) | (c << 6) | d;
+
+  if (a < min)
+    return -1;  /* Overlong sequence. */
+
+  if (a > 0x10FFFF)
+    return -1;  /* Four-byte sequence > U+10FFFF. */
+
+  if (a >= 0xD800 && a <= 0xDFFF)
+    return -1;  /* Surrogate pair. */
+
+  return a;
+}
+
+unsigned uv__utf8_decode1(const char** p, const char* pe) {
+  unsigned a;
+
+  a = (unsigned char) *(*p)++;
+
+  if (a < 128)
+    return a;  /* ASCII, common case. */
+
+  return uv__utf8_decode1_slow(p, pe, a);
+}
+
+#define foreach_codepoint(c, p, pe) \
+  for (; (void) (*p <= pe && (c = uv__utf8_decode1(p, pe))), *p <= pe;)
+
+static int uv__idna_toascii_label(const char* s, const char* se,
+                                  char** d, char* de) {
+  static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+  const char* ss;
+  unsigned c;
+  unsigned h;
+  unsigned k;
+  unsigned n;
+  unsigned m;
+  unsigned q;
+  unsigned t;
+  unsigned x;
+  unsigned y;
+  unsigned bias;
+  unsigned delta;
+  unsigned todo;
+  int first;
+
+  h = 0;
+  ss = s;
+  todo = 0;
+
+  foreach_codepoint(c, &s, se) {
+    if (c < 128)
+      h++;
+    else if (c == (unsigned) -1)
+      return UV_EINVAL;
+    else
+      todo++;
+  }
+
+  if (todo > 0) {
+    if (*d < de) *(*d)++ = 'x';
+    if (*d < de) *(*d)++ = 'n';
+    if (*d < de) *(*d)++ = '-';
+    if (*d < de) *(*d)++ = '-';
+  }
+
+  x = 0;
+  s = ss;
+  foreach_codepoint(c, &s, se) {
+    if (c > 127)
+      continue;
+
+    if (*d < de)
+      *(*d)++ = c;
+
+    if (++x == h)
+      break;  /* Visited all ASCII characters. */
+  }
+
+  if (todo == 0)
+    return h;
+
+  /* Only write separator when we've written ASCII characters first. */
+  if (h > 0)
+    if (*d < de)
+      *(*d)++ = '-';
+
+  n = 128;
+  bias = 72;
+  delta = 0;
+  first = 1;
+
+  while (todo > 0) {
+    m = -1;
+    s = ss;
+    foreach_codepoint(c, &s, se)
+      if (c >= n)
+        if (c < m)
+          m = c;
+
+    x = m - n;
+    y = h + 1;
+
+    if (x > ~delta / y)
+      return UV_E2BIG;  /* Overflow. */
+
+    delta += x * y;
+    n = m;
+
+    s = ss;
+    foreach_codepoint(c, &s, se) {
+      if (c < n)
+        if (++delta == 0)
+          return UV_E2BIG;  /* Overflow. */
+
+      if (c != n)
+        continue;
+
+      for (k = 36, q = delta; /* empty */; k += 36) {
+        t = 1;
+
+        if (k > bias)
+          t = k - bias;
+
+        if (t > 26)
+          t = 26;
+
+        if (q < t)
+          break;
+
+        /* TODO(bnoordhuis) Since 1 <= t <= 26 and therefore
+         * 10 <= y <= 35, we can optimize the long division
+         * into a table-based reciprocal multiplication.
+         */
+        x = q - t;
+        y = 36 - t;  /* 10 <= y <= 35 since 1 <= t <= 26. */
+        q = x / y;
+        t = t + x % y;  /* 1 <= t <= 35 because of y. */
+
+        if (*d < de)
+          *(*d)++ = alphabet[t];
+      }
+
+      if (*d < de)
+        *(*d)++ = alphabet[q];
+
+      delta /= 2;
+
+      if (first) {
+        delta /= 350;
+        first = 0;
+      }
+
+      /* No overflow check is needed because |delta| was just
+       * divided by 2 and |delta+delta >= delta + delta/h|.
+       */
+      h++;
+      delta += delta / h;
+
+      for (bias = 0; delta > 35 * 26 / 2; bias += 36)
+        delta /= 35;
+
+      bias += 36 * delta / (delta + 38);
+      delta = 0;
+      todo--;
+    }
+
+    delta++;
+    n++;
+  }
+
+  return 0;
+}
+
+#undef foreach_codepoint
+
+long uv__idna_toascii(const char* s, const char* se, char* d, char* de) {
+  const char* si;
+  const char* st;
+  unsigned c;
+  char* ds;
+  int rc;
+
+  ds = d;
+
+  for (si = s; si < se; /* empty */) {
+    st = si;
+    c = uv__utf8_decode1(&si, se);
+
+    if (c != '.')
+      if (c != 0x3002)  /* 。 */
+        if (c != 0xFF0E)  /* ． */
+          if (c != 0xFF61)  /* ｡ */
+            continue;
+
+    rc = uv__idna_toascii_label(s, st, &d, de);
+
+    if (rc < 0)
+      return rc;
+
+    if (d < de)
+      *d++ = '.';
+
+    s = si;
+  }
+
+  if (s < se) {
+    rc = uv__idna_toascii_label(s, se, &d, de);
+
+    if (rc < 0)
+      return rc;
+  }
+
+  if (d < de)
+    *d++ = '\0';
+
+  return d - ds;  /* Number of bytes written. */
+}
diff --git a/third_party/libuv/src/idna.h b/third_party/libuv/src/idna.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/idna.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2011, 2018 Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UV_SRC_IDNA_H_
+#define UV_SRC_IDNA_H_
+
+/* Decode a single codepoint. Returns the codepoint or UINT32_MAX on error.
+ * |p| is updated on success _and_ error, i.e., bad multi-byte sequences are
+ * skipped in their entirety, not just the first bad byte.
+ */
+unsigned uv__utf8_decode1(const char** p, const char* pe);
+
+/* Convert a UTF-8 domain name to IDNA 2008 / Punycode. A return value >= 0
+ * is the number of bytes written to |d|, including the trailing nul byte.
+ * A return value < 0 is a libuv error code. |s| and |d| can not overlap.
+ */
+long uv__idna_toascii(const char* s, const char* se, char* d, char* de);
+
+#endif  /* UV_SRC_IDNA_H_ */
diff --git a/third_party/libuv/src/inet.c b/third_party/libuv/src/inet.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/inet.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#include "uv.h"
+#include "uv-common.h"
+
+#define UV__INET_ADDRSTRLEN         16
+#define UV__INET6_ADDRSTRLEN        46
+
+
+static int inet_ntop4(const unsigned char *src, char *dst, size_t size);
+static int inet_ntop6(const unsigned char *src, char *dst, size_t size);
+static int inet_pton4(const char *src, unsigned char *dst);
+static int inet_pton6(const char *src, unsigned char *dst);
+
+
+int uv_inet_ntop(int af, const void* src, char* dst, size_t size) {
+  switch (af) {
+  case AF_INET:
+    return (inet_ntop4(src, dst, size));
+  case AF_INET6:
+    return (inet_ntop6(src, dst, size));
+  default:
+    return UV_EAFNOSUPPORT;
+  }
+  /* NOTREACHED */
+}
+
+
+static int inet_ntop4(const unsigned char *src, char *dst, size_t size) {
+  static const char fmt[] = "%u.%u.%u.%u";
+  char tmp[UV__INET_ADDRSTRLEN];
+  int l;
+
+  l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
+  if (l <= 0 || (size_t) l >= size) {
+    return UV_ENOSPC;
+  }
+  uv__strscpy(dst, tmp, size);
+  return 0;
+}
+
+
+static int inet_ntop6(const unsigned char *src, char *dst, size_t size) {
+  /*
+   * Note that int32_t and int16_t need only be "at least" large enough
+   * to contain a value of the specified size.  On some systems, like
+   * Crays, there is no such thing as an integer variable with 16 bits.
+   * Keep this in mind if you think this function should have been coded
+   * to use pointer overlays.  All the world's not a VAX.
+   */
+  char tmp[UV__INET6_ADDRSTRLEN], *tp;
+  struct { int base, len; } best, cur;
+  unsigned int words[sizeof(struct in6_addr) / sizeof(uint16_t)];
+  int i;
+
+  /*
+   * Preprocess:
+   *  Copy the input (bytewise) array into a wordwise array.
+   *  Find the longest run of 0x00's in src[] for :: shorthanding.
+   */
+  memset(words, '\0', sizeof words);
+  for (i = 0; i < (int) sizeof(struct in6_addr); i++)
+    words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+  best.base = -1;
+  best.len = 0;
+  cur.base = -1;
+  cur.len = 0;
+  for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
+    if (words[i] == 0) {
+      if (cur.base == -1)
+        cur.base = i, cur.len = 1;
+      else
+        cur.len++;
+    } else {
+      if (cur.base != -1) {
+        if (best.base == -1 || cur.len > best.len)
+          best = cur;
+        cur.base = -1;
+      }
+    }
+  }
+  if (cur.base != -1) {
+    if (best.base == -1 || cur.len > best.len)
+      best = cur;
+  }
+  if (best.base != -1 && best.len < 2)
+    best.base = -1;
+
+  /*
+   * Format the result.
+   */
+  tp = tmp;
+  for (i = 0; i < (int) ARRAY_SIZE(words); i++) {
+    /* Are we inside the best run of 0x00's? */
+    if (best.base != -1 && i >= best.base &&
+        i < (best.base + best.len)) {
+      if (i == best.base)
+        *tp++ = ':';
+      continue;
+    }
+    /* Are we following an initial run of 0x00s or any real hex? */
+    if (i != 0)
+      *tp++ = ':';
+    /* Is this address an encapsulated IPv4? */
+    if (i == 6 && best.base == 0 && (best.len == 6 ||
+        (best.len == 7 && words[7] != 0x0001) ||
+        (best.len == 5 && words[5] == 0xffff))) {
+      int err = inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp));
+      if (err)
+        return err;
+      tp += strlen(tp);
+      break;
+    }
+    tp += sprintf(tp, "%x", words[i]);
+  }
+  /* Was it a trailing run of 0x00's? */
+  if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words))
+    *tp++ = ':';
+  *tp++ = '\0';
+  if (UV_E2BIG == uv__strscpy(dst, tmp, size))
+    return UV_ENOSPC;
+  return 0;
+}
+
+
+int uv_inet_pton(int af, const char* src, void* dst) {
+  if (src == NULL || dst == NULL)
+    return UV_EINVAL;
+
+  switch (af) {
+  case AF_INET:
+    return (inet_pton4(src, dst));
+  case AF_INET6: {
+    int len;
+    char tmp[UV__INET6_ADDRSTRLEN], *s, *p;
+    s = (char*) src;
+    p = strchr(src, '%');
+    if (p != NULL) {
+      s = tmp;
+      len = p - src;
+      if (len > UV__INET6_ADDRSTRLEN-1)
+        return UV_EINVAL;
+      memcpy(s, src, len);
+      s[len] = '\0';
+    }
+    return inet_pton6(s, dst);
+  }
+  default:
+    return UV_EAFNOSUPPORT;
+  }
+  /* NOTREACHED */
+}
+
+
+static int inet_pton4(const char *src, unsigned char *dst) {
+  static const char digits[] = "0123456789";
+  int saw_digit, octets, ch;
+  unsigned char tmp[sizeof(struct in_addr)], *tp;
+
+  saw_digit = 0;
+  octets = 0;
+  *(tp = tmp) = 0;
+  while ((ch = *src++) != '\0') {
+    const char *pch;
+
+    if ((pch = strchr(digits, ch)) != NULL) {
+      unsigned int nw = *tp * 10 + (pch - digits);
+
+      if (saw_digit && *tp == 0)
+        return UV_EINVAL;
+      if (nw > 255)
+        return UV_EINVAL;
+      *tp = nw;
+      if (!saw_digit) {
+        if (++octets > 4)
+          return UV_EINVAL;
+        saw_digit = 1;
+      }
+    } else if (ch == '.' && saw_digit) {
+      if (octets == 4)
+        return UV_EINVAL;
+      *++tp = 0;
+      saw_digit = 0;
+    } else
+      return UV_EINVAL;
+  }
+  if (octets < 4)
+    return UV_EINVAL;
+  memcpy(dst, tmp, sizeof(struct in_addr));
+  return 0;
+}
+
+
+static int inet_pton6(const char *src, unsigned char *dst) {
+  static const char xdigits_l[] = "0123456789abcdef",
+                    xdigits_u[] = "0123456789ABCDEF";
+  unsigned char tmp[sizeof(struct in6_addr)], *tp, *endp, *colonp;
+  const char *xdigits, *curtok;
+  int ch, seen_xdigits;
+  unsigned int val;
+
+  memset((tp = tmp), '\0', sizeof tmp);
+  endp = tp + sizeof tmp;
+  colonp = NULL;
+  /* Leading :: requires some special handling. */
+  if (*src == ':')
+    if (*++src != ':')
+      return UV_EINVAL;
+  curtok = src;
+  seen_xdigits = 0;
+  val = 0;
+  while ((ch = *src++) != '\0') {
+    const char *pch;
+
+    if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+      pch = strchr((xdigits = xdigits_u), ch);
+    if (pch != NULL) {
+      val <<= 4;
+      val |= (pch - xdigits);
+      if (++seen_xdigits > 4)
+        return UV_EINVAL;
+      continue;
+    }
+    if (ch == ':') {
+      curtok = src;
+      if (!seen_xdigits) {
+        if (colonp)
+          return UV_EINVAL;
+        colonp = tp;
+        continue;
+      } else if (*src == '\0') {
+        return UV_EINVAL;
+      }
+      if (tp + sizeof(uint16_t) > endp)
+        return UV_EINVAL;
+      *tp++ = (unsigned char) (val >> 8) & 0xff;
+      *tp++ = (unsigned char) val & 0xff;
+      seen_xdigits = 0;
+      val = 0;
+      continue;
+    }
+    if (ch == '.' && ((tp + sizeof(struct in_addr)) <= endp)) {
+      int err = inet_pton4(curtok, tp);
+      if (err == 0) {
+        tp += sizeof(struct in_addr);
+        seen_xdigits = 0;
+        break;  /*%< '\\0' was seen by inet_pton4(). */
+      }
+    }
+    return UV_EINVAL;
+  }
+  if (seen_xdigits) {
+    if (tp + sizeof(uint16_t) > endp)
+      return UV_EINVAL;
+    *tp++ = (unsigned char) (val >> 8) & 0xff;
+    *tp++ = (unsigned char) val & 0xff;
+  }
+  if (colonp != NULL) {
+    /*
+     * Since some memmove()'s erroneously fail to handle
+     * overlapping regions, we'll do the shift by hand.
+     */
+    const int n = tp - colonp;
+    int i;
+
+    if (tp == endp)
+      return UV_EINVAL;
+    for (i = 1; i <= n; i++) {
+      endp[- i] = colonp[n - i];
+      colonp[n - i] = 0;
+    }
+    tp = endp;
+  }
+  if (tp != endp)
+    return UV_EINVAL;
+  memcpy(dst, tmp, sizeof tmp);
+  return 0;
+}
diff --git a/third_party/libuv/src/queue.h b/third_party/libuv/src/queue.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/queue.h
@@ -0,0 +1,108 @@
+/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef QUEUE_H_
+#define QUEUE_H_
+
+#include <stddef.h>
+
+typedef void *QUEUE[2];
+
+/* Private macros. */
+#define QUEUE_NEXT(q)       (*(QUEUE **) &((*(q))[0]))
+#define QUEUE_PREV(q)       (*(QUEUE **) &((*(q))[1]))
+#define QUEUE_PREV_NEXT(q)  (QUEUE_NEXT(QUEUE_PREV(q)))
+#define QUEUE_NEXT_PREV(q)  (QUEUE_PREV(QUEUE_NEXT(q)))
+
+/* Public macros. */
+#define QUEUE_DATA(ptr, type, field)                                          \
+  ((type *) ((char *) (ptr) - offsetof(type, field)))
+
+/* Important note: mutating the list while QUEUE_FOREACH is
+ * iterating over its elements results in undefined behavior.
+ */
+#define QUEUE_FOREACH(q, h)                                                   \
+  for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
+
+#define QUEUE_EMPTY(q)                                                        \
+  ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
+
+#define QUEUE_HEAD(q)                                                         \
+  (QUEUE_NEXT(q))
+
+#define QUEUE_INIT(q)                                                         \
+  do {                                                                        \
+    QUEUE_NEXT(q) = (q);                                                      \
+    QUEUE_PREV(q) = (q);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_ADD(h, n)                                                       \
+  do {                                                                        \
+    QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n);                                       \
+    QUEUE_NEXT_PREV(n) = QUEUE_PREV(h);                                       \
+    QUEUE_PREV(h) = QUEUE_PREV(n);                                            \
+    QUEUE_PREV_NEXT(h) = (h);                                                 \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_SPLIT(h, q, n)                                                  \
+  do {                                                                        \
+    QUEUE_PREV(n) = QUEUE_PREV(h);                                            \
+    QUEUE_PREV_NEXT(n) = (n);                                                 \
+    QUEUE_NEXT(n) = (q);                                                      \
+    QUEUE_PREV(h) = QUEUE_PREV(q);                                            \
+    QUEUE_PREV_NEXT(h) = (h);                                                 \
+    QUEUE_PREV(q) = (n);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_MOVE(h, n)                                                      \
+  do {                                                                        \
+    if (QUEUE_EMPTY(h))                                                       \
+      QUEUE_INIT(n);                                                          \
+    else {                                                                    \
+      QUEUE* q = QUEUE_HEAD(h);                                               \
+      QUEUE_SPLIT(h, q, n);                                                   \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_INSERT_HEAD(h, q)                                               \
+  do {                                                                        \
+    QUEUE_NEXT(q) = QUEUE_NEXT(h);                                            \
+    QUEUE_PREV(q) = (h);                                                      \
+    QUEUE_NEXT_PREV(q) = (q);                                                 \
+    QUEUE_NEXT(h) = (q);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_INSERT_TAIL(h, q)                                               \
+  do {                                                                        \
+    QUEUE_NEXT(q) = (h);                                                      \
+    QUEUE_PREV(q) = QUEUE_PREV(h);                                            \
+    QUEUE_PREV_NEXT(q) = (q);                                                 \
+    QUEUE_PREV(h) = (q);                                                      \
+  }                                                                           \
+  while (0)
+
+#define QUEUE_REMOVE(q)                                                       \
+  do {                                                                        \
+    QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q);                                       \
+    QUEUE_NEXT_PREV(q) = QUEUE_PREV(q);                                       \
+  }                                                                           \
+  while (0)
+
+#endif /* QUEUE_H_ */
diff --git a/third_party/libuv/src/strscpy.c b/third_party/libuv/src/strscpy.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/strscpy.c
@@ -0,0 +1,38 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "strscpy.h"
+#include <limits.h>  /* SSIZE_MAX */
+
+ssize_t uv__strscpy(char* d, const char* s, size_t n) {
+  size_t i;
+
+  for (i = 0; i < n; i++)
+    if ('\0' == (d[i] = s[i]))
+      return i > SSIZE_MAX ? UV_E2BIG : (ssize_t) i;
+
+  if (i == 0)
+    return 0;
+
+  d[--i] = '\0';
+
+  return UV_E2BIG;
+}
diff --git a/third_party/libuv/src/strscpy.h b/third_party/libuv/src/strscpy.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/strscpy.h
@@ -0,0 +1,39 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_STRSCPY_H_
+#define UV_STRSCPY_H_
+
+/* Include uv.h for its definitions of size_t and ssize_t.
+ * size_t can be obtained directly from <stddef.h> but ssize_t requires
+ * some hoop jumping on Windows that I didn't want to duplicate here.
+ */
+#include "uv.h"
+
+/* Copies up to |n-1| bytes from |d| to |s| and always zero-terminates
+ * the result, except when |n==0|. Returns the number of bytes copied
+ * or UV_E2BIG if |d| is too small.
+ *
+ * See https://www.kernel.org/doc/htmldocs/kernel-api/API-strscpy.html
+ */
+ssize_t uv__strscpy(char* d, const char* s, size_t n);
+
+#endif  /* UV_STRSCPY_H_ */
diff --git a/third_party/libuv/src/threadpool.c b/third_party/libuv/src/threadpool.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/threadpool.c
@@ -0,0 +1,388 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv-common.h"
+
+#if !defined(_WIN32)
+# include "unix/internal.h"
+#endif
+
+#include <stdlib.h>
+
+#define MAX_THREADPOOL_SIZE 1024
+
+static uv_once_t once = UV_ONCE_INIT;
+static uv_cond_t cond;
+static uv_mutex_t mutex;
+static unsigned int idle_threads;
+static unsigned int slow_io_work_running;
+static unsigned int nthreads;
+static uv_thread_t* threads;
+static uv_thread_t default_threads[4];
+static QUEUE exit_message;
+static QUEUE wq;
+static QUEUE run_slow_work_message;
+static QUEUE slow_io_pending_wq;
+
+static unsigned int slow_work_thread_threshold(void) {
+  return (nthreads + 1) / 2;
+}
+
+static void uv__cancelled(struct uv__work* w) {
+  abort();
+}
+
+
+/* To avoid deadlock with uv_cancel() it's crucial that the worker
+ * never holds the global mutex and the loop-local mutex at the same time.
+ */
+static void worker(void* arg) {
+  struct uv__work* w;
+  QUEUE* q;
+  int is_slow_work;
+
+  uv_sem_post((uv_sem_t*) arg);
+  arg = NULL;
+
+  uv_mutex_lock(&mutex);
+  for (;;) {
+    /* `mutex` should always be locked at this point. */
+
+    /* Keep waiting while either no work is present or only slow I/O
+       and we're at the threshold for that. */
+    while (QUEUE_EMPTY(&wq) ||
+           (QUEUE_HEAD(&wq) == &run_slow_work_message &&
+            QUEUE_NEXT(&run_slow_work_message) == &wq &&
+            slow_io_work_running >= slow_work_thread_threshold())) {
+      idle_threads += 1;
+      uv_cond_wait(&cond, &mutex);
+      idle_threads -= 1;
+    }
+
+    q = QUEUE_HEAD(&wq);
+    if (q == &exit_message) {
+      uv_cond_signal(&cond);
+      uv_mutex_unlock(&mutex);
+      break;
+    }
+
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);  /* Signal uv_cancel() that the work req is executing. */
+
+    is_slow_work = 0;
+    if (q == &run_slow_work_message) {
+      /* If we're at the slow I/O threshold, re-schedule until after all
+         other work in the queue is done. */
+      if (slow_io_work_running >= slow_work_thread_threshold()) {
+        QUEUE_INSERT_TAIL(&wq, q);
+        continue;
+      }
+
+      /* If we encountered a request to run slow I/O work but there is none
+         to run, that means it's cancelled => Start over. */
+      if (QUEUE_EMPTY(&slow_io_pending_wq))
+        continue;
+
+      is_slow_work = 1;
+      slow_io_work_running++;
+
+      q = QUEUE_HEAD(&slow_io_pending_wq);
+      QUEUE_REMOVE(q);
+      QUEUE_INIT(q);
+
+      /* If there is more slow I/O work, schedule it to be run as well. */
+      if (!QUEUE_EMPTY(&slow_io_pending_wq)) {
+        QUEUE_INSERT_TAIL(&wq, &run_slow_work_message);
+        if (idle_threads > 0)
+          uv_cond_signal(&cond);
+      }
+    }
+
+    uv_mutex_unlock(&mutex);
+
+    w = QUEUE_DATA(q, struct uv__work, wq);
+    w->work(w);
+
+    uv_mutex_lock(&w->loop->wq_mutex);
+    w->work = NULL;  /* Signal uv_cancel() that the work req is done
+                        executing. */
+    QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
+    uv_async_send(&w->loop->wq_async);
+    uv_mutex_unlock(&w->loop->wq_mutex);
+
+    /* Lock `mutex` since that is expected at the start of the next
+     * iteration. */
+    uv_mutex_lock(&mutex);
+    if (is_slow_work) {
+      /* `slow_io_work_running` is protected by `mutex`. */
+      slow_io_work_running--;
+    }
+  }
+}
+
+
+static void post(QUEUE* q, enum uv__work_kind kind) {
+  uv_mutex_lock(&mutex);
+  if (kind == UV__WORK_SLOW_IO) {
+    /* Insert into a separate queue. */
+    QUEUE_INSERT_TAIL(&slow_io_pending_wq, q);
+    if (!QUEUE_EMPTY(&run_slow_work_message)) {
+      /* Running slow I/O tasks is already scheduled => Nothing to do here.
+         The worker that runs said other task will schedule this one as well. */
+      uv_mutex_unlock(&mutex);
+      return;
+    }
+    q = &run_slow_work_message;
+  }
+
+  QUEUE_INSERT_TAIL(&wq, q);
+  if (idle_threads > 0)
+    uv_cond_signal(&cond);
+  uv_mutex_unlock(&mutex);
+}
+
+
+void uv__threadpool_cleanup(void) {
+#ifndef _WIN32
+  unsigned int i;
+
+  if (nthreads == 0)
+    return;
+
+  post(&exit_message, UV__WORK_CPU);
+
+  for (i = 0; i < nthreads; i++)
+    if (uv_thread_join(threads + i))
+      abort();
+
+  if (threads != default_threads)
+    uv__free(threads);
+
+  uv_mutex_destroy(&mutex);
+  uv_cond_destroy(&cond);
+
+  threads = NULL;
+  nthreads = 0;
+#endif
+}
+
+
+static void init_threads(void) {
+  unsigned int i;
+  const char* val;
+  uv_sem_t sem;
+
+  nthreads = ARRAY_SIZE(default_threads);
+  val = getenv("UV_THREADPOOL_SIZE");
+  if (val != NULL)
+    nthreads = atoi(val);
+  if (nthreads == 0)
+    nthreads = 1;
+  if (nthreads > MAX_THREADPOOL_SIZE)
+    nthreads = MAX_THREADPOOL_SIZE;
+
+  threads = default_threads;
+  if (nthreads > ARRAY_SIZE(default_threads)) {
+    threads = uv__malloc(nthreads * sizeof(threads[0]));
+    if (threads == NULL) {
+      nthreads = ARRAY_SIZE(default_threads);
+      threads = default_threads;
+    }
+  }
+
+  if (uv_cond_init(&cond))
+    abort();
+
+  if (uv_mutex_init(&mutex))
+    abort();
+
+  QUEUE_INIT(&wq);
+  QUEUE_INIT(&slow_io_pending_wq);
+  QUEUE_INIT(&run_slow_work_message);
+
+  if (uv_sem_init(&sem, 0))
+    abort();
+
+  for (i = 0; i < nthreads; i++)
+    if (uv_thread_create(threads + i, worker, &sem))
+      abort();
+
+  for (i = 0; i < nthreads; i++)
+    uv_sem_wait(&sem);
+
+  uv_sem_destroy(&sem);
+}
+
+
+#ifndef _WIN32
+static void reset_once(void) {
+  uv_once_t child_once = UV_ONCE_INIT;
+  memcpy(&once, &child_once, sizeof(child_once));
+}
+#endif
+
+
+static void init_once(void) {
+#ifndef _WIN32
+  /* Re-initialize the threadpool after fork.
+   * Note that this discards the global mutex and condition as well
+   * as the work queue.
+   */
+  if (pthread_atfork(NULL, NULL, &reset_once))
+    abort();
+#endif
+  init_threads();
+}
+
+
+void uv__work_submit(uv_loop_t* loop,
+                     struct uv__work* w,
+                     enum uv__work_kind kind,
+                     void (*work)(struct uv__work* w),
+                     void (*done)(struct uv__work* w, int status)) {
+  uv_once(&once, init_once);
+  w->loop = loop;
+  w->work = work;
+  w->done = done;
+  post(&w->wq, kind);
+}
+
+
+static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) {
+  int cancelled;
+
+  uv_mutex_lock(&mutex);
+  uv_mutex_lock(&w->loop->wq_mutex);
+
+  cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL;
+  if (cancelled)
+    QUEUE_REMOVE(&w->wq);
+
+  uv_mutex_unlock(&w->loop->wq_mutex);
+  uv_mutex_unlock(&mutex);
+
+  if (!cancelled)
+    return UV_EBUSY;
+
+  w->work = uv__cancelled;
+  uv_mutex_lock(&loop->wq_mutex);
+  QUEUE_INSERT_TAIL(&loop->wq, &w->wq);
+  uv_async_send(&loop->wq_async);
+  uv_mutex_unlock(&loop->wq_mutex);
+
+  return 0;
+}
+
+
+void uv__work_done(uv_async_t* handle) {
+  struct uv__work* w;
+  uv_loop_t* loop;
+  QUEUE* q;
+  QUEUE wq;
+  int err;
+
+  loop = container_of(handle, uv_loop_t, wq_async);
+  uv_mutex_lock(&loop->wq_mutex);
+  QUEUE_MOVE(&loop->wq, &wq);
+  uv_mutex_unlock(&loop->wq_mutex);
+
+  while (!QUEUE_EMPTY(&wq)) {
+    q = QUEUE_HEAD(&wq);
+    QUEUE_REMOVE(q);
+
+    w = container_of(q, struct uv__work, wq);
+    err = (w->work == uv__cancelled) ? UV_ECANCELED : 0;
+    w->done(w, err);
+  }
+}
+
+
+static void uv__queue_work(struct uv__work* w) {
+  uv_work_t* req = container_of(w, uv_work_t, work_req);
+
+  req->work_cb(req);
+}
+
+
+static void uv__queue_done(struct uv__work* w, int err) {
+  uv_work_t* req;
+
+  req = container_of(w, uv_work_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  if (req->after_work_cb == NULL)
+    return;
+
+  req->after_work_cb(req, err);
+}
+
+
+int uv_queue_work(uv_loop_t* loop,
+                  uv_work_t* req,
+                  uv_work_cb work_cb,
+                  uv_after_work_cb after_work_cb) {
+  if (work_cb == NULL)
+    return UV_EINVAL;
+
+  uv__req_init(loop, req, UV_WORK);
+  req->loop = loop;
+  req->work_cb = work_cb;
+  req->after_work_cb = after_work_cb;
+  uv__work_submit(loop,
+                  &req->work_req,
+                  UV__WORK_CPU,
+                  uv__queue_work,
+                  uv__queue_done);
+  return 0;
+}
+
+
+int uv_cancel(uv_req_t* req) {
+  struct uv__work* wreq;
+  uv_loop_t* loop;
+
+  switch (req->type) {
+  case UV_FS:
+    loop =  ((uv_fs_t*) req)->loop;
+    wreq = &((uv_fs_t*) req)->work_req;
+    break;
+  case UV_GETADDRINFO:
+    loop =  ((uv_getaddrinfo_t*) req)->loop;
+    wreq = &((uv_getaddrinfo_t*) req)->work_req;
+    break;
+  case UV_GETNAMEINFO:
+    loop = ((uv_getnameinfo_t*) req)->loop;
+    wreq = &((uv_getnameinfo_t*) req)->work_req;
+    break;
+  case UV_RANDOM:
+    loop = ((uv_random_t*) req)->loop;
+    wreq = &((uv_random_t*) req)->work_req;
+    break;
+  case UV_WORK:
+    loop =  ((uv_work_t*) req)->loop;
+    wreq = &((uv_work_t*) req)->work_req;
+    break;
+  default:
+    return UV_EINVAL;
+  }
+
+  return uv__work_cancel(loop, req, wreq);
+}
diff --git a/third_party/libuv/src/timer.c b/third_party/libuv/src/timer.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/timer.c
@@ -0,0 +1,176 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv-common.h"
+#include "heap-inl.h"
+
+#include <assert.h>
+#include <limits.h>
+
+
+static struct heap *timer_heap(const uv_loop_t* loop) {
+#ifdef _WIN32
+  return (struct heap*) loop->timer_heap;
+#else
+  return (struct heap*) &loop->timer_heap;
+#endif
+}
+
+
+static int timer_less_than(const struct heap_node* ha,
+                           const struct heap_node* hb) {
+  const uv_timer_t* a;
+  const uv_timer_t* b;
+
+  a = container_of(ha, uv_timer_t, heap_node);
+  b = container_of(hb, uv_timer_t, heap_node);
+
+  if (a->timeout < b->timeout)
+    return 1;
+  if (b->timeout < a->timeout)
+    return 0;
+
+  /* Compare start_id when both have the same timeout. start_id is
+   * allocated with loop->timer_counter in uv_timer_start().
+   */
+  return a->start_id < b->start_id;
+}
+
+
+int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER);
+  handle->timer_cb = NULL;
+  handle->repeat = 0;
+  return 0;
+}
+
+
+int uv_timer_start(uv_timer_t* handle,
+                   uv_timer_cb cb,
+                   uint64_t timeout,
+                   uint64_t repeat) {
+  uint64_t clamped_timeout;
+
+  if (uv__is_closing(handle) || cb == NULL)
+    return UV_EINVAL;
+
+  if (uv__is_active(handle))
+    uv_timer_stop(handle);
+
+  clamped_timeout = handle->loop->time + timeout;
+  if (clamped_timeout < timeout)
+    clamped_timeout = (uint64_t) -1;
+
+  handle->timer_cb = cb;
+  handle->timeout = clamped_timeout;
+  handle->repeat = repeat;
+  /* start_id is the second index to be compared in timer_less_than() */
+  handle->start_id = handle->loop->timer_counter++;
+
+  heap_insert(timer_heap(handle->loop),
+              (struct heap_node*) &handle->heap_node,
+              timer_less_than);
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+int uv_timer_stop(uv_timer_t* handle) {
+  if (!uv__is_active(handle))
+    return 0;
+
+  heap_remove(timer_heap(handle->loop),
+              (struct heap_node*) &handle->heap_node,
+              timer_less_than);
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_timer_again(uv_timer_t* handle) {
+  if (handle->timer_cb == NULL)
+    return UV_EINVAL;
+
+  if (handle->repeat) {
+    uv_timer_stop(handle);
+    uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat);
+  }
+
+  return 0;
+}
+
+
+void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) {
+  handle->repeat = repeat;
+}
+
+
+uint64_t uv_timer_get_repeat(const uv_timer_t* handle) {
+  return handle->repeat;
+}
+
+
+int uv__next_timeout(const uv_loop_t* loop) {
+  const struct heap_node* heap_node;
+  const uv_timer_t* handle;
+  uint64_t diff;
+
+  heap_node = heap_min(timer_heap(loop));
+  if (heap_node == NULL)
+    return -1; /* block indefinitely */
+
+  handle = container_of(heap_node, uv_timer_t, heap_node);
+  if (handle->timeout <= loop->time)
+    return 0;
+
+  diff = handle->timeout - loop->time;
+  if (diff > INT_MAX)
+    diff = INT_MAX;
+
+  return (int) diff;
+}
+
+
+void uv__run_timers(uv_loop_t* loop) {
+  struct heap_node* heap_node;
+  uv_timer_t* handle;
+
+  for (;;) {
+    heap_node = heap_min(timer_heap(loop));
+    if (heap_node == NULL)
+      break;
+
+    handle = container_of(heap_node, uv_timer_t, heap_node);
+    if (handle->timeout > loop->time)
+      break;
+
+    uv_timer_stop(handle);
+    uv_timer_again(handle);
+    handle->timer_cb(handle);
+  }
+}
+
+
+void uv__timer_close(uv_timer_t* handle) {
+  uv_timer_stop(handle);
+}
diff --git a/third_party/libuv/src/uv-common.c b/third_party/libuv/src/uv-common.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/uv-common.c
@@ -0,0 +1,928 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "uv-common.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h> /* NULL */
+#include <stdio.h>
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+#if defined(_WIN32)
+# include <malloc.h> /* malloc */
+#else
+# include <net/if.h> /* if_nametoindex */
+# include <sys/un.h> /* AF_UNIX, sockaddr_un */
+#endif
+
+
+typedef struct {
+  uv_malloc_func local_malloc;
+  uv_realloc_func local_realloc;
+  uv_calloc_func local_calloc;
+  uv_free_func local_free;
+} uv__allocator_t;
+
+static uv__allocator_t uv__allocator = {
+  malloc,
+  realloc,
+  calloc,
+  free,
+};
+
+char* uv__strdup(const char* s) {
+  size_t len = strlen(s) + 1;
+  char* m = uv__malloc(len);
+  if (m == NULL)
+    return NULL;
+  return memcpy(m, s, len);
+}
+
+char* uv__strndup(const char* s, size_t n) {
+  char* m;
+  size_t len = strlen(s);
+  if (n < len)
+    len = n;
+  m = uv__malloc(len + 1);
+  if (m == NULL)
+    return NULL;
+  m[len] = '\0';
+  return memcpy(m, s, len);
+}
+
+void* uv__malloc(size_t size) {
+  if (size > 0)
+    return uv__allocator.local_malloc(size);
+  return NULL;
+}
+
+void uv__free(void* ptr) {
+  int saved_errno;
+
+  /* Libuv expects that free() does not clobber errno.  The system allocator
+   * honors that assumption but custom allocators may not be so careful.
+   */
+  saved_errno = errno;
+  uv__allocator.local_free(ptr);
+  errno = saved_errno;
+}
+
+void* uv__calloc(size_t count, size_t size) {
+  return uv__allocator.local_calloc(count, size);
+}
+
+void* uv__realloc(void* ptr, size_t size) {
+  if (size > 0)
+    return uv__allocator.local_realloc(ptr, size);
+  uv__free(ptr);
+  return NULL;
+}
+
+void* uv__reallocf(void* ptr, size_t size) {
+  void* newptr;
+
+  newptr = uv__realloc(ptr, size);
+  if (newptr == NULL)
+    if (size > 0)
+      uv__free(ptr);
+
+  return newptr;
+}
+
+int uv_replace_allocator(uv_malloc_func malloc_func,
+                         uv_realloc_func realloc_func,
+                         uv_calloc_func calloc_func,
+                         uv_free_func free_func) {
+  if (malloc_func == NULL || realloc_func == NULL ||
+      calloc_func == NULL || free_func == NULL) {
+    return UV_EINVAL;
+  }
+
+  uv__allocator.local_malloc = malloc_func;
+  uv__allocator.local_realloc = realloc_func;
+  uv__allocator.local_calloc = calloc_func;
+  uv__allocator.local_free = free_func;
+
+  return 0;
+}
+
+#define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t);
+
+size_t uv_handle_size(uv_handle_type type) {
+  switch (type) {
+    UV_HANDLE_TYPE_MAP(XX)
+    default:
+      return -1;
+  }
+}
+
+size_t uv_req_size(uv_req_type type) {
+  switch(type) {
+    UV_REQ_TYPE_MAP(XX)
+    default:
+      return -1;
+  }
+}
+
+#undef XX
+
+
+size_t uv_loop_size(void) {
+  return sizeof(uv_loop_t);
+}
+
+
+uv_buf_t uv_buf_init(char* base, unsigned int len) {
+  uv_buf_t buf;
+  buf.base = base;
+  buf.len = len;
+  return buf;
+}
+
+
+static const char* uv__unknown_err_code(int err) {
+  char buf[32];
+  char* copy;
+
+  snprintf(buf, sizeof(buf), "Unknown system error %d", err);
+  copy = uv__strdup(buf);
+
+  return copy != NULL ? copy : "Unknown system error";
+}
+
+#define UV_ERR_NAME_GEN_R(name, _) \
+case UV_## name: \
+  uv__strscpy(buf, #name, buflen); break;
+char* uv_err_name_r(int err, char* buf, size_t buflen) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_ERR_NAME_GEN_R)
+    default: snprintf(buf, buflen, "Unknown system error %d", err);
+  }
+  return buf;
+}
+#undef UV_ERR_NAME_GEN_R
+
+
+#define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name;
+const char* uv_err_name(int err) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_ERR_NAME_GEN)
+  }
+  return uv__unknown_err_code(err);
+}
+#undef UV_ERR_NAME_GEN
+
+
+#define UV_STRERROR_GEN_R(name, msg) \
+case UV_ ## name: \
+  snprintf(buf, buflen, "%s", msg); break;
+char* uv_strerror_r(int err, char* buf, size_t buflen) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_STRERROR_GEN_R)
+    default: snprintf(buf, buflen, "Unknown system error %d", err);
+  }
+  return buf;
+}
+#undef UV_STRERROR_GEN_R
+
+
+#define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg;
+const char* uv_strerror(int err) {
+  switch (err) {
+    UV_ERRNO_MAP(UV_STRERROR_GEN)
+  }
+  return uv__unknown_err_code(err);
+}
+#undef UV_STRERROR_GEN
+
+
+int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) {
+  memset(addr, 0, sizeof(*addr));
+  addr->sin_family = AF_INET;
+  addr->sin_port = htons(port);
+#ifdef SIN6_LEN
+  addr->sin_len = sizeof(*addr);
+#endif
+  return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr));
+}
+
+
+int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) {
+  char address_part[40];
+  size_t address_part_size;
+  const char* zone_index;
+
+  memset(addr, 0, sizeof(*addr));
+  addr->sin6_family = AF_INET6;
+  addr->sin6_port = htons(port);
+#ifdef SIN6_LEN
+  addr->sin6_len = sizeof(*addr);
+#endif
+
+  zone_index = strchr(ip, '%');
+  if (zone_index != NULL) {
+    address_part_size = zone_index - ip;
+    if (address_part_size >= sizeof(address_part))
+      address_part_size = sizeof(address_part) - 1;
+
+    memcpy(address_part, ip, address_part_size);
+    address_part[address_part_size] = '\0';
+    ip = address_part;
+
+    zone_index++; /* skip '%' */
+    /* NOTE: unknown interface (id=0) is silently ignored */
+#ifdef _WIN32
+    addr->sin6_scope_id = atoi(zone_index);
+#else
+    addr->sin6_scope_id = if_nametoindex(zone_index);
+#endif
+  }
+
+  return uv_inet_pton(AF_INET6, ip, &addr->sin6_addr);
+}
+
+
+int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) {
+  return uv_inet_ntop(AF_INET, &src->sin_addr, dst, size);
+}
+
+
+int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) {
+  return uv_inet_ntop(AF_INET6, &src->sin6_addr, dst, size);
+}
+
+
+int uv_tcp_bind(uv_tcp_t* handle,
+                const struct sockaddr* addr,
+                unsigned int flags) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_TCP)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  return uv__tcp_bind(handle, addr, addrlen, flags);
+}
+
+
+int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned flags) {
+  unsigned extra_flags;
+  int domain;
+  int rc;
+
+  /* Use the lower 8 bits for the domain. */
+  domain = flags & 0xFF;
+  if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
+    return UV_EINVAL;
+
+  /* Use the higher bits for extra flags. */
+  extra_flags = flags & ~0xFF;
+  if (extra_flags & ~UV_UDP_RECVMMSG)
+    return UV_EINVAL;
+
+  rc = uv__udp_init_ex(loop, handle, flags, domain);
+
+  if (rc == 0)
+    if (extra_flags & UV_UDP_RECVMMSG)
+      handle->flags |= UV_HANDLE_UDP_RECVMMSG;
+
+  return rc;
+}
+
+
+int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) {
+  return uv_udp_init_ex(loop, handle, AF_UNSPEC);
+}
+
+
+int uv_udp_bind(uv_udp_t* handle,
+                const struct sockaddr* addr,
+                unsigned int flags) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  return uv__udp_bind(handle, addr, addrlen, flags);
+}
+
+
+int uv_tcp_connect(uv_connect_t* req,
+                   uv_tcp_t* handle,
+                   const struct sockaddr* addr,
+                   uv_connect_cb cb) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_TCP)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  return uv__tcp_connect(req, handle, addr, addrlen, cb);
+}
+
+
+int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  /* Disconnect the handle */
+  if (addr == NULL) {
+    if (!(handle->flags & UV_HANDLE_UDP_CONNECTED))
+      return UV_ENOTCONN;
+
+    return uv__udp_disconnect(handle);
+  }
+
+  if (addr->sa_family == AF_INET)
+    addrlen = sizeof(struct sockaddr_in);
+  else if (addr->sa_family == AF_INET6)
+    addrlen = sizeof(struct sockaddr_in6);
+  else
+    return UV_EINVAL;
+
+  if (handle->flags & UV_HANDLE_UDP_CONNECTED)
+    return UV_EISCONN;
+
+  return uv__udp_connect(handle, addr, addrlen);
+}
+
+
+int uv__udp_is_connected(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+  if (handle->type != UV_UDP)
+    return 0;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
+int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EISCONN;
+
+  if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EDESTADDRREQ;
+
+  if (addr != NULL) {
+    if (addr->sa_family == AF_INET)
+      addrlen = sizeof(struct sockaddr_in);
+    else if (addr->sa_family == AF_INET6)
+      addrlen = sizeof(struct sockaddr_in6);
+#if defined(AF_UNIX) && !defined(_WIN32)
+    else if (addr->sa_family == AF_UNIX)
+      addrlen = sizeof(struct sockaddr_un);
+#endif
+    else
+      return UV_EINVAL;
+  } else {
+    addrlen = 0;
+  }
+
+  return addrlen;
+}
+
+
+int uv_udp_send(uv_udp_send_t* req,
+                uv_udp_t* handle,
+                const uv_buf_t bufs[],
+                unsigned int nbufs,
+                const struct sockaddr* addr,
+                uv_udp_send_cb send_cb) {
+  int addrlen;
+
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
+
+  return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
+}
+
+
+int uv_udp_try_send(uv_udp_t* handle,
+                    const uv_buf_t bufs[],
+                    unsigned int nbufs,
+                    const struct sockaddr* addr) {
+  int addrlen;
+
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
+
+  return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen);
+}
+
+
+int uv_udp_recv_start(uv_udp_t* handle,
+                      uv_alloc_cb alloc_cb,
+                      uv_udp_recv_cb recv_cb) {
+  if (handle->type != UV_UDP || alloc_cb == NULL || recv_cb == NULL)
+    return UV_EINVAL;
+  else
+    return uv__udp_recv_start(handle, alloc_cb, recv_cb);
+}
+
+
+int uv_udp_recv_stop(uv_udp_t* handle) {
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+  else
+    return uv__udp_recv_stop(handle);
+}
+
+
+void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
+  QUEUE queue;
+  QUEUE* q;
+  uv_handle_t* h;
+
+  QUEUE_MOVE(&loop->handle_queue, &queue);
+  while (!QUEUE_EMPTY(&queue)) {
+    q = QUEUE_HEAD(&queue);
+    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+
+    QUEUE_REMOVE(q);
+    QUEUE_INSERT_TAIL(&loop->handle_queue, q);
+
+    if (h->flags & UV_HANDLE_INTERNAL) continue;
+    walk_cb(h, arg);
+  }
+}
+
+
+static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) {
+  const char* type;
+  QUEUE* q;
+  uv_handle_t* h;
+
+  if (loop == NULL)
+    loop = uv_default_loop();
+
+  QUEUE_FOREACH(q, &loop->handle_queue) {
+    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+
+    if (only_active && !uv__is_active(h))
+      continue;
+
+    switch (h->type) {
+#define X(uc, lc) case UV_##uc: type = #lc; break;
+      UV_HANDLE_TYPE_MAP(X)
+#undef X
+      default: type = "<unknown>";
+    }
+
+    fprintf(stream,
+            "[%c%c%c] %-8s %p\n",
+            "R-"[!(h->flags & UV_HANDLE_REF)],
+            "A-"[!(h->flags & UV_HANDLE_ACTIVE)],
+            "I-"[!(h->flags & UV_HANDLE_INTERNAL)],
+            type,
+            (void*)h);
+  }
+}
+
+
+void uv_print_all_handles(uv_loop_t* loop, FILE* stream) {
+  uv__print_handles(loop, 0, stream);
+}
+
+
+void uv_print_active_handles(uv_loop_t* loop, FILE* stream) {
+  uv__print_handles(loop, 1, stream);
+}
+
+
+void uv_ref(uv_handle_t* handle) {
+  uv__handle_ref(handle);
+}
+
+
+void uv_unref(uv_handle_t* handle) {
+  uv__handle_unref(handle);
+}
+
+
+int uv_has_ref(const uv_handle_t* handle) {
+  return uv__has_ref(handle);
+}
+
+
+void uv_stop(uv_loop_t* loop) {
+  loop->stop_flag = 1;
+}
+
+
+uint64_t uv_now(const uv_loop_t* loop) {
+  return loop->time;
+}
+
+
+
+size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs) {
+  unsigned int i;
+  size_t bytes;
+
+  bytes = 0;
+  for (i = 0; i < nbufs; i++)
+    bytes += (size_t) bufs[i].len;
+
+  return bytes;
+}
+
+int uv_recv_buffer_size(uv_handle_t* handle, int* value) {
+  return uv__socket_sockopt(handle, SO_RCVBUF, value);
+}
+
+int uv_send_buffer_size(uv_handle_t* handle, int *value) {
+  return uv__socket_sockopt(handle, SO_SNDBUF, value);
+}
+
+int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) {
+  size_t required_len;
+
+  if (!uv__is_active(handle)) {
+    *size = 0;
+    return UV_EINVAL;
+  }
+
+  required_len = strlen(handle->path);
+  if (required_len >= *size) {
+    *size = required_len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, handle->path, required_len);
+  *size = required_len;
+  buffer[required_len] = '\0';
+
+  return 0;
+}
+
+/* The windows implementation does not have the same structure layout as
+ * the unix implementation (nbufs is not directly inside req but is
+ * contained in a nested union/struct) so this function locates it.
+*/
+static unsigned int* uv__get_nbufs(uv_fs_t* req) {
+#ifdef _WIN32
+  return &req->fs.info.nbufs;
+#else
+  return &req->nbufs;
+#endif
+}
+
+/* uv_fs_scandir() uses the system allocator to allocate memory on non-Windows
+ * systems. So, the memory should be released using free(). On Windows,
+ * uv__malloc() is used, so use uv__free() to free memory.
+*/
+#ifdef _WIN32
+# define uv__fs_scandir_free uv__free
+#else
+# define uv__fs_scandir_free free
+#endif
+
+void uv__fs_scandir_cleanup(uv_fs_t* req) {
+  uv__dirent_t** dents;
+
+  unsigned int* nbufs = uv__get_nbufs(req);
+
+  dents = req->ptr;
+  if (*nbufs > 0 && *nbufs != (unsigned int) req->result)
+    (*nbufs)--;
+  for (; *nbufs < (unsigned int) req->result; (*nbufs)++)
+    uv__fs_scandir_free(dents[*nbufs]);
+
+  uv__fs_scandir_free(req->ptr);
+  req->ptr = NULL;
+}
+
+
+int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
+  uv__dirent_t** dents;
+  uv__dirent_t* dent;
+  unsigned int* nbufs;
+
+  /* Check to see if req passed */
+  if (req->result < 0)
+    return req->result;
+
+  /* Ptr will be null if req was canceled or no files found */
+  if (!req->ptr)
+    return UV_EOF;
+
+  nbufs = uv__get_nbufs(req);
+  assert(nbufs);
+
+  dents = req->ptr;
+
+  /* Free previous entity */
+  if (*nbufs > 0)
+    uv__fs_scandir_free(dents[*nbufs - 1]);
+
+  /* End was already reached */
+  if (*nbufs == (unsigned int) req->result) {
+    uv__fs_scandir_free(dents);
+    req->ptr = NULL;
+    return UV_EOF;
+  }
+
+  dent = dents[(*nbufs)++];
+
+  ent->name = dent->d_name;
+  ent->type = uv__fs_get_dirent_type(dent);
+
+  return 0;
+}
+
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent) {
+  uv_dirent_type_t type;
+
+#ifdef HAVE_DIRENT_TYPES
+  switch (dent->d_type) {
+    case UV__DT_DIR:
+      type = UV_DIRENT_DIR;
+      break;
+    case UV__DT_FILE:
+      type = UV_DIRENT_FILE;
+      break;
+    case UV__DT_LINK:
+      type = UV_DIRENT_LINK;
+      break;
+    case UV__DT_FIFO:
+      type = UV_DIRENT_FIFO;
+      break;
+    case UV__DT_SOCKET:
+      type = UV_DIRENT_SOCKET;
+      break;
+    case UV__DT_CHAR:
+      type = UV_DIRENT_CHAR;
+      break;
+    case UV__DT_BLOCK:
+      type = UV_DIRENT_BLOCK;
+      break;
+    default:
+      type = UV_DIRENT_UNKNOWN;
+  }
+#else
+  type = UV_DIRENT_UNKNOWN;
+#endif
+
+  return type;
+}
+
+void uv__fs_readdir_cleanup(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  int i;
+
+  if (req->ptr == NULL)
+    return;
+
+  dir = req->ptr;
+  dirents = dir->dirents;
+  req->ptr = NULL;
+
+  if (dirents == NULL)
+    return;
+
+  for (i = 0; i < req->result; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
+}
+
+
+int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) {
+  va_list ap;
+  int err;
+
+  va_start(ap, option);
+  /* Any platform-agnostic options should be handled here. */
+  err = uv__loop_configure(loop, option, ap);
+  va_end(ap);
+
+  return err;
+}
+
+
+static uv_loop_t default_loop_struct;
+static uv_loop_t* default_loop_ptr;
+
+
+uv_loop_t* uv_default_loop(void) {
+  if (default_loop_ptr != NULL)
+    return default_loop_ptr;
+
+  if (uv_loop_init(&default_loop_struct))
+    return NULL;
+
+  default_loop_ptr = &default_loop_struct;
+  return default_loop_ptr;
+}
+
+
+uv_loop_t* uv_loop_new(void) {
+  uv_loop_t* loop;
+
+  loop = uv__malloc(sizeof(*loop));
+  if (loop == NULL)
+    return NULL;
+
+  if (uv_loop_init(loop)) {
+    uv__free(loop);
+    return NULL;
+  }
+
+  return loop;
+}
+
+
+int uv_loop_close(uv_loop_t* loop) {
+  QUEUE* q;
+  uv_handle_t* h;
+#ifndef NDEBUG
+  void* saved_data;
+#endif
+
+  if (uv__has_active_reqs(loop))
+    return UV_EBUSY;
+
+  QUEUE_FOREACH(q, &loop->handle_queue) {
+    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+    if (!(h->flags & UV_HANDLE_INTERNAL))
+      return UV_EBUSY;
+  }
+
+  uv__loop_close(loop);
+
+#ifndef NDEBUG
+  saved_data = loop->data;
+  memset(loop, -1, sizeof(*loop));
+  loop->data = saved_data;
+#endif
+  if (loop == default_loop_ptr)
+    default_loop_ptr = NULL;
+
+  return 0;
+}
+
+
+void uv_loop_delete(uv_loop_t* loop) {
+  uv_loop_t* default_loop;
+  int err;
+
+  default_loop = default_loop_ptr;
+
+  err = uv_loop_close(loop);
+  (void) err;    /* Squelch compiler warnings. */
+  assert(err == 0);
+  if (loop != default_loop)
+    uv__free(loop);
+}
+
+
+void uv_os_free_environ(uv_env_item_t* envitems, int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(envitems[i].name);
+  }
+
+  uv__free(envitems);
+}
+
+
+void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
+  int i;
+
+  for (i = 0; i < count; i++)
+    uv__free(cpu_infos[i].model);
+
+  uv__free(cpu_infos);
+}
+
+
+#ifdef __GNUC__  /* Also covers __clang__ and __INTEL_COMPILER. */
+__attribute__((destructor))
+#endif
+void uv_library_shutdown(void) {
+  static int was_shutdown;
+
+  if (uv__load_relaxed(&was_shutdown))
+    return;
+
+  uv__process_title_cleanup();
+  uv__signal_cleanup();
+  uv__threadpool_cleanup();
+  uv__store_relaxed(&was_shutdown, 1);
+}
+
+
+void uv__metrics_update_idle_time(uv_loop_t* loop) {
+  uv__loop_metrics_t* loop_metrics;
+  uint64_t entry_time;
+  uint64_t exit_time;
+
+  if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME))
+    return;
+
+  loop_metrics = uv__get_loop_metrics(loop);
+
+  /* The thread running uv__metrics_update_idle_time() is always the same
+   * thread that sets provider_entry_time. So it's unnecessary to lock before
+   * retrieving this value.
+   */
+  if (loop_metrics->provider_entry_time == 0)
+    return;
+
+  exit_time = uv_hrtime();
+
+  uv_mutex_lock(&loop_metrics->lock);
+  entry_time = loop_metrics->provider_entry_time;
+  loop_metrics->provider_entry_time = 0;
+  loop_metrics->provider_idle_time += exit_time - entry_time;
+  uv_mutex_unlock(&loop_metrics->lock);
+}
+
+
+void uv__metrics_set_provider_entry_time(uv_loop_t* loop) {
+  uv__loop_metrics_t* loop_metrics;
+  uint64_t now;
+
+  if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME))
+    return;
+
+  now = uv_hrtime();
+  loop_metrics = uv__get_loop_metrics(loop);
+  uv_mutex_lock(&loop_metrics->lock);
+  loop_metrics->provider_entry_time = now;
+  uv_mutex_unlock(&loop_metrics->lock);
+}
+
+
+uint64_t uv_metrics_idle_time(uv_loop_t* loop) {
+  uv__loop_metrics_t* loop_metrics;
+  uint64_t entry_time;
+  uint64_t idle_time;
+
+  loop_metrics = uv__get_loop_metrics(loop);
+  uv_mutex_lock(&loop_metrics->lock);
+  idle_time = loop_metrics->provider_idle_time;
+  entry_time = loop_metrics->provider_entry_time;
+  uv_mutex_unlock(&loop_metrics->lock);
+
+  if (entry_time > 0)
+    idle_time += uv_hrtime() - entry_time;
+  return idle_time;
+}
diff --git a/third_party/libuv/src/uv-common.h b/third_party/libuv/src/uv-common.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/uv-common.h
@@ -0,0 +1,368 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * This file is private to libuv. It provides common functionality to both
+ * Windows and Unix backends.
+ */
+
+#ifndef UV_COMMON_H_
+#define UV_COMMON_H_
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#include "uv.h"
+#include "uv/tree.h"
+#include "queue.h"
+#include "strscpy.h"
+
+#if EDOM > 0
+# define UV__ERR(x) (-(x))
+#else
+# define UV__ERR(x) (x)
+#endif
+
+#if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900
+extern int snprintf(char*, size_t, const char*, ...);
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#define container_of(ptr, type, member) \
+  ((type *) ((char *) (ptr) - offsetof(type, member)))
+
+#define STATIC_ASSERT(expr)                                                   \
+  void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)])
+
+#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7)
+#define uv__load_relaxed(p) __atomic_load_n(p, __ATOMIC_RELAXED)
+#define uv__store_relaxed(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED)
+#else
+#define uv__load_relaxed(p) (*p)
+#define uv__store_relaxed(p, v) do *p = v; while (0)
+#endif
+
+/* Handle flags. Some flags are specific to Windows or UNIX. */
+enum {
+  /* Used by all handles. */
+  UV_HANDLE_CLOSING                     = 0x00000001,
+  UV_HANDLE_CLOSED                      = 0x00000002,
+  UV_HANDLE_ACTIVE                      = 0x00000004,
+  UV_HANDLE_REF                         = 0x00000008,
+  UV_HANDLE_INTERNAL                    = 0x00000010,
+  UV_HANDLE_ENDGAME_QUEUED              = 0x00000020,
+
+  /* Used by streams. */
+  UV_HANDLE_LISTENING                   = 0x00000040,
+  UV_HANDLE_CONNECTION                  = 0x00000080,
+  UV_HANDLE_SHUTTING                    = 0x00000100,
+  UV_HANDLE_SHUT                        = 0x00000200,
+  UV_HANDLE_READ_PARTIAL                = 0x00000400,
+  UV_HANDLE_READ_EOF                    = 0x00000800,
+
+  /* Used by streams and UDP handles. */
+  UV_HANDLE_READING                     = 0x00001000,
+  UV_HANDLE_BOUND                       = 0x00002000,
+  UV_HANDLE_READABLE                    = 0x00004000,
+  UV_HANDLE_WRITABLE                    = 0x00008000,
+  UV_HANDLE_READ_PENDING                = 0x00010000,
+  UV_HANDLE_SYNC_BYPASS_IOCP            = 0x00020000,
+  UV_HANDLE_ZERO_READ                   = 0x00040000,
+  UV_HANDLE_EMULATE_IOCP                = 0x00080000,
+  UV_HANDLE_BLOCKING_WRITES             = 0x00100000,
+  UV_HANDLE_CANCELLATION_PENDING        = 0x00200000,
+
+  /* Used by uv_tcp_t and uv_udp_t handles */
+  UV_HANDLE_IPV6                        = 0x00400000,
+
+  /* Only used by uv_tcp_t handles. */
+  UV_HANDLE_TCP_NODELAY                 = 0x01000000,
+  UV_HANDLE_TCP_KEEPALIVE               = 0x02000000,
+  UV_HANDLE_TCP_SINGLE_ACCEPT           = 0x04000000,
+  UV_HANDLE_TCP_ACCEPT_STATE_CHANGING   = 0x08000000,
+  UV_HANDLE_TCP_SOCKET_CLOSED           = 0x10000000,
+  UV_HANDLE_SHARED_TCP_SOCKET           = 0x20000000,
+
+  /* Only used by uv_udp_t handles. */
+  UV_HANDLE_UDP_PROCESSING              = 0x01000000,
+  UV_HANDLE_UDP_CONNECTED               = 0x02000000,
+  UV_HANDLE_UDP_RECVMMSG                = 0x04000000,
+
+  /* Only used by uv_pipe_t handles. */
+  UV_HANDLE_NON_OVERLAPPED_PIPE         = 0x01000000,
+  UV_HANDLE_PIPESERVER                  = 0x02000000,
+
+  /* Only used by uv_tty_t handles. */
+  UV_HANDLE_TTY_READABLE                = 0x01000000,
+  UV_HANDLE_TTY_RAW                     = 0x02000000,
+  UV_HANDLE_TTY_SAVED_POSITION          = 0x04000000,
+  UV_HANDLE_TTY_SAVED_ATTRIBUTES        = 0x08000000,
+
+  /* Only used by uv_signal_t handles. */
+  UV_SIGNAL_ONE_SHOT_DISPATCHED         = 0x01000000,
+  UV_SIGNAL_ONE_SHOT                    = 0x02000000,
+
+  /* Only used by uv_poll_t handles. */
+  UV_HANDLE_POLL_SLOW                   = 0x01000000
+};
+
+int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
+
+void uv__loop_close(uv_loop_t* loop);
+
+int uv__tcp_bind(uv_tcp_t* tcp,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags);
+
+int uv__tcp_connect(uv_connect_t* req,
+                   uv_tcp_t* handle,
+                   const struct sockaddr* addr,
+                   unsigned int addrlen,
+                   uv_connect_cb cb);
+
+int uv__udp_init_ex(uv_loop_t* loop,
+                    uv_udp_t* handle,
+                    unsigned flags,
+                    int domain);
+
+int uv__udp_bind(uv_udp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int  addrlen,
+                 unsigned int flags);
+
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen);
+
+int uv__udp_disconnect(uv_udp_t* handle);
+
+int uv__udp_is_connected(uv_udp_t* handle);
+
+int uv__udp_send(uv_udp_send_t* req,
+                 uv_udp_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 uv_udp_send_cb send_cb);
+
+int uv__udp_try_send(uv_udp_t* handle,
+                     const uv_buf_t bufs[],
+                     unsigned int nbufs,
+                     const struct sockaddr* addr,
+                     unsigned int addrlen);
+
+int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb,
+                       uv_udp_recv_cb recv_cb);
+
+int uv__udp_recv_stop(uv_udp_t* handle);
+
+void uv__fs_poll_close(uv_fs_poll_t* handle);
+
+int uv__getaddrinfo_translate_error(int sys_err);    /* EAI_* error. */
+
+enum uv__work_kind {
+  UV__WORK_CPU,
+  UV__WORK_FAST_IO,
+  UV__WORK_SLOW_IO
+};
+
+void uv__work_submit(uv_loop_t* loop,
+                     struct uv__work *w,
+                     enum uv__work_kind kind,
+                     void (*work)(struct uv__work *w),
+                     void (*done)(struct uv__work *w, int status));
+
+void uv__work_done(uv_async_t* handle);
+
+size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs);
+
+int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value);
+
+void uv__fs_scandir_cleanup(uv_fs_t* req);
+void uv__fs_readdir_cleanup(uv_fs_t* req);
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent);
+
+int uv__next_timeout(const uv_loop_t* loop);
+void uv__run_timers(uv_loop_t* loop);
+void uv__timer_close(uv_timer_t* handle);
+
+void uv__process_title_cleanup(void);
+void uv__signal_cleanup(void);
+void uv__threadpool_cleanup(void);
+
+#define uv__has_active_reqs(loop)                                             \
+  ((loop)->active_reqs.count > 0)
+
+#define uv__req_register(loop, req)                                           \
+  do {                                                                        \
+    (loop)->active_reqs.count++;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__req_unregister(loop, req)                                         \
+  do {                                                                        \
+    assert(uv__has_active_reqs(loop));                                        \
+    (loop)->active_reqs.count--;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__has_active_handles(loop)                                          \
+  ((loop)->active_handles > 0)
+
+#define uv__active_handle_add(h)                                              \
+  do {                                                                        \
+    (h)->loop->active_handles++;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__active_handle_rm(h)                                               \
+  do {                                                                        \
+    (h)->loop->active_handles--;                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__is_active(h)                                                      \
+  (((h)->flags & UV_HANDLE_ACTIVE) != 0)
+
+#define uv__is_closing(h)                                                     \
+  (((h)->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)) != 0)
+
+#define uv__handle_start(h)                                                   \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break;                          \
+    (h)->flags |= UV_HANDLE_ACTIVE;                                           \
+    if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_add(h);          \
+  }                                                                           \
+  while (0)
+
+#define uv__handle_stop(h)                                                    \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_ACTIVE) == 0) break;                          \
+    (h)->flags &= ~UV_HANDLE_ACTIVE;                                          \
+    if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_rm(h);           \
+  }                                                                           \
+  while (0)
+
+#define uv__handle_ref(h)                                                     \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_REF) != 0) break;                             \
+    (h)->flags |= UV_HANDLE_REF;                                              \
+    if (((h)->flags & UV_HANDLE_CLOSING) != 0) break;                         \
+    if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_add(h);       \
+  }                                                                           \
+  while (0)
+
+#define uv__handle_unref(h)                                                   \
+  do {                                                                        \
+    if (((h)->flags & UV_HANDLE_REF) == 0) break;                             \
+    (h)->flags &= ~UV_HANDLE_REF;                                             \
+    if (((h)->flags & UV_HANDLE_CLOSING) != 0) break;                         \
+    if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_rm(h);        \
+  }                                                                           \
+  while (0)
+
+#define uv__has_ref(h)                                                        \
+  (((h)->flags & UV_HANDLE_REF) != 0)
+
+#if defined(_WIN32)
+# define uv__handle_platform_init(h) ((h)->u.fd = -1)
+#else
+# define uv__handle_platform_init(h) ((h)->next_closing = NULL)
+#endif
+
+#define uv__handle_init(loop_, h, type_)                                      \
+  do {                                                                        \
+    (h)->loop = (loop_);                                                      \
+    (h)->type = (type_);                                                      \
+    (h)->flags = UV_HANDLE_REF;  /* Ref the loop when active. */              \
+    QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue);            \
+    uv__handle_platform_init(h);                                              \
+  }                                                                           \
+  while (0)
+
+/* Note: uses an open-coded version of SET_REQ_SUCCESS() because of
+ * a circular dependency between src/uv-common.h and src/win/internal.h.
+ */
+#if defined(_WIN32)
+# define UV_REQ_INIT(req, typ)                                                \
+  do {                                                                        \
+    (req)->type = (typ);                                                      \
+    (req)->u.io.overlapped.Internal = 0;  /* SET_REQ_SUCCESS() */             \
+  }                                                                           \
+  while (0)
+#else
+# define UV_REQ_INIT(req, typ)                                                \
+  do {                                                                        \
+    (req)->type = (typ);                                                      \
+  }                                                                           \
+  while (0)
+#endif
+
+#define uv__req_init(loop, req, typ)                                          \
+  do {                                                                        \
+    UV_REQ_INIT(req, typ);                                                    \
+    uv__req_register(loop, req);                                              \
+  }                                                                           \
+  while (0)
+
+#define uv__get_internal_fields(loop)                                         \
+  ((uv__loop_internal_fields_t*) loop->internal_fields)
+
+#define uv__get_loop_metrics(loop)                                            \
+  (&uv__get_internal_fields(loop)->loop_metrics)
+
+/* Allocator prototypes */
+void *uv__calloc(size_t count, size_t size);
+char *uv__strdup(const char* s);
+char *uv__strndup(const char* s, size_t n);
+void* uv__malloc(size_t size);
+void uv__free(void* ptr);
+void* uv__realloc(void* ptr, size_t size);
+void* uv__reallocf(void* ptr, size_t size);
+
+typedef struct uv__loop_metrics_s uv__loop_metrics_t;
+typedef struct uv__loop_internal_fields_s uv__loop_internal_fields_t;
+
+struct uv__loop_metrics_s {
+  uint64_t provider_entry_time;
+  uint64_t provider_idle_time;
+  uv_mutex_t lock;
+};
+
+void uv__metrics_update_idle_time(uv_loop_t* loop);
+void uv__metrics_set_provider_entry_time(uv_loop_t* loop);
+
+struct uv__loop_internal_fields_s {
+  unsigned int flags;
+  uv__loop_metrics_t loop_metrics;
+};
+
+#endif /* UV_COMMON_H_ */
diff --git a/third_party/libuv/src/uv-data-getter-setters.c b/third_party/libuv/src/uv-data-getter-setters.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/uv-data-getter-setters.c
@@ -0,0 +1,119 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+
+const char* uv_handle_type_name(uv_handle_type type) {
+  switch (type) {
+#define XX(uc,lc) case UV_##uc: return #lc;
+  UV_HANDLE_TYPE_MAP(XX)
+#undef XX
+  case UV_FILE: return "file";
+  case UV_HANDLE_TYPE_MAX:
+  case UV_UNKNOWN_HANDLE: return NULL;
+  }
+  return NULL;
+}
+
+uv_handle_type uv_handle_get_type(const uv_handle_t* handle) {
+  return handle->type;
+}
+
+void* uv_handle_get_data(const uv_handle_t* handle) {
+  return handle->data;
+}
+
+uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle) {
+  return handle->loop;
+}
+
+void uv_handle_set_data(uv_handle_t* handle, void* data) {
+  handle->data = data;
+}
+
+const char* uv_req_type_name(uv_req_type type) {
+  switch (type) {
+#define XX(uc,lc) case UV_##uc: return #lc;
+  UV_REQ_TYPE_MAP(XX)
+#undef XX
+  case UV_REQ_TYPE_MAX:
+  case UV_UNKNOWN_REQ:
+  default: /* UV_REQ_TYPE_PRIVATE */
+    break;
+  }
+  return NULL;
+}
+
+uv_req_type uv_req_get_type(const uv_req_t* req) {
+  return req->type;
+}
+
+void* uv_req_get_data(const uv_req_t* req) {
+  return req->data;
+}
+
+void uv_req_set_data(uv_req_t* req, void* data) {
+  req->data = data;
+}
+
+size_t uv_stream_get_write_queue_size(const uv_stream_t* stream) {
+  return stream->write_queue_size;
+}
+
+size_t uv_udp_get_send_queue_size(const uv_udp_t* handle) {
+  return handle->send_queue_size;
+}
+
+size_t uv_udp_get_send_queue_count(const uv_udp_t* handle) {
+  return handle->send_queue_count;
+}
+
+uv_pid_t uv_process_get_pid(const uv_process_t* proc) {
+  return proc->pid;
+}
+
+uv_fs_type uv_fs_get_type(const uv_fs_t* req) {
+  return req->fs_type;
+}
+
+ssize_t uv_fs_get_result(const uv_fs_t* req) {
+  return req->result;
+}
+
+void* uv_fs_get_ptr(const uv_fs_t* req) {
+  return req->ptr;
+}
+
+const char* uv_fs_get_path(const uv_fs_t* req) {
+  return req->path;
+}
+
+uv_stat_t* uv_fs_get_statbuf(uv_fs_t* req) {
+  return &req->statbuf;
+}
+
+void* uv_loop_get_data(const uv_loop_t* loop) {
+  return loop->data;
+}
+
+void uv_loop_set_data(uv_loop_t* loop, void* data) {
+  loop->data = data;
+}
diff --git a/third_party/libuv/src/version.c b/third_party/libuv/src/version.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/version.c
@@ -0,0 +1,45 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+
+#define UV_STRINGIFY(v) UV_STRINGIFY_HELPER(v)
+#define UV_STRINGIFY_HELPER(v) #v
+
+#define UV_VERSION_STRING_BASE  UV_STRINGIFY(UV_VERSION_MAJOR) "." \
+                                UV_STRINGIFY(UV_VERSION_MINOR) "." \
+                                UV_STRINGIFY(UV_VERSION_PATCH)
+
+#if UV_VERSION_IS_RELEASE
+# define UV_VERSION_STRING  UV_VERSION_STRING_BASE
+#else
+# define UV_VERSION_STRING  UV_VERSION_STRING_BASE "-" UV_VERSION_SUFFIX
+#endif
+
+
+unsigned int uv_version(void) {
+  return UV_VERSION_HEX;
+}
+
+
+const char* uv_version_string(void) {
+  return UV_VERSION_STRING;
+}
diff --git a/third_party/libuv/src/win/async.c b/third_party/libuv/src/win/async.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/async.c
@@ -0,0 +1,98 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "atomicops-inl.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
+  if (handle->flags & UV_HANDLE_CLOSING &&
+      !handle->async_sent) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    uv__handle_close(handle);
+  }
+}
+
+
+int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
+  uv_req_t* req;
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_ASYNC);
+  handle->async_sent = 0;
+  handle->async_cb = async_cb;
+
+  req = &handle->async_req;
+  UV_REQ_INIT(req, UV_WAKEUP);
+  req->data = handle;
+
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+void uv_async_close(uv_loop_t* loop, uv_async_t* handle) {
+  if (!((uv_async_t*)handle)->async_sent) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+  }
+
+  uv__handle_closing(handle);
+}
+
+
+int uv_async_send(uv_async_t* handle) {
+  uv_loop_t* loop = handle->loop;
+
+  if (handle->type != UV_ASYNC) {
+    /* Can't set errno because that's not thread-safe. */
+    return -1;
+  }
+
+  /* The user should make sure never to call uv_async_send to a closing or
+   * closed handle. */
+  assert(!(handle->flags & UV_HANDLE_CLOSING));
+
+  if (!uv__atomic_exchange_set(&handle->async_sent)) {
+    POST_COMPLETION_FOR_REQ(loop, &handle->async_req);
+  }
+
+  return 0;
+}
+
+
+void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
+    uv_req_t* req) {
+  assert(handle->type == UV_ASYNC);
+  assert(req->type == UV_WAKEUP);
+
+  handle->async_sent = 0;
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    uv_want_endgame(loop, (uv_handle_t*)handle);
+  } else if (handle->async_cb != NULL) {
+    handle->async_cb(handle);
+  }
+}
diff --git a/third_party/libuv/src/win/atomicops-inl.h b/third_party/libuv/src/win/atomicops-inl.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/atomicops-inl.h
@@ -0,0 +1,57 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_ATOMICOPS_INL_H_
+#define UV_WIN_ATOMICOPS_INL_H_
+
+#include "uv.h"
+#include "internal.h"
+
+
+/* Atomic set operation on char */
+#ifdef _MSC_VER /* MSVC */
+
+/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less
+ * efficient than InterlockedExchange, but InterlockedExchange8 does not exist,
+ * and interlocked operations on larger targets might require the target to be
+ * aligned. */
+#pragma intrinsic(_InterlockedOr8)
+
+static char INLINE uv__atomic_exchange_set(char volatile* target) {
+  return _InterlockedOr8(target, 1);
+}
+
+#else /* GCC */
+
+/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */
+static inline char uv__atomic_exchange_set(char volatile* target) {
+  const char one = 1;
+  char old_value;
+  __asm__ __volatile__ ("lock xchgb %0, %1\n\t"
+                        : "=r"(old_value), "=m"(*target)
+                        : "0"(one), "m"(*target)
+                        : "memory");
+  return old_value;
+}
+
+#endif
+
+#endif /* UV_WIN_ATOMICOPS_INL_H_ */
diff --git a/third_party/libuv/src/win/core.c b/third_party/libuv/src/win/core.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/core.c
@@ -0,0 +1,748 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
+#include <crtdbg.h>
+#endif
+
+#include "uv.h"
+#include "internal.h"
+#include "queue.h"
+#include "handle-inl.h"
+#include "heap-inl.h"
+#include "req-inl.h"
+
+/* uv_once initialization guards */
+static uv_once_t uv_init_guard_ = UV_ONCE_INIT;
+
+
+#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR))
+/* Our crt debug report handler allows us to temporarily disable asserts
+ * just for the current thread.
+ */
+
+UV_THREAD_LOCAL int uv__crt_assert_enabled = TRUE;
+
+static int uv__crt_dbg_report_handler(int report_type, char *message, int *ret_val) {
+  if (uv__crt_assert_enabled || report_type != _CRT_ASSERT)
+    return FALSE;
+
+  if (ret_val) {
+    /* Set ret_val to 0 to continue with normal execution.
+     * Set ret_val to 1 to trigger a breakpoint.
+    */
+
+    if(IsDebuggerPresent())
+      *ret_val = 1;
+    else
+      *ret_val = 0;
+  }
+
+  /* Don't call _CrtDbgReport. */
+  return TRUE;
+}
+#else
+UV_THREAD_LOCAL int uv__crt_assert_enabled = FALSE;
+#endif
+
+
+#if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800
+static void uv__crt_invalid_parameter_handler(const wchar_t* expression,
+    const wchar_t* function, const wchar_t * file, unsigned int line,
+    uintptr_t reserved) {
+  /* No-op. */
+}
+#endif
+
+static uv_loop_t** uv__loops;
+static int uv__loops_size;
+static int uv__loops_capacity;
+#define UV__LOOPS_CHUNK_SIZE 8
+static uv_mutex_t uv__loops_lock;
+
+static void uv__loops_init(void) {
+  uv_mutex_init(&uv__loops_lock);
+}
+
+static int uv__loops_add(uv_loop_t* loop) {
+  uv_loop_t** new_loops;
+  int new_capacity, i;
+
+  uv_mutex_lock(&uv__loops_lock);
+
+  if (uv__loops_size == uv__loops_capacity) {
+    new_capacity = uv__loops_capacity + UV__LOOPS_CHUNK_SIZE;
+    new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity);
+    if (!new_loops)
+      goto failed_loops_realloc;
+    uv__loops = new_loops;
+    for (i = uv__loops_capacity; i < new_capacity; ++i)
+      uv__loops[i] = NULL;
+    uv__loops_capacity = new_capacity;
+  }
+  uv__loops[uv__loops_size] = loop;
+  ++uv__loops_size;
+
+  uv_mutex_unlock(&uv__loops_lock);
+  return 0;
+
+failed_loops_realloc:
+  uv_mutex_unlock(&uv__loops_lock);
+  return ERROR_OUTOFMEMORY;
+}
+
+static void uv__loops_remove(uv_loop_t* loop) {
+  int loop_index;
+  int smaller_capacity;
+  uv_loop_t** new_loops;
+
+  uv_mutex_lock(&uv__loops_lock);
+
+  for (loop_index = 0; loop_index < uv__loops_size; ++loop_index) {
+    if (uv__loops[loop_index] == loop)
+      break;
+  }
+  /* If loop was not found, ignore */
+  if (loop_index == uv__loops_size)
+    goto loop_removed;
+
+  uv__loops[loop_index] = uv__loops[uv__loops_size - 1];
+  uv__loops[uv__loops_size - 1] = NULL;
+  --uv__loops_size;
+
+  if (uv__loops_size == 0) {
+    uv__loops_capacity = 0;
+    uv__free(uv__loops);
+    uv__loops = NULL;
+    goto loop_removed;
+  }
+
+  /* If we didn't grow to big skip downsizing */
+  if (uv__loops_capacity < 4 * UV__LOOPS_CHUNK_SIZE)
+    goto loop_removed;
+
+  /* Downsize only if more than half of buffer is free */
+  smaller_capacity = uv__loops_capacity / 2;
+  if (uv__loops_size >= smaller_capacity)
+    goto loop_removed;
+  new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity);
+  if (!new_loops)
+    goto loop_removed;
+  uv__loops = new_loops;
+  uv__loops_capacity = smaller_capacity;
+
+loop_removed:
+  uv_mutex_unlock(&uv__loops_lock);
+}
+
+void uv__wake_all_loops(void) {
+  int i;
+  uv_loop_t* loop;
+
+  uv_mutex_lock(&uv__loops_lock);
+  for (i = 0; i < uv__loops_size; ++i) {
+    loop = uv__loops[i];
+    assert(loop);
+    if (loop->iocp != INVALID_HANDLE_VALUE)
+      PostQueuedCompletionStatus(loop->iocp, 0, 0, NULL);
+  }
+  uv_mutex_unlock(&uv__loops_lock);
+}
+
+static void uv_init(void) {
+  /* Tell Windows that we will handle critical errors. */
+  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
+               SEM_NOOPENFILEERRORBOX);
+
+  /* Tell the CRT to not exit the application when an invalid parameter is
+   * passed. The main issue is that invalid FDs will trigger this behavior.
+   */
+#if !defined(__MINGW32__) || __MSVCRT_VERSION__ >= 0x800
+  _set_invalid_parameter_handler(uv__crt_invalid_parameter_handler);
+#endif
+
+  /* We also need to setup our debug report handler because some CRT
+   * functions (eg _get_osfhandle) raise an assert when called with invalid
+   * FDs even though they return the proper error code in the release build.
+   */
+#if defined(_DEBUG) && (defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR))
+  _CrtSetReportHook(uv__crt_dbg_report_handler);
+#endif
+
+  /* Initialize tracking of all uv loops */
+  uv__loops_init();
+
+  /* Fetch winapi function pointers. This must be done first because other
+   * initialization code might need these function pointers to be loaded.
+   */
+  uv_winapi_init();
+
+  /* Initialize winsock */
+  uv_winsock_init();
+
+  /* Initialize FS */
+  uv_fs_init();
+
+  /* Initialize signal stuff */
+  uv_signals_init();
+
+  /* Initialize console */
+  uv_console_init();
+
+  /* Initialize utilities */
+  uv__util_init();
+
+  /* Initialize system wakeup detection */
+  uv__init_detect_system_wakeup();
+}
+
+
+int uv_loop_init(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+  struct heap* timer_heap;
+  int err;
+
+  /* Initialize libuv itself first */
+  uv__once_init();
+
+  /* Create an I/O completion port */
+  loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
+  if (loop->iocp == NULL)
+    return uv_translate_sys_error(GetLastError());
+
+  lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields));
+  if (lfields == NULL)
+    return UV_ENOMEM;
+  loop->internal_fields = lfields;
+
+  err = uv_mutex_init(&lfields->loop_metrics.lock);
+  if (err)
+    goto fail_metrics_mutex_init;
+
+  /* To prevent uninitialized memory access, loop->time must be initialized
+   * to zero before calling uv_update_time for the first time.
+   */
+  loop->time = 0;
+  uv_update_time(loop);
+
+  QUEUE_INIT(&loop->wq);
+  QUEUE_INIT(&loop->handle_queue);
+  loop->active_reqs.count = 0;
+  loop->active_handles = 0;
+
+  loop->pending_reqs_tail = NULL;
+
+  loop->endgame_handles = NULL;
+
+  loop->timer_heap = timer_heap = uv__malloc(sizeof(*timer_heap));
+  if (timer_heap == NULL) {
+    err = UV_ENOMEM;
+    goto fail_timers_alloc;
+  }
+
+  heap_init(timer_heap);
+
+  loop->check_handles = NULL;
+  loop->prepare_handles = NULL;
+  loop->idle_handles = NULL;
+
+  loop->next_prepare_handle = NULL;
+  loop->next_check_handle = NULL;
+  loop->next_idle_handle = NULL;
+
+  memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets);
+
+  loop->active_tcp_streams = 0;
+  loop->active_udp_streams = 0;
+
+  loop->timer_counter = 0;
+  loop->stop_flag = 0;
+
+  err = uv_mutex_init(&loop->wq_mutex);
+  if (err)
+    goto fail_mutex_init;
+
+  err = uv_async_init(loop, &loop->wq_async, uv__work_done);
+  if (err)
+    goto fail_async_init;
+
+  uv__handle_unref(&loop->wq_async);
+  loop->wq_async.flags |= UV_HANDLE_INTERNAL;
+
+  err = uv__loops_add(loop);
+  if (err)
+    goto fail_async_init;
+
+  return 0;
+
+fail_async_init:
+  uv_mutex_destroy(&loop->wq_mutex);
+
+fail_mutex_init:
+  uv__free(timer_heap);
+  loop->timer_heap = NULL;
+
+fail_timers_alloc:
+  uv_mutex_destroy(&lfields->loop_metrics.lock);
+
+fail_metrics_mutex_init:
+  uv__free(lfields);
+  loop->internal_fields = NULL;
+  CloseHandle(loop->iocp);
+  loop->iocp = INVALID_HANDLE_VALUE;
+
+  return err;
+}
+
+
+void uv_update_time(uv_loop_t* loop) {
+  uint64_t new_time = uv__hrtime(1000);
+  assert(new_time >= loop->time);
+  loop->time = new_time;
+}
+
+
+void uv__once_init(void) {
+  uv_once(&uv_init_guard_, uv_init);
+}
+
+
+void uv__loop_close(uv_loop_t* loop) {
+  uv__loop_internal_fields_t* lfields;
+  size_t i;
+
+  uv__loops_remove(loop);
+
+  /* Close the async handle without needing an extra loop iteration.
+   * We might have a pending message, but we're just going to destroy the IOCP
+   * soon, so we can just discard it now without the usual risk of a getting
+   * another notification from GetQueuedCompletionStatusEx after calling the
+   * close_cb (which we also skip defining). We'll assert later that queue was
+   * actually empty and all reqs handled. */
+  loop->wq_async.async_sent = 0;
+  loop->wq_async.close_cb = NULL;
+  uv__handle_closing(&loop->wq_async);
+  uv__handle_close(&loop->wq_async);
+
+  for (i = 0; i < ARRAY_SIZE(loop->poll_peer_sockets); i++) {
+    SOCKET sock = loop->poll_peer_sockets[i];
+    if (sock != 0 && sock != INVALID_SOCKET)
+      closesocket(sock);
+  }
+
+  uv_mutex_lock(&loop->wq_mutex);
+  assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
+  assert(!uv__has_active_reqs(loop));
+  uv_mutex_unlock(&loop->wq_mutex);
+  uv_mutex_destroy(&loop->wq_mutex);
+
+  uv__free(loop->timer_heap);
+  loop->timer_heap = NULL;
+
+  lfields = uv__get_internal_fields(loop);
+  uv_mutex_destroy(&lfields->loop_metrics.lock);
+  uv__free(lfields);
+  loop->internal_fields = NULL;
+
+  CloseHandle(loop->iocp);
+}
+
+
+int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
+  uv__loop_internal_fields_t* lfields;
+
+  lfields = uv__get_internal_fields(loop);
+  if (option == UV_METRICS_IDLE_TIME) {
+    lfields->flags |= UV_METRICS_IDLE_TIME;
+    return 0;
+  }
+
+  return UV_ENOSYS;
+}
+
+
+int uv_backend_fd(const uv_loop_t* loop) {
+  return -1;
+}
+
+
+int uv_loop_fork(uv_loop_t* loop) {
+  return UV_ENOSYS;
+}
+
+
+int uv_backend_timeout(const uv_loop_t* loop) {
+  if (loop->stop_flag != 0)
+    return 0;
+
+  if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop))
+    return 0;
+
+  if (loop->pending_reqs_tail)
+    return 0;
+
+  if (loop->endgame_handles)
+    return 0;
+
+  if (loop->idle_handles)
+    return 0;
+
+  return uv__next_timeout(loop);
+}
+
+
+static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
+  DWORD bytes;
+  ULONG_PTR key;
+  OVERLAPPED* overlapped;
+  uv_req_t* req;
+  int repeat;
+  uint64_t timeout_time;
+  uint64_t user_timeout;
+  int reset_timeout;
+
+  timeout_time = loop->time + timeout;
+
+  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+  }
+
+  for (repeat = 0; ; repeat++) {
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    GetQueuedCompletionStatus(loop->iocp,
+                              &bytes,
+                              &key,
+                              &overlapped,
+                              timeout);
+
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+    }
+
+    /* Placed here because on success the loop will break whether there is an
+     * empty package or not, or if GetQueuedCompletionStatus returned early then
+     * the timeout will be updated and the loop will run again. In either case
+     * the idle time will need to be updated.
+     */
+    uv__metrics_update_idle_time(loop);
+
+    if (overlapped) {
+      /* Package was dequeued */
+      req = uv_overlapped_to_req(overlapped);
+      uv_insert_pending_req(loop, req);
+
+      /* Some time might have passed waiting for I/O,
+       * so update the loop time here.
+       */
+      uv_update_time(loop);
+    } else if (GetLastError() != WAIT_TIMEOUT) {
+      /* Serious error */
+      uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus");
+    } else if (timeout > 0) {
+      /* GetQueuedCompletionStatus can occasionally return a little early.
+       * Make sure that the desired timeout target time is reached.
+       */
+      uv_update_time(loop);
+      if (timeout_time > loop->time) {
+        timeout = (DWORD)(timeout_time - loop->time);
+        /* The first call to GetQueuedCompletionStatus should return very
+         * close to the target time and the second should reach it, but
+         * this is not stated in the documentation. To make sure a busy
+         * loop cannot happen, the timeout is increased exponentially
+         * starting on the third round.
+         */
+        timeout += repeat ? (1 << (repeat - 1)) : 0;
+        continue;
+      }
+    }
+    break;
+  }
+}
+
+
+static void uv__poll(uv_loop_t* loop, DWORD timeout) {
+  BOOL success;
+  uv_req_t* req;
+  OVERLAPPED_ENTRY overlappeds[128];
+  ULONG count;
+  ULONG i;
+  int repeat;
+  uint64_t timeout_time;
+  uint64_t user_timeout;
+  int reset_timeout;
+
+  timeout_time = loop->time + timeout;
+
+  if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
+    reset_timeout = 1;
+    user_timeout = timeout;
+    timeout = 0;
+  } else {
+    reset_timeout = 0;
+  }
+
+  for (repeat = 0; ; repeat++) {
+    /* Only need to set the provider_entry_time if timeout != 0. The function
+     * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
+     */
+    if (timeout != 0)
+      uv__metrics_set_provider_entry_time(loop);
+
+    success = pGetQueuedCompletionStatusEx(loop->iocp,
+                                           overlappeds,
+                                           ARRAY_SIZE(overlappeds),
+                                           &count,
+                                           timeout,
+                                           FALSE);
+
+    if (reset_timeout != 0) {
+      timeout = user_timeout;
+      reset_timeout = 0;
+    }
+
+    /* Placed here because on success the loop will break whether there is an
+     * empty package or not, or if GetQueuedCompletionStatus returned early then
+     * the timeout will be updated and the loop will run again. In either case
+     * the idle time will need to be updated.
+     */
+    uv__metrics_update_idle_time(loop);
+
+    if (success) {
+      for (i = 0; i < count; i++) {
+        /* Package was dequeued, but see if it is not a empty package
+         * meant only to wake us up.
+         */
+        if (overlappeds[i].lpOverlapped) {
+          req = uv_overlapped_to_req(overlappeds[i].lpOverlapped);
+          uv_insert_pending_req(loop, req);
+        }
+      }
+
+      /* Some time might have passed waiting for I/O,
+       * so update the loop time here.
+       */
+      uv_update_time(loop);
+    } else if (GetLastError() != WAIT_TIMEOUT) {
+      /* Serious error */
+      uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx");
+    } else if (timeout > 0) {
+      /* GetQueuedCompletionStatus can occasionally return a little early.
+       * Make sure that the desired timeout target time is reached.
+       */
+      uv_update_time(loop);
+      if (timeout_time > loop->time) {
+        timeout = (DWORD)(timeout_time - loop->time);
+        /* The first call to GetQueuedCompletionStatus should return very
+         * close to the target time and the second should reach it, but
+         * this is not stated in the documentation. To make sure a busy
+         * loop cannot happen, the timeout is increased exponentially
+         * starting on the third round.
+         */
+        timeout += repeat ? (1 << (repeat - 1)) : 0;
+        continue;
+      }
+    }
+    break;
+  }
+}
+
+
+static int uv__loop_alive(const uv_loop_t* loop) {
+  return uv__has_active_handles(loop) ||
+         uv__has_active_reqs(loop) ||
+         loop->endgame_handles != NULL;
+}
+
+
+int uv_loop_alive(const uv_loop_t* loop) {
+    return uv__loop_alive(loop);
+}
+
+
+int uv_run(uv_loop_t *loop, uv_run_mode mode) {
+  DWORD timeout;
+  int r;
+  int ran_pending;
+
+  r = uv__loop_alive(loop);
+  if (!r)
+    uv_update_time(loop);
+
+  while (r != 0 && loop->stop_flag == 0) {
+    uv_update_time(loop);
+    uv__run_timers(loop);
+
+    ran_pending = uv_process_reqs(loop);
+    uv_idle_invoke(loop);
+    uv_prepare_invoke(loop);
+
+    timeout = 0;
+    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
+      timeout = uv_backend_timeout(loop);
+
+    if (pGetQueuedCompletionStatusEx)
+      uv__poll(loop, timeout);
+    else
+      uv__poll_wine(loop, timeout);
+
+    /* Run one final update on the provider_idle_time in case uv__poll*
+     * returned because the timeout expired, but no events were received. This
+     * call will be ignored if the provider_entry_time was either never set (if
+     * the timeout == 0) or was already updated b/c an event was received.
+     */
+    uv__metrics_update_idle_time(loop);
+
+    uv_check_invoke(loop);
+    uv_process_endgames(loop);
+
+    if (mode == UV_RUN_ONCE) {
+      /* UV_RUN_ONCE implies forward progress: at least one callback must have
+       * been invoked when it returns. uv__io_poll() can return without doing
+       * I/O (meaning: no callbacks) when its timeout expires - which means we
+       * have pending timers that satisfy the forward progress constraint.
+       *
+       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
+       * the check.
+       */
+      uv__run_timers(loop);
+    }
+
+    r = uv__loop_alive(loop);
+    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
+      break;
+  }
+
+  /* The if statement lets the compiler compile it to a conditional store.
+   * Avoids dirtying a cache line.
+   */
+  if (loop->stop_flag != 0)
+    loop->stop_flag = 0;
+
+  return r;
+}
+
+
+int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) {
+  uv_os_fd_t fd_out;
+
+  switch (handle->type) {
+  case UV_TCP:
+    fd_out = (uv_os_fd_t)((uv_tcp_t*) handle)->socket;
+    break;
+
+  case UV_NAMED_PIPE:
+    fd_out = ((uv_pipe_t*) handle)->handle;
+    break;
+
+  case UV_TTY:
+    fd_out = ((uv_tty_t*) handle)->handle;
+    break;
+
+  case UV_UDP:
+    fd_out = (uv_os_fd_t)((uv_udp_t*) handle)->socket;
+    break;
+
+  case UV_POLL:
+    fd_out = (uv_os_fd_t)((uv_poll_t*) handle)->socket;
+    break;
+
+  default:
+    return UV_EINVAL;
+  }
+
+  if (uv_is_closing(handle) || fd_out == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+
+  *fd = fd_out;
+  return 0;
+}
+
+
+int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) {
+  int r;
+  int len;
+  SOCKET socket;
+
+  if (handle == NULL || value == NULL)
+    return UV_EINVAL;
+
+  if (handle->type == UV_TCP)
+    socket = ((uv_tcp_t*) handle)->socket;
+  else if (handle->type == UV_UDP)
+    socket = ((uv_udp_t*) handle)->socket;
+  else
+    return UV_ENOTSUP;
+
+  len = sizeof(*value);
+
+  if (*value == 0)
+    r = getsockopt(socket, SOL_SOCKET, optname, (char*) value, &len);
+  else
+    r = setsockopt(socket, SOL_SOCKET, optname, (const char*) value, len);
+
+  if (r == SOCKET_ERROR)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return 0;
+}
+
+int uv_cpumask_size(void) {
+  return (int)(sizeof(DWORD_PTR) * 8);
+}
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error) {
+
+  int result;
+  uv_os_fd_t fd;
+
+  result = uv_fileno(handle, &fd);
+  if (result != 0)
+    return result;
+
+  if (delayed_error)
+    return uv_translate_sys_error(delayed_error);
+
+  result = func((SOCKET) fd, name, namelen);
+  if (result != 0)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return 0;
+}
diff --git a/third_party/libuv/src/win/detect-wakeup.c b/third_party/libuv/src/win/detect-wakeup.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/detect-wakeup.c
@@ -0,0 +1,56 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include "winapi.h"
+
+static void uv__register_system_resume_callback(void);
+
+void uv__init_detect_system_wakeup(void) {
+  /* Try registering system power event callback. This is the cleanest
+   * method, but it will only work on Win8 and above.
+   */
+  uv__register_system_resume_callback();
+}
+
+static ULONG CALLBACK uv__system_resume_callback(PVOID Context,
+                                                 ULONG Type,
+                                                 PVOID Setting) {
+  if (Type == PBT_APMRESUMESUSPEND || Type == PBT_APMRESUMEAUTOMATIC)
+    uv__wake_all_loops();
+
+  return 0;
+}
+
+static void uv__register_system_resume_callback(void) {
+  _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS recipient;
+  _HPOWERNOTIFY registration_handle;
+
+  if (pPowerRegisterSuspendResumeNotification == NULL)
+    return;
+
+  recipient.Callback = uv__system_resume_callback;
+  recipient.Context = NULL;
+  (*pPowerRegisterSuspendResumeNotification)(DEVICE_NOTIFY_CALLBACK,
+                                             &recipient,
+                                             &registration_handle);
+}
diff --git a/third_party/libuv/src/win/dl.c b/third_party/libuv/src/win/dl.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/dl.c
@@ -0,0 +1,136 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno);
+
+
+int uv_dlopen(const char* filename, uv_lib_t* lib) {
+  WCHAR filename_w[32768];
+
+  lib->handle = NULL;
+  lib->errmsg = NULL;
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           filename,
+                           -1,
+                           filename_w,
+                           ARRAY_SIZE(filename_w))) {
+    return uv__dlerror(lib, filename, GetLastError());
+  }
+
+  lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+  if (lib->handle == NULL) {
+    return uv__dlerror(lib, filename, GetLastError());
+  }
+
+  return 0;
+}
+
+
+void uv_dlclose(uv_lib_t* lib) {
+  if (lib->errmsg) {
+    LocalFree((void*)lib->errmsg);
+    lib->errmsg = NULL;
+  }
+
+  if (lib->handle) {
+    /* Ignore errors. No good way to signal them without leaking memory. */
+    FreeLibrary(lib->handle);
+    lib->handle = NULL;
+  }
+}
+
+
+int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) {
+  /* Cast though integer to suppress pedantic warning about forbidden cast. */
+  *ptr = (void*)(uintptr_t) GetProcAddress(lib->handle, name);
+  return uv__dlerror(lib, "", *ptr ? 0 : GetLastError());
+}
+
+
+const char* uv_dlerror(const uv_lib_t* lib) {
+  return lib->errmsg ? lib->errmsg : "no error";
+}
+
+
+static void uv__format_fallback_error(uv_lib_t* lib, int errorno){
+  static const CHAR fallback_error[] = "error: %1!d!";
+  DWORD_PTR args[1];
+  args[0] = (DWORD_PTR) errorno;
+
+  FormatMessageA(FORMAT_MESSAGE_FROM_STRING |
+                 FORMAT_MESSAGE_ARGUMENT_ARRAY |
+                 FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                 fallback_error, 0, 0,
+                 (LPSTR) &lib->errmsg,
+                 0, (va_list*) args);
+}
+
+
+
+static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno) {
+  DWORD_PTR arg;
+  DWORD res;
+  char* msg;
+
+  if (lib->errmsg) {
+    LocalFree(lib->errmsg);
+    lib->errmsg = NULL;
+  }
+
+  if (errorno == 0)
+    return 0;
+
+  res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
+                       MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+                       (LPSTR) &lib->errmsg, 0, NULL);
+
+  if (!res && (GetLastError() == ERROR_MUI_FILE_NOT_FOUND ||
+               GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND)) {
+    res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                         FORMAT_MESSAGE_FROM_SYSTEM |
+                         FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
+                         0, (LPSTR) &lib->errmsg, 0, NULL);
+  }
+
+  if (res && errorno == ERROR_BAD_EXE_FORMAT && strstr(lib->errmsg, "%1")) {
+    msg = lib->errmsg;
+    lib->errmsg = NULL;
+    arg = (DWORD_PTR) filename;
+    res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                         FORMAT_MESSAGE_ARGUMENT_ARRAY |
+                         FORMAT_MESSAGE_FROM_STRING,
+                         msg,
+                         0, 0, (LPSTR) &lib->errmsg, 0, (va_list*) &arg);
+    LocalFree(msg);
+  }
+
+  if (!res)
+    uv__format_fallback_error(lib, errorno);
+
+  return -1;
+}
diff --git a/third_party/libuv/src/win/error.c b/third_party/libuv/src/win/error.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/error.c
@@ -0,0 +1,173 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+/*
+ * Display an error message and abort the event loop.
+ */
+void uv_fatal_error(const int errorno, const char* syscall) {
+  char* buf = NULL;
+  const char* errmsg;
+
+  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+      FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
+      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL);
+
+  if (buf) {
+    errmsg = buf;
+  } else {
+    errmsg = "Unknown error";
+  }
+
+  /* FormatMessage messages include a newline character already, so don't add
+   * another. */
+  if (syscall) {
+    fprintf(stderr, "%s: (%d) %s", syscall, errorno, errmsg);
+  } else {
+    fprintf(stderr, "(%d) %s", errorno, errmsg);
+  }
+
+  if (buf) {
+    LocalFree(buf);
+  }
+
+  DebugBreak();
+  abort();
+}
+
+
+int uv_translate_sys_error(int sys_errno) {
+  if (sys_errno <= 0) {
+    return sys_errno;  /* If < 0 then it's already a libuv error. */
+  }
+
+  switch (sys_errno) {
+    case ERROR_NOACCESS:                    return UV_EACCES;
+    case WSAEACCES:                         return UV_EACCES;
+    case ERROR_ELEVATION_REQUIRED:          return UV_EACCES;
+    case ERROR_CANT_ACCESS_FILE:            return UV_EACCES;
+    case ERROR_ADDRESS_ALREADY_ASSOCIATED:  return UV_EADDRINUSE;
+    case WSAEADDRINUSE:                     return UV_EADDRINUSE;
+    case WSAEADDRNOTAVAIL:                  return UV_EADDRNOTAVAIL;
+    case WSAEAFNOSUPPORT:                   return UV_EAFNOSUPPORT;
+    case WSAEWOULDBLOCK:                    return UV_EAGAIN;
+    case WSAEALREADY:                       return UV_EALREADY;
+    case ERROR_INVALID_FLAGS:               return UV_EBADF;
+    case ERROR_INVALID_HANDLE:              return UV_EBADF;
+    case ERROR_LOCK_VIOLATION:              return UV_EBUSY;
+    case ERROR_PIPE_BUSY:                   return UV_EBUSY;
+    case ERROR_SHARING_VIOLATION:           return UV_EBUSY;
+    case ERROR_OPERATION_ABORTED:           return UV_ECANCELED;
+    case WSAEINTR:                          return UV_ECANCELED;
+    case ERROR_NO_UNICODE_TRANSLATION:      return UV_ECHARSET;
+    case ERROR_CONNECTION_ABORTED:          return UV_ECONNABORTED;
+    case WSAECONNABORTED:                   return UV_ECONNABORTED;
+    case ERROR_CONNECTION_REFUSED:          return UV_ECONNREFUSED;
+    case WSAECONNREFUSED:                   return UV_ECONNREFUSED;
+    case ERROR_NETNAME_DELETED:             return UV_ECONNRESET;
+    case WSAECONNRESET:                     return UV_ECONNRESET;
+    case ERROR_ALREADY_EXISTS:              return UV_EEXIST;
+    case ERROR_FILE_EXISTS:                 return UV_EEXIST;
+    case ERROR_BUFFER_OVERFLOW:             return UV_EFAULT;
+    case WSAEFAULT:                         return UV_EFAULT;
+    case ERROR_HOST_UNREACHABLE:            return UV_EHOSTUNREACH;
+    case WSAEHOSTUNREACH:                   return UV_EHOSTUNREACH;
+    case ERROR_INSUFFICIENT_BUFFER:         return UV_EINVAL;
+    case ERROR_INVALID_DATA:                return UV_EINVAL;
+    case ERROR_INVALID_PARAMETER:           return UV_EINVAL;
+    case ERROR_SYMLINK_NOT_SUPPORTED:       return UV_EINVAL;
+    case WSAEINVAL:                         return UV_EINVAL;
+    case WSAEPFNOSUPPORT:                   return UV_EINVAL;
+    case WSAESOCKTNOSUPPORT:                return UV_EINVAL;
+    case ERROR_BEGINNING_OF_MEDIA:          return UV_EIO;
+    case ERROR_BUS_RESET:                   return UV_EIO;
+    case ERROR_CRC:                         return UV_EIO;
+    case ERROR_DEVICE_DOOR_OPEN:            return UV_EIO;
+    case ERROR_DEVICE_REQUIRES_CLEANING:    return UV_EIO;
+    case ERROR_DISK_CORRUPT:                return UV_EIO;
+    case ERROR_EOM_OVERFLOW:                return UV_EIO;
+    case ERROR_FILEMARK_DETECTED:           return UV_EIO;
+    case ERROR_GEN_FAILURE:                 return UV_EIO;
+    case ERROR_INVALID_BLOCK_LENGTH:        return UV_EIO;
+    case ERROR_IO_DEVICE:                   return UV_EIO;
+    case ERROR_NO_DATA_DETECTED:            return UV_EIO;
+    case ERROR_NO_SIGNAL_SENT:              return UV_EIO;
+    case ERROR_OPEN_FAILED:                 return UV_EIO;
+    case ERROR_SETMARK_DETECTED:            return UV_EIO;
+    case ERROR_SIGNAL_REFUSED:              return UV_EIO;
+    case WSAEISCONN:                        return UV_EISCONN;
+    case ERROR_CANT_RESOLVE_FILENAME:       return UV_ELOOP;
+    case ERROR_TOO_MANY_OPEN_FILES:         return UV_EMFILE;
+    case WSAEMFILE:                         return UV_EMFILE;
+    case WSAEMSGSIZE:                       return UV_EMSGSIZE;
+    case ERROR_FILENAME_EXCED_RANGE:        return UV_ENAMETOOLONG;
+    case ERROR_NETWORK_UNREACHABLE:         return UV_ENETUNREACH;
+    case WSAENETUNREACH:                    return UV_ENETUNREACH;
+    case WSAENOBUFS:                        return UV_ENOBUFS;
+    case ERROR_BAD_PATHNAME:                return UV_ENOENT;
+    case ERROR_DIRECTORY:                   return UV_ENOENT;
+    case ERROR_ENVVAR_NOT_FOUND:            return UV_ENOENT;
+    case ERROR_FILE_NOT_FOUND:              return UV_ENOENT;
+    case ERROR_INVALID_NAME:                return UV_ENOENT;
+    case ERROR_INVALID_DRIVE:               return UV_ENOENT;
+    case ERROR_INVALID_REPARSE_DATA:        return UV_ENOENT;
+    case ERROR_MOD_NOT_FOUND:               return UV_ENOENT;
+    case ERROR_PATH_NOT_FOUND:              return UV_ENOENT;
+    case WSAHOST_NOT_FOUND:                 return UV_ENOENT;
+    case WSANO_DATA:                        return UV_ENOENT;
+    case ERROR_NOT_ENOUGH_MEMORY:           return UV_ENOMEM;
+    case ERROR_OUTOFMEMORY:                 return UV_ENOMEM;
+    case ERROR_CANNOT_MAKE:                 return UV_ENOSPC;
+    case ERROR_DISK_FULL:                   return UV_ENOSPC;
+    case ERROR_EA_TABLE_FULL:               return UV_ENOSPC;
+    case ERROR_END_OF_MEDIA:                return UV_ENOSPC;
+    case ERROR_HANDLE_DISK_FULL:            return UV_ENOSPC;
+    case ERROR_NOT_CONNECTED:               return UV_ENOTCONN;
+    case WSAENOTCONN:                       return UV_ENOTCONN;
+    case ERROR_DIR_NOT_EMPTY:               return UV_ENOTEMPTY;
+    case WSAENOTSOCK:                       return UV_ENOTSOCK;
+    case ERROR_NOT_SUPPORTED:               return UV_ENOTSUP;
+    case ERROR_BROKEN_PIPE:                 return UV_EOF;
+    case ERROR_ACCESS_DENIED:               return UV_EPERM;
+    case ERROR_PRIVILEGE_NOT_HELD:          return UV_EPERM;
+    case ERROR_BAD_PIPE:                    return UV_EPIPE;
+    case ERROR_NO_DATA:                     return UV_EPIPE;
+    case ERROR_PIPE_NOT_CONNECTED:          return UV_EPIPE;
+    case WSAESHUTDOWN:                      return UV_EPIPE;
+    case WSAEPROTONOSUPPORT:                return UV_EPROTONOSUPPORT;
+    case ERROR_WRITE_PROTECT:               return UV_EROFS;
+    case ERROR_SEM_TIMEOUT:                 return UV_ETIMEDOUT;
+    case WSAETIMEDOUT:                      return UV_ETIMEDOUT;
+    case ERROR_NOT_SAME_DEVICE:             return UV_EXDEV;
+    case ERROR_INVALID_FUNCTION:            return UV_EISDIR;
+    case ERROR_META_EXPANSION_TOO_LONG:     return UV_E2BIG;
+    default:                                return UV_UNKNOWN;
+  }
+}
diff --git a/third_party/libuv/src/win/fs-event.c b/third_party/libuv/src/win/fs-event.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/fs-event.c
@@ -0,0 +1,608 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+const unsigned int uv_directory_watcher_buffer_size = 4096;
+
+
+static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
+    uv_fs_event_t* handle) {
+  assert(handle->dir_handle != INVALID_HANDLE_VALUE);
+  assert(!handle->req_pending);
+
+  memset(&(handle->req.u.io.overlapped), 0,
+         sizeof(handle->req.u.io.overlapped));
+  if (!ReadDirectoryChangesW(handle->dir_handle,
+                             handle->buffer,
+                             uv_directory_watcher_buffer_size,
+                             (handle->flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
+                             FILE_NOTIFY_CHANGE_FILE_NAME      |
+                               FILE_NOTIFY_CHANGE_DIR_NAME     |
+                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
+                               FILE_NOTIFY_CHANGE_SIZE         |
+                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
+                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
+                               FILE_NOTIFY_CHANGE_CREATION     |
+                               FILE_NOTIFY_CHANGE_SECURITY,
+                             NULL,
+                             &handle->req.u.io.overlapped,
+                             NULL)) {
+    /* Make this req pending reporting an error. */
+    SET_REQ_ERROR(&handle->req, GetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*)&handle->req);
+  }
+
+  handle->req_pending = 1;
+}
+
+static void uv_relative_path(const WCHAR* filename,
+                             const WCHAR* dir,
+                             WCHAR** relpath) {
+  size_t relpathlen;
+  size_t filenamelen = wcslen(filename);
+  size_t dirlen = wcslen(dir);
+  assert(!_wcsnicmp(filename, dir, dirlen));
+  if (dirlen > 0 && dir[dirlen - 1] == '\\')
+    dirlen--;
+  relpathlen = filenamelen - dirlen - 1;
+  *relpath = uv__malloc((relpathlen + 1) * sizeof(WCHAR));
+  if (!*relpath)
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  wcsncpy(*relpath, filename + dirlen + 1, relpathlen);
+  (*relpath)[relpathlen] = L'\0';
+}
+
+static int uv_split_path(const WCHAR* filename, WCHAR** dir,
+    WCHAR** file) {
+  size_t len, i;
+  DWORD dir_len;
+
+  if (filename == NULL) {
+    if (dir != NULL)
+      *dir = NULL;
+    *file = NULL;
+    return 0;
+  }
+
+  len = wcslen(filename);
+  i = len;
+  while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
+
+  if (i == 0) {
+    if (dir) {
+      dir_len = GetCurrentDirectoryW(0, NULL);
+      if (dir_len == 0) {
+        return -1;
+      }
+      *dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR));
+      if (!*dir) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+
+      if (!GetCurrentDirectoryW(dir_len, *dir)) {
+        uv__free(*dir);
+        *dir = NULL;
+        return -1;
+      }
+    }
+
+    *file = wcsdup(filename);
+  } else {
+    if (dir) {
+      *dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
+      if (!*dir) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+      wcsncpy(*dir, filename, i + 1);
+      (*dir)[i + 1] = L'\0';
+    }
+
+    *file = (WCHAR*)uv__malloc((len - i) * sizeof(WCHAR));
+    if (!*file) {
+      uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+    }
+    wcsncpy(*file, filename + i + 1, len - i - 1);
+    (*file)[len - i - 1] = L'\0';
+  }
+
+  return 0;
+}
+
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_FS_EVENT);
+  handle->dir_handle = INVALID_HANDLE_VALUE;
+  handle->buffer = NULL;
+  handle->req_pending = 0;
+  handle->filew = NULL;
+  handle->short_filew = NULL;
+  handle->dirw = NULL;
+
+  UV_REQ_INIT(&handle->req, UV_FS_EVENT_REQ);
+  handle->req.data = handle;
+
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+  int name_size, is_path_dir, size;
+  DWORD attr, last_error;
+  WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
+  DWORD short_path_buffer_len;
+  WCHAR *short_path_buffer;
+  WCHAR* short_path, *long_path;
+
+  short_path = NULL;
+  if (uv__is_active(handle))
+    return UV_EINVAL;
+
+  handle->cb = cb;
+  handle->path = uv__strdup(path);
+  if (!handle->path) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  uv__handle_start(handle);
+
+  /* Convert name to UTF16. */
+
+  name_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) *
+              sizeof(WCHAR);
+  pathw = (WCHAR*)uv__malloc(name_size);
+  if (!pathw) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           path,
+                           -1,
+                           pathw,
+                           name_size / sizeof(WCHAR))) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* Determine whether path is a file or a directory. */
+  attr = GetFileAttributesW(pathw);
+  if (attr == INVALID_FILE_ATTRIBUTES) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
+
+  if (is_path_dir) {
+     /* path is a directory, so that's the directory that we will watch. */
+
+    /* Convert to long path. */
+    size = GetLongPathNameW(pathw, NULL, 0);
+
+    if (size) {
+      long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
+      if (!long_path) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+
+      size = GetLongPathNameW(pathw, long_path, size);
+      if (size) {
+        long_path[size] = '\0';
+      } else {
+        uv__free(long_path);
+        long_path = NULL;
+      }
+
+      if (long_path) {
+        uv__free(pathw);
+        pathw = long_path;
+      }
+    }
+
+    dir_to_watch = pathw;
+  } else {
+    /*
+     * path is a file.  So we split path into dir & file parts, and
+     * watch the dir directory.
+     */
+
+    /* Convert to short path. */
+    short_path_buffer = NULL;
+    short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0);
+    if (short_path_buffer_len == 0) {
+      goto short_path_done;
+    }
+    short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR));
+    if (short_path_buffer == NULL) {
+      goto short_path_done;
+    }
+    if (GetShortPathNameW(pathw,
+                          short_path_buffer,
+                          short_path_buffer_len) == 0) {
+      uv__free(short_path_buffer);
+      short_path_buffer = NULL;
+    }
+short_path_done:
+    short_path = short_path_buffer;
+
+    if (uv_split_path(pathw, &dir, &handle->filew) != 0) {
+      last_error = GetLastError();
+      goto error;
+    }
+
+    if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) {
+      last_error = GetLastError();
+      goto error;
+    }
+
+    dir_to_watch = dir;
+    uv__free(pathw);
+    pathw = NULL;
+  }
+
+  handle->dir_handle = CreateFileW(dir_to_watch,
+                                   FILE_LIST_DIRECTORY,
+                                   FILE_SHARE_READ | FILE_SHARE_DELETE |
+                                     FILE_SHARE_WRITE,
+                                   NULL,
+                                   OPEN_EXISTING,
+                                   FILE_FLAG_BACKUP_SEMANTICS |
+                                     FILE_FLAG_OVERLAPPED,
+                                   NULL);
+
+  if (dir) {
+    uv__free(dir);
+    dir = NULL;
+  }
+
+  if (handle->dir_handle == INVALID_HANDLE_VALUE) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  if (CreateIoCompletionPort(handle->dir_handle,
+                             handle->loop->iocp,
+                             (ULONG_PTR)handle,
+                             0) == NULL) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  if (!handle->buffer) {
+    handle->buffer = (char*)uv__malloc(uv_directory_watcher_buffer_size);
+  }
+  if (!handle->buffer) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  memset(&(handle->req.u.io.overlapped), 0,
+         sizeof(handle->req.u.io.overlapped));
+
+  if (!ReadDirectoryChangesW(handle->dir_handle,
+                             handle->buffer,
+                             uv_directory_watcher_buffer_size,
+                             (flags & UV_FS_EVENT_RECURSIVE) ? TRUE : FALSE,
+                             FILE_NOTIFY_CHANGE_FILE_NAME      |
+                               FILE_NOTIFY_CHANGE_DIR_NAME     |
+                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
+                               FILE_NOTIFY_CHANGE_SIZE         |
+                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
+                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
+                               FILE_NOTIFY_CHANGE_CREATION     |
+                               FILE_NOTIFY_CHANGE_SECURITY,
+                             NULL,
+                             &handle->req.u.io.overlapped,
+                             NULL)) {
+    last_error = GetLastError();
+    goto error;
+  }
+
+  assert(is_path_dir ? pathw != NULL : pathw == NULL);
+  handle->dirw = pathw;
+  handle->req_pending = 1;
+  return 0;
+
+error:
+  if (handle->path) {
+    uv__free(handle->path);
+    handle->path = NULL;
+  }
+
+  if (handle->filew) {
+    uv__free(handle->filew);
+    handle->filew = NULL;
+  }
+
+  if (handle->short_filew) {
+    uv__free(handle->short_filew);
+    handle->short_filew = NULL;
+  }
+
+  uv__free(pathw);
+
+  if (handle->dir_handle != INVALID_HANDLE_VALUE) {
+    CloseHandle(handle->dir_handle);
+    handle->dir_handle = INVALID_HANDLE_VALUE;
+  }
+
+  if (handle->buffer) {
+    uv__free(handle->buffer);
+    handle->buffer = NULL;
+  }
+
+  if (uv__is_active(handle))
+    uv__handle_stop(handle);
+
+  uv__free(short_path);
+
+  return uv_translate_sys_error(last_error);
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  if (!uv__is_active(handle))
+    return 0;
+
+  if (handle->dir_handle != INVALID_HANDLE_VALUE) {
+    CloseHandle(handle->dir_handle);
+    handle->dir_handle = INVALID_HANDLE_VALUE;
+  }
+
+  uv__handle_stop(handle);
+
+  if (handle->filew) {
+    uv__free(handle->filew);
+    handle->filew = NULL;
+  }
+
+  if (handle->short_filew) {
+    uv__free(handle->short_filew);
+    handle->short_filew = NULL;
+  }
+
+  if (handle->path) {
+    uv__free(handle->path);
+    handle->path = NULL;
+  }
+
+  if (handle->dirw) {
+    uv__free(handle->dirw);
+    handle->dirw = NULL;
+  }
+
+  return 0;
+}
+
+
+static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) {
+  size_t str_len;
+
+  if (str == NULL)
+    return -1;
+
+  str_len = wcslen(str);
+
+  /*
+    Since we only care about equality, return early if the strings
+    aren't the same length
+  */
+  if (str_len != (file_name_len / sizeof(WCHAR)))
+    return -1;
+
+  return _wcsnicmp(str, file_name, str_len);
+}
+
+
+void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
+    uv_fs_event_t* handle) {
+  FILE_NOTIFY_INFORMATION* file_info;
+  int err, sizew, size;
+  char* filename = NULL;
+  WCHAR* filenamew = NULL;
+  WCHAR* long_filenamew = NULL;
+  DWORD offset = 0;
+
+  assert(req->type == UV_FS_EVENT_REQ);
+  assert(handle->req_pending);
+  handle->req_pending = 0;
+
+  /* Don't report any callbacks if:
+   * - We're closing, just push the handle onto the endgame queue
+   * - We are not active, just ignore the callback
+   */
+  if (!uv__is_active(handle)) {
+    if (handle->flags & UV_HANDLE_CLOSING) {
+      uv_want_endgame(loop, (uv_handle_t*) handle);
+    }
+    return;
+  }
+
+  file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset);
+
+  if (REQ_SUCCESS(req)) {
+    if (req->u.io.overlapped.InternalHigh > 0) {
+      do {
+        file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset);
+        assert(!filename);
+        assert(!filenamew);
+        assert(!long_filenamew);
+
+        /*
+         * Fire the event only if we were asked to watch a directory,
+         * or if the filename filter matches.
+         */
+        if (handle->dirw ||
+            file_info_cmp(handle->filew,
+                          file_info->FileName,
+                          file_info->FileNameLength) == 0 ||
+            file_info_cmp(handle->short_filew,
+                          file_info->FileName,
+                          file_info->FileNameLength) == 0) {
+
+          if (handle->dirw) {
+            /*
+             * We attempt to resolve the long form of the file name explicitly.
+             * We only do this for file names that might still exist on disk.
+             * If this fails, we use the name given by ReadDirectoryChangesW.
+             * This may be the long form or the 8.3 short name in some cases.
+             */
+            if (file_info->Action != FILE_ACTION_REMOVED &&
+              file_info->Action != FILE_ACTION_RENAMED_OLD_NAME) {
+              /* Construct a full path to the file. */
+              size = wcslen(handle->dirw) +
+                file_info->FileNameLength / sizeof(WCHAR) + 2;
+
+              filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
+              if (!filenamew) {
+                uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+              }
+
+              _snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw,
+                file_info->FileNameLength / (DWORD)sizeof(WCHAR),
+                file_info->FileName);
+
+              filenamew[size - 1] = L'\0';
+
+              /* Convert to long name. */
+              size = GetLongPathNameW(filenamew, NULL, 0);
+
+              if (size) {
+                long_filenamew = (WCHAR*)uv__malloc(size * sizeof(WCHAR));
+                if (!long_filenamew) {
+                  uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+                }
+
+                size = GetLongPathNameW(filenamew, long_filenamew, size);
+                if (size) {
+                  long_filenamew[size] = '\0';
+                } else {
+                  uv__free(long_filenamew);
+                  long_filenamew = NULL;
+                }
+              }
+
+              uv__free(filenamew);
+
+              if (long_filenamew) {
+                /* Get the file name out of the long path. */
+                uv_relative_path(long_filenamew,
+                                 handle->dirw,
+                                 &filenamew);
+                uv__free(long_filenamew);
+                long_filenamew = filenamew;
+                sizew = -1;
+              } else {
+                /* We couldn't get the long filename, use the one reported. */
+                filenamew = file_info->FileName;
+                sizew = file_info->FileNameLength / sizeof(WCHAR);
+              }
+            } else {
+              /*
+               * Removed or renamed events cannot be resolved to the long form.
+               * We therefore use the name given by ReadDirectoryChangesW.
+               * This may be the long form or the 8.3 short name in some cases.
+               */
+              filenamew = file_info->FileName;
+              sizew = file_info->FileNameLength / sizeof(WCHAR);
+            }
+          } else {
+            /* We already have the long name of the file, so just use it. */
+            filenamew = handle->filew;
+            sizew = -1;
+          }
+
+          /* Convert the filename to utf8. */
+          uv__convert_utf16_to_utf8(filenamew, sizew, &filename);
+
+          switch (file_info->Action) {
+            case FILE_ACTION_ADDED:
+            case FILE_ACTION_REMOVED:
+            case FILE_ACTION_RENAMED_OLD_NAME:
+            case FILE_ACTION_RENAMED_NEW_NAME:
+              handle->cb(handle, filename, UV_RENAME, 0);
+              break;
+
+            case FILE_ACTION_MODIFIED:
+              handle->cb(handle, filename, UV_CHANGE, 0);
+              break;
+          }
+
+          uv__free(filename);
+          filename = NULL;
+          uv__free(long_filenamew);
+          long_filenamew = NULL;
+          filenamew = NULL;
+        }
+
+        offset = file_info->NextEntryOffset;
+      } while (offset && !(handle->flags & UV_HANDLE_CLOSING));
+    } else {
+      handle->cb(handle, NULL, UV_CHANGE, 0);
+    }
+  } else {
+    err = GET_REQ_ERROR(req);
+    handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
+  }
+
+  if (!(handle->flags & UV_HANDLE_CLOSING)) {
+    uv_fs_event_queue_readdirchanges(loop, handle);
+  } else {
+    uv_want_endgame(loop, (uv_handle_t*)handle);
+  }
+}
+
+
+void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+
+  uv__handle_closing(handle);
+
+  if (!handle->req_pending) {
+    uv_want_endgame(loop, (uv_handle_t*)handle);
+  }
+
+}
+
+
+void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
+  if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+    if (handle->buffer) {
+      uv__free(handle->buffer);
+      handle->buffer = NULL;
+    }
+
+    uv__handle_close(handle);
+  }
+}
diff --git a/third_party/libuv/src/win/fs.c b/third_party/libuv/src/win/fs.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/fs.c
@@ -0,0 +1,3428 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include <stdio.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "req-inl.h"
+#include "handle-inl.h"
+#include "fs-fd-hash-inl.h"
+
+
+#define UV_FS_FREE_PATHS         0x0002
+#define UV_FS_FREE_PTR           0x0008
+#define UV_FS_CLEANEDUP          0x0010
+
+
+#define INIT(subtype)                                                         \
+  do {                                                                        \
+    if (req == NULL)                                                          \
+      return UV_EINVAL;                                                       \
+    uv_fs_req_init(loop, req, subtype, cb);                                   \
+  }                                                                           \
+  while (0)
+
+#define POST                                                                  \
+  do {                                                                        \
+    if (cb != NULL) {                                                         \
+      uv__req_register(loop, req);                                            \
+      uv__work_submit(loop,                                                   \
+                      &req->work_req,                                         \
+                      UV__WORK_FAST_IO,                                       \
+                      uv__fs_work,                                            \
+                      uv__fs_done);                                           \
+      return 0;                                                               \
+    } else {                                                                  \
+      uv__fs_work(&req->work_req);                                            \
+      return req->result;                                                     \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define SET_REQ_RESULT(req, result_value)                                   \
+  do {                                                                      \
+    req->result = (result_value);                                           \
+    assert(req->result != -1);                                              \
+  } while (0)
+
+#define SET_REQ_WIN32_ERROR(req, sys_errno)                                 \
+  do {                                                                      \
+    req->sys_errno_ = (sys_errno);                                          \
+    req->result = uv_translate_sys_error(req->sys_errno_);                  \
+  } while (0)
+
+#define SET_REQ_UV_ERROR(req, uv_errno, sys_errno)                          \
+  do {                                                                      \
+    req->result = (uv_errno);                                               \
+    req->sys_errno_ = (sys_errno);                                          \
+  } while (0)
+
+#define VERIFY_FD(fd, req)                                                  \
+  if (fd == -1) {                                                           \
+    req->result = UV_EBADF;                                                 \
+    req->sys_errno_ = ERROR_INVALID_HANDLE;                                 \
+    return;                                                                 \
+  }
+
+#define MILLIONu (1000U * 1000U)
+#define BILLIONu (1000U * 1000U * 1000U)
+
+#define FILETIME_TO_UINT(filetime)                                          \
+   (*((uint64_t*) &(filetime)) - (uint64_t) 116444736 * BILLIONu)
+
+#define FILETIME_TO_TIME_T(filetime)                                        \
+   (FILETIME_TO_UINT(filetime) / (10u * MILLIONu))
+
+#define FILETIME_TO_TIME_NS(filetime, secs)                                 \
+   ((FILETIME_TO_UINT(filetime) - (secs * (uint64_t) 10 * MILLIONu)) * 100U)
+
+#define FILETIME_TO_TIMESPEC(ts, filetime)                                  \
+   do {                                                                     \
+     (ts).tv_sec = (long) FILETIME_TO_TIME_T(filetime);                     \
+     (ts).tv_nsec = (long) FILETIME_TO_TIME_NS(filetime, (ts).tv_sec);      \
+   } while(0)
+
+#define TIME_T_TO_FILETIME(time, filetime_ptr)                              \
+  do {                                                                      \
+    uint64_t bigtime = ((uint64_t) ((time) * (uint64_t) 10 * MILLIONu)) +   \
+                       (uint64_t) 116444736 * BILLIONu;                     \
+    (filetime_ptr)->dwLowDateTime = bigtime & 0xFFFFFFFF;                   \
+    (filetime_ptr)->dwHighDateTime = bigtime >> 32;                         \
+  } while(0)
+
+#define IS_SLASH(c) ((c) == L'\\' || (c) == L'/')
+#define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \
+  ((c) >= L'A' && (c) <= L'Z'))
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+const WCHAR JUNCTION_PREFIX[] = L"\\??\\";
+const WCHAR JUNCTION_PREFIX_LEN = 4;
+
+const WCHAR LONG_PATH_PREFIX[] = L"\\\\?\\";
+const WCHAR LONG_PATH_PREFIX_LEN = 4;
+
+const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\";
+const WCHAR UNC_PATH_PREFIX_LEN = 8;
+
+static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+
+static DWORD uv__allocation_granularity;
+
+
+void uv_fs_init(void) {
+  SYSTEM_INFO system_info;
+
+  GetSystemInfo(&system_info);
+  uv__allocation_granularity = system_info.dwAllocationGranularity;
+
+  uv__fd_hash_init();
+}
+
+
+INLINE static int fs__capture_path(uv_fs_t* req, const char* path,
+    const char* new_path, const int copy_path) {
+  char* buf;
+  char* pos;
+  ssize_t buf_sz = 0, path_len = 0, pathw_len = 0, new_pathw_len = 0;
+
+  /* new_path can only be set if path is also set. */
+  assert(new_path == NULL || path != NULL);
+
+  if (path != NULL) {
+    pathw_len = MultiByteToWideChar(CP_UTF8,
+                                    0,
+                                    path,
+                                    -1,
+                                    NULL,
+                                    0);
+    if (pathw_len == 0) {
+      return GetLastError();
+    }
+
+    buf_sz += pathw_len * sizeof(WCHAR);
+  }
+
+  if (path != NULL && copy_path) {
+    path_len = 1 + strlen(path);
+    buf_sz += path_len;
+  }
+
+  if (new_path != NULL) {
+    new_pathw_len = MultiByteToWideChar(CP_UTF8,
+                                        0,
+                                        new_path,
+                                        -1,
+                                        NULL,
+                                        0);
+    if (new_pathw_len == 0) {
+      return GetLastError();
+    }
+
+    buf_sz += new_pathw_len * sizeof(WCHAR);
+  }
+
+
+  if (buf_sz == 0) {
+    req->file.pathw = NULL;
+    req->fs.info.new_pathw = NULL;
+    req->path = NULL;
+    return 0;
+  }
+
+  buf = (char*) uv__malloc(buf_sz);
+  if (buf == NULL) {
+    return ERROR_OUTOFMEMORY;
+  }
+
+  pos = buf;
+
+  if (path != NULL) {
+    DWORD r = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  path,
+                                  -1,
+                                  (WCHAR*) pos,
+                                  pathw_len);
+    assert(r == (DWORD) pathw_len);
+    req->file.pathw = (WCHAR*) pos;
+    pos += r * sizeof(WCHAR);
+  } else {
+    req->file.pathw = NULL;
+  }
+
+  if (new_path != NULL) {
+    DWORD r = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  new_path,
+                                  -1,
+                                  (WCHAR*) pos,
+                                  new_pathw_len);
+    assert(r == (DWORD) new_pathw_len);
+    req->fs.info.new_pathw = (WCHAR*) pos;
+    pos += r * sizeof(WCHAR);
+  } else {
+    req->fs.info.new_pathw = NULL;
+  }
+
+  req->path = path;
+  if (path != NULL && copy_path) {
+    memcpy(pos, path, path_len);
+    assert(path_len == buf_sz - (pos - buf));
+    req->path = pos;
+  }
+
+  req->flags |= UV_FS_FREE_PATHS;
+
+  return 0;
+}
+
+
+
+INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req,
+    uv_fs_type fs_type, const uv_fs_cb cb) {
+  uv__once_init();
+  UV_REQ_INIT(req, UV_FS);
+  req->loop = loop;
+  req->flags = 0;
+  req->fs_type = fs_type;
+  req->sys_errno_ = 0;
+  req->result = 0;
+  req->ptr = NULL;
+  req->path = NULL;
+  req->cb = cb;
+  memset(&req->fs, 0, sizeof(req->fs));
+}
+
+
+static int fs__wide_to_utf8(WCHAR* w_source_ptr,
+                               DWORD w_source_len,
+                               char** target_ptr,
+                               uint64_t* target_len_ptr) {
+  int r;
+  int target_len;
+  char* target;
+  target_len = WideCharToMultiByte(CP_UTF8,
+                                   0,
+                                   w_source_ptr,
+                                   w_source_len,
+                                   NULL,
+                                   0,
+                                   NULL,
+                                   NULL);
+
+  if (target_len == 0) {
+    return -1;
+  }
+
+  if (target_len_ptr != NULL) {
+    *target_len_ptr = target_len;
+  }
+
+  if (target_ptr == NULL) {
+    return 0;
+  }
+
+  target = uv__malloc(target_len + 1);
+  if (target == NULL) {
+    SetLastError(ERROR_OUTOFMEMORY);
+    return -1;
+  }
+
+  r = WideCharToMultiByte(CP_UTF8,
+                          0,
+                          w_source_ptr,
+                          w_source_len,
+                          target,
+                          target_len,
+                          NULL,
+                          NULL);
+  assert(r == target_len);
+  target[target_len] = '\0';
+  *target_ptr = target;
+  return 0;
+}
+
+
+INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
+    uint64_t* target_len_ptr) {
+  char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+  REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer;
+  WCHAR* w_target;
+  DWORD w_target_len;
+  DWORD bytes;
+  size_t i;
+  size_t len;
+
+  if (!DeviceIoControl(handle,
+                       FSCTL_GET_REPARSE_POINT,
+                       NULL,
+                       0,
+                       buffer,
+                       sizeof buffer,
+                       &bytes,
+                       NULL)) {
+    return -1;
+  }
+
+  if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+    /* Real symlink */
+    w_target = reparse_data->SymbolicLinkReparseBuffer.PathBuffer +
+        (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset /
+        sizeof(WCHAR));
+    w_target_len =
+        reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength /
+        sizeof(WCHAR);
+
+    /* Real symlinks can contain pretty much everything, but the only thing we
+     * really care about is undoing the implicit conversion to an NT namespaced
+     * path that CreateSymbolicLink will perform on absolute paths. If the path
+     * is win32-namespaced then the user must have explicitly made it so, and
+     * we better just return the unmodified reparse data. */
+    if (w_target_len >= 4 &&
+        w_target[0] == L'\\' &&
+        w_target[1] == L'?' &&
+        w_target[2] == L'?' &&
+        w_target[3] == L'\\') {
+      /* Starts with \??\ */
+      if (w_target_len >= 6 &&
+          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
+           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
+          w_target[5] == L':' &&
+          (w_target_len == 6 || w_target[6] == L'\\')) {
+        /* \??\<drive>:\ */
+        w_target += 4;
+        w_target_len -= 4;
+
+      } else if (w_target_len >= 8 &&
+                 (w_target[4] == L'U' || w_target[4] == L'u') &&
+                 (w_target[5] == L'N' || w_target[5] == L'n') &&
+                 (w_target[6] == L'C' || w_target[6] == L'c') &&
+                 w_target[7] == L'\\') {
+        /* \??\UNC\<server>\<share>\ - make sure the final path looks like
+         * \\<server>\<share>\ */
+        w_target += 6;
+        w_target[0] = L'\\';
+        w_target_len -= 6;
+      }
+    }
+
+  } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+    /* Junction. */
+    w_target = reparse_data->MountPointReparseBuffer.PathBuffer +
+        (reparse_data->MountPointReparseBuffer.SubstituteNameOffset /
+        sizeof(WCHAR));
+    w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength /
+        sizeof(WCHAR);
+
+    /* Only treat junctions that look like \??\<drive>:\ as symlink. Junctions
+     * can also be used as mount points, like \??\Volume{<guid>}, but that's
+     * confusing for programs since they wouldn't be able to actually
+     * understand such a path when returned by uv_readlink(). UNC paths are
+     * never valid for junctions so we don't care about them. */
+    if (!(w_target_len >= 6 &&
+          w_target[0] == L'\\' &&
+          w_target[1] == L'?' &&
+          w_target[2] == L'?' &&
+          w_target[3] == L'\\' &&
+          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
+           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
+          w_target[5] == L':' &&
+          (w_target_len == 6 || w_target[6] == L'\\'))) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+
+    /* Remove leading \??\ */
+    w_target += 4;
+    w_target_len -= 4;
+
+  } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
+    /* String #3 in the list has the target filename. */
+    if (reparse_data->AppExecLinkReparseBuffer.StringCount < 3) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+    w_target = reparse_data->AppExecLinkReparseBuffer.StringList;
+    /* The StringList buffer contains a list of strings separated by "\0",   */
+    /* with "\0\0" terminating the list. Move to the 3rd string in the list: */
+    for (i = 0; i < 2; ++i) {
+      len = wcslen(w_target);
+      if (len == 0) {
+        SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+        return -1;
+      }
+      w_target += len + 1;
+    }
+    w_target_len = wcslen(w_target);
+    if (w_target_len == 0) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+    /* Make sure it is an absolute path. */
+    if (!(w_target_len >= 3 &&
+         ((w_target[0] >= L'a' && w_target[0] <= L'z') ||
+          (w_target[0] >= L'A' && w_target[0] <= L'Z')) &&
+         w_target[1] == L':' &&
+         w_target[2] == L'\\')) {
+      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+      return -1;
+    }
+
+  } else {
+    /* Reparse tag does not indicate a symlink. */
+    SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
+    return -1;
+  }
+
+  return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr);
+}
+
+
+void fs__open(uv_fs_t* req) {
+  DWORD access;
+  DWORD share;
+  DWORD disposition;
+  DWORD attributes = 0;
+  HANDLE file;
+  int fd, current_umask;
+  int flags = req->fs.info.file_flags;
+  struct uv__fd_info_s fd_info;
+
+  /* Adjust flags to be compatible with the memory file mapping. Save the
+   * original flags to emulate the correct behavior. */
+  if (flags & UV_FS_O_FILEMAP) {
+    fd_info.flags = flags;
+    fd_info.current_pos.QuadPart = 0;
+
+    if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) ==
+        UV_FS_O_WRONLY) {
+      /* CreateFileMapping always needs read access */
+      flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR;
+    }
+
+    if (flags & UV_FS_O_APPEND) {
+      /* Clear the append flag and ensure RDRW mode */
+      flags &= ~UV_FS_O_APPEND;
+      flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
+      flags |= UV_FS_O_RDWR;
+    }
+  }
+
+  /* Obtain the active umask. umask() never fails and returns the previous
+   * umask. */
+  current_umask = umask(0);
+  umask(current_umask);
+
+  /* convert flags and mode to CreateFile parameters */
+  switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) {
+  case UV_FS_O_RDONLY:
+    access = FILE_GENERIC_READ;
+    break;
+  case UV_FS_O_WRONLY:
+    access = FILE_GENERIC_WRITE;
+    break;
+  case UV_FS_O_RDWR:
+    access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+    break;
+  default:
+    goto einval;
+  }
+
+  if (flags & UV_FS_O_APPEND) {
+    access &= ~FILE_WRITE_DATA;
+    access |= FILE_APPEND_DATA;
+  }
+
+  /*
+   * Here is where we deviate significantly from what CRT's _open()
+   * does. We indiscriminately use all the sharing modes, to match
+   * UNIX semantics. In particular, this ensures that the file can
+   * be deleted even whilst it's open, fixing issue
+   * https://github.com/nodejs/node-v0.x-archive/issues/1449.
+   * We still support exclusive sharing mode, since it is necessary
+   * for opening raw block devices, otherwise Windows will prevent
+   * any attempt to write past the master boot record.
+   */
+  if (flags & UV_FS_O_EXLOCK) {
+    share = 0;
+  } else {
+    share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+  }
+
+  switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) {
+  case 0:
+  case UV_FS_O_EXCL:
+    disposition = OPEN_EXISTING;
+    break;
+  case UV_FS_O_CREAT:
+    disposition = OPEN_ALWAYS;
+    break;
+  case UV_FS_O_CREAT | UV_FS_O_EXCL:
+  case UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL:
+    disposition = CREATE_NEW;
+    break;
+  case UV_FS_O_TRUNC:
+  case UV_FS_O_TRUNC | UV_FS_O_EXCL:
+    disposition = TRUNCATE_EXISTING;
+    break;
+  case UV_FS_O_CREAT | UV_FS_O_TRUNC:
+    disposition = CREATE_ALWAYS;
+    break;
+  default:
+    goto einval;
+  }
+
+  attributes |= FILE_ATTRIBUTE_NORMAL;
+  if (flags & UV_FS_O_CREAT) {
+    if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) {
+      attributes |= FILE_ATTRIBUTE_READONLY;
+    }
+  }
+
+  if (flags & UV_FS_O_TEMPORARY ) {
+    attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
+    access |= DELETE;
+  }
+
+  if (flags & UV_FS_O_SHORT_LIVED) {
+    attributes |= FILE_ATTRIBUTE_TEMPORARY;
+  }
+
+  switch (flags & (UV_FS_O_SEQUENTIAL | UV_FS_O_RANDOM)) {
+  case 0:
+    break;
+  case UV_FS_O_SEQUENTIAL:
+    attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+    break;
+  case UV_FS_O_RANDOM:
+    attributes |= FILE_FLAG_RANDOM_ACCESS;
+    break;
+  default:
+    goto einval;
+  }
+
+  if (flags & UV_FS_O_DIRECT) {
+    /*
+     * FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually exclusive.
+     * Windows returns 87, ERROR_INVALID_PARAMETER if these are combined.
+     *
+     * FILE_APPEND_DATA is included in FILE_GENERIC_WRITE:
+     *
+     * FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE |
+     *                      FILE_WRITE_DATA |
+     *                      FILE_WRITE_ATTRIBUTES |
+     *                      FILE_WRITE_EA |
+     *                      FILE_APPEND_DATA |
+     *                      SYNCHRONIZE
+     *
+     * Note: Appends are also permitted by FILE_WRITE_DATA.
+     *
+     * In order for direct writes and direct appends to succeed, we therefore
+     * exclude FILE_APPEND_DATA if FILE_WRITE_DATA is specified, and otherwise
+     * fail if the user's sole permission is a direct append, since this
+     * particular combination is invalid.
+     */
+    if (access & FILE_APPEND_DATA) {
+      if (access & FILE_WRITE_DATA) {
+        access &= ~FILE_APPEND_DATA;
+      } else {
+        goto einval;
+      }
+    }
+    attributes |= FILE_FLAG_NO_BUFFERING;
+  }
+
+  switch (flags & (UV_FS_O_DSYNC | UV_FS_O_SYNC)) {
+  case 0:
+    break;
+  case UV_FS_O_DSYNC:
+  case UV_FS_O_SYNC:
+    attributes |= FILE_FLAG_WRITE_THROUGH;
+    break;
+  default:
+    goto einval;
+  }
+
+  /* Setting this flag makes it possible to open a directory. */
+  attributes |= FILE_FLAG_BACKUP_SEMANTICS;
+
+  file = CreateFileW(req->file.pathw,
+                     access,
+                     share,
+                     NULL,
+                     disposition,
+                     attributes,
+                     NULL);
+  if (file == INVALID_HANDLE_VALUE) {
+    DWORD error = GetLastError();
+    if (error == ERROR_FILE_EXISTS && (flags & UV_FS_O_CREAT) &&
+        !(flags & UV_FS_O_EXCL)) {
+      /* Special case: when ERROR_FILE_EXISTS happens and UV_FS_O_CREAT was
+       * specified, it means the path referred to a directory. */
+      SET_REQ_UV_ERROR(req, UV_EISDIR, error);
+    } else {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+    }
+    return;
+  }
+
+  fd = _open_osfhandle((intptr_t) file, flags);
+  if (fd < 0) {
+    /* The only known failure mode for _open_osfhandle() is EMFILE, in which
+     * case GetLastError() will return zero. However we'll try to handle other
+     * errors as well, should they ever occur.
+     */
+    if (errno == EMFILE)
+      SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
+    else if (GetLastError() != ERROR_SUCCESS)
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+    else
+      SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN);
+    CloseHandle(file);
+    return;
+  }
+
+  if (flags & UV_FS_O_FILEMAP) {
+    FILE_STANDARD_INFO file_info;
+    if (!GetFileInformationByHandleEx(file,
+                                      FileStandardInfo,
+                                      &file_info,
+                                      sizeof file_info)) {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+      CloseHandle(file);
+      return;
+    }
+    fd_info.is_directory = file_info.Directory;
+
+    if (fd_info.is_directory) {
+      fd_info.size.QuadPart = 0;
+      fd_info.mapping = INVALID_HANDLE_VALUE;
+    } else {
+      if (!GetFileSizeEx(file, &fd_info.size)) {
+        SET_REQ_WIN32_ERROR(req, GetLastError());
+        CloseHandle(file);
+        return;
+      }
+
+      if (fd_info.size.QuadPart == 0) {
+        fd_info.mapping = INVALID_HANDLE_VALUE;
+      } else {
+        DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY |
+          UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE;
+        fd_info.mapping = CreateFileMapping(file,
+                                            NULL,
+                                            flProtect,
+                                            fd_info.size.HighPart,
+                                            fd_info.size.LowPart,
+                                            NULL);
+        if (fd_info.mapping == NULL) {
+          SET_REQ_WIN32_ERROR(req, GetLastError());
+          CloseHandle(file);
+          return;
+        }
+      }
+    }
+
+    uv__fd_hash_add(fd, &fd_info);
+  }
+
+  SET_REQ_RESULT(req, fd);
+  return;
+
+ einval:
+  SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+}
+
+void fs__close(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int result;
+  struct uv__fd_info_s fd_info;
+
+  VERIFY_FD(fd, req);
+
+  if (uv__fd_hash_remove(fd, &fd_info)) {
+    if (fd_info.mapping != INVALID_HANDLE_VALUE) {
+      CloseHandle(fd_info.mapping);
+    }
+  }
+
+  if (fd > 2)
+    result = _close(fd);
+  else
+    result = 0;
+
+  /* _close doesn't set _doserrno on failure, but it does always set errno
+   * to EBADF on failure.
+   */
+  if (result == -1) {
+    assert(errno == EBADF);
+    SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE);
+  } else {
+    SET_REQ_RESULT(req, 0);
+  }
+}
+
+
+LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep,
+                           int* perror) {
+  if (excode != (LONG)EXCEPTION_IN_PAGE_ERROR) {
+    return EXCEPTION_CONTINUE_SEARCH;
+  }
+
+  assert(perror != NULL);
+  if (pep != NULL && pep->ExceptionRecord != NULL &&
+      pep->ExceptionRecord->NumberParameters >= 3) {
+    NTSTATUS status = (NTSTATUS)pep->ExceptionRecord->ExceptionInformation[3];
+    *perror = pRtlNtStatusToDosError(status);
+    if (*perror != ERROR_SUCCESS) {
+      return EXCEPTION_EXECUTE_HANDLER;
+    }
+  }
+  *perror = UV_UNKNOWN;
+  return EXCEPTION_EXECUTE_HANDLER;
+}
+
+
+void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) {
+  int fd = req->file.fd; /* VERIFY_FD done in fs__read */
+  int rw_flags = fd_info->flags &
+    (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
+  size_t read_size, done_read;
+  unsigned int index;
+  LARGE_INTEGER pos, end_pos;
+  size_t view_offset;
+  LARGE_INTEGER view_base;
+  void* view;
+
+  if (rw_flags == UV_FS_O_WRONLY) {
+    SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
+    return;
+  }
+  if (fd_info->is_directory) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION);
+    return;
+  }
+
+  if (req->fs.info.offset == -1) {
+    pos = fd_info->current_pos;
+  } else {
+    pos.QuadPart = req->fs.info.offset;
+  }
+
+  /* Make sure we wont read past EOF. */
+  if (pos.QuadPart >= fd_info->size.QuadPart) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  read_size = 0;
+  for (index = 0; index < req->fs.info.nbufs; ++index) {
+    read_size += req->fs.info.bufs[index].len;
+  }
+  read_size = (size_t) MIN((LONGLONG) read_size,
+                           fd_info->size.QuadPart - pos.QuadPart);
+  if (read_size == 0) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  end_pos.QuadPart = pos.QuadPart + read_size;
+
+  view_offset = pos.QuadPart % uv__allocation_granularity;
+  view_base.QuadPart = pos.QuadPart - view_offset;
+  view = MapViewOfFile(fd_info->mapping,
+                       FILE_MAP_READ,
+                       view_base.HighPart,
+                       view_base.LowPart,
+                       view_offset + read_size);
+  if (view == NULL) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  done_read = 0;
+  for (index = 0;
+       index < req->fs.info.nbufs && done_read < read_size;
+       ++index) {
+    size_t this_read_size = MIN(req->fs.info.bufs[index].len,
+                                read_size - done_read);
+#ifdef _MSC_VER
+    int err = 0;
+    __try {
+#endif
+      memcpy(req->fs.info.bufs[index].base,
+             (char*)view + view_offset + done_read,
+             this_read_size);
+#ifdef _MSC_VER
+    }
+    __except (fs__filemap_ex_filter(GetExceptionCode(),
+                                    GetExceptionInformation(), &err)) {
+      SET_REQ_WIN32_ERROR(req, err);
+      UnmapViewOfFile(view);
+      return;
+    }
+#endif
+    done_read += this_read_size;
+  }
+  assert(done_read == read_size);
+
+  if (!UnmapViewOfFile(view)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (req->fs.info.offset == -1) {
+    fd_info->current_pos = end_pos;
+    uv__fd_hash_add(fd, fd_info);
+  }
+
+  SET_REQ_RESULT(req, read_size);
+  return;
+}
+
+void fs__read(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int64_t offset = req->fs.info.offset;
+  HANDLE handle;
+  OVERLAPPED overlapped, *overlapped_ptr;
+  LARGE_INTEGER offset_;
+  DWORD bytes;
+  DWORD error;
+  int result;
+  unsigned int index;
+  LARGE_INTEGER original_position;
+  LARGE_INTEGER zero_offset;
+  int restore_position;
+  struct uv__fd_info_s fd_info;
+
+  VERIFY_FD(fd, req);
+
+  if (uv__fd_hash_get(fd, &fd_info)) {
+    fs__read_filemap(req, &fd_info);
+    return;
+  }
+
+  zero_offset.QuadPart = 0;
+  restore_position = 0;
+  handle = uv__get_osfhandle(fd);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (offset != -1) {
+    memset(&overlapped, 0, sizeof overlapped);
+    overlapped_ptr = &overlapped;
+    if (SetFilePointerEx(handle, zero_offset, &original_position,
+                         FILE_CURRENT)) {
+      restore_position = 1;
+    }
+  } else {
+    overlapped_ptr = NULL;
+  }
+
+  index = 0;
+  bytes = 0;
+  do {
+    DWORD incremental_bytes;
+
+    if (offset != -1) {
+      offset_.QuadPart = offset + bytes;
+      overlapped.Offset = offset_.LowPart;
+      overlapped.OffsetHigh = offset_.HighPart;
+    }
+
+    result = ReadFile(handle,
+                      req->fs.info.bufs[index].base,
+                      req->fs.info.bufs[index].len,
+                      &incremental_bytes,
+                      overlapped_ptr);
+    bytes += incremental_bytes;
+    ++index;
+  } while (result && index < req->fs.info.nbufs);
+
+  if (restore_position)
+    SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN);
+
+  if (result || bytes > 0) {
+    SET_REQ_RESULT(req, bytes);
+  } else {
+    error = GetLastError();
+    if (error == ERROR_HANDLE_EOF) {
+      SET_REQ_RESULT(req, bytes);
+    } else {
+      SET_REQ_WIN32_ERROR(req, error);
+    }
+  }
+}
+
+
+void fs__write_filemap(uv_fs_t* req, HANDLE file,
+                       struct uv__fd_info_s* fd_info) {
+  int fd = req->file.fd; /* VERIFY_FD done in fs__write */
+  int force_append = fd_info->flags & UV_FS_O_APPEND;
+  int rw_flags = fd_info->flags &
+    (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
+  size_t write_size, done_write;
+  unsigned int index;
+  LARGE_INTEGER pos, end_pos;
+  size_t view_offset;
+  LARGE_INTEGER view_base;
+  void* view;
+  FILETIME ft;
+
+  if (rw_flags == UV_FS_O_RDONLY) {
+    SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
+    return;
+  }
+  if (fd_info->is_directory) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION);
+    return;
+  }
+
+  write_size = 0;
+  for (index = 0; index < req->fs.info.nbufs; ++index) {
+    write_size += req->fs.info.bufs[index].len;
+  }
+
+  if (write_size == 0) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  if (force_append) {
+    pos = fd_info->size;
+  } else if (req->fs.info.offset == -1) {
+    pos = fd_info->current_pos;
+  } else {
+    pos.QuadPart = req->fs.info.offset;
+  }
+
+  end_pos.QuadPart = pos.QuadPart + write_size;
+
+  /* Recreate the mapping to enlarge the file if needed */
+  if (end_pos.QuadPart > fd_info->size.QuadPart) {
+    if (fd_info->mapping != INVALID_HANDLE_VALUE) {
+      CloseHandle(fd_info->mapping);
+    }
+
+    fd_info->mapping = CreateFileMapping(file,
+                                         NULL,
+                                         PAGE_READWRITE,
+                                         end_pos.HighPart,
+                                         end_pos.LowPart,
+                                         NULL);
+    if (fd_info->mapping == NULL) {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+      CloseHandle(file);
+      fd_info->mapping = INVALID_HANDLE_VALUE;
+      fd_info->size.QuadPart = 0;
+      fd_info->current_pos.QuadPart = 0;
+      uv__fd_hash_add(fd, fd_info);
+      return;
+    }
+
+    fd_info->size = end_pos;
+    uv__fd_hash_add(fd, fd_info);
+  }
+
+  view_offset = pos.QuadPart % uv__allocation_granularity;
+  view_base.QuadPart = pos.QuadPart - view_offset;
+  view = MapViewOfFile(fd_info->mapping,
+                       FILE_MAP_WRITE,
+                       view_base.HighPart,
+                       view_base.LowPart,
+                       view_offset + write_size);
+  if (view == NULL) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  done_write = 0;
+  for (index = 0; index < req->fs.info.nbufs; ++index) {
+#ifdef _MSC_VER
+    int err = 0;
+    __try {
+#endif
+      memcpy((char*)view + view_offset + done_write,
+             req->fs.info.bufs[index].base,
+             req->fs.info.bufs[index].len);
+#ifdef _MSC_VER
+    }
+    __except (fs__filemap_ex_filter(GetExceptionCode(),
+                                    GetExceptionInformation(), &err)) {
+      SET_REQ_WIN32_ERROR(req, err);
+      UnmapViewOfFile(view);
+      return;
+    }
+#endif
+    done_write += req->fs.info.bufs[index].len;
+  }
+  assert(done_write == write_size);
+
+  if (!FlushViewOfFile(view, 0)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    UnmapViewOfFile(view);
+    return;
+  }
+  if (!UnmapViewOfFile(view)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (req->fs.info.offset == -1) {
+    fd_info->current_pos = end_pos;
+    uv__fd_hash_add(fd, fd_info);
+  }
+
+  GetSystemTimeAsFileTime(&ft);
+  SetFileTime(file, NULL, NULL, &ft);
+
+  SET_REQ_RESULT(req, done_write);
+}
+
+void fs__write(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int64_t offset = req->fs.info.offset;
+  HANDLE handle;
+  OVERLAPPED overlapped, *overlapped_ptr;
+  LARGE_INTEGER offset_;
+  DWORD bytes;
+  int result;
+  unsigned int index;
+  LARGE_INTEGER original_position;
+  LARGE_INTEGER zero_offset;
+  int restore_position;
+  struct uv__fd_info_s fd_info;
+
+  VERIFY_FD(fd, req);
+
+  zero_offset.QuadPart = 0;
+  restore_position = 0;
+  handle = uv__get_osfhandle(fd);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (uv__fd_hash_get(fd, &fd_info)) {
+    fs__write_filemap(req, handle, &fd_info);
+    return;
+  }
+
+  if (offset != -1) {
+    memset(&overlapped, 0, sizeof overlapped);
+    overlapped_ptr = &overlapped;
+    if (SetFilePointerEx(handle, zero_offset, &original_position,
+                         FILE_CURRENT)) {
+      restore_position = 1;
+    }
+  } else {
+    overlapped_ptr = NULL;
+  }
+
+  index = 0;
+  bytes = 0;
+  do {
+    DWORD incremental_bytes;
+
+    if (offset != -1) {
+      offset_.QuadPart = offset + bytes;
+      overlapped.Offset = offset_.LowPart;
+      overlapped.OffsetHigh = offset_.HighPart;
+    }
+
+    result = WriteFile(handle,
+                       req->fs.info.bufs[index].base,
+                       req->fs.info.bufs[index].len,
+                       &incremental_bytes,
+                       overlapped_ptr);
+    bytes += incremental_bytes;
+    ++index;
+  } while (result && index < req->fs.info.nbufs);
+
+  if (restore_position)
+    SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN);
+
+  if (result || bytes > 0) {
+    SET_REQ_RESULT(req, bytes);
+  } else {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+  }
+}
+
+
+void fs__rmdir(uv_fs_t* req) {
+  int result = _wrmdir(req->file.pathw);
+  if (result == -1)
+    SET_REQ_WIN32_ERROR(req, _doserrno);
+  else
+    SET_REQ_RESULT(req, 0);
+}
+
+
+void fs__unlink(uv_fs_t* req) {
+  const WCHAR* pathw = req->file.pathw;
+  HANDLE handle;
+  BY_HANDLE_FILE_INFORMATION info;
+  FILE_DISPOSITION_INFORMATION disposition;
+  IO_STATUS_BLOCK iosb;
+  NTSTATUS status;
+
+  handle = CreateFileW(pathw,
+                       FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (!GetFileInformationByHandle(handle, &info)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    CloseHandle(handle);
+    return;
+  }
+
+  if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+    /* Do not allow deletion of directories, unless it is a symlink. When the
+     * path refers to a non-symlink directory, report EPERM as mandated by
+     * POSIX.1. */
+
+    /* Check if it is a reparse point. If it's not, it's a normal directory. */
+    if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+      SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
+      CloseHandle(handle);
+      return;
+    }
+
+    /* Read the reparse point and check if it is a valid symlink. If not, don't
+     * unlink. */
+    if (fs__readlink_handle(handle, NULL, NULL) < 0) {
+      DWORD error = GetLastError();
+      if (error == ERROR_SYMLINK_NOT_SUPPORTED)
+        error = ERROR_ACCESS_DENIED;
+      SET_REQ_WIN32_ERROR(req, error);
+      CloseHandle(handle);
+      return;
+    }
+  }
+
+  if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
+    /* Remove read-only attribute */
+    FILE_BASIC_INFORMATION basic = { 0 };
+
+    basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
+                           FILE_ATTRIBUTE_ARCHIVE;
+
+    status = pNtSetInformationFile(handle,
+                                   &iosb,
+                                   &basic,
+                                   sizeof basic,
+                                   FileBasicInformation);
+    if (!NT_SUCCESS(status)) {
+      SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+      CloseHandle(handle);
+      return;
+    }
+  }
+
+  /* Try to set the delete flag. */
+  disposition.DeleteFile = TRUE;
+  status = pNtSetInformationFile(handle,
+                                 &iosb,
+                                 &disposition,
+                                 sizeof disposition,
+                                 FileDispositionInformation);
+  if (NT_SUCCESS(status)) {
+    SET_REQ_SUCCESS(req);
+  } else {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+  }
+
+  CloseHandle(handle);
+}
+
+
+void fs__mkdir(uv_fs_t* req) {
+  /* TODO: use req->mode. */
+  if (CreateDirectoryW(req->file.pathw, NULL)) {
+    SET_REQ_RESULT(req, 0);
+  } else {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    if (req->sys_errno_ == ERROR_INVALID_NAME)
+      req->result = UV_EINVAL;
+  }
+}
+
+typedef int (*uv__fs_mktemp_func)(uv_fs_t* req);
+
+/* OpenBSD original: lib/libc/stdio/mktemp.c */
+void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) {
+  static const WCHAR *tempchars =
+    L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+  static const size_t num_chars = 62;
+  static const size_t num_x = 6;
+  WCHAR *cp, *ep;
+  unsigned int tries, i;
+  size_t len;
+  uint64_t v;
+  char* path;
+  
+  path = req->path;
+  len = wcslen(req->file.pathw);
+  ep = req->file.pathw + len;
+  if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    goto clobber;
+  }
+
+  tries = TMP_MAX;
+  do {
+    if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) {
+      SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE);
+      goto clobber;
+    }
+
+    cp = ep - num_x;
+    for (i = 0; i < num_x; i++) {
+      *cp++ = tempchars[v % num_chars];
+      v /= num_chars;
+    }
+
+    if (func(req)) {
+      if (req->result >= 0) {
+        len = strlen(path);
+        wcstombs(path + len - num_x, ep - num_x, num_x);
+      }
+      return;
+    }
+  } while (--tries);
+
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+
+clobber:
+  path[0] = '\0';
+}
+
+
+static int fs__mkdtemp_func(uv_fs_t* req) {
+  DWORD error;
+  if (CreateDirectoryW(req->file.pathw, NULL)) {
+    SET_REQ_RESULT(req, 0);
+    return 1;
+  }
+  error = GetLastError();
+  if (error != ERROR_ALREADY_EXISTS) {
+    SET_REQ_WIN32_ERROR(req, error);
+    return 1;
+  }
+
+  return 0;
+}
+
+
+void fs__mkdtemp(uv_fs_t* req) {
+  fs__mktemp(req, fs__mkdtemp_func);
+}
+
+
+static int fs__mkstemp_func(uv_fs_t* req) {
+  HANDLE file;
+  int fd;
+
+  file = CreateFileW(req->file.pathw,
+                     GENERIC_READ | GENERIC_WRITE,
+                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                     NULL,
+                     CREATE_NEW,
+                     FILE_ATTRIBUTE_NORMAL,
+                     NULL);
+
+  if (file == INVALID_HANDLE_VALUE) {
+    DWORD error;
+    error = GetLastError();
+
+    /* If the file exists, the main fs__mktemp() function
+       will retry. If it's another error, we want to stop. */
+    if (error != ERROR_FILE_EXISTS) {
+      SET_REQ_WIN32_ERROR(req, error);
+      return 1;
+    }
+
+    return 0;
+  }
+
+  fd = _open_osfhandle((intptr_t) file, 0);
+  if (fd < 0) {
+    /* The only known failure mode for _open_osfhandle() is EMFILE, in which
+     * case GetLastError() will return zero. However we'll try to handle other
+     * errors as well, should they ever occur.
+     */
+    if (errno == EMFILE)
+      SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
+    else if (GetLastError() != ERROR_SUCCESS)
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+    else
+      SET_REQ_WIN32_ERROR(req, UV_UNKNOWN);
+    CloseHandle(file);
+    return 1;
+  }
+
+  SET_REQ_RESULT(req, fd);
+
+  return 1;
+}
+
+
+void fs__mkstemp(uv_fs_t* req) {
+  fs__mktemp(req, fs__mkstemp_func);
+}
+
+
+void fs__scandir(uv_fs_t* req) {
+  static const size_t dirents_initial_size = 32;
+
+  HANDLE dir_handle = INVALID_HANDLE_VALUE;
+
+  uv__dirent_t** dirents = NULL;
+  size_t dirents_size = 0;
+  size_t dirents_used = 0;
+
+  IO_STATUS_BLOCK iosb;
+  NTSTATUS status;
+
+  /* Buffer to hold directory entries returned by NtQueryDirectoryFile.
+   * It's important that this buffer can hold at least one entry, regardless
+   * of the length of the file names present in the enumerated directory.
+   * A file name is at most 256 WCHARs long.
+   * According to MSDN, the buffer must be aligned at an 8-byte boundary.
+   */
+#if _MSC_VER
+  __declspec(align(8)) char buffer[8192];
+#else
+  __attribute__ ((aligned (8))) char buffer[8192];
+#endif
+
+  STATIC_ASSERT(sizeof buffer >=
+                sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR));
+
+  /* Open the directory. */
+  dir_handle =
+      CreateFileW(req->file.pathw,
+                  FILE_LIST_DIRECTORY | SYNCHRONIZE,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  NULL,
+                  OPEN_EXISTING,
+                  FILE_FLAG_BACKUP_SEMANTICS,
+                  NULL);
+  if (dir_handle == INVALID_HANDLE_VALUE)
+    goto win32_error;
+
+  /* Read the first chunk. */
+  status = pNtQueryDirectoryFile(dir_handle,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 &iosb,
+                                 &buffer,
+                                 sizeof buffer,
+                                 FileDirectoryInformation,
+                                 FALSE,
+                                 NULL,
+                                 TRUE);
+
+  /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER.
+   * This should be reported back as UV_ENOTDIR.
+   */
+  if (status == (NTSTATUS)STATUS_INVALID_PARAMETER)
+    goto not_a_directory_error;
+
+  while (NT_SUCCESS(status)) {
+    char* position = buffer;
+    size_t next_entry_offset = 0;
+
+    do {
+      FILE_DIRECTORY_INFORMATION* info;
+      uv__dirent_t* dirent;
+
+      size_t wchar_len;
+      size_t utf8_len;
+
+      /* Obtain a pointer to the current directory entry. */
+      position += next_entry_offset;
+      info = (FILE_DIRECTORY_INFORMATION*) position;
+
+      /* Fetch the offset to the next directory entry. */
+      next_entry_offset = info->NextEntryOffset;
+
+      /* Compute the length of the filename in WCHARs. */
+      wchar_len = info->FileNameLength / sizeof info->FileName[0];
+
+      /* Skip over '.' and '..' entries.  It has been reported that
+       * the SharePoint driver includes the terminating zero byte in
+       * the filename length.  Strip those first.
+       */
+      while (wchar_len > 0 && info->FileName[wchar_len - 1] == L'\0')
+        wchar_len -= 1;
+
+      if (wchar_len == 0)
+        continue;
+      if (wchar_len == 1 && info->FileName[0] == L'.')
+        continue;
+      if (wchar_len == 2 && info->FileName[0] == L'.' &&
+          info->FileName[1] == L'.')
+        continue;
+
+      /* Compute the space required to store the filename as UTF-8. */
+      utf8_len = WideCharToMultiByte(
+          CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL);
+      if (utf8_len == 0)
+        goto win32_error;
+
+      /* Resize the dirent array if needed. */
+      if (dirents_used >= dirents_size) {
+        size_t new_dirents_size =
+            dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
+        uv__dirent_t** new_dirents =
+            uv__realloc(dirents, new_dirents_size * sizeof *dirents);
+
+        if (new_dirents == NULL)
+          goto out_of_memory_error;
+
+        dirents_size = new_dirents_size;
+        dirents = new_dirents;
+      }
+
+      /* Allocate space for the uv dirent structure. The dirent structure
+       * includes room for the first character of the filename, but `utf8_len`
+       * doesn't count the NULL terminator at this point.
+       */
+      dirent = uv__malloc(sizeof *dirent + utf8_len);
+      if (dirent == NULL)
+        goto out_of_memory_error;
+
+      dirents[dirents_used++] = dirent;
+
+      /* Convert file name to UTF-8. */
+      if (WideCharToMultiByte(CP_UTF8,
+                              0,
+                              &info->FileName[0],
+                              wchar_len,
+                              &dirent->d_name[0],
+                              utf8_len,
+                              NULL,
+                              NULL) == 0)
+        goto win32_error;
+
+      /* Add a null terminator to the filename. */
+      dirent->d_name[utf8_len] = '\0';
+
+      /* Fill out the type field. */
+      if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)
+        dirent->d_type = UV__DT_CHAR;
+      else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+        dirent->d_type = UV__DT_LINK;
+      else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        dirent->d_type = UV__DT_DIR;
+      else
+        dirent->d_type = UV__DT_FILE;
+    } while (next_entry_offset != 0);
+
+    /* Read the next chunk. */
+    status = pNtQueryDirectoryFile(dir_handle,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   &iosb,
+                                   &buffer,
+                                   sizeof buffer,
+                                   FileDirectoryInformation,
+                                   FALSE,
+                                   NULL,
+                                   FALSE);
+
+    /* After the first pNtQueryDirectoryFile call, the function may return
+     * STATUS_SUCCESS even if the buffer was too small to hold at least one
+     * directory entry.
+     */
+    if (status == STATUS_SUCCESS && iosb.Information == 0)
+      status = STATUS_BUFFER_OVERFLOW;
+  }
+
+  if (status != STATUS_NO_MORE_FILES)
+    goto nt_error;
+
+  CloseHandle(dir_handle);
+
+  /* Store the result in the request object. */
+  req->ptr = dirents;
+  if (dirents != NULL)
+    req->flags |= UV_FS_FREE_PTR;
+
+  SET_REQ_RESULT(req, dirents_used);
+
+  /* `nbufs` will be used as index by uv_fs_scandir_next. */
+  req->fs.info.nbufs = 0;
+
+  return;
+
+nt_error:
+  SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+  goto cleanup;
+
+win32_error:
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  goto cleanup;
+
+not_a_directory_error:
+  SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
+  goto cleanup;
+
+out_of_memory_error:
+  SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+  goto cleanup;
+
+cleanup:
+  if (dir_handle != INVALID_HANDLE_VALUE)
+    CloseHandle(dir_handle);
+  while (dirents_used > 0)
+    uv__free(dirents[--dirents_used]);
+  if (dirents != NULL)
+    uv__free(dirents);
+}
+
+void fs__opendir(uv_fs_t* req) {
+  WCHAR* pathw;
+  size_t len;
+  const WCHAR* fmt;
+  WCHAR* find_path;
+  uv_dir_t* dir;
+
+  pathw = req->file.pathw;
+  dir = NULL;
+  find_path = NULL;
+
+  /* Figure out whether path is a file or a directory. */
+  if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {
+    SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
+    goto error;
+  }
+
+  dir = uv__malloc(sizeof(*dir));
+  if (dir == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  len = wcslen(pathw);
+
+  if (len == 0)
+    fmt = L"./*";
+  else if (IS_SLASH(pathw[len - 1]))
+    fmt = L"%s*";
+  else
+    fmt = L"%s\\*";
+
+  find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
+  if (find_path == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  _snwprintf(find_path, len + 3, fmt, pathw);
+  dir->dir_handle = FindFirstFileW(find_path, &dir->find_data);
+  uv__free(find_path);
+  find_path = NULL;
+  if (dir->dir_handle == INVALID_HANDLE_VALUE &&
+      GetLastError() != ERROR_FILE_NOT_FOUND) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  dir->need_find_call = FALSE;
+  req->ptr = dir;
+  SET_REQ_RESULT(req, 0);
+  return;
+
+error:
+  uv__free(dir);
+  uv__free(find_path);
+  req->ptr = NULL;
+}
+
+void fs__readdir(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  uv__dirent_t dent;
+  unsigned int dirent_idx;
+  PWIN32_FIND_DATAW find_data;
+  unsigned int i;
+  int r;
+
+  req->flags |= UV_FS_FREE_PTR;
+  dir = req->ptr;
+  dirents = dir->dirents;
+  memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
+  find_data = &dir->find_data;
+  dirent_idx = 0;
+
+  while (dirent_idx < dir->nentries) {
+    if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) {
+      if (GetLastError() == ERROR_NO_MORE_FILES)
+        break;
+      goto error;
+    }
+
+    /* Skip "." and ".." entries. */
+    if (find_data->cFileName[0] == L'.' &&
+        (find_data->cFileName[1] == L'\0' ||
+        (find_data->cFileName[1] == L'.' &&
+        find_data->cFileName[2] == L'\0'))) {
+      dir->need_find_call = TRUE;
+      continue;
+    }
+
+    r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName,
+                                  -1,
+                                  (char**) &dirents[dirent_idx].name);
+    if (r != 0)
+      goto error;
+
+    /* Copy file type. */
+    if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+      dent.d_type = UV__DT_DIR;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
+      dent.d_type = UV__DT_LINK;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
+      dent.d_type = UV__DT_CHAR;
+    else
+      dent.d_type = UV__DT_FILE;
+
+    dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent);
+    dir->need_find_call = TRUE;
+    ++dirent_idx;
+  }
+
+  SET_REQ_RESULT(req, dirent_idx);
+  return;
+
+error:
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  for (i = 0; i < dirent_idx; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
+}
+
+void fs__closedir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = req->ptr;
+  FindClose(dir->dir_handle);
+  uv__free(req->ptr);
+  SET_REQ_RESULT(req, 0);
+}
+
+INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
+    int do_lstat) {
+  FILE_ALL_INFORMATION file_info;
+  FILE_FS_VOLUME_INFORMATION volume_info;
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+
+  nt_status = pNtQueryInformationFile(handle,
+                                      &io_status,
+                                      &file_info,
+                                      sizeof file_info,
+                                      FileAllInformation);
+
+  /* Buffer overflow (a warning status code) is expected here. */
+  if (NT_ERROR(nt_status)) {
+    SetLastError(pRtlNtStatusToDosError(nt_status));
+    return -1;
+  }
+
+  nt_status = pNtQueryVolumeInformationFile(handle,
+                                            &io_status,
+                                            &volume_info,
+                                            sizeof volume_info,
+                                            FileFsVolumeInformation);
+
+  /* Buffer overflow (a warning status code) is expected here. */
+  if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
+    statbuf->st_dev = 0;
+  } else if (NT_ERROR(nt_status)) {
+    SetLastError(pRtlNtStatusToDosError(nt_status));
+    return -1;
+  } else {
+    statbuf->st_dev = volume_info.VolumeSerialNumber;
+  }
+
+  /* Todo: st_mode should probably always be 0666 for everyone. We might also
+   * want to report 0777 if the file is a .exe or a directory.
+   *
+   * Currently it's based on whether the 'readonly' attribute is set, which
+   * makes little sense because the semantics are so different: the 'read-only'
+   * flag is just a way for a user to protect against accidental deletion, and
+   * serves no security purpose. Windows uses ACLs for that.
+   *
+   * Also people now use uv_fs_chmod() to take away the writable bit for good
+   * reasons. Windows however just makes the file read-only, which makes it
+   * impossible to delete the file afterwards, since read-only files can't be
+   * deleted.
+   *
+   * IOW it's all just a clusterfuck and we should think of something that
+   * makes slightly more sense.
+   *
+   * And uv_fs_chmod should probably just fail on windows or be a total no-op.
+   * There's nothing sensible it can do anyway.
+   */
+  statbuf->st_mode = 0;
+
+  /*
+  * On Windows, FILE_ATTRIBUTE_REPARSE_POINT is a general purpose mechanism
+  * by which filesystem drivers can intercept and alter file system requests.
+  *
+  * The only reparse points we care about are symlinks and mount points, both
+  * of which are treated as POSIX symlinks. Further, we only care when
+  * invoked via lstat, which seeks information about the link instead of its
+  * target. Otherwise, reparse points must be treated as regular files.
+  */
+  if (do_lstat &&
+      (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+    /*
+     * If reading the link fails, the reparse point is not a symlink and needs
+     * to be treated as a regular file. The higher level lstat function will
+     * detect this failure and retry without do_lstat if appropriate.
+     */
+    if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0)
+      return -1;
+    statbuf->st_mode |= S_IFLNK;
+  }
+
+  if (statbuf->st_mode == 0) {
+    if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+      statbuf->st_mode |= _S_IFDIR;
+      statbuf->st_size = 0;
+    } else {
+      statbuf->st_mode |= _S_IFREG;
+      statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
+    }
+  }
+
+  if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
+    statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
+  else
+    statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
+                        ((_S_IREAD | _S_IWRITE) >> 6);
+
+  FILETIME_TO_TIMESPEC(statbuf->st_atim, file_info.BasicInformation.LastAccessTime);
+  FILETIME_TO_TIMESPEC(statbuf->st_ctim, file_info.BasicInformation.ChangeTime);
+  FILETIME_TO_TIMESPEC(statbuf->st_mtim, file_info.BasicInformation.LastWriteTime);
+  FILETIME_TO_TIMESPEC(statbuf->st_birthtim, file_info.BasicInformation.CreationTime);
+
+  statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
+
+  /* st_blocks contains the on-disk allocation size in 512-byte units. */
+  statbuf->st_blocks =
+      (uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9;
+
+  statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
+
+  /* The st_blksize is supposed to be the 'optimal' number of bytes for reading
+   * and writing to the disk. That is, for any definition of 'optimal' - it's
+   * supposed to at least avoid read-update-write behavior when writing to the
+   * disk.
+   *
+   * However nobody knows this and even fewer people actually use this value,
+   * and in order to fill it out we'd have to make another syscall to query the
+   * volume for FILE_FS_SECTOR_SIZE_INFORMATION.
+   *
+   * Therefore we'll just report a sensible value that's quite commonly okay
+   * on modern hardware.
+   *
+   * 4096 is the minimum required to be compatible with newer Advanced Format
+   * drives (which have 4096 bytes per physical sector), and to be backwards
+   * compatible with older drives (which have 512 bytes per physical sector).
+   */
+  statbuf->st_blksize = 4096;
+
+  /* Todo: set st_flags to something meaningful. Also provide a wrapper for
+   * chattr(2).
+   */
+  statbuf->st_flags = 0;
+
+  /* Windows has nothing sensible to say about these values, so they'll just
+   * remain empty.
+   */
+  statbuf->st_gid = 0;
+  statbuf->st_uid = 0;
+  statbuf->st_rdev = 0;
+  statbuf->st_gen = 0;
+
+  return 0;
+}
+
+
+INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
+  size_t len = wcslen(pathw);
+
+  /* TODO: ignore namespaced paths. */
+  if (len > 1 && pathw[len - 2] != L':' &&
+      (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) {
+    pathw[len - 1] = '\0';
+  }
+}
+
+
+INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
+                                            int do_lstat,
+                                            uv_stat_t* statbuf) {
+  HANDLE handle;
+  DWORD flags;
+  DWORD ret;
+
+  flags = FILE_FLAG_BACKUP_SEMANTICS;
+  if (do_lstat)
+    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+  handle = CreateFileW(path,
+                       FILE_READ_ATTRIBUTES,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       flags,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE)
+    ret = GetLastError();
+  else if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
+    ret = GetLastError();
+  else
+    ret = 0;
+
+  CloseHandle(handle);
+  return ret;
+}
+
+
+INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
+  DWORD error;
+
+  error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf);
+  if (error != 0) {
+    if (do_lstat &&
+        (error == ERROR_SYMLINK_NOT_SUPPORTED ||
+         error == ERROR_NOT_A_REPARSE_POINT)) {
+      /* We opened a reparse point but it was not a symlink. Try again. */
+      fs__stat_impl(req, 0);
+    } else {
+      /* Stat failed. */
+      SET_REQ_WIN32_ERROR(req, error);
+    }
+
+    return;
+  }
+
+  req->ptr = &req->statbuf;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__stat(uv_fs_t* req) {
+  fs__stat_prepare_path(req->file.pathw);
+  fs__stat_impl(req, 0);
+}
+
+
+static void fs__lstat(uv_fs_t* req) {
+  fs__stat_prepare_path(req->file.pathw);
+  fs__stat_impl(req, 1);
+}
+
+
+static void fs__fstat(uv_fs_t* req) {
+  int fd = req->file.fd;
+  HANDLE handle;
+
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (fs__stat_handle(handle, &req->statbuf, 0) != 0) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  req->ptr = &req->statbuf;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__rename(uv_fs_t* req) {
+  if (!MoveFileExW(req->file.pathw, req->fs.info.new_pathw, MOVEFILE_REPLACE_EXISTING)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  SET_REQ_RESULT(req, 0);
+}
+
+
+INLINE static void fs__sync_impl(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int result;
+
+  VERIFY_FD(fd, req);
+
+  result = FlushFileBuffers(uv__get_osfhandle(fd)) ? 0 : -1;
+  if (result == -1) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+  } else {
+    SET_REQ_RESULT(req, result);
+  }
+}
+
+
+static void fs__fsync(uv_fs_t* req) {
+  fs__sync_impl(req);
+}
+
+
+static void fs__fdatasync(uv_fs_t* req) {
+  fs__sync_impl(req);
+}
+
+
+static void fs__ftruncate(uv_fs_t* req) {
+  int fd = req->file.fd;
+  HANDLE handle;
+  struct uv__fd_info_s fd_info = { 0 };
+  NTSTATUS status;
+  IO_STATUS_BLOCK io_status;
+  FILE_END_OF_FILE_INFORMATION eof_info;
+
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  if (uv__fd_hash_get(fd, &fd_info)) {
+    if (fd_info.is_directory) {
+      SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
+      return;
+    }
+
+    if (fd_info.mapping != INVALID_HANDLE_VALUE) {
+      CloseHandle(fd_info.mapping);
+    }
+  }
+
+  eof_info.EndOfFile.QuadPart = req->fs.info.offset;
+
+  status = pNtSetInformationFile(handle,
+                                 &io_status,
+                                 &eof_info,
+                                 sizeof eof_info,
+                                 FileEndOfFileInformation);
+
+  if (NT_SUCCESS(status)) {
+    SET_REQ_RESULT(req, 0);
+  } else {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
+
+    if (fd_info.flags) {
+      CloseHandle(handle);
+      fd_info.mapping = INVALID_HANDLE_VALUE;
+      fd_info.size.QuadPart = 0;
+      fd_info.current_pos.QuadPart = 0;
+      uv__fd_hash_add(fd, &fd_info);
+      return;
+    }
+  }
+
+  if (fd_info.flags) {
+    fd_info.size = eof_info.EndOfFile;
+
+    if (fd_info.size.QuadPart == 0) {
+      fd_info.mapping = INVALID_HANDLE_VALUE;
+    } else {
+      DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY |
+        UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE;
+      fd_info.mapping = CreateFileMapping(handle,
+                                          NULL,
+                                          flProtect,
+                                          fd_info.size.HighPart,
+                                          fd_info.size.LowPart,
+                                          NULL);
+      if (fd_info.mapping == NULL) {
+        SET_REQ_WIN32_ERROR(req, GetLastError());
+        CloseHandle(handle);
+        fd_info.mapping = INVALID_HANDLE_VALUE;
+        fd_info.size.QuadPart = 0;
+        fd_info.current_pos.QuadPart = 0;
+        uv__fd_hash_add(fd, &fd_info);
+        return;
+      }
+    }
+
+    uv__fd_hash_add(fd, &fd_info);
+  }
+}
+
+
+static void fs__copyfile(uv_fs_t* req) {
+  int flags;
+  int overwrite;
+  uv_stat_t statbuf;
+  uv_stat_t new_statbuf;
+
+  flags = req->fs.info.file_flags;
+
+  if (flags & UV_FS_COPYFILE_FICLONE_FORCE) {
+    SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
+    return;
+  }
+
+  overwrite = flags & UV_FS_COPYFILE_EXCL;
+
+  if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) != 0) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  if (req->result != UV_EBUSY)
+    return;
+
+  /* if error UV_EBUSY check if src and dst file are the same */
+  if (fs__stat_impl_from_path(req->file.pathw, 0, &statbuf) != 0 ||
+      fs__stat_impl_from_path(req->fs.info.new_pathw, 0, &new_statbuf) != 0) {
+    return;
+  }
+
+  if (statbuf.st_dev == new_statbuf.st_dev &&
+      statbuf.st_ino == new_statbuf.st_ino) {
+    SET_REQ_RESULT(req, 0);
+  }
+}
+
+
+static void fs__sendfile(uv_fs_t* req) {
+  int fd_in = req->file.fd, fd_out = req->fs.info.fd_out;
+  size_t length = req->fs.info.bufsml[0].len;
+  int64_t offset = req->fs.info.offset;
+  const size_t max_buf_size = 65536;
+  size_t buf_size = length < max_buf_size ? length : max_buf_size;
+  int n, result = 0;
+  int64_t result_offset = 0;
+  char* buf = (char*) uv__malloc(buf_size);
+  if (!buf) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (offset != -1) {
+    result_offset = _lseeki64(fd_in, offset, SEEK_SET);
+  }
+
+  if (result_offset == -1) {
+    result = -1;
+  } else {
+    while (length > 0) {
+      n = _read(fd_in, buf, length < buf_size ? length : buf_size);
+      if (n == 0) {
+        break;
+      } else if (n == -1) {
+        result = -1;
+        break;
+      }
+
+      length -= n;
+
+      n = _write(fd_out, buf, n);
+      if (n == -1) {
+        result = -1;
+        break;
+      }
+
+      result += n;
+    }
+  }
+
+  uv__free(buf);
+
+  SET_REQ_RESULT(req, result);
+}
+
+
+static void fs__access(uv_fs_t* req) {
+  DWORD attr = GetFileAttributesW(req->file.pathw);
+
+  if (attr == INVALID_FILE_ATTRIBUTES) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  /*
+   * Access is possible if
+   * - write access wasn't requested,
+   * - or the file isn't read-only,
+   * - or it's a directory.
+   * (Directories cannot be read-only on Windows.)
+   */
+  if (!(req->fs.info.mode & W_OK) ||
+      !(attr & FILE_ATTRIBUTE_READONLY) ||
+      (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+    SET_REQ_RESULT(req, 0);
+  } else {
+    SET_REQ_WIN32_ERROR(req, UV_EPERM);
+  }
+
+}
+
+
+static void fs__chmod(uv_fs_t* req) {
+  int result = _wchmod(req->file.pathw, req->fs.info.mode);
+  if (result == -1)
+    SET_REQ_WIN32_ERROR(req, _doserrno);
+  else
+    SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__fchmod(uv_fs_t* req) {
+  int fd = req->file.fd;
+  int clear_archive_flag;
+  HANDLE handle;
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_BASIC_INFORMATION file_info;
+
+  VERIFY_FD(fd, req);
+
+  handle = ReOpenFile(uv__get_osfhandle(fd), FILE_WRITE_ATTRIBUTES, 0, 0);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  nt_status = pNtQueryInformationFile(handle,
+                                      &io_status,
+                                      &file_info,
+                                      sizeof file_info,
+                                      FileBasicInformation);
+
+  if (!NT_SUCCESS(nt_status)) {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+    goto fchmod_cleanup;
+  }
+
+  /* Test if the Archive attribute is cleared */
+  if ((file_info.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) {
+      /* Set Archive flag, otherwise setting or clearing the read-only
+         flag will not work */
+      file_info.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
+      nt_status = pNtSetInformationFile(handle,
+                                        &io_status,
+                                        &file_info,
+                                        sizeof file_info,
+                                        FileBasicInformation);
+      if (!NT_SUCCESS(nt_status)) {
+        SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+        goto fchmod_cleanup;
+      }
+      /* Remeber to clear the flag later on */
+      clear_archive_flag = 1;
+  } else {
+      clear_archive_flag = 0;
+  }
+
+  if (req->fs.info.mode & _S_IWRITE) {
+    file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
+  } else {
+    file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+  }
+
+  nt_status = pNtSetInformationFile(handle,
+                                    &io_status,
+                                    &file_info,
+                                    sizeof file_info,
+                                    FileBasicInformation);
+
+  if (!NT_SUCCESS(nt_status)) {
+    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+    goto fchmod_cleanup;
+  }
+
+  if (clear_archive_flag) {
+      file_info.FileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
+      if (file_info.FileAttributes == 0) {
+          file_info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+      }
+      nt_status = pNtSetInformationFile(handle,
+                                        &io_status,
+                                        &file_info,
+                                        sizeof file_info,
+                                        FileBasicInformation);
+      if (!NT_SUCCESS(nt_status)) {
+        SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
+        goto fchmod_cleanup;
+      }
+  }
+
+  SET_REQ_SUCCESS(req);
+fchmod_cleanup:
+  CloseHandle(handle);
+}
+
+
+INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
+  FILETIME filetime_a, filetime_m;
+
+  TIME_T_TO_FILETIME(atime, &filetime_a);
+  TIME_T_TO_FILETIME(mtime, &filetime_m);
+
+  if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
+    return -1;
+  }
+
+  return 0;
+}
+
+INLINE static DWORD fs__utime_impl_from_path(WCHAR* path,
+                                             double atime,
+                                             double mtime,
+                                             int do_lutime) {
+  HANDLE handle;
+  DWORD flags;
+  DWORD ret;
+
+  flags = FILE_FLAG_BACKUP_SEMANTICS;
+  if (do_lutime) {
+    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+  }
+
+  handle = CreateFileW(path,
+                       FILE_WRITE_ATTRIBUTES,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       flags,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    ret = GetLastError();
+  } else if (fs__utime_handle(handle, atime, mtime) != 0) {
+    ret = GetLastError();
+  } else {
+    ret = 0;
+  }
+
+  CloseHandle(handle);
+  return ret;
+}
+
+INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) {
+  DWORD error;
+
+  error = fs__utime_impl_from_path(req->file.pathw,
+                                   req->fs.time.atime,
+                                   req->fs.time.mtime,
+                                   do_lutime);
+
+  if (error != 0) {
+    if (do_lutime &&
+        (error == ERROR_SYMLINK_NOT_SUPPORTED ||
+         error == ERROR_NOT_A_REPARSE_POINT)) {
+      /* Opened file is a reparse point but not a symlink. Try again. */
+      fs__utime_impl(req, 0);
+    } else {
+      /* utime failed. */
+      SET_REQ_WIN32_ERROR(req, error);
+    }
+
+    return;
+  }
+
+  SET_REQ_RESULT(req, 0);
+}
+
+static void fs__utime(uv_fs_t* req) {
+  fs__utime_impl(req, /* do_lutime */ 0);
+}
+
+
+static void fs__futime(uv_fs_t* req) {
+  int fd = req->file.fd;
+  HANDLE handle;
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
+    return;
+  }
+
+  if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  SET_REQ_RESULT(req, 0);
+}
+
+static void fs__lutime(uv_fs_t* req) {
+  fs__utime_impl(req, /* do_lutime */ 1);
+}
+
+
+static void fs__link(uv_fs_t* req) {
+  DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL);
+  if (r == 0)
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+  else
+    SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
+    const WCHAR* new_path) {
+  HANDLE handle = INVALID_HANDLE_VALUE;
+  REPARSE_DATA_BUFFER *buffer = NULL;
+  int created = 0;
+  int target_len;
+  int is_absolute, is_long_path;
+  int needed_buf_size, used_buf_size, used_data_size, path_buf_len;
+  int start, len, i;
+  int add_slash;
+  DWORD bytes;
+  WCHAR* path_buf;
+
+  target_len = wcslen(path);
+  is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0;
+
+  if (is_long_path) {
+    is_absolute = 1;
+  } else {
+    is_absolute = target_len >= 3 && IS_LETTER(path[0]) &&
+      path[1] == L':' && IS_SLASH(path[2]);
+  }
+
+  if (!is_absolute) {
+    /* Not supporting relative paths */
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED);
+    return;
+  }
+
+  /* Do a pessimistic calculation of the required buffer size */
+  needed_buf_size =
+      FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
+      JUNCTION_PREFIX_LEN * sizeof(WCHAR) +
+      2 * (target_len + 2) * sizeof(WCHAR);
+
+  /* Allocate the buffer */
+  buffer = (REPARSE_DATA_BUFFER*)uv__malloc(needed_buf_size);
+  if (!buffer) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  /* Grab a pointer to the part of the buffer where filenames go */
+  path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer);
+  path_buf_len = 0;
+
+  /* Copy the substitute (internal) target path */
+  start = path_buf_len;
+
+  wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX,
+    JUNCTION_PREFIX_LEN);
+  path_buf_len += JUNCTION_PREFIX_LEN;
+
+  add_slash = 0;
+  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
+    if (IS_SLASH(path[i])) {
+      add_slash = 1;
+      continue;
+    }
+
+    if (add_slash) {
+      path_buf[path_buf_len++] = L'\\';
+      add_slash = 0;
+    }
+
+    path_buf[path_buf_len++] = path[i];
+  }
+  path_buf[path_buf_len++] = L'\\';
+  len = path_buf_len - start;
+
+  /* Set the info about the substitute name */
+  buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
+  buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
+
+  /* Insert null terminator */
+  path_buf[path_buf_len++] = L'\0';
+
+  /* Copy the print name of the target path */
+  start = path_buf_len;
+  add_slash = 0;
+  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
+    if (IS_SLASH(path[i])) {
+      add_slash = 1;
+      continue;
+    }
+
+    if (add_slash) {
+      path_buf[path_buf_len++] = L'\\';
+      add_slash = 0;
+    }
+
+    path_buf[path_buf_len++] = path[i];
+  }
+  len = path_buf_len - start;
+  if (len == 2) {
+    path_buf[path_buf_len++] = L'\\';
+    len++;
+  }
+
+  /* Set the info about the print name */
+  buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
+  buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
+
+  /* Insert another null terminator */
+  path_buf[path_buf_len++] = L'\0';
+
+  /* Calculate how much buffer space was actually used */
+  used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
+    path_buf_len * sizeof(WCHAR);
+  used_data_size = used_buf_size -
+    FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
+
+  /* Put general info in the data buffer */
+  buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+  buffer->ReparseDataLength = used_data_size;
+  buffer->Reserved = 0;
+
+  /* Create a new directory */
+  if (!CreateDirectoryW(new_path, NULL)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+  created = 1;
+
+  /* Open the directory */
+  handle = CreateFileW(new_path,
+                       GENERIC_WRITE,
+                       0,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_FLAG_BACKUP_SEMANTICS |
+                         FILE_FLAG_OPEN_REPARSE_POINT,
+                       NULL);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  /* Create the actual reparse point */
+  if (!DeviceIoControl(handle,
+                       FSCTL_SET_REPARSE_POINT,
+                       buffer,
+                       used_buf_size,
+                       NULL,
+                       0,
+                       &bytes,
+                       NULL)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  /* Clean up */
+  CloseHandle(handle);
+  uv__free(buffer);
+
+  SET_REQ_RESULT(req, 0);
+  return;
+
+error:
+  uv__free(buffer);
+
+  if (handle != INVALID_HANDLE_VALUE) {
+    CloseHandle(handle);
+  }
+
+  if (created) {
+    RemoveDirectoryW(new_path);
+  }
+}
+
+
+static void fs__symlink(uv_fs_t* req) {
+  WCHAR* pathw;
+  WCHAR* new_pathw;
+  int flags;
+  int err;
+
+  pathw = req->file.pathw;
+  new_pathw = req->fs.info.new_pathw;
+
+  if (req->fs.info.file_flags & UV_FS_SYMLINK_JUNCTION) {
+    fs__create_junction(req, pathw, new_pathw);
+    return;
+  }
+
+  if (req->fs.info.file_flags & UV_FS_SYMLINK_DIR)
+    flags = SYMBOLIC_LINK_FLAG_DIRECTORY | uv__file_symlink_usermode_flag;
+  else
+    flags = uv__file_symlink_usermode_flag;
+
+  if (CreateSymbolicLinkW(new_pathw, pathw, flags)) {
+    SET_REQ_RESULT(req, 0);
+    return;
+  }
+
+  /* Something went wrong. We will test if it is because of user-mode
+   * symlinks.
+   */
+  err = GetLastError();
+  if (err == ERROR_INVALID_PARAMETER &&
+      flags & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) {
+    /* This system does not support user-mode symlinks. We will clear the
+     * unsupported flag and retry.
+     */
+    uv__file_symlink_usermode_flag = 0;
+    fs__symlink(req);
+  } else {
+    SET_REQ_WIN32_ERROR(req, err);
+  }
+}
+
+
+static void fs__readlink(uv_fs_t* req) {
+  HANDLE handle;
+
+  handle = CreateFileW(req->file.pathw,
+                       0,
+                       0,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    CloseHandle(handle);
+    return;
+  }
+
+  req->flags |= UV_FS_FREE_PTR;
+  SET_REQ_RESULT(req, 0);
+
+  CloseHandle(handle);
+}
+
+
+static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
+  int r;
+  DWORD w_realpath_len;
+  WCHAR* w_realpath_ptr = NULL;
+  WCHAR* w_realpath_buf;
+
+  w_realpath_len = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
+  if (w_realpath_len == 0) {
+    return -1;
+  }
+
+  w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
+  if (w_realpath_buf == NULL) {
+    SetLastError(ERROR_OUTOFMEMORY);
+    return -1;
+  }
+  w_realpath_ptr = w_realpath_buf;
+
+  if (GetFinalPathNameByHandleW(
+          handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) {
+    uv__free(w_realpath_buf);
+    SetLastError(ERROR_INVALID_HANDLE);
+    return -1;
+  }
+
+  /* convert UNC path to long path */
+  if (wcsncmp(w_realpath_ptr,
+              UNC_PATH_PREFIX,
+              UNC_PATH_PREFIX_LEN) == 0) {
+    w_realpath_ptr += 6;
+    *w_realpath_ptr = L'\\';
+    w_realpath_len -= 6;
+  } else if (wcsncmp(w_realpath_ptr,
+                      LONG_PATH_PREFIX,
+                      LONG_PATH_PREFIX_LEN) == 0) {
+    w_realpath_ptr += 4;
+    w_realpath_len -= 4;
+  } else {
+    uv__free(w_realpath_buf);
+    SetLastError(ERROR_INVALID_HANDLE);
+    return -1;
+  }
+
+  r = fs__wide_to_utf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
+  uv__free(w_realpath_buf);
+  return r;
+}
+
+static void fs__realpath(uv_fs_t* req) {
+  HANDLE handle;
+
+  handle = CreateFileW(req->file.pathw,
+                       0,
+                       0,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) {
+    CloseHandle(handle);
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  CloseHandle(handle);
+  req->flags |= UV_FS_FREE_PTR;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__chown(uv_fs_t* req) {
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__fchown(uv_fs_t* req) {
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__lchown(uv_fs_t* req) {
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void fs__statfs(uv_fs_t* req) {
+  uv_statfs_t* stat_fs;
+  DWORD sectors_per_cluster;
+  DWORD bytes_per_sector;
+  DWORD free_clusters;
+  DWORD total_clusters;
+  WCHAR* pathw;
+
+  pathw = req->file.pathw;
+retry_get_disk_free_space:
+  if (0 == GetDiskFreeSpaceW(pathw,
+                             &sectors_per_cluster,
+                             &bytes_per_sector,
+                             &free_clusters,
+                             &total_clusters)) {
+    DWORD err;
+    WCHAR* fpart;
+    size_t len;
+    DWORD ret;
+    BOOL is_second;
+
+    err = GetLastError();
+    is_second = pathw != req->file.pathw;
+    if (err != ERROR_DIRECTORY || is_second) {
+      if (is_second)
+        uv__free(pathw);
+
+      SET_REQ_WIN32_ERROR(req, err);
+      return;
+    }
+
+    len = MAX_PATH + 1;
+    pathw = uv__malloc(len * sizeof(*pathw));
+    if (pathw == NULL) {
+      SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+      return;
+    }
+retry_get_full_path_name:
+    ret = GetFullPathNameW(req->file.pathw,
+                           len,
+                           pathw,
+                           &fpart);
+    if (ret == 0) {
+      uv__free(pathw);
+      SET_REQ_WIN32_ERROR(req, err);
+      return;
+    } else if (ret > len) {
+      len = ret;
+      pathw = uv__reallocf(pathw, len * sizeof(*pathw));
+      if (pathw == NULL) {
+        SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+        return;
+      }
+      goto retry_get_full_path_name;
+    }
+    if (fpart != 0)
+      *fpart = L'\0';
+
+    goto retry_get_disk_free_space;
+  }
+  if (pathw != req->file.pathw) {
+    uv__free(pathw);
+  }
+
+  stat_fs = uv__malloc(sizeof(*stat_fs));
+  if (stat_fs == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    return;
+  }
+
+  stat_fs->f_type = 0;
+  stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster;
+  stat_fs->f_blocks = total_clusters;
+  stat_fs->f_bfree = free_clusters;
+  stat_fs->f_bavail = free_clusters;
+  stat_fs->f_files = 0;
+  stat_fs->f_ffree = 0;
+  req->ptr = stat_fs;
+  req->flags |= UV_FS_FREE_PTR;
+  SET_REQ_RESULT(req, 0);
+}
+
+
+static void uv__fs_work(struct uv__work* w) {
+  uv_fs_t* req;
+
+  req = container_of(w, uv_fs_t, work_req);
+  assert(req->type == UV_FS);
+
+#define XX(uc, lc)  case UV_FS_##uc: fs__##lc(req); break;
+  switch (req->fs_type) {
+    XX(OPEN, open)
+    XX(CLOSE, close)
+    XX(READ, read)
+    XX(WRITE, write)
+    XX(COPYFILE, copyfile)
+    XX(SENDFILE, sendfile)
+    XX(STAT, stat)
+    XX(LSTAT, lstat)
+    XX(FSTAT, fstat)
+    XX(FTRUNCATE, ftruncate)
+    XX(UTIME, utime)
+    XX(FUTIME, futime)
+    XX(LUTIME, lutime)
+    XX(ACCESS, access)
+    XX(CHMOD, chmod)
+    XX(FCHMOD, fchmod)
+    XX(FSYNC, fsync)
+    XX(FDATASYNC, fdatasync)
+    XX(UNLINK, unlink)
+    XX(RMDIR, rmdir)
+    XX(MKDIR, mkdir)
+    XX(MKDTEMP, mkdtemp)
+    XX(MKSTEMP, mkstemp)
+    XX(RENAME, rename)
+    XX(SCANDIR, scandir)
+    XX(READDIR, readdir)
+    XX(OPENDIR, opendir)
+    XX(CLOSEDIR, closedir)
+    XX(LINK, link)
+    XX(SYMLINK, symlink)
+    XX(READLINK, readlink)
+    XX(REALPATH, realpath)
+    XX(CHOWN, chown)
+    XX(FCHOWN, fchown)
+    XX(LCHOWN, lchown)
+    XX(STATFS, statfs)
+    default:
+      assert(!"bad uv_fs_type");
+  }
+}
+
+
+static void uv__fs_done(struct uv__work* w, int status) {
+  uv_fs_t* req;
+
+  req = container_of(w, uv_fs_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  if (status == UV_ECANCELED) {
+    assert(req->result == 0);
+    SET_REQ_UV_ERROR(req, UV_ECANCELED, 0);
+  }
+
+  req->cb(req);
+}
+
+
+void uv_fs_req_cleanup(uv_fs_t* req) {
+  if (req == NULL)
+    return;
+
+  if (req->flags & UV_FS_CLEANEDUP)
+    return;
+
+  if (req->flags & UV_FS_FREE_PATHS)
+    uv__free(req->file.pathw);
+
+  if (req->flags & UV_FS_FREE_PTR) {
+    if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
+      uv__fs_scandir_cleanup(req);
+    else if (req->fs_type == UV_FS_READDIR)
+      uv__fs_readdir_cleanup(req);
+    else
+      uv__free(req->ptr);
+  }
+
+  if (req->fs.info.bufs != req->fs.info.bufsml)
+    uv__free(req->fs.info.bufs);
+
+  req->path = NULL;
+  req->file.pathw = NULL;
+  req->fs.info.new_pathw = NULL;
+  req->fs.info.bufs = NULL;
+  req->ptr = NULL;
+
+  req->flags |= UV_FS_CLEANEDUP;
+}
+
+
+int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
+    int mode, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_OPEN);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_CLOSE);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_read(uv_loop_t* loop,
+               uv_fs_t* req,
+               uv_file fd,
+               const uv_buf_t bufs[],
+               unsigned int nbufs,
+               int64_t offset,
+               uv_fs_cb cb) {
+  INIT(UV_FS_READ);
+
+  if (bufs == NULL || nbufs == 0) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  req->file.fd = fd;
+
+  req->fs.info.nbufs = nbufs;
+  req->fs.info.bufs = req->fs.info.bufsml;
+  if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->fs.info.bufs == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    return UV_ENOMEM;
+  }
+
+  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->fs.info.offset = offset;
+  POST;
+}
+
+
+int uv_fs_write(uv_loop_t* loop,
+                uv_fs_t* req,
+                uv_file fd,
+                const uv_buf_t bufs[],
+                unsigned int nbufs,
+                int64_t offset,
+                uv_fs_cb cb) {
+  INIT(UV_FS_WRITE);
+
+  if (bufs == NULL || nbufs == 0) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  req->file.fd = fd;
+
+  req->fs.info.nbufs = nbufs;
+  req->fs.info.bufs = req->fs.info.bufsml;
+  if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
+    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->fs.info.bufs == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    return UV_ENOMEM;
+  }
+
+  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->fs.info.offset = offset;
+  POST;
+}
+
+
+int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_UNLINK);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_MKDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_mkdtemp(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* tpl,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_MKDTEMP);
+  err = fs__capture_path(req, tpl, NULL, TRUE);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_mkstemp(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* tpl,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_MKSTEMP);
+  err = fs__capture_path(req, tpl, NULL, TRUE);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_RMDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_SCANDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  POST;
+}
+
+int uv_fs_opendir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_OPENDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+  POST;
+}
+
+int uv_fs_readdir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  uv_dir_t* dir,
+                  uv_fs_cb cb) {
+  INIT(UV_FS_READDIR);
+
+  if (dir == NULL ||
+      dir->dirents == NULL ||
+      dir->dir_handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_closedir(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_dir_t* dir,
+                   uv_fs_cb cb) {
+  INIT(UV_FS_CLOSEDIR);
+  if (dir == NULL) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    const char* new_path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LINK);
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    const char* new_path, int flags, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_SYMLINK);
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  POST;
+}
+
+
+int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_READLINK);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_REALPATH);
+
+  if (!path) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
+    uv_gid_t gid, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_CHOWN);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid,
+    uv_gid_t gid, uv_fs_cb cb) {
+  INIT(UV_FS_FCHOWN);
+  POST;
+}
+
+
+int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
+    uv_gid_t gid, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LCHOWN);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_STAT);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LSTAT);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_FSTAT);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    const char* new_path, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_RENAME);
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+
+int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_FSYNC);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  INIT(UV_FS_FDATASYNC);
+  req->file.fd = fd;
+  POST;
+}
+
+
+int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd,
+    int64_t offset, uv_fs_cb cb) {
+  INIT(UV_FS_FTRUNCATE);
+  req->file.fd = fd;
+  req->fs.info.offset = offset;
+  POST;
+}
+
+
+int uv_fs_copyfile(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   const char* path,
+                   const char* new_path,
+                   int flags,
+                   uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_COPYFILE);
+
+  if (flags & ~(UV_FS_COPYFILE_EXCL |
+                UV_FS_COPYFILE_FICLONE |
+                UV_FS_COPYFILE_FICLONE_FORCE)) {
+    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
+    return UV_EINVAL;
+  }
+
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.file_flags = flags;
+  POST;
+}
+
+
+int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out,
+    uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) {
+  INIT(UV_FS_SENDFILE);
+  req->file.fd = fd_in;
+  req->fs.info.fd_out = fd_out;
+  req->fs.info.offset = in_offset;
+  req->fs.info.bufsml[0].len = length;
+  POST;
+}
+
+
+int uv_fs_access(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 int flags,
+                 uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_ACCESS);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.mode = flags;
+  POST;
+}
+
+
+int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+    uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_CHMOD);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode,
+    uv_fs_cb cb) {
+  INIT(UV_FS_FCHMOD);
+  req->file.fd = fd;
+  req->fs.info.mode = mode;
+  POST;
+}
+
+
+int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
+    double mtime, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_UTIME);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+  POST;
+}
+
+
+int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
+    double mtime, uv_fs_cb cb) {
+  INIT(UV_FS_FUTIME);
+  req->file.fd = fd;
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+  POST;
+}
+
+int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
+    double mtime, uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_LUTIME);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+  POST;
+}
+
+
+int uv_fs_statfs(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_STATFS);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    SET_REQ_WIN32_ERROR(req, err);
+    return req->result;
+  }
+
+  POST;
+}
+
+int uv_fs_get_system_error(const uv_fs_t* req) {
+  return req->sys_errno_;
+}
diff --git a/third_party/libuv/src/win/getaddrinfo.c b/third_party/libuv/src/win/getaddrinfo.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/getaddrinfo.c
@@ -0,0 +1,463 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "req-inl.h"
+#include "idna.h"
+
+/* EAI_* constants. */
+#include <winsock2.h>
+
+/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */
+#include <iphlpapi.h>
+
+int uv__getaddrinfo_translate_error(int sys_err) {
+  switch (sys_err) {
+    case 0:                       return 0;
+    case WSATRY_AGAIN:            return UV_EAI_AGAIN;
+    case WSAEINVAL:               return UV_EAI_BADFLAGS;
+    case WSANO_RECOVERY:          return UV_EAI_FAIL;
+    case WSAEAFNOSUPPORT:         return UV_EAI_FAMILY;
+    case WSA_NOT_ENOUGH_MEMORY:   return UV_EAI_MEMORY;
+    case WSAHOST_NOT_FOUND:       return UV_EAI_NONAME;
+    case WSATYPE_NOT_FOUND:       return UV_EAI_SERVICE;
+    case WSAESOCKTNOSUPPORT:      return UV_EAI_SOCKTYPE;
+    default:                      return uv_translate_sys_error(sys_err);
+  }
+}
+
+
+/*
+ * MinGW is missing this
+ */
+#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
+  typedef struct addrinfoW {
+    int ai_flags;
+    int ai_family;
+    int ai_socktype;
+    int ai_protocol;
+    size_t ai_addrlen;
+    WCHAR* ai_canonname;
+    struct sockaddr* ai_addr;
+    struct addrinfoW* ai_next;
+  } ADDRINFOW, *PADDRINFOW;
+
+  DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const WCHAR* node,
+                                          const WCHAR* service,
+                                          const ADDRINFOW* hints,
+                                          PADDRINFOW* result);
+
+  DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
+#endif
+
+
+/* Adjust size value to be multiple of 4. Use to keep pointer aligned.
+ * Do we need different versions of this for different architectures? */
+#define ALIGNED_SIZE(X)     ((((X) + 3) >> 2) << 2)
+
+#ifndef NDIS_IF_MAX_STRING_SIZE
+#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
+#endif
+
+static void uv__getaddrinfo_work(struct uv__work* w) {
+  uv_getaddrinfo_t* req;
+  struct addrinfoW* hints;
+  int err;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+  hints = req->addrinfow;
+  req->addrinfow = NULL;
+  err = GetAddrInfoW(req->node, req->service, hints, &req->addrinfow);
+  req->retcode = uv__getaddrinfo_translate_error(err);
+}
+
+
+/*
+ * Called from uv_run when complete. Call user specified callback
+ * then free returned addrinfo
+ * Returned addrinfo strings are converted from UTF-16 to UTF-8.
+ *
+ * To minimize allocation we calculate total size required,
+ * and copy all structs and referenced strings into the one block.
+ * Each size calculation is adjusted to avoid unaligned pointers.
+ */
+static void uv__getaddrinfo_done(struct uv__work* w, int status) {
+  uv_getaddrinfo_t* req;
+  int addrinfo_len = 0;
+  int name_len = 0;
+  size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
+  struct addrinfoW* addrinfow_ptr;
+  struct addrinfo* addrinfo_ptr;
+  char* alloc_ptr = NULL;
+  char* cur_ptr = NULL;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+
+  /* release input parameter memory */
+  uv__free(req->alloc);
+  req->alloc = NULL;
+
+  if (status == UV_ECANCELED) {
+    assert(req->retcode == 0);
+    req->retcode = UV_EAI_CANCELED;
+    goto complete;
+  }
+
+  if (req->retcode == 0) {
+    /* Convert addrinfoW to addrinfo. First calculate required length. */
+    addrinfow_ptr = req->addrinfow;
+    while (addrinfow_ptr != NULL) {
+      addrinfo_len += addrinfo_struct_len +
+          ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
+      if (addrinfow_ptr->ai_canonname != NULL) {
+        name_len = WideCharToMultiByte(CP_UTF8,
+                                       0,
+                                       addrinfow_ptr->ai_canonname,
+                                       -1,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       NULL);
+        if (name_len == 0) {
+          req->retcode = uv_translate_sys_error(GetLastError());
+          goto complete;
+        }
+        addrinfo_len += ALIGNED_SIZE(name_len);
+      }
+      addrinfow_ptr = addrinfow_ptr->ai_next;
+    }
+
+    /* allocate memory for addrinfo results */
+    alloc_ptr = (char*)uv__malloc(addrinfo_len);
+
+    /* do conversions */
+    if (alloc_ptr != NULL) {
+      cur_ptr = alloc_ptr;
+      addrinfow_ptr = req->addrinfow;
+
+      while (addrinfow_ptr != NULL) {
+        /* copy addrinfo struct data */
+        assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
+        addrinfo_ptr = (struct addrinfo*)cur_ptr;
+        addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
+        addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
+        addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
+        addrinfo_ptr->ai_flags = addrinfow_ptr->ai_flags;
+        addrinfo_ptr->ai_addrlen = addrinfow_ptr->ai_addrlen;
+        addrinfo_ptr->ai_canonname = NULL;
+        addrinfo_ptr->ai_addr = NULL;
+        addrinfo_ptr->ai_next = NULL;
+
+        cur_ptr += addrinfo_struct_len;
+
+        /* copy sockaddr */
+        if (addrinfo_ptr->ai_addrlen > 0) {
+          assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
+                 alloc_ptr + addrinfo_len);
+          memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
+          addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
+          cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
+        }
+
+        /* convert canonical name to UTF-8 */
+        if (addrinfow_ptr->ai_canonname != NULL) {
+          name_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         addrinfow_ptr->ai_canonname,
+                                         -1,
+                                         NULL,
+                                         0,
+                                         NULL,
+                                         NULL);
+          assert(name_len > 0);
+          assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len);
+          name_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         addrinfow_ptr->ai_canonname,
+                                         -1,
+                                         cur_ptr,
+                                         name_len,
+                                         NULL,
+                                         NULL);
+          assert(name_len > 0);
+          addrinfo_ptr->ai_canonname = cur_ptr;
+          cur_ptr += ALIGNED_SIZE(name_len);
+        }
+        assert(cur_ptr <= alloc_ptr + addrinfo_len);
+
+        /* set next ptr */
+        addrinfow_ptr = addrinfow_ptr->ai_next;
+        if (addrinfow_ptr != NULL) {
+          addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
+        }
+      }
+      req->addrinfo = (struct addrinfo*)alloc_ptr;
+    } else {
+      req->retcode = UV_EAI_MEMORY;
+    }
+  }
+
+  /* return memory to system */
+  if (req->addrinfow != NULL) {
+    FreeAddrInfoW(req->addrinfow);
+    req->addrinfow = NULL;
+  }
+
+complete:
+  uv__req_unregister(req->loop, req);
+
+  /* finally do callback with converted result */
+  if (req->getaddrinfo_cb)
+    req->getaddrinfo_cb(req, req->retcode, req->addrinfo);
+}
+
+
+void uv_freeaddrinfo(struct addrinfo* ai) {
+  char* alloc_ptr = (char*)ai;
+
+  /* release copied result memory */
+  uv__free(alloc_ptr);
+}
+
+
+/*
+ * Entry point for getaddrinfo
+ * we convert the UTF-8 strings to UNICODE
+ * and save the UNICODE string pointers in the req
+ * We also copy hints so that caller does not need to keep memory until the
+ * callback.
+ * return 0 if a callback will be made
+ * return error code if validation fails
+ *
+ * To minimize allocation we calculate total size required,
+ * and copy all structs and referenced strings into the one block.
+ * Each size calculation is adjusted to avoid unaligned pointers.
+ */
+int uv_getaddrinfo(uv_loop_t* loop,
+                   uv_getaddrinfo_t* req,
+                   uv_getaddrinfo_cb getaddrinfo_cb,
+                   const char* node,
+                   const char* service,
+                   const struct addrinfo* hints) {
+  char hostname_ascii[256];
+  int nodesize = 0;
+  int servicesize = 0;
+  int hintssize = 0;
+  char* alloc_ptr = NULL;
+  int err;
+  long rc;
+
+  if (req == NULL || (node == NULL && service == NULL)) {
+    return UV_EINVAL;
+  }
+
+  UV_REQ_INIT(req, UV_GETADDRINFO);
+  req->getaddrinfo_cb = getaddrinfo_cb;
+  req->addrinfo = NULL;
+  req->loop = loop;
+  req->retcode = 0;
+
+  /* calculate required memory size for all input values */
+  if (node != NULL) {
+    rc = uv__idna_toascii(node,
+                          node + strlen(node),
+                          hostname_ascii,
+                          hostname_ascii + sizeof(hostname_ascii));
+    if (rc < 0)
+      return rc;
+    nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii,
+                                                -1, NULL, 0) * sizeof(WCHAR));
+    if (nodesize == 0) {
+      err = GetLastError();
+      goto error;
+    }
+    node = hostname_ascii;
+  }
+
+  if (service != NULL) {
+    servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8,
+                                                   0,
+                                                   service,
+                                                   -1,
+                                                   NULL,
+                                                   0) *
+                               sizeof(WCHAR));
+    if (servicesize == 0) {
+      err = GetLastError();
+      goto error;
+    }
+  }
+  if (hints != NULL) {
+    hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
+  }
+
+  /* allocate memory for inputs, and partition it as needed */
+  alloc_ptr = (char*)uv__malloc(nodesize + servicesize + hintssize);
+  if (!alloc_ptr) {
+    err = WSAENOBUFS;
+    goto error;
+  }
+
+  /* save alloc_ptr now so we can free if error */
+  req->alloc = (void*)alloc_ptr;
+
+  /* Convert node string to UTF16 into allocated memory and save pointer in the
+   * request. */
+  if (node != NULL) {
+    req->node = (WCHAR*)alloc_ptr;
+    if (MultiByteToWideChar(CP_UTF8,
+                            0,
+                            node,
+                            -1,
+                            (WCHAR*) alloc_ptr,
+                            nodesize / sizeof(WCHAR)) == 0) {
+      err = GetLastError();
+      goto error;
+    }
+    alloc_ptr += nodesize;
+  } else {
+    req->node = NULL;
+  }
+
+  /* Convert service string to UTF16 into allocated memory and save pointer in
+   * the req. */
+  if (service != NULL) {
+    req->service = (WCHAR*)alloc_ptr;
+    if (MultiByteToWideChar(CP_UTF8,
+                            0,
+                            service,
+                            -1,
+                            (WCHAR*) alloc_ptr,
+                            servicesize / sizeof(WCHAR)) == 0) {
+      err = GetLastError();
+      goto error;
+    }
+    alloc_ptr += servicesize;
+  } else {
+    req->service = NULL;
+  }
+
+  /* copy hints to allocated memory and save pointer in req */
+  if (hints != NULL) {
+    req->addrinfow = (struct addrinfoW*)alloc_ptr;
+    req->addrinfow->ai_family = hints->ai_family;
+    req->addrinfow->ai_socktype = hints->ai_socktype;
+    req->addrinfow->ai_protocol = hints->ai_protocol;
+    req->addrinfow->ai_flags = hints->ai_flags;
+    req->addrinfow->ai_addrlen = 0;
+    req->addrinfow->ai_canonname = NULL;
+    req->addrinfow->ai_addr = NULL;
+    req->addrinfow->ai_next = NULL;
+  } else {
+    req->addrinfow = NULL;
+  }
+
+  uv__req_register(loop, req);
+
+  if (getaddrinfo_cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    UV__WORK_SLOW_IO,
+                    uv__getaddrinfo_work,
+                    uv__getaddrinfo_done);
+    return 0;
+  } else {
+    uv__getaddrinfo_work(&req->work_req);
+    uv__getaddrinfo_done(&req->work_req, 0);
+    return req->retcode;
+  }
+
+error:
+  if (req != NULL) {
+    uv__free(req->alloc);
+    req->alloc = NULL;
+  }
+  return uv_translate_sys_error(err);
+}
+
+int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
+  NET_LUID luid;
+  wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
+  DWORD bufsize;
+  int r;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  r = ConvertInterfaceIndexToLuid(ifindex, &luid);
+
+  if (r != 0)
+    return uv_translate_sys_error(r);
+
+  r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));
+
+  if (r != 0)
+    return uv_translate_sys_error(r);
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);
+
+  if (bufsize == 0) {
+    return uv_translate_sys_error(GetLastError());
+  } else if (bufsize > *size) {
+    *size = bufsize;
+    return UV_ENOBUFS;
+  }
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                wname,
+                                -1,
+                                buffer,
+                                *size,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  *size = bufsize - 1;
+  return 0;
+}
+
+int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
+  int r;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  r = snprintf(buffer, *size, "%d", ifindex);
+
+  if (r < 0)
+    return uv_translate_sys_error(r);
+
+  if (r >= (int) *size) {
+    *size = r + 1;
+    return UV_ENOBUFS;
+  }
+
+  *size = r;
+  return 0;
+}
diff --git a/third_party/libuv/src/win/getnameinfo.c b/third_party/libuv/src/win/getnameinfo.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/getnameinfo.c
@@ -0,0 +1,157 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to
+* deal in the Software without restriction, including without limitation the
+* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+* sell copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+* IN THE SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "req-inl.h"
+
+#ifndef GetNameInfo
+int WSAAPI GetNameInfoW(
+  const SOCKADDR *pSockaddr,
+  socklen_t SockaddrLength,
+  PWCHAR pNodeBuffer,
+  DWORD NodeBufferSize,
+  PWCHAR pServiceBuffer,
+  DWORD ServiceBufferSize,
+  INT Flags
+);
+#endif
+
+static void uv__getnameinfo_work(struct uv__work* w) {
+  uv_getnameinfo_t* req;
+  WCHAR host[NI_MAXHOST];
+  WCHAR service[NI_MAXSERV];
+  int ret;
+
+  req = container_of(w, uv_getnameinfo_t, work_req);
+  if (GetNameInfoW((struct sockaddr*)&req->storage,
+                   sizeof(req->storage),
+                   host,
+                   ARRAY_SIZE(host),
+                   service,
+                   ARRAY_SIZE(service),
+                   req->flags)) {
+    ret = WSAGetLastError();
+    req->retcode = uv__getaddrinfo_translate_error(ret);
+    return;
+  }
+
+  ret = WideCharToMultiByte(CP_UTF8,
+                            0,
+                            host,
+                            -1,
+                            req->host,
+                            sizeof(req->host),
+                            NULL,
+                            NULL);
+  if (ret == 0) {
+    req->retcode = uv_translate_sys_error(GetLastError());
+    return;
+  }
+
+  ret = WideCharToMultiByte(CP_UTF8,
+                            0,
+                            service,
+                            -1,
+                            req->service,
+                            sizeof(req->service),
+                            NULL,
+                            NULL);
+  if (ret == 0) {
+    req->retcode = uv_translate_sys_error(GetLastError());
+  }
+}
+
+
+/*
+* Called from uv_run when complete.
+*/
+static void uv__getnameinfo_done(struct uv__work* w, int status) {
+  uv_getnameinfo_t* req;
+  char* host;
+  char* service;
+
+  req = container_of(w, uv_getnameinfo_t, work_req);
+  uv__req_unregister(req->loop, req);
+  host = service = NULL;
+
+  if (status == UV_ECANCELED) {
+    assert(req->retcode == 0);
+    req->retcode = UV_EAI_CANCELED;
+  } else if (req->retcode == 0) {
+    host = req->host;
+    service = req->service;
+  }
+
+  if (req->getnameinfo_cb)
+    req->getnameinfo_cb(req, req->retcode, host, service);
+}
+
+
+/*
+* Entry point for getnameinfo
+* return 0 if a callback will be made
+* return error code if validation fails
+*/
+int uv_getnameinfo(uv_loop_t* loop,
+                   uv_getnameinfo_t* req,
+                   uv_getnameinfo_cb getnameinfo_cb,
+                   const struct sockaddr* addr,
+                   int flags) {
+  if (req == NULL || addr == NULL)
+    return UV_EINVAL;
+
+  if (addr->sa_family == AF_INET) {
+    memcpy(&req->storage,
+           addr,
+           sizeof(struct sockaddr_in));
+  } else if (addr->sa_family == AF_INET6) {
+    memcpy(&req->storage,
+           addr,
+           sizeof(struct sockaddr_in6));
+  } else {
+    return UV_EINVAL;
+  }
+
+  UV_REQ_INIT(req, UV_GETNAMEINFO);
+  uv__req_register(loop, req);
+
+  req->getnameinfo_cb = getnameinfo_cb;
+  req->flags = flags;
+  req->loop = loop;
+  req->retcode = 0;
+
+  if (getnameinfo_cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    UV__WORK_SLOW_IO,
+                    uv__getnameinfo_work,
+                    uv__getnameinfo_done);
+    return 0;
+  } else {
+    uv__getnameinfo_work(&req->work_req);
+    uv__getnameinfo_done(&req->work_req, 0);
+    return req->retcode;
+  }
+}
diff --git a/third_party/libuv/src/win/handle-inl.h b/third_party/libuv/src/win/handle-inl.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/handle-inl.h
@@ -0,0 +1,180 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_HANDLE_INL_H_
+#define UV_WIN_HANDLE_INL_H_
+
+#include <assert.h>
+#include <io.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+#define DECREASE_ACTIVE_COUNT(loop, handle)                             \
+  do {                                                                  \
+    if (--(handle)->activecnt == 0 &&                                   \
+        !((handle)->flags & UV_HANDLE_CLOSING)) {                       \
+      uv__handle_stop((handle));                                        \
+    }                                                                   \
+    assert((handle)->activecnt >= 0);                                   \
+  } while (0)
+
+
+#define INCREASE_ACTIVE_COUNT(loop, handle)                             \
+  do {                                                                  \
+    if ((handle)->activecnt++ == 0) {                                   \
+      uv__handle_start((handle));                                       \
+    }                                                                   \
+    assert((handle)->activecnt > 0);                                    \
+  } while (0)
+
+
+#define DECREASE_PENDING_REQ_COUNT(handle)                              \
+  do {                                                                  \
+    assert(handle->reqs_pending > 0);                                   \
+    handle->reqs_pending--;                                             \
+                                                                        \
+    if (handle->flags & UV_HANDLE_CLOSING &&                            \
+        handle->reqs_pending == 0) {                                    \
+      uv_want_endgame(loop, (uv_handle_t*)handle);                      \
+    }                                                                   \
+  } while (0)
+
+
+#define uv__handle_closing(handle)                                      \
+  do {                                                                  \
+    assert(!((handle)->flags & UV_HANDLE_CLOSING));                     \
+                                                                        \
+    if (!(((handle)->flags & UV_HANDLE_ACTIVE) &&                       \
+          ((handle)->flags & UV_HANDLE_REF)))                           \
+      uv__active_handle_add((uv_handle_t*) (handle));                   \
+                                                                        \
+    (handle)->flags |= UV_HANDLE_CLOSING;                               \
+    (handle)->flags &= ~UV_HANDLE_ACTIVE;                               \
+  } while (0)
+
+
+#define uv__handle_close(handle)                                        \
+  do {                                                                  \
+    QUEUE_REMOVE(&(handle)->handle_queue);                              \
+    uv__active_handle_rm((uv_handle_t*) (handle));                      \
+                                                                        \
+    (handle)->flags |= UV_HANDLE_CLOSED;                                \
+                                                                        \
+    if ((handle)->close_cb)                                             \
+      (handle)->close_cb((uv_handle_t*) (handle));                      \
+  } while (0)
+
+
+INLINE static void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) {
+  if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) {
+    handle->flags |= UV_HANDLE_ENDGAME_QUEUED;
+
+    handle->endgame_next = loop->endgame_handles;
+    loop->endgame_handles = handle;
+  }
+}
+
+
+INLINE static void uv_process_endgames(uv_loop_t* loop) {
+  uv_handle_t* handle;
+
+  while (loop->endgame_handles) {
+    handle = loop->endgame_handles;
+    loop->endgame_handles = handle->endgame_next;
+
+    handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED;
+
+    switch (handle->type) {
+      case UV_TCP:
+        uv_tcp_endgame(loop, (uv_tcp_t*) handle);
+        break;
+
+      case UV_NAMED_PIPE:
+        uv_pipe_endgame(loop, (uv_pipe_t*) handle);
+        break;
+
+      case UV_TTY:
+        uv_tty_endgame(loop, (uv_tty_t*) handle);
+        break;
+
+      case UV_UDP:
+        uv_udp_endgame(loop, (uv_udp_t*) handle);
+        break;
+
+      case UV_POLL:
+        uv_poll_endgame(loop, (uv_poll_t*) handle);
+        break;
+
+      case UV_TIMER:
+        uv__timer_close((uv_timer_t*) handle);
+        uv__handle_close(handle);
+        break;
+
+      case UV_PREPARE:
+      case UV_CHECK:
+      case UV_IDLE:
+        uv_loop_watcher_endgame(loop, handle);
+        break;
+
+      case UV_ASYNC:
+        uv_async_endgame(loop, (uv_async_t*) handle);
+        break;
+
+      case UV_SIGNAL:
+        uv_signal_endgame(loop, (uv_signal_t*) handle);
+        break;
+
+      case UV_PROCESS:
+        uv_process_endgame(loop, (uv_process_t*) handle);
+        break;
+
+      case UV_FS_EVENT:
+        uv_fs_event_endgame(loop, (uv_fs_event_t*) handle);
+        break;
+
+      case UV_FS_POLL:
+        uv__fs_poll_endgame(loop, (uv_fs_poll_t*) handle);
+        break;
+
+      default:
+        assert(0);
+        break;
+    }
+  }
+}
+
+INLINE static HANDLE uv__get_osfhandle(int fd)
+{
+  /* _get_osfhandle() raises an assert in debug builds if the FD is invalid.
+   * But it also correctly checks the FD and returns INVALID_HANDLE_VALUE for
+   * invalid FDs in release builds (or if you let the assert continue). So this
+   * wrapper function disables asserts when calling _get_osfhandle. */
+
+  HANDLE handle;
+  UV_BEGIN_DISABLE_CRT_ASSERT();
+  handle = (HANDLE) _get_osfhandle(fd);
+  UV_END_DISABLE_CRT_ASSERT();
+  return handle;
+}
+
+#endif /* UV_WIN_HANDLE_INL_H_ */
diff --git a/third_party/libuv/src/win/handle.c b/third_party/libuv/src/win/handle.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/handle.c
@@ -0,0 +1,162 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+
+
+uv_handle_type uv_guess_handle(uv_file file) {
+  HANDLE handle;
+  DWORD mode;
+
+  if (file < 0) {
+    return UV_UNKNOWN_HANDLE;
+  }
+
+  handle = uv__get_osfhandle(file);
+
+  switch (GetFileType(handle)) {
+    case FILE_TYPE_CHAR:
+      if (GetConsoleMode(handle, &mode)) {
+        return UV_TTY;
+      } else {
+        return UV_FILE;
+      }
+
+    case FILE_TYPE_PIPE:
+      return UV_NAMED_PIPE;
+
+    case FILE_TYPE_DISK:
+      return UV_FILE;
+
+    default:
+      return UV_UNKNOWN_HANDLE;
+  }
+}
+
+
+int uv_is_active(const uv_handle_t* handle) {
+  return (handle->flags & UV_HANDLE_ACTIVE) &&
+        !(handle->flags & UV_HANDLE_CLOSING);
+}
+
+
+void uv_close(uv_handle_t* handle, uv_close_cb cb) {
+  uv_loop_t* loop = handle->loop;
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    assert(0);
+    return;
+  }
+
+  handle->close_cb = cb;
+
+  /* Handle-specific close actions */
+  switch (handle->type) {
+    case UV_TCP:
+      uv_tcp_close(loop, (uv_tcp_t*)handle);
+      return;
+
+    case UV_NAMED_PIPE:
+      uv_pipe_close(loop, (uv_pipe_t*) handle);
+      return;
+
+    case UV_TTY:
+      uv_tty_close((uv_tty_t*) handle);
+      return;
+
+    case UV_UDP:
+      uv_udp_close(loop, (uv_udp_t*) handle);
+      return;
+
+    case UV_POLL:
+      uv_poll_close(loop, (uv_poll_t*) handle);
+      return;
+
+    case UV_TIMER:
+      uv_timer_stop((uv_timer_t*)handle);
+      uv__handle_closing(handle);
+      uv_want_endgame(loop, handle);
+      return;
+
+    case UV_PREPARE:
+      uv_prepare_stop((uv_prepare_t*)handle);
+      uv__handle_closing(handle);
+      uv_want_endgame(loop, handle);
+      return;
+
+    case UV_CHECK:
+      uv_check_stop((uv_check_t*)handle);
+      uv__handle_closing(handle);
+      uv_want_endgame(loop, handle);
+      return;
+
+    case UV_IDLE:
+      uv_idle_stop((uv_idle_t*)handle);
+      uv__handle_closing(handle);
+      uv_want_endgame(loop, handle);
+      return;
+
+    case UV_ASYNC:
+      uv_async_close(loop, (uv_async_t*) handle);
+      return;
+
+    case UV_SIGNAL:
+      uv_signal_close(loop, (uv_signal_t*) handle);
+      return;
+
+    case UV_PROCESS:
+      uv_process_close(loop, (uv_process_t*) handle);
+      return;
+
+    case UV_FS_EVENT:
+      uv_fs_event_close(loop, (uv_fs_event_t*) handle);
+      return;
+
+    case UV_FS_POLL:
+      uv__fs_poll_close((uv_fs_poll_t*) handle);
+      uv__handle_closing(handle);
+      return;
+
+    default:
+      /* Not supported */
+      abort();
+  }
+}
+
+
+int uv_is_closing(const uv_handle_t* handle) {
+  return !!(handle->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED));
+}
+
+
+uv_os_fd_t uv_get_osfhandle(int fd) {
+  return uv__get_osfhandle(fd);
+}
+
+int uv_open_osfhandle(uv_os_fd_t os_fd) {
+  return _open_osfhandle((intptr_t) os_fd, 0);
+}
diff --git a/third_party/libuv/src/win/internal.h b/third_party/libuv/src/win/internal.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/internal.h
@@ -0,0 +1,344 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_INTERNAL_H_
+#define UV_WIN_INTERNAL_H_
+
+#include "uv.h"
+#include "../uv-common.h"
+
+#include "uv/tree.h"
+#include "winapi.h"
+#include "winsock.h"
+
+#ifdef _MSC_VER
+# define INLINE __inline
+# define UV_THREAD_LOCAL __declspec( thread )
+#else
+# define INLINE inline
+# define UV_THREAD_LOCAL __thread
+#endif
+
+
+#ifdef _DEBUG
+
+extern UV_THREAD_LOCAL int uv__crt_assert_enabled;
+
+#define UV_BEGIN_DISABLE_CRT_ASSERT()                           \
+  {                                                             \
+    int uv__saved_crt_assert_enabled = uv__crt_assert_enabled;  \
+    uv__crt_assert_enabled = FALSE;
+
+
+#define UV_END_DISABLE_CRT_ASSERT()                             \
+    uv__crt_assert_enabled = uv__saved_crt_assert_enabled;      \
+  }
+
+#else
+#define UV_BEGIN_DISABLE_CRT_ASSERT()
+#define UV_END_DISABLE_CRT_ASSERT()
+#endif
+
+/*
+ * TCP
+ */
+
+typedef enum {
+  UV__IPC_SOCKET_XFER_NONE = 0,
+  UV__IPC_SOCKET_XFER_TCP_CONNECTION,
+  UV__IPC_SOCKET_XFER_TCP_SERVER
+} uv__ipc_socket_xfer_type_t;
+
+typedef struct {
+  WSAPROTOCOL_INFOW socket_info;
+  uint32_t delayed_error;
+} uv__ipc_socket_xfer_info_t;
+
+int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb);
+int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client);
+int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb);
+int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle,
+    const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
+int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[],
+    unsigned int nbufs);
+
+void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req);
+void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_write_t* req);
+void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_req_t* req);
+void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_connect_t* req);
+
+void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp);
+void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle);
+
+int uv__tcp_xfer_export(uv_tcp_t* handle,
+                        int pid,
+                        uv__ipc_socket_xfer_type_t* xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info);
+int uv__tcp_xfer_import(uv_tcp_t* tcp,
+                        uv__ipc_socket_xfer_type_t xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info);
+
+
+/*
+ * UDP
+ */
+void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req);
+void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
+    uv_udp_send_t* req);
+
+void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle);
+void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle);
+
+
+/*
+ * Pipes
+ */
+int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
+    char* name, size_t nameSize);
+
+int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
+int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client);
+int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb);
+void uv__pipe_read_stop(uv_pipe_t* handle);
+int uv__pipe_write(uv_loop_t* loop,
+                   uv_write_t* req,
+                   uv_pipe_t* handle,
+                   const uv_buf_t bufs[],
+                   size_t nbufs,
+                   uv_stream_t* send_handle,
+                   uv_write_cb cb);
+
+void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_req_t* req);
+void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_write_t* req);
+void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_req_t* raw_req);
+void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_connect_t* req);
+void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_shutdown_t* req);
+
+void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle);
+void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle);
+void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle);
+
+
+/*
+ * TTY
+ */
+void uv_console_init(void);
+
+int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb);
+int uv_tty_read_stop(uv_tty_t* handle);
+int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle,
+    const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
+int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[],
+    unsigned int nbufs);
+void uv_tty_close(uv_tty_t* handle);
+
+void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req);
+void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_write_t* req);
+/*
+ * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* raw_req);
+/*
+ * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_connect_t* req);
+
+void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle);
+
+
+/*
+ * Poll watchers
+ */
+void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
+    uv_req_t* req);
+
+int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle);
+void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle);
+
+
+/*
+ * Loop watchers
+ */
+void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle);
+
+void uv_prepare_invoke(uv_loop_t* loop);
+void uv_check_invoke(uv_loop_t* loop);
+void uv_idle_invoke(uv_loop_t* loop);
+
+void uv__once_init(void);
+
+
+/*
+ * Async watcher
+ */
+void uv_async_close(uv_loop_t* loop, uv_async_t* handle);
+void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle);
+
+void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
+    uv_req_t* req);
+
+
+/*
+ * Signal watcher
+ */
+void uv_signals_init(void);
+int uv__signal_dispatch(int signum);
+
+void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle);
+void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle);
+
+void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+    uv_req_t* req);
+
+
+/*
+ * Spawn
+ */
+void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle);
+void uv_process_close(uv_loop_t* loop, uv_process_t* handle);
+void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle);
+
+
+/*
+ * Error
+ */
+int uv_translate_sys_error(int sys_errno);
+
+
+/*
+ * FS
+ */
+void uv_fs_init(void);
+
+
+/*
+ * FS Event
+ */
+void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
+    uv_fs_event_t* handle);
+void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle);
+void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle);
+
+
+/*
+ * Stat poller.
+ */
+void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle);
+
+
+/*
+ * Utilities.
+ */
+void uv__util_init(void);
+
+uint64_t uv__hrtime(unsigned int scale);
+__declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall);
+int uv__getpwuid_r(uv_passwd_t* pwd);
+int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
+int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16);
+
+typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*);
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error);
+
+int uv__random_rtlgenrandom(void* buf, size_t buflen);
+
+
+/*
+ * Process stdio handles.
+ */
+int uv__stdio_create(uv_loop_t* loop,
+                     const uv_process_options_t* options,
+                     BYTE** buffer_ptr);
+void uv__stdio_destroy(BYTE* buffer);
+void uv__stdio_noinherit(BYTE* buffer);
+int uv__stdio_verify(BYTE* buffer, WORD size);
+WORD uv__stdio_size(BYTE* buffer);
+HANDLE uv__stdio_handle(BYTE* buffer, int fd);
+
+
+/*
+ * Winapi and ntapi utility functions
+ */
+void uv_winapi_init(void);
+
+
+/*
+ * Winsock utility functions
+ */
+void uv_winsock_init(void);
+
+int uv_ntstatus_to_winsock_error(NTSTATUS status);
+
+BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target);
+BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target);
+
+int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
+    int* addr_len, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
+
+int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
+    AFD_POLL_INFO* info_out, OVERLAPPED* overlapped);
+
+/* Whether there are any non-IFS LSPs stacked on TCP */
+extern int uv_tcp_non_ifs_lsp_ipv4;
+extern int uv_tcp_non_ifs_lsp_ipv6;
+
+/* Ip address used to bind to any port at any interface */
+extern struct sockaddr_in uv_addr_ip4_any_;
+extern struct sockaddr_in6 uv_addr_ip6_any_;
+
+/*
+ * Wake all loops with fake message
+ */
+void uv__wake_all_loops(void);
+
+/*
+ * Init system wake-up detection
+ */
+void uv__init_detect_system_wakeup(void);
+
+#endif /* UV_WIN_INTERNAL_H_ */
diff --git a/third_party/libuv/src/win/loop-watcher.c b/third_party/libuv/src/win/loop-watcher.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/loop-watcher.c
@@ -0,0 +1,122 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+
+
+void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    handle->flags |= UV_HANDLE_CLOSED;
+    uv__handle_close(handle);
+  }
+}
+
+
+#define UV_LOOP_WATCHER_DEFINE(name, NAME)                                    \
+  int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) {              \
+    uv__handle_init(loop, (uv_handle_t*) handle, UV_##NAME);                  \
+                                                                              \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+                                                                              \
+  int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) {           \
+    uv_loop_t* loop = handle->loop;                                           \
+    uv_##name##_t* old_head;                                                  \
+                                                                              \
+    assert(handle->type == UV_##NAME);                                        \
+                                                                              \
+    if (uv__is_active(handle))                                                \
+      return 0;                                                               \
+                                                                              \
+    if (cb == NULL)                                                           \
+      return UV_EINVAL;                                                       \
+                                                                              \
+    old_head = loop->name##_handles;                                          \
+                                                                              \
+    handle->name##_next = old_head;                                           \
+    handle->name##_prev = NULL;                                               \
+                                                                              \
+    if (old_head) {                                                           \
+      old_head->name##_prev = handle;                                         \
+    }                                                                         \
+                                                                              \
+    loop->name##_handles = handle;                                            \
+                                                                              \
+    handle->name##_cb = cb;                                                   \
+    uv__handle_start(handle);                                                 \
+                                                                              \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+                                                                              \
+  int uv_##name##_stop(uv_##name##_t* handle) {                               \
+    uv_loop_t* loop = handle->loop;                                           \
+                                                                              \
+    assert(handle->type == UV_##NAME);                                        \
+                                                                              \
+    if (!uv__is_active(handle))                                               \
+      return 0;                                                               \
+                                                                              \
+    /* Update loop head if needed */                                          \
+    if (loop->name##_handles == handle) {                                     \
+      loop->name##_handles = handle->name##_next;                             \
+    }                                                                         \
+                                                                              \
+    /* Update the iterator-next pointer of needed */                          \
+    if (loop->next_##name##_handle == handle) {                               \
+      loop->next_##name##_handle = handle->name##_next;                       \
+    }                                                                         \
+                                                                              \
+    if (handle->name##_prev) {                                                \
+      handle->name##_prev->name##_next = handle->name##_next;                 \
+    }                                                                         \
+    if (handle->name##_next) {                                                \
+      handle->name##_next->name##_prev = handle->name##_prev;                 \
+    }                                                                         \
+                                                                              \
+    uv__handle_stop(handle);                                                  \
+                                                                              \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+                                                                              \
+  void uv_##name##_invoke(uv_loop_t* loop) {                                  \
+    uv_##name##_t* handle;                                                    \
+                                                                              \
+    (loop)->next_##name##_handle = (loop)->name##_handles;                    \
+                                                                              \
+    while ((loop)->next_##name##_handle != NULL) {                            \
+      handle = (loop)->next_##name##_handle;                                  \
+      (loop)->next_##name##_handle = handle->name##_next;                     \
+                                                                              \
+      handle->name##_cb(handle);                                              \
+    }                                                                         \
+  }
+
+UV_LOOP_WATCHER_DEFINE(prepare, PREPARE)
+UV_LOOP_WATCHER_DEFINE(check, CHECK)
+UV_LOOP_WATCHER_DEFINE(idle, IDLE)
diff --git a/third_party/libuv/src/win/pipe.c b/third_party/libuv/src/win/pipe.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/pipe.c
@@ -0,0 +1,2393 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "handle-inl.h"
+#include "internal.h"
+#include "req-inl.h"
+#include "stream-inl.h"
+#include "uv-common.h"
+#include "uv.h"
+
+#include <aclapi.h>
+#include <accctrl.h>
+
+/* A zero-size buffer for use by uv_pipe_read */
+static char uv_zero_[] = "";
+
+/* Null uv_buf_t */
+static const uv_buf_t uv_null_buf_ = { 0, NULL };
+
+/* The timeout that the pipe will wait for the remote end to write data when
+ * the local ends wants to shut it down. */
+static const int64_t eof_timeout = 50; /* ms */
+
+static const int default_pending_pipe_instances = 4;
+
+/* Pipe prefix */
+static char pipe_prefix[] = "\\\\?\\pipe";
+static const int pipe_prefix_len = sizeof(pipe_prefix) - 1;
+
+/* IPC incoming xfer queue item. */
+typedef struct {
+  uv__ipc_socket_xfer_type_t xfer_type;
+  uv__ipc_socket_xfer_info_t xfer_info;
+  QUEUE member;
+} uv__ipc_xfer_queue_item_t;
+
+/* IPC frame header flags. */
+/* clang-format off */
+enum {
+  UV__IPC_FRAME_HAS_DATA                = 0x01,
+  UV__IPC_FRAME_HAS_SOCKET_XFER         = 0x02,
+  UV__IPC_FRAME_XFER_IS_TCP_CONNECTION  = 0x04,
+  /* These are combinations of the flags above. */
+  UV__IPC_FRAME_XFER_FLAGS              = 0x06,
+  UV__IPC_FRAME_VALID_FLAGS             = 0x07
+};
+/* clang-format on */
+
+/* IPC frame header. */
+typedef struct {
+  uint32_t flags;
+  uint32_t reserved1;   /* Ignored. */
+  uint32_t data_length; /* Must be zero if there is no data. */
+  uint32_t reserved2;   /* Must be zero. */
+} uv__ipc_frame_header_t;
+
+/* To implement the IPC protocol correctly, these structures must have exactly
+ * the right size. */
+STATIC_ASSERT(sizeof(uv__ipc_frame_header_t) == 16);
+STATIC_ASSERT(sizeof(uv__ipc_socket_xfer_info_t) == 632);
+
+/* Coalesced write request. */
+typedef struct {
+  uv_write_t req;       /* Internal heap-allocated write request. */
+  uv_write_t* user_req; /* Pointer to user-specified uv_write_t. */
+} uv__coalesced_write_t;
+
+
+static void eof_timer_init(uv_pipe_t* pipe);
+static void eof_timer_start(uv_pipe_t* pipe);
+static void eof_timer_stop(uv_pipe_t* pipe);
+static void eof_timer_cb(uv_timer_t* timer);
+static void eof_timer_destroy(uv_pipe_t* pipe);
+static void eof_timer_close_cb(uv_handle_t* handle);
+
+
+static void uv_unique_pipe_name(char* ptr, char* name, size_t size) {
+  snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId());
+}
+
+
+int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
+  uv_stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
+
+  handle->reqs_pending = 0;
+  handle->handle = INVALID_HANDLE_VALUE;
+  handle->name = NULL;
+  handle->pipe.conn.ipc_remote_pid = 0;
+  handle->pipe.conn.ipc_data_frame.payload_remaining = 0;
+  QUEUE_INIT(&handle->pipe.conn.ipc_xfer_queue);
+  handle->pipe.conn.ipc_xfer_queue_length = 0;
+  handle->ipc = ipc;
+  handle->pipe.conn.non_overlapped_writes_tail = NULL;
+
+  return 0;
+}
+
+
+static void uv_pipe_connection_init(uv_pipe_t* handle) {
+  uv_connection_init((uv_stream_t*) handle);
+  handle->read_req.data = handle;
+  handle->pipe.conn.eof_timer = NULL;
+  assert(!(handle->flags & UV_HANDLE_PIPESERVER));
+  if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+    handle->pipe.conn.readfile_thread_handle = NULL;
+    InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+  }
+}
+
+
+static HANDLE open_named_pipe(const WCHAR* name, DWORD* duplex_flags) {
+  HANDLE pipeHandle;
+
+  /*
+   * Assume that we have a duplex pipe first, so attempt to
+   * connect with GENERIC_READ | GENERIC_WRITE.
+   */
+  pipeHandle = CreateFileW(name,
+                           GENERIC_READ | GENERIC_WRITE,
+                           0,
+                           NULL,
+                           OPEN_EXISTING,
+                           FILE_FLAG_OVERLAPPED,
+                           NULL);
+  if (pipeHandle != INVALID_HANDLE_VALUE) {
+    *duplex_flags = UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+    return pipeHandle;
+  }
+
+  /*
+   * If the pipe is not duplex CreateFileW fails with
+   * ERROR_ACCESS_DENIED.  In that case try to connect
+   * as a read-only or write-only.
+   */
+  if (GetLastError() == ERROR_ACCESS_DENIED) {
+    pipeHandle = CreateFileW(name,
+                             GENERIC_READ | FILE_WRITE_ATTRIBUTES,
+                             0,
+                             NULL,
+                             OPEN_EXISTING,
+                             FILE_FLAG_OVERLAPPED,
+                             NULL);
+
+    if (pipeHandle != INVALID_HANDLE_VALUE) {
+      *duplex_flags = UV_HANDLE_READABLE;
+      return pipeHandle;
+    }
+  }
+
+  if (GetLastError() == ERROR_ACCESS_DENIED) {
+    pipeHandle = CreateFileW(name,
+                             GENERIC_WRITE | FILE_READ_ATTRIBUTES,
+                             0,
+                             NULL,
+                             OPEN_EXISTING,
+                             FILE_FLAG_OVERLAPPED,
+                             NULL);
+
+    if (pipeHandle != INVALID_HANDLE_VALUE) {
+      *duplex_flags = UV_HANDLE_WRITABLE;
+      return pipeHandle;
+    }
+  }
+
+  return INVALID_HANDLE_VALUE;
+}
+
+
+static void close_pipe(uv_pipe_t* pipe) {
+  assert(pipe->u.fd == -1 || pipe->u.fd > 2);
+  if (pipe->u.fd == -1)
+    CloseHandle(pipe->handle);
+  else
+    close(pipe->u.fd);
+
+  pipe->u.fd = -1;
+  pipe->handle = INVALID_HANDLE_VALUE;
+}
+
+
+int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access,
+    char* name, size_t nameSize) {
+  HANDLE pipeHandle;
+  int err;
+  char* ptr = (char*)handle;
+
+  for (;;) {
+    uv_unique_pipe_name(ptr, name, nameSize);
+
+    pipeHandle = CreateNamedPipeA(name,
+      access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC,
+      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0,
+      NULL);
+
+    if (pipeHandle != INVALID_HANDLE_VALUE) {
+      /* No name collisions.  We're done. */
+      break;
+    }
+
+    err = GetLastError();
+    if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) {
+      goto error;
+    }
+
+    /* Pipe name collision.  Increment the pointer and try again. */
+    ptr++;
+  }
+
+  if (CreateIoCompletionPort(pipeHandle,
+                             loop->iocp,
+                             (ULONG_PTR)handle,
+                             0) == NULL) {
+    err = GetLastError();
+    goto error;
+  }
+
+  uv_pipe_connection_init(handle);
+  handle->handle = pipeHandle;
+
+  return 0;
+
+ error:
+  if (pipeHandle != INVALID_HANDLE_VALUE)
+    CloseHandle(pipeHandle);
+
+  return err;
+}
+
+
+static int uv_set_pipe_handle(uv_loop_t* loop,
+                              uv_pipe_t* handle,
+                              HANDLE pipeHandle,
+                              int fd,
+                              DWORD duplex_flags) {
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_MODE_INFORMATION mode_info;
+  DWORD mode = PIPE_READMODE_BYTE | PIPE_WAIT;
+  DWORD current_mode = 0;
+  DWORD err = 0;
+
+  if (handle->flags & UV_HANDLE_PIPESERVER)
+    return UV_EINVAL;
+  if (handle->handle != INVALID_HANDLE_VALUE)
+    return UV_EBUSY;
+
+  if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) {
+    err = GetLastError();
+    if (err == ERROR_ACCESS_DENIED) {
+      /*
+       * SetNamedPipeHandleState can fail if the handle doesn't have either
+       * GENERIC_WRITE  or FILE_WRITE_ATTRIBUTES.
+       * But if the handle already has the desired wait and blocking modes
+       * we can continue.
+       */
+      if (!GetNamedPipeHandleState(pipeHandle, &current_mode, NULL, NULL,
+                                   NULL, NULL, 0)) {
+        return -1;
+      } else if (current_mode & PIPE_NOWAIT) {
+        SetLastError(ERROR_ACCESS_DENIED);
+        return -1;
+      }
+    } else {
+      /* If this returns ERROR_INVALID_PARAMETER we probably opened
+       * something that is not a pipe. */
+      if (err == ERROR_INVALID_PARAMETER) {
+        SetLastError(WSAENOTSOCK);
+      }
+      return -1;
+    }
+  }
+
+  /* Check if the pipe was created with FILE_FLAG_OVERLAPPED. */
+  nt_status = pNtQueryInformationFile(pipeHandle,
+                                      &io_status,
+                                      &mode_info,
+                                      sizeof(mode_info),
+                                      FileModeInformation);
+  if (nt_status != STATUS_SUCCESS) {
+    return -1;
+  }
+
+  if (mode_info.Mode & FILE_SYNCHRONOUS_IO_ALERT ||
+      mode_info.Mode & FILE_SYNCHRONOUS_IO_NONALERT) {
+    /* Non-overlapped pipe. */
+    handle->flags |= UV_HANDLE_NON_OVERLAPPED_PIPE;
+  } else {
+    /* Overlapped pipe.  Try to associate with IOCP. */
+    if (CreateIoCompletionPort(pipeHandle,
+                               loop->iocp,
+                               (ULONG_PTR) handle,
+                               0) == NULL) {
+      handle->flags |= UV_HANDLE_EMULATE_IOCP;
+    }
+  }
+
+  handle->handle = pipeHandle;
+  handle->u.fd = fd;
+  handle->flags |= duplex_flags;
+
+  return 0;
+}
+
+
+static int pipe_alloc_accept(uv_loop_t* loop, uv_pipe_t* handle,
+                             uv_pipe_accept_t* req, BOOL firstInstance) {
+  assert(req->pipeHandle == INVALID_HANDLE_VALUE);
+
+  req->pipeHandle =
+      CreateNamedPipeW(handle->name,
+                       PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC |
+                         (firstInstance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
+                       PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+                       PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL);
+
+  if (req->pipeHandle == INVALID_HANDLE_VALUE) {
+    return 0;
+  }
+
+  /* Associate it with IOCP so we can get events. */
+  if (CreateIoCompletionPort(req->pipeHandle,
+                             loop->iocp,
+                             (ULONG_PTR) handle,
+                             0) == NULL) {
+    uv_fatal_error(GetLastError(), "CreateIoCompletionPort");
+  }
+
+  /* Stash a handle in the server object for use from places such as
+   * getsockname and chmod. As we transfer ownership of these to client
+   * objects, we'll allocate new ones here. */
+  handle->handle = req->pipeHandle;
+
+  return 1;
+}
+
+
+static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) {
+  uv_loop_t* loop;
+  uv_pipe_t* handle;
+  uv_shutdown_t* req;
+
+  req = (uv_shutdown_t*) parameter;
+  assert(req);
+  handle = (uv_pipe_t*) req->handle;
+  assert(handle);
+  loop = handle->loop;
+  assert(loop);
+
+  FlushFileBuffers(handle->handle);
+
+  /* Post completed */
+  POST_COMPLETION_FOR_REQ(loop, req);
+
+  return 0;
+}
+
+
+void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
+  int err;
+  DWORD result;
+  uv_shutdown_t* req;
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_PIPE_LOCAL_INFORMATION pipe_info;
+  uv__ipc_xfer_queue_item_t* xfer_queue_item;
+
+  if ((handle->flags & UV_HANDLE_CONNECTION) &&
+      handle->stream.conn.shutdown_req != NULL &&
+      handle->stream.conn.write_reqs_pending == 0) {
+    req = handle->stream.conn.shutdown_req;
+
+    /* Clear the shutdown_req field so we don't go here again. */
+    handle->stream.conn.shutdown_req = NULL;
+
+    if (handle->flags & UV_HANDLE_CLOSING) {
+      UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+      /* Already closing. Cancel the shutdown. */
+      if (req->cb) {
+        req->cb(req, UV_ECANCELED);
+      }
+
+      DECREASE_PENDING_REQ_COUNT(handle);
+      return;
+    }
+
+    /* Try to avoid flushing the pipe buffer in the thread pool. */
+    nt_status = pNtQueryInformationFile(handle->handle,
+                                        &io_status,
+                                        &pipe_info,
+                                        sizeof pipe_info,
+                                        FilePipeLocalInformation);
+
+    if (nt_status != STATUS_SUCCESS) {
+      /* Failure */
+      UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+      handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
+      if (req->cb) {
+        err = pRtlNtStatusToDosError(nt_status);
+        req->cb(req, uv_translate_sys_error(err));
+      }
+
+      DECREASE_PENDING_REQ_COUNT(handle);
+      return;
+    }
+
+    if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) {
+      /* Short-circuit, no need to call FlushFileBuffers. */
+      uv_insert_pending_req(loop, (uv_req_t*) req);
+      return;
+    }
+
+    /* Run FlushFileBuffers in the thread pool. */
+    result = QueueUserWorkItem(pipe_shutdown_thread_proc,
+                               req,
+                               WT_EXECUTELONGFUNCTION);
+    if (result) {
+      return;
+
+    } else {
+      /* Failure. */
+      UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+      handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
+      if (req->cb) {
+        err = GetLastError();
+        req->cb(req, uv_translate_sys_error(err));
+      }
+
+      DECREASE_PENDING_REQ_COUNT(handle);
+      return;
+    }
+  }
+
+  if (handle->flags & UV_HANDLE_CLOSING &&
+      handle->reqs_pending == 0) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+    if (handle->flags & UV_HANDLE_CONNECTION) {
+      /* Free pending sockets */
+      while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) {
+        QUEUE* q;
+        SOCKET socket;
+
+        q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue);
+        QUEUE_REMOVE(q);
+        xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+
+        /* Materialize socket and close it */
+        socket = WSASocketW(FROM_PROTOCOL_INFO,
+                            FROM_PROTOCOL_INFO,
+                            FROM_PROTOCOL_INFO,
+                            &xfer_queue_item->xfer_info.socket_info,
+                            0,
+                            WSA_FLAG_OVERLAPPED);
+        uv__free(xfer_queue_item);
+
+        if (socket != INVALID_SOCKET)
+          closesocket(socket);
+      }
+      handle->pipe.conn.ipc_xfer_queue_length = 0;
+
+      if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+        if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
+          UnregisterWait(handle->read_req.wait_handle);
+          handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+        }
+        if (handle->read_req.event_handle != NULL) {
+          CloseHandle(handle->read_req.event_handle);
+          handle->read_req.event_handle = NULL;
+        }
+      }
+
+      if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
+        DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+    }
+
+    if (handle->flags & UV_HANDLE_PIPESERVER) {
+      assert(handle->pipe.serv.accept_reqs);
+      uv__free(handle->pipe.serv.accept_reqs);
+      handle->pipe.serv.accept_reqs = NULL;
+    }
+
+    uv__handle_close(handle);
+  }
+}
+
+
+void uv_pipe_pending_instances(uv_pipe_t* handle, int count) {
+  if (handle->flags & UV_HANDLE_BOUND)
+    return;
+  handle->pipe.serv.pending_instances = count;
+  handle->flags |= UV_HANDLE_PIPESERVER;
+}
+
+
+/* Creates a pipe server. */
+int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
+  uv_loop_t* loop = handle->loop;
+  int i, err, nameSize;
+  uv_pipe_accept_t* req;
+
+  if (handle->flags & UV_HANDLE_BOUND) {
+    return UV_EINVAL;
+  }
+
+  if (!name) {
+    return UV_EINVAL;
+  }
+
+  if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
+    handle->pipe.serv.pending_instances = default_pending_pipe_instances;
+  }
+
+  handle->pipe.serv.accept_reqs = (uv_pipe_accept_t*)
+    uv__malloc(sizeof(uv_pipe_accept_t) * handle->pipe.serv.pending_instances);
+  if (!handle->pipe.serv.accept_reqs) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
+    req = &handle->pipe.serv.accept_reqs[i];
+    UV_REQ_INIT(req, UV_ACCEPT);
+    req->data = handle;
+    req->pipeHandle = INVALID_HANDLE_VALUE;
+    req->next_pending = NULL;
+  }
+
+  /* Convert name to UTF16. */
+  nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
+  handle->name = uv__malloc(nameSize);
+  if (!handle->name) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           name,
+                           -1,
+                           handle->name,
+                           nameSize / sizeof(WCHAR))) {
+    err = GetLastError();
+    goto error;
+  }
+
+  /*
+   * Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE.
+   * If this fails then there's already a pipe server for the given pipe name.
+   */
+  if (!pipe_alloc_accept(loop,
+                         handle,
+                         &handle->pipe.serv.accept_reqs[0],
+                         TRUE)) {
+    err = GetLastError();
+    if (err == ERROR_ACCESS_DENIED) {
+      err = WSAEADDRINUSE;  /* Translates to UV_EADDRINUSE. */
+    } else if (err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_NAME) {
+      err = WSAEACCES;  /* Translates to UV_EACCES. */
+    }
+    goto error;
+  }
+
+  handle->pipe.serv.pending_accepts = NULL;
+  handle->flags |= UV_HANDLE_PIPESERVER;
+  handle->flags |= UV_HANDLE_BOUND;
+
+  return 0;
+
+error:
+  if (handle->name) {
+    uv__free(handle->name);
+    handle->name = NULL;
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+static DWORD WINAPI pipe_connect_thread_proc(void* parameter) {
+  uv_loop_t* loop;
+  uv_pipe_t* handle;
+  uv_connect_t* req;
+  HANDLE pipeHandle = INVALID_HANDLE_VALUE;
+  DWORD duplex_flags;
+
+  req = (uv_connect_t*) parameter;
+  assert(req);
+  handle = (uv_pipe_t*) req->handle;
+  assert(handle);
+  loop = handle->loop;
+  assert(loop);
+
+  /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait
+   * for the pipe to become available with WaitNamedPipe. */
+  while (WaitNamedPipeW(handle->name, 30000)) {
+    /* The pipe is now available, try to connect. */
+    pipeHandle = open_named_pipe(handle->name, &duplex_flags);
+    if (pipeHandle != INVALID_HANDLE_VALUE)
+      break;
+
+    SwitchToThread();
+  }
+
+  if (pipeHandle != INVALID_HANDLE_VALUE &&
+      !uv_set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags)) {
+    SET_REQ_SUCCESS(req);
+  } else {
+    SET_REQ_ERROR(req, GetLastError());
+  }
+
+  /* Post completed */
+  POST_COMPLETION_FOR_REQ(loop, req);
+
+  return 0;
+}
+
+
+void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
+    const char* name, uv_connect_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int err, nameSize;
+  HANDLE pipeHandle = INVALID_HANDLE_VALUE;
+  DWORD duplex_flags;
+
+  UV_REQ_INIT(req, UV_CONNECT);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+
+  /* Convert name to UTF16. */
+  nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
+  handle->name = uv__malloc(nameSize);
+  if (!handle->name) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  if (!MultiByteToWideChar(CP_UTF8,
+                           0,
+                           name,
+                           -1,
+                           handle->name,
+                           nameSize / sizeof(WCHAR))) {
+    err = GetLastError();
+    goto error;
+  }
+
+  pipeHandle = open_named_pipe(handle->name, &duplex_flags);
+  if (pipeHandle == INVALID_HANDLE_VALUE) {
+    if (GetLastError() == ERROR_PIPE_BUSY) {
+      /* Wait for the server to make a pipe instance available. */
+      if (!QueueUserWorkItem(&pipe_connect_thread_proc,
+                             req,
+                             WT_EXECUTELONGFUNCTION)) {
+        err = GetLastError();
+        goto error;
+      }
+
+      REGISTER_HANDLE_REQ(loop, handle, req);
+      handle->reqs_pending++;
+
+      return;
+    }
+
+    err = GetLastError();
+    goto error;
+  }
+
+  assert(pipeHandle != INVALID_HANDLE_VALUE);
+
+  if (uv_set_pipe_handle(loop,
+                         (uv_pipe_t*) req->handle,
+                         pipeHandle,
+                         -1,
+                         duplex_flags)) {
+    err = GetLastError();
+    goto error;
+  }
+
+  SET_REQ_SUCCESS(req);
+  uv_insert_pending_req(loop, (uv_req_t*) req);
+  handle->reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+  return;
+
+error:
+  if (handle->name) {
+    uv__free(handle->name);
+    handle->name = NULL;
+  }
+
+  if (pipeHandle != INVALID_HANDLE_VALUE) {
+    CloseHandle(pipeHandle);
+  }
+
+  /* Make this req pending reporting an error. */
+  SET_REQ_ERROR(req, err);
+  uv_insert_pending_req(loop, (uv_req_t*) req);
+  handle->reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+  return;
+}
+
+
+void uv__pipe_interrupt_read(uv_pipe_t* handle) {
+  BOOL r;
+
+  if (!(handle->flags & UV_HANDLE_READ_PENDING))
+    return; /* No pending reads. */
+  if (handle->flags & UV_HANDLE_CANCELLATION_PENDING)
+    return; /* Already cancelled. */
+  if (handle->handle == INVALID_HANDLE_VALUE)
+    return; /* Pipe handle closed. */
+
+  if (!(handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)) {
+    /* Cancel asynchronous read. */
+    r = CancelIoEx(handle->handle, &handle->read_req.u.io.overlapped);
+    assert(r || GetLastError() == ERROR_NOT_FOUND);
+
+  } else {
+    /* Cancel synchronous read (which is happening in the thread pool). */
+    HANDLE thread;
+    volatile HANDLE* thread_ptr = &handle->pipe.conn.readfile_thread_handle;
+
+    EnterCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+
+    thread = *thread_ptr;
+    if (thread == NULL) {
+      /* The thread pool thread has not yet reached the point of blocking, we
+       * can pre-empt it by setting thread_handle to INVALID_HANDLE_VALUE. */
+      *thread_ptr = INVALID_HANDLE_VALUE;
+
+    } else {
+      /* Spin until the thread has acknowledged (by setting the thread to
+       * INVALID_HANDLE_VALUE) that it is past the point of blocking. */
+      while (thread != INVALID_HANDLE_VALUE) {
+        r = CancelSynchronousIo(thread);
+        assert(r || GetLastError() == ERROR_NOT_FOUND);
+        SwitchToThread(); /* Yield thread. */
+        thread = *thread_ptr;
+      }
+    }
+
+    LeaveCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+  }
+
+  /* Set flag to indicate that read has been cancelled. */
+  handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
+}
+
+
+void uv__pipe_read_stop(uv_pipe_t* handle) {
+  handle->flags &= ~UV_HANDLE_READING;
+  DECREASE_ACTIVE_COUNT(handle->loop, handle);
+
+  uv__pipe_interrupt_read(handle);
+}
+
+
+/* Cleans up uv_pipe_t (server or connection) and all resources associated with
+ * it. */
+void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) {
+  int i;
+  HANDLE pipeHandle;
+
+  uv__pipe_interrupt_read(handle);
+
+  if (handle->name) {
+    uv__free(handle->name);
+    handle->name = NULL;
+  }
+
+  if (handle->flags & UV_HANDLE_PIPESERVER) {
+    for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
+      pipeHandle = handle->pipe.serv.accept_reqs[i].pipeHandle;
+      if (pipeHandle != INVALID_HANDLE_VALUE) {
+        CloseHandle(pipeHandle);
+        handle->pipe.serv.accept_reqs[i].pipeHandle = INVALID_HANDLE_VALUE;
+      }
+    }
+    handle->handle = INVALID_HANDLE_VALUE;
+  }
+
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    handle->flags &= ~UV_HANDLE_WRITABLE;
+    eof_timer_destroy(handle);
+  }
+
+  if ((handle->flags & UV_HANDLE_CONNECTION)
+      && handle->handle != INVALID_HANDLE_VALUE)
+    close_pipe(handle);
+}
+
+
+void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
+  if (handle->flags & UV_HANDLE_READING) {
+    handle->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->flags &= ~UV_HANDLE_LISTENING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  uv_pipe_cleanup(loop, handle);
+
+  if (handle->reqs_pending == 0) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+  }
+
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  uv__handle_closing(handle);
+}
+
+
+void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_pipe_accept_t* req, BOOL firstInstance) {
+  assert(handle->flags & UV_HANDLE_LISTENING);
+
+  if (!firstInstance && !pipe_alloc_accept(loop, handle, req, FALSE)) {
+    SET_REQ_ERROR(req, GetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*) req);
+    handle->reqs_pending++;
+    return;
+  }
+
+  assert(req->pipeHandle != INVALID_HANDLE_VALUE);
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+
+  if (!ConnectNamedPipe(req->pipeHandle, &req->u.io.overlapped) &&
+      GetLastError() != ERROR_IO_PENDING) {
+    if (GetLastError() == ERROR_PIPE_CONNECTED) {
+      SET_REQ_SUCCESS(req);
+    } else {
+      CloseHandle(req->pipeHandle);
+      req->pipeHandle = INVALID_HANDLE_VALUE;
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, GetLastError());
+    }
+    uv_insert_pending_req(loop, (uv_req_t*) req);
+    handle->reqs_pending++;
+    return;
+  }
+
+  /* Wait for completion via IOCP */
+  handle->reqs_pending++;
+}
+
+
+int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
+  uv_loop_t* loop = server->loop;
+  uv_pipe_t* pipe_client;
+  uv_pipe_accept_t* req;
+  QUEUE* q;
+  uv__ipc_xfer_queue_item_t* item;
+  int err;
+
+  if (server->ipc) {
+    if (QUEUE_EMPTY(&server->pipe.conn.ipc_xfer_queue)) {
+      /* No valid pending sockets. */
+      return WSAEWOULDBLOCK;
+    }
+
+    q = QUEUE_HEAD(&server->pipe.conn.ipc_xfer_queue);
+    QUEUE_REMOVE(q);
+    server->pipe.conn.ipc_xfer_queue_length--;
+    item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+
+    err = uv__tcp_xfer_import(
+        (uv_tcp_t*) client, item->xfer_type, &item->xfer_info);
+    if (err != 0)
+      return err;
+
+    uv__free(item);
+
+  } else {
+    pipe_client = (uv_pipe_t*) client;
+
+    /* Find a connection instance that has been connected, but not yet
+     * accepted. */
+    req = server->pipe.serv.pending_accepts;
+
+    if (!req) {
+      /* No valid connections found, so we error out. */
+      return WSAEWOULDBLOCK;
+    }
+
+    /* Initialize the client handle and copy the pipeHandle to the client */
+    uv_pipe_connection_init(pipe_client);
+    pipe_client->handle = req->pipeHandle;
+    pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+
+    /* Prepare the req to pick up a new connection */
+    server->pipe.serv.pending_accepts = req->next_pending;
+    req->next_pending = NULL;
+    req->pipeHandle = INVALID_HANDLE_VALUE;
+
+    server->handle = INVALID_HANDLE_VALUE;
+    if (!(server->flags & UV_HANDLE_CLOSING)) {
+      uv_pipe_queue_accept(loop, server, req, FALSE);
+    }
+  }
+
+  return 0;
+}
+
+
+/* Starts listening for connections for the given pipe. */
+int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int i;
+
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->stream.serv.connection_cb = cb;
+  }
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    return WSAEINVAL;
+  }
+
+  if (handle->flags & UV_HANDLE_READING) {
+    return WSAEISCONN;
+  }
+
+  if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
+    return ERROR_NOT_SUPPORTED;
+  }
+
+  if (handle->ipc) {
+    return WSAEINVAL;
+  }
+
+  handle->flags |= UV_HANDLE_LISTENING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  handle->stream.serv.connection_cb = cb;
+
+  /* First pipe handle should have already been created in uv_pipe_bind */
+  assert(handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE);
+
+  for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
+    uv_pipe_queue_accept(loop, handle, &handle->pipe.serv.accept_reqs[i], i == 0);
+  }
+
+  return 0;
+}
+
+
+static DWORD WINAPI uv_pipe_zero_readfile_thread_proc(void* arg) {
+  uv_read_t* req = (uv_read_t*) arg;
+  uv_pipe_t* handle = (uv_pipe_t*) req->data;
+  uv_loop_t* loop = handle->loop;
+  volatile HANDLE* thread_ptr = &handle->pipe.conn.readfile_thread_handle;
+  CRITICAL_SECTION* lock = &handle->pipe.conn.readfile_thread_lock;
+  HANDLE thread;
+  DWORD bytes;
+  DWORD err;
+
+  assert(req->type == UV_READ);
+  assert(handle->type == UV_NAMED_PIPE);
+
+  err = 0;
+
+  /* Create a handle to the current thread. */
+  if (!DuplicateHandle(GetCurrentProcess(),
+                       GetCurrentThread(),
+                       GetCurrentProcess(),
+                       &thread,
+                       0,
+                       FALSE,
+                       DUPLICATE_SAME_ACCESS)) {
+    err = GetLastError();
+    goto out1;
+  }
+
+  /* The lock needs to be held when thread handle is modified. */
+  EnterCriticalSection(lock);
+  if (*thread_ptr == INVALID_HANDLE_VALUE) {
+    /* uv__pipe_interrupt_read() cancelled reading before we got here. */
+    err = ERROR_OPERATION_ABORTED;
+  } else {
+    /* Let main thread know which worker thread is doing the blocking read. */
+    assert(*thread_ptr == NULL);
+    *thread_ptr = thread;
+  }
+  LeaveCriticalSection(lock);
+
+  if (err)
+    goto out2;
+
+  /* Block the thread until data is available on the pipe, or the read is
+   * cancelled. */
+  if (!ReadFile(handle->handle, &uv_zero_, 0, &bytes, NULL))
+    err = GetLastError();
+
+  /* Let the main thread know the worker is past the point of blocking. */
+  assert(thread == *thread_ptr);
+  *thread_ptr = INVALID_HANDLE_VALUE;
+
+  /* Briefly acquire the mutex. Since the main thread holds the lock while it
+   * is spinning trying to cancel this thread's I/O, we will block here until
+   * it stops doing that. */
+  EnterCriticalSection(lock);
+  LeaveCriticalSection(lock);
+
+out2:
+  /* Close the handle to the current thread. */
+  CloseHandle(thread);
+
+out1:
+  /* Set request status and post a completion record to the IOCP. */
+  if (err)
+    SET_REQ_ERROR(req, err);
+  else
+    SET_REQ_SUCCESS(req);
+  POST_COMPLETION_FOR_REQ(loop, req);
+
+  return 0;
+}
+
+
+static DWORD WINAPI uv_pipe_writefile_thread_proc(void* parameter) {
+  int result;
+  DWORD bytes;
+  uv_write_t* req = (uv_write_t*) parameter;
+  uv_pipe_t* handle = (uv_pipe_t*) req->handle;
+  uv_loop_t* loop = handle->loop;
+
+  assert(req != NULL);
+  assert(req->type == UV_WRITE);
+  assert(handle->type == UV_NAMED_PIPE);
+  assert(req->write_buffer.base);
+
+  result = WriteFile(handle->handle,
+                     req->write_buffer.base,
+                     req->write_buffer.len,
+                     &bytes,
+                     NULL);
+
+  if (!result) {
+    SET_REQ_ERROR(req, GetLastError());
+  }
+
+  POST_COMPLETION_FOR_REQ(loop, req);
+  return 0;
+}
+
+
+static void CALLBACK post_completion_read_wait(void* context, BOOLEAN timed_out) {
+  uv_read_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_read_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->data;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+static void CALLBACK post_completion_write_wait(void* context, BOOLEAN timed_out) {
+  uv_write_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_write_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->handle;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
+  uv_read_t* req;
+  int result;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  assert(handle->handle != INVALID_HANDLE_VALUE);
+
+  req = &handle->read_req;
+
+  if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+    handle->pipe.conn.readfile_thread_handle = NULL; /* Reset cancellation. */
+    if (!QueueUserWorkItem(&uv_pipe_zero_readfile_thread_proc,
+                           req,
+                           WT_EXECUTELONGFUNCTION)) {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, GetLastError());
+      goto error;
+    }
+  } else {
+    memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      assert(req->event_handle != NULL);
+      req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
+    }
+
+    /* Do 0-read */
+    result = ReadFile(handle->handle,
+                      &uv_zero_,
+                      0,
+                      NULL,
+                      &req->u.io.overlapped);
+
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, GetLastError());
+      goto error;
+    }
+
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      if (req->wait_handle == INVALID_HANDLE_VALUE) {
+        if (!RegisterWaitForSingleObject(&req->wait_handle,
+            req->event_handle, post_completion_read_wait, (void*) req,
+            INFINITE, WT_EXECUTEINWAITTHREAD)) {
+          SET_REQ_ERROR(req, GetLastError());
+          goto error;
+        }
+      }
+    }
+  }
+
+  /* Start the eof timer if there is one */
+  eof_timer_start(handle);
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+  return;
+
+error:
+  uv_insert_pending_req(loop, (uv_req_t*)req);
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+}
+
+
+int uv_pipe_read_start(uv_pipe_t* handle,
+                       uv_alloc_cb alloc_cb,
+                       uv_read_cb read_cb) {
+  uv_loop_t* loop = handle->loop;
+
+  handle->flags |= UV_HANDLE_READING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  handle->read_cb = read_cb;
+  handle->alloc_cb = alloc_cb;
+
+  /* If reading was stopped and then started again, there could still be a read
+   * request pending. */
+  if (!(handle->flags & UV_HANDLE_READ_PENDING)) {
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        handle->read_req.event_handle == NULL) {
+      handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
+      if (handle->read_req.event_handle == NULL) {
+        uv_fatal_error(GetLastError(), "CreateEvent");
+      }
+    }
+    uv_pipe_queue_read(loop, handle);
+  }
+
+  return 0;
+}
+
+
+static void uv_insert_non_overlapped_write_req(uv_pipe_t* handle,
+    uv_write_t* req) {
+  req->next_req = NULL;
+  if (handle->pipe.conn.non_overlapped_writes_tail) {
+    req->next_req =
+      handle->pipe.conn.non_overlapped_writes_tail->next_req;
+    handle->pipe.conn.non_overlapped_writes_tail->next_req = (uv_req_t*)req;
+    handle->pipe.conn.non_overlapped_writes_tail = req;
+  } else {
+    req->next_req = (uv_req_t*)req;
+    handle->pipe.conn.non_overlapped_writes_tail = req;
+  }
+}
+
+
+static uv_write_t* uv_remove_non_overlapped_write_req(uv_pipe_t* handle) {
+  uv_write_t* req;
+
+  if (handle->pipe.conn.non_overlapped_writes_tail) {
+    req = (uv_write_t*)handle->pipe.conn.non_overlapped_writes_tail->next_req;
+
+    if (req == handle->pipe.conn.non_overlapped_writes_tail) {
+      handle->pipe.conn.non_overlapped_writes_tail = NULL;
+    } else {
+      handle->pipe.conn.non_overlapped_writes_tail->next_req =
+        req->next_req;
+    }
+
+    return req;
+  } else {
+    /* queue empty */
+    return NULL;
+  }
+}
+
+
+static void uv_queue_non_overlapped_write(uv_pipe_t* handle) {
+  uv_write_t* req = uv_remove_non_overlapped_write_req(handle);
+  if (req) {
+    if (!QueueUserWorkItem(&uv_pipe_writefile_thread_proc,
+                           req,
+                           WT_EXECUTELONGFUNCTION)) {
+      uv_fatal_error(GetLastError(), "QueueUserWorkItem");
+    }
+  }
+}
+
+
+static int uv__build_coalesced_write_req(uv_write_t* user_req,
+                                         const uv_buf_t bufs[],
+                                         size_t nbufs,
+                                         uv_write_t** req_out,
+                                         uv_buf_t* write_buf_out) {
+  /* Pack into a single heap-allocated buffer:
+   *   (a) a uv_write_t structure where libuv stores the actual state.
+   *   (b) a pointer to the original uv_write_t.
+   *   (c) data from all `bufs` entries.
+   */
+  char* heap_buffer;
+  size_t heap_buffer_length, heap_buffer_offset;
+  uv__coalesced_write_t* coalesced_write_req; /* (a) + (b) */
+  char* data_start;                           /* (c) */
+  size_t data_length;
+  unsigned int i;
+
+  /* Compute combined size of all combined buffers from `bufs`. */
+  data_length = 0;
+  for (i = 0; i < nbufs; i++)
+    data_length += bufs[i].len;
+
+  /* The total combined size of data buffers should not exceed UINT32_MAX,
+   * because WriteFile() won't accept buffers larger than that. */
+  if (data_length > UINT32_MAX)
+    return WSAENOBUFS; /* Maps to UV_ENOBUFS. */
+
+  /* Compute heap buffer size. */
+  heap_buffer_length = sizeof *coalesced_write_req + /* (a) + (b) */
+                       data_length;                  /* (c) */
+
+  /* Allocate buffer. */
+  heap_buffer = uv__malloc(heap_buffer_length);
+  if (heap_buffer == NULL)
+    return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
+
+  /* Copy uv_write_t information to the buffer. */
+  coalesced_write_req = (uv__coalesced_write_t*) heap_buffer;
+  coalesced_write_req->req = *user_req; /* copy (a) */
+  coalesced_write_req->req.coalesced = 1;
+  coalesced_write_req->user_req = user_req;         /* copy (b) */
+  heap_buffer_offset = sizeof *coalesced_write_req; /* offset (a) + (b) */
+
+  /* Copy data buffers to the heap buffer. */
+  data_start = &heap_buffer[heap_buffer_offset];
+  for (i = 0; i < nbufs; i++) {
+    memcpy(&heap_buffer[heap_buffer_offset],
+           bufs[i].base,
+           bufs[i].len);               /* copy (c) */
+    heap_buffer_offset += bufs[i].len; /* offset (c) */
+  }
+  assert(heap_buffer_offset == heap_buffer_length);
+
+  /* Set out arguments and return. */
+  *req_out = &coalesced_write_req->req;
+  *write_buf_out = uv_buf_init(data_start, (unsigned int) data_length);
+  return 0;
+}
+
+
+static int uv__pipe_write_data(uv_loop_t* loop,
+                               uv_write_t* req,
+                               uv_pipe_t* handle,
+                               const uv_buf_t bufs[],
+                               size_t nbufs,
+                               uv_write_cb cb,
+                               int copy_always) {
+  int err;
+  int result;
+  uv_buf_t write_buf;
+
+  assert(handle->handle != INVALID_HANDLE_VALUE);
+
+  UV_REQ_INIT(req, UV_WRITE);
+  req->handle = (uv_stream_t*) handle;
+  req->send_handle = NULL;
+  req->cb = cb;
+  /* Private fields. */
+  req->coalesced = 0;
+  req->event_handle = NULL;
+  req->wait_handle = INVALID_HANDLE_VALUE;
+
+  /* Prepare the overlapped structure. */
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & (UV_HANDLE_EMULATE_IOCP | UV_HANDLE_BLOCKING_WRITES)) {
+    req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+    if (req->event_handle == NULL) {
+      uv_fatal_error(GetLastError(), "CreateEvent");
+    }
+    req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
+  }
+  req->write_buffer = uv_null_buf_;
+
+  if (nbufs == 0) {
+    /* Write empty buffer. */
+    write_buf = uv_null_buf_;
+  } else if (nbufs == 1 && !copy_always) {
+    /* Write directly from bufs[0]. */
+    write_buf = bufs[0];
+  } else {
+    /* Coalesce all `bufs` into one big buffer. This also creates a new
+     * write-request structure that replaces the old one. */
+    err = uv__build_coalesced_write_req(req, bufs, nbufs, &req, &write_buf);
+    if (err != 0)
+      return err;
+  }
+
+  if ((handle->flags &
+      (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) ==
+      (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) {
+    DWORD bytes;
+    result =
+        WriteFile(handle->handle, write_buf.base, write_buf.len, &bytes, NULL);
+
+    if (!result) {
+      err = GetLastError();
+      return err;
+    } else {
+      /* Request completed immediately. */
+      req->u.io.queued_bytes = 0;
+    }
+
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    POST_COMPLETION_FOR_REQ(loop, req);
+    return 0;
+  } else if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+    req->write_buffer = write_buf;
+    uv_insert_non_overlapped_write_req(handle, req);
+    if (handle->stream.conn.write_reqs_pending == 0) {
+      uv_queue_non_overlapped_write(handle);
+    }
+
+    /* Request queued by the kernel. */
+    req->u.io.queued_bytes = write_buf.len;
+    handle->write_queue_size += req->u.io.queued_bytes;
+  } else if (handle->flags & UV_HANDLE_BLOCKING_WRITES) {
+    /* Using overlapped IO, but wait for completion before returning */
+    result = WriteFile(handle->handle,
+                       write_buf.base,
+                       write_buf.len,
+                       NULL,
+                       &req->u.io.overlapped);
+
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      err = GetLastError();
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+      return err;
+    }
+
+    if (result) {
+      /* Request completed immediately. */
+      req->u.io.queued_bytes = 0;
+    } else {
+      /* Request queued by the kernel. */
+      req->u.io.queued_bytes = write_buf.len;
+      handle->write_queue_size += req->u.io.queued_bytes;
+      if (WaitForSingleObject(req->event_handle, INFINITE) !=
+          WAIT_OBJECT_0) {
+        err = GetLastError();
+        CloseHandle(req->event_handle);
+        req->event_handle = NULL;
+        return err;
+      }
+    }
+    CloseHandle(req->event_handle);
+    req->event_handle = NULL;
+
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    return 0;
+  } else {
+    result = WriteFile(handle->handle,
+                       write_buf.base,
+                       write_buf.len,
+                       NULL,
+                       &req->u.io.overlapped);
+
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      return GetLastError();
+    }
+
+    if (result) {
+      /* Request completed immediately. */
+      req->u.io.queued_bytes = 0;
+    } else {
+      /* Request queued by the kernel. */
+      req->u.io.queued_bytes = write_buf.len;
+      handle->write_queue_size += req->u.io.queued_bytes;
+    }
+
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      if (!RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_completion_write_wait, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD)) {
+        return GetLastError();
+      }
+    }
+  }
+
+  REGISTER_HANDLE_REQ(loop, handle, req);
+  handle->reqs_pending++;
+  handle->stream.conn.write_reqs_pending++;
+
+  return 0;
+}
+
+
+static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) {
+  DWORD* pid = &handle->pipe.conn.ipc_remote_pid;
+
+  /* If the both ends of the IPC pipe are owned by the same process,
+   * the remote end pid may not yet be set. If so, do it here.
+   * TODO: this is weird; it'd probably better to use a handshake. */
+  if (*pid == 0)
+    *pid = GetCurrentProcessId();
+
+  return *pid;
+}
+
+
+int uv__pipe_write_ipc(uv_loop_t* loop,
+                       uv_write_t* req,
+                       uv_pipe_t* handle,
+                       const uv_buf_t data_bufs[],
+                       size_t data_buf_count,
+                       uv_stream_t* send_handle,
+                       uv_write_cb cb) {
+  uv_buf_t stack_bufs[6];
+  uv_buf_t* bufs;
+  size_t buf_count, buf_index;
+  uv__ipc_frame_header_t frame_header;
+  uv__ipc_socket_xfer_type_t xfer_type = UV__IPC_SOCKET_XFER_NONE;
+  uv__ipc_socket_xfer_info_t xfer_info;
+  uint64_t data_length;
+  size_t i;
+  int err;
+
+  /* Compute the combined size of data buffers. */
+  data_length = 0;
+  for (i = 0; i < data_buf_count; i++)
+    data_length += data_bufs[i].len;
+  if (data_length > UINT32_MAX)
+    return WSAENOBUFS; /* Maps to UV_ENOBUFS. */
+
+  /* Prepare the frame's socket xfer payload. */
+  if (send_handle != NULL) {
+    uv_tcp_t* send_tcp_handle = (uv_tcp_t*) send_handle;
+
+    /* Verify that `send_handle` it is indeed a tcp handle. */
+    if (send_tcp_handle->type != UV_TCP)
+      return ERROR_NOT_SUPPORTED;
+
+    /* Export the tcp handle. */
+    err = uv__tcp_xfer_export(send_tcp_handle,
+                              uv__pipe_get_ipc_remote_pid(handle),
+                              &xfer_type,
+                              &xfer_info);
+    if (err != 0)
+      return err;
+  }
+
+  /* Compute the number of uv_buf_t's required. */
+  buf_count = 1 + data_buf_count; /* Frame header and data buffers. */
+  if (send_handle != NULL)
+    buf_count += 1; /* One extra for the socket xfer information. */
+
+  /* Use the on-stack buffer array if it is big enough; otherwise allocate
+   * space for it on the heap. */
+  if (buf_count < ARRAY_SIZE(stack_bufs)) {
+    /* Use on-stack buffer array. */
+    bufs = stack_bufs;
+  } else {
+    /* Use heap-allocated buffer array. */
+    bufs = uv__calloc(buf_count, sizeof(uv_buf_t));
+    if (bufs == NULL)
+      return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */
+  }
+  buf_index = 0;
+
+  /* Initialize frame header and add it to the buffers list. */
+  memset(&frame_header, 0, sizeof frame_header);
+  bufs[buf_index++] = uv_buf_init((char*) &frame_header, sizeof frame_header);
+
+  if (send_handle != NULL) {
+    /* Add frame header flags. */
+    switch (xfer_type) {
+      case UV__IPC_SOCKET_XFER_TCP_CONNECTION:
+        frame_header.flags |= UV__IPC_FRAME_HAS_SOCKET_XFER |
+                              UV__IPC_FRAME_XFER_IS_TCP_CONNECTION;
+        break;
+      case UV__IPC_SOCKET_XFER_TCP_SERVER:
+        frame_header.flags |= UV__IPC_FRAME_HAS_SOCKET_XFER;
+        break;
+      default:
+        assert(0);  /* Unreachable. */
+    }
+    /* Add xfer info buffer. */
+    bufs[buf_index++] = uv_buf_init((char*) &xfer_info, sizeof xfer_info);
+  }
+
+  if (data_length > 0) {
+    /* Update frame header. */
+    frame_header.flags |= UV__IPC_FRAME_HAS_DATA;
+    frame_header.data_length = (uint32_t) data_length;
+    /* Add data buffers to buffers list. */
+    for (i = 0; i < data_buf_count; i++)
+      bufs[buf_index++] = data_bufs[i];
+  }
+
+  /* Write buffers. We set the `always_copy` flag, so it is not a problem that
+   * some of the written data lives on the stack. */
+  err = uv__pipe_write_data(loop, req, handle, bufs, buf_count, cb, 1);
+
+  /* If we had to heap-allocate the bufs array, free it now. */
+  if (bufs != stack_bufs) {
+    uv__free(bufs);
+  }
+
+  return err;
+}
+
+
+int uv__pipe_write(uv_loop_t* loop,
+                   uv_write_t* req,
+                   uv_pipe_t* handle,
+                   const uv_buf_t bufs[],
+                   size_t nbufs,
+                   uv_stream_t* send_handle,
+                   uv_write_cb cb) {
+  if (handle->ipc) {
+    /* IPC pipe write: use framing protocol. */
+    return uv__pipe_write_ipc(loop, req, handle, bufs, nbufs, send_handle, cb);
+  } else {
+    /* Non-IPC pipe write: put data on the wire directly. */
+    assert(send_handle == NULL);
+    return uv__pipe_write_data(loop, req, handle, bufs, nbufs, cb, 0);
+  }
+}
+
+
+static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_buf_t buf) {
+  /* If there is an eof timer running, we don't need it any more, so discard
+   * it. */
+  eof_timer_destroy(handle);
+
+  handle->flags &= ~UV_HANDLE_READABLE;
+  uv_read_stop((uv_stream_t*) handle);
+
+  handle->read_cb((uv_stream_t*) handle, UV_EOF, &buf);
+}
+
+
+static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error,
+    uv_buf_t buf) {
+  /* If there is an eof timer running, we don't need it any more, so discard
+   * it. */
+  eof_timer_destroy(handle);
+
+  uv_read_stop((uv_stream_t*) handle);
+
+  handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(error), &buf);
+}
+
+
+static void uv_pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle,
+    int error, uv_buf_t buf) {
+  if (error == ERROR_BROKEN_PIPE) {
+    uv_pipe_read_eof(loop, handle, buf);
+  } else {
+    uv_pipe_read_error(loop, handle, error, buf);
+  }
+}
+
+
+static void uv__pipe_queue_ipc_xfer_info(
+    uv_pipe_t* handle,
+    uv__ipc_socket_xfer_type_t xfer_type,
+    uv__ipc_socket_xfer_info_t* xfer_info) {
+  uv__ipc_xfer_queue_item_t* item;
+
+  item = (uv__ipc_xfer_queue_item_t*) uv__malloc(sizeof(*item));
+  if (item == NULL)
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+
+  item->xfer_type = xfer_type;
+  item->xfer_info = *xfer_info;
+
+  QUEUE_INSERT_TAIL(&handle->pipe.conn.ipc_xfer_queue, &item->member);
+  handle->pipe.conn.ipc_xfer_queue_length++;
+}
+
+
+/* Read an exact number of bytes from a pipe. If an error or end-of-file is
+ * encountered before the requested number of bytes are read, an error is
+ * returned. */
+static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
+  DWORD bytes_read, bytes_read_now;
+
+  bytes_read = 0;
+  while (bytes_read < count) {
+    if (!ReadFile(h,
+                  (char*) buffer + bytes_read,
+                  count - bytes_read,
+                  &bytes_read_now,
+                  NULL)) {
+      return GetLastError();
+    }
+
+    bytes_read += bytes_read_now;
+  }
+
+  assert(bytes_read == count);
+  return 0;
+}
+
+
+static DWORD uv__pipe_read_data(uv_loop_t* loop,
+                                uv_pipe_t* handle,
+                                DWORD suggested_bytes,
+                                DWORD max_bytes) {
+  DWORD bytes_read;
+  uv_buf_t buf;
+
+  /* Ask the user for a buffer to read data into. */
+  buf = uv_buf_init(NULL, 0);
+  handle->alloc_cb((uv_handle_t*) handle, suggested_bytes, &buf);
+  if (buf.base == NULL || buf.len == 0) {
+    handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
+    return 0; /* Break out of read loop. */
+  }
+
+  /* Ensure we read at most the smaller of:
+   *   (a) the length of the user-allocated buffer.
+   *   (b) the maximum data length as specified by the `max_bytes` argument.
+   */
+  if (max_bytes > buf.len)
+    max_bytes = buf.len;
+
+  /* Read into the user buffer. */
+  if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) {
+    uv_pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
+    return 0; /* Break out of read loop. */
+  }
+
+  /* Call the read callback. */
+  handle->read_cb((uv_stream_t*) handle, bytes_read, &buf);
+
+  return bytes_read;
+}
+
+
+static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
+  uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
+  int err;
+
+  if (*data_remaining > 0) {
+    /* Read frame data payload. */
+    DWORD bytes_read =
+        uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining);
+    *data_remaining -= bytes_read;
+    return bytes_read;
+
+  } else {
+    /* Start of a new IPC frame. */
+    uv__ipc_frame_header_t frame_header;
+    uint32_t xfer_flags;
+    uv__ipc_socket_xfer_type_t xfer_type;
+    uv__ipc_socket_xfer_info_t xfer_info;
+
+    /* Read the IPC frame header. */
+    err = uv__pipe_read_exactly(
+        handle->handle, &frame_header, sizeof frame_header);
+    if (err)
+      goto error;
+
+    /* Validate that flags are valid. */
+    if ((frame_header.flags & ~UV__IPC_FRAME_VALID_FLAGS) != 0)
+      goto invalid;
+    /* Validate that reserved2 is zero. */
+    if (frame_header.reserved2 != 0)
+      goto invalid;
+
+    /* Parse xfer flags. */
+    xfer_flags = frame_header.flags & UV__IPC_FRAME_XFER_FLAGS;
+    if (xfer_flags & UV__IPC_FRAME_HAS_SOCKET_XFER) {
+      /* Socket coming -- determine the type. */
+      xfer_type = xfer_flags & UV__IPC_FRAME_XFER_IS_TCP_CONNECTION
+                      ? UV__IPC_SOCKET_XFER_TCP_CONNECTION
+                      : UV__IPC_SOCKET_XFER_TCP_SERVER;
+    } else if (xfer_flags == 0) {
+      /* No socket. */
+      xfer_type = UV__IPC_SOCKET_XFER_NONE;
+    } else {
+      /* Invalid flags. */
+      goto invalid;
+    }
+
+    /* Parse data frame information. */
+    if (frame_header.flags & UV__IPC_FRAME_HAS_DATA) {
+      *data_remaining = frame_header.data_length;
+    } else if (frame_header.data_length != 0) {
+      /* Data length greater than zero but data flag not set -- invalid. */
+      goto invalid;
+    }
+
+    /* If no socket xfer info follows, return here. Data will be read in a
+     * subsequent invocation of uv__pipe_read_ipc(). */
+    if (xfer_type == UV__IPC_SOCKET_XFER_NONE)
+      return sizeof frame_header; /* Number of bytes read. */
+
+    /* Read transferred socket information. */
+    err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info);
+    if (err)
+      goto error;
+
+    /* Store the pending socket info. */
+    uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
+
+    /* Return number of bytes read. */
+    return sizeof frame_header + sizeof xfer_info;
+  }
+
+invalid:
+  /* Invalid frame. */
+  err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
+
+error:
+  uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
+  return 0; /* Break out of read loop. */
+}
+
+
+void uv_process_pipe_read_req(uv_loop_t* loop,
+                              uv_pipe_t* handle,
+                              uv_req_t* req) {
+  assert(handle->type == UV_NAMED_PIPE);
+
+  handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
+  DECREASE_PENDING_REQ_COUNT(handle);
+  eof_timer_stop(handle);
+
+  /* At this point, we're done with bookkeeping. If the user has stopped
+   * reading the pipe in the meantime, there is nothing left to do, since there
+   * is no callback that we can call. */
+  if (!(handle->flags & UV_HANDLE_READING))
+    return;
+
+  if (!REQ_SUCCESS(req)) {
+    /* An error occurred doing the zero-read. */
+    DWORD err = GET_REQ_ERROR(req);
+
+    /* If the read was cancelled by uv__pipe_interrupt_read(), the request may
+     * indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to
+     * the user; we'll start a new zero-read at the end of this function. */
+    if (err != ERROR_OPERATION_ABORTED)
+      uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
+
+  } else {
+    /* The zero-read completed without error, indicating there is data
+     * available in the kernel buffer. */
+    DWORD avail;
+
+    /* Get the number of bytes available. */
+    avail = 0;
+    if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL))
+      uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
+
+    /* Read until we've either read all the bytes available, or the 'reading'
+     * flag is cleared. */
+    while (avail > 0 && handle->flags & UV_HANDLE_READING) {
+      /* Depending on the type of pipe, read either IPC frames or raw data. */
+      DWORD bytes_read =
+          handle->ipc ? uv__pipe_read_ipc(loop, handle)
+                      : uv__pipe_read_data(loop, handle, avail, (DWORD) -1);
+
+      /* If no bytes were read, treat this as an indication that an error
+       * occurred, and break out of the read loop. */
+      if (bytes_read == 0)
+        break;
+
+      /* It is possible that more bytes were read than we thought were
+       * available. To prevent `avail` from underflowing, break out of the loop
+       * if this is the case. */
+      if (bytes_read > avail)
+        break;
+
+      /* Recompute the number of bytes available. */
+      avail -= bytes_read;
+    }
+  }
+
+  /* Start another zero-read request if necessary. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv_pipe_queue_read(loop, handle);
+  }
+}
+
+
+void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_write_t* req) {
+  int err;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  assert(handle->write_queue_size >= req->u.io.queued_bytes);
+  handle->write_queue_size -= req->u.io.queued_bytes;
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    if (req->wait_handle != INVALID_HANDLE_VALUE) {
+      UnregisterWait(req->wait_handle);
+      req->wait_handle = INVALID_HANDLE_VALUE;
+    }
+    if (req->event_handle) {
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+    }
+  }
+
+  err = GET_REQ_ERROR(req);
+
+  /* If this was a coalesced write, extract pointer to the user_provided
+   * uv_write_t structure so we can pass the expected pointer to the callback,
+   * then free the heap-allocated write req. */
+  if (req->coalesced) {
+    uv__coalesced_write_t* coalesced_write =
+        container_of(req, uv__coalesced_write_t, req);
+    req = coalesced_write->user_req;
+    uv__free(coalesced_write);
+  }
+  if (req->cb) {
+    req->cb(req, uv_translate_sys_error(err));
+  }
+
+  handle->stream.conn.write_reqs_pending--;
+
+  if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE &&
+      handle->pipe.conn.non_overlapped_writes_tail) {
+    assert(handle->stream.conn.write_reqs_pending > 0);
+    uv_queue_non_overlapped_write(handle);
+  }
+
+  if (handle->stream.conn.shutdown_req != NULL &&
+      handle->stream.conn.write_reqs_pending == 0) {
+    uv_want_endgame(loop, (uv_handle_t*)handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_req_t* raw_req) {
+  uv_pipe_accept_t* req = (uv_pipe_accept_t*) raw_req;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    /* The req->pipeHandle should be freed already in uv_pipe_cleanup(). */
+    assert(req->pipeHandle == INVALID_HANDLE_VALUE);
+    DECREASE_PENDING_REQ_COUNT(handle);
+    return;
+  }
+
+  if (REQ_SUCCESS(req)) {
+    assert(req->pipeHandle != INVALID_HANDLE_VALUE);
+    req->next_pending = handle->pipe.serv.pending_accepts;
+    handle->pipe.serv.pending_accepts = req;
+
+    if (handle->stream.serv.connection_cb) {
+      handle->stream.serv.connection_cb((uv_stream_t*)handle, 0);
+    }
+  } else {
+    if (req->pipeHandle != INVALID_HANDLE_VALUE) {
+      CloseHandle(req->pipeHandle);
+      req->pipeHandle = INVALID_HANDLE_VALUE;
+    }
+    if (!(handle->flags & UV_HANDLE_CLOSING)) {
+      uv_pipe_queue_accept(loop, handle, req, FALSE);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_connect_t* req) {
+  int err;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (req->cb) {
+    err = 0;
+    if (REQ_SUCCESS(req)) {
+      uv_pipe_connection_init(handle);
+    } else {
+      err = GET_REQ_ERROR(req);
+    }
+    req->cb(req, uv_translate_sys_error(err));
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_shutdown_t* req) {
+  assert(handle->type == UV_NAMED_PIPE);
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (handle->flags & UV_HANDLE_READABLE) {
+    /* Initialize and optionally start the eof timer. Only do this if the pipe
+     * is readable and we haven't seen EOF come in ourselves. */
+    eof_timer_init(handle);
+
+    /* If reading start the timer right now. Otherwise uv_pipe_queue_read will
+     * start it. */
+    if (handle->flags & UV_HANDLE_READ_PENDING) {
+      eof_timer_start(handle);
+    }
+
+  } else {
+    /* This pipe is not readable. We can just close it to let the other end
+     * know that we're done writing. */
+    close_pipe(handle);
+  }
+
+  if (req->cb) {
+    req->cb(req, 0);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+static void eof_timer_init(uv_pipe_t* pipe) {
+  int r;
+
+  assert(pipe->pipe.conn.eof_timer == NULL);
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  pipe->pipe.conn.eof_timer = (uv_timer_t*) uv__malloc(sizeof *pipe->pipe.conn.eof_timer);
+
+  r = uv_timer_init(pipe->loop, pipe->pipe.conn.eof_timer);
+  assert(r == 0); /* timers can't fail */
+  pipe->pipe.conn.eof_timer->data = pipe;
+  uv_unref((uv_handle_t*) pipe->pipe.conn.eof_timer);
+}
+
+
+static void eof_timer_start(uv_pipe_t* pipe) {
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  if (pipe->pipe.conn.eof_timer != NULL) {
+    uv_timer_start(pipe->pipe.conn.eof_timer, eof_timer_cb, eof_timeout, 0);
+  }
+}
+
+
+static void eof_timer_stop(uv_pipe_t* pipe) {
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  if (pipe->pipe.conn.eof_timer != NULL) {
+    uv_timer_stop(pipe->pipe.conn.eof_timer);
+  }
+}
+
+
+static void eof_timer_cb(uv_timer_t* timer) {
+  uv_pipe_t* pipe = (uv_pipe_t*) timer->data;
+  uv_loop_t* loop = timer->loop;
+
+  assert(pipe->type == UV_NAMED_PIPE);
+
+  /* This should always be true, since we start the timer only in
+   * uv_pipe_queue_read after successfully calling ReadFile, or in
+   * uv_process_pipe_shutdown_req if a read is pending, and we always
+   * immediately stop the timer in uv_process_pipe_read_req. */
+  assert(pipe->flags & UV_HANDLE_READ_PENDING);
+
+  /* If there are many packets coming off the iocp then the timer callback may
+   * be called before the read request is coming off the queue. Therefore we
+   * check here if the read request has completed but will be processed later.
+   */
+  if ((pipe->flags & UV_HANDLE_READ_PENDING) &&
+      HasOverlappedIoCompleted(&pipe->read_req.u.io.overlapped)) {
+    return;
+  }
+
+  /* Force both ends off the pipe. */
+  close_pipe(pipe);
+
+  /* Stop reading, so the pending read that is going to fail will not be
+   * reported to the user. */
+  uv_read_stop((uv_stream_t*) pipe);
+
+  /* Report the eof and update flags. This will get reported even if the user
+   * stopped reading in the meantime. TODO: is that okay? */
+  uv_pipe_read_eof(loop, pipe, uv_null_buf_);
+}
+
+
+static void eof_timer_destroy(uv_pipe_t* pipe) {
+  assert(pipe->flags & UV_HANDLE_CONNECTION);
+
+  if (pipe->pipe.conn.eof_timer) {
+    uv_close((uv_handle_t*) pipe->pipe.conn.eof_timer, eof_timer_close_cb);
+    pipe->pipe.conn.eof_timer = NULL;
+  }
+}
+
+
+static void eof_timer_close_cb(uv_handle_t* handle) {
+  assert(handle->type == UV_TIMER);
+  uv__free(handle);
+}
+
+
+int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
+  HANDLE os_handle = uv__get_osfhandle(file);
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_ACCESS_INFORMATION access;
+  DWORD duplex_flags = 0;
+
+  if (os_handle == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+
+  uv__once_init();
+  /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
+   * underlying OS handle and forget about the original fd.
+   * We could also opt to use the original OS handle and just never close it,
+   * but then there would be no reliable way to cancel pending read operations
+   * upon close.
+   */
+  if (file <= 2) {
+    if (!DuplicateHandle(INVALID_HANDLE_VALUE,
+                         os_handle,
+                         INVALID_HANDLE_VALUE,
+                         &os_handle,
+                         0,
+                         FALSE,
+                         DUPLICATE_SAME_ACCESS))
+      return uv_translate_sys_error(GetLastError());
+    file = -1;
+  }
+
+  /* Determine what kind of permissions we have on this handle.
+   * Cygwin opens the pipe in message mode, but we can support it,
+   * just query the access flags and set the stream flags accordingly.
+   */
+  nt_status = pNtQueryInformationFile(os_handle,
+                                      &io_status,
+                                      &access,
+                                      sizeof(access),
+                                      FileAccessInformation);
+  if (nt_status != STATUS_SUCCESS)
+    return UV_EINVAL;
+
+  if (pipe->ipc) {
+    if (!(access.AccessFlags & FILE_WRITE_DATA) ||
+        !(access.AccessFlags & FILE_READ_DATA)) {
+      return UV_EINVAL;
+    }
+  }
+
+  if (access.AccessFlags & FILE_WRITE_DATA)
+    duplex_flags |= UV_HANDLE_WRITABLE;
+  if (access.AccessFlags & FILE_READ_DATA)
+    duplex_flags |= UV_HANDLE_READABLE;
+
+  if (os_handle == INVALID_HANDLE_VALUE ||
+      uv_set_pipe_handle(pipe->loop,
+                         pipe,
+                         os_handle,
+                         file,
+                         duplex_flags) == -1) {
+    return UV_EINVAL;
+  }
+
+  uv_pipe_connection_init(pipe);
+
+  if (pipe->ipc) {
+    assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
+    pipe->pipe.conn.ipc_remote_pid = uv_os_getppid();
+    assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1);
+  }
+  return 0;
+}
+
+
+static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_NAME_INFORMATION tmp_name_info;
+  FILE_NAME_INFORMATION* name_info;
+  WCHAR* name_buf;
+  unsigned int addrlen;
+  unsigned int name_size;
+  unsigned int name_len;
+  int err;
+
+  uv__once_init();
+  name_info = NULL;
+
+  if (handle->handle == INVALID_HANDLE_VALUE) {
+    *size = 0;
+    return UV_EINVAL;
+  }
+
+  /* NtQueryInformationFile will block if another thread is performing a
+   * blocking operation on the queried handle. If the pipe handle is
+   * synchronous, there may be a worker thread currently calling ReadFile() on
+   * the pipe handle, which could cause a deadlock. To avoid this, interrupt
+   * the read. */
+  if (handle->flags & UV_HANDLE_CONNECTION &&
+      handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+    uv__pipe_interrupt_read((uv_pipe_t*) handle); /* cast away const warning */
+  }
+
+  nt_status = pNtQueryInformationFile(handle->handle,
+                                      &io_status,
+                                      &tmp_name_info,
+                                      sizeof tmp_name_info,
+                                      FileNameInformation);
+  if (nt_status == STATUS_BUFFER_OVERFLOW) {
+    name_size = sizeof(*name_info) + tmp_name_info.FileNameLength;
+    name_info = uv__malloc(name_size);
+    if (!name_info) {
+      *size = 0;
+      err = UV_ENOMEM;
+      goto cleanup;
+    }
+
+    nt_status = pNtQueryInformationFile(handle->handle,
+                                        &io_status,
+                                        name_info,
+                                        name_size,
+                                        FileNameInformation);
+  }
+
+  if (nt_status != STATUS_SUCCESS) {
+    *size = 0;
+    err = uv_translate_sys_error(pRtlNtStatusToDosError(nt_status));
+    goto error;
+  }
+
+  if (!name_info) {
+    /* the struct on stack was used */
+    name_buf = tmp_name_info.FileName;
+    name_len = tmp_name_info.FileNameLength;
+  } else {
+    name_buf = name_info->FileName;
+    name_len = name_info->FileNameLength;
+  }
+
+  if (name_len == 0) {
+    *size = 0;
+    err = 0;
+    goto error;
+  }
+
+  name_len /= sizeof(WCHAR);
+
+  /* check how much space we need */
+  addrlen = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                name_buf,
+                                name_len,
+                                NULL,
+                                0,
+                                NULL,
+                                NULL);
+  if (!addrlen) {
+    *size = 0;
+    err = uv_translate_sys_error(GetLastError());
+    goto error;
+  } else if (pipe_prefix_len + addrlen >= *size) {
+    /* "\\\\.\\pipe" + name */
+    *size = pipe_prefix_len + addrlen + 1;
+    err = UV_ENOBUFS;
+    goto error;
+  }
+
+  memcpy(buffer, pipe_prefix, pipe_prefix_len);
+  addrlen = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                name_buf,
+                                name_len,
+                                buffer+pipe_prefix_len,
+                                *size-pipe_prefix_len,
+                                NULL,
+                                NULL);
+  if (!addrlen) {
+    *size = 0;
+    err = uv_translate_sys_error(GetLastError());
+    goto error;
+  }
+
+  addrlen += pipe_prefix_len;
+  *size = addrlen;
+  buffer[addrlen] = '\0';
+
+  err = 0;
+
+error:
+  uv__free(name_info);
+
+cleanup:
+  return err;
+}
+
+
+int uv_pipe_pending_count(uv_pipe_t* handle) {
+  if (!handle->ipc)
+    return 0;
+  return handle->pipe.conn.ipc_xfer_queue_length;
+}
+
+
+int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  if (handle->flags & UV_HANDLE_BOUND)
+    return uv__pipe_getname(handle, buffer, size);
+
+  if (handle->flags & UV_HANDLE_CONNECTION ||
+      handle->handle != INVALID_HANDLE_VALUE) {
+    *size = 0;
+    return 0;
+  }
+
+  return UV_EBADF;
+}
+
+
+int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  /* emulate unix behaviour */
+  if (handle->flags & UV_HANDLE_BOUND)
+    return UV_ENOTCONN;
+
+  if (handle->handle != INVALID_HANDLE_VALUE)
+    return uv__pipe_getname(handle, buffer, size);
+
+  return UV_EBADF;
+}
+
+
+uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
+  if (!handle->ipc)
+    return UV_UNKNOWN_HANDLE;
+  if (handle->pipe.conn.ipc_xfer_queue_length == 0)
+    return UV_UNKNOWN_HANDLE;
+  else
+    return UV_TCP;
+}
+
+int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
+  SID_IDENTIFIER_AUTHORITY sid_world = { SECURITY_WORLD_SID_AUTHORITY };
+  PACL old_dacl, new_dacl;
+  PSECURITY_DESCRIPTOR sd;
+  EXPLICIT_ACCESS ea;
+  PSID everyone;
+  int error;
+
+  if (handle == NULL || handle->handle == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+
+  if (mode != UV_READABLE &&
+      mode != UV_WRITABLE &&
+      mode != (UV_WRITABLE | UV_READABLE))
+    return UV_EINVAL;
+
+  if (!AllocateAndInitializeSid(&sid_world,
+                                1,
+                                SECURITY_WORLD_RID,
+                                0, 0, 0, 0, 0, 0, 0,
+                                &everyone)) {
+    error = GetLastError();
+    goto done;
+  }
+
+  if (GetSecurityInfo(handle->handle,
+                      SE_KERNEL_OBJECT,
+                      DACL_SECURITY_INFORMATION,
+                      NULL,
+                      NULL,
+                      &old_dacl,
+                      NULL,
+                      &sd)) {
+    error = GetLastError();
+    goto clean_sid;
+  }
+
+  memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
+  if (mode & UV_READABLE)
+    ea.grfAccessPermissions |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
+  if (mode & UV_WRITABLE)
+    ea.grfAccessPermissions |= GENERIC_WRITE | FILE_READ_ATTRIBUTES;
+  ea.grfAccessPermissions |= SYNCHRONIZE;
+  ea.grfAccessMode = SET_ACCESS;
+  ea.grfInheritance = NO_INHERITANCE;
+  ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+  ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
+  ea.Trustee.ptstrName = (LPTSTR)everyone;
+
+  if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl)) {
+    error = GetLastError();
+    goto clean_sd;
+  }
+
+  if (SetSecurityInfo(handle->handle,
+                      SE_KERNEL_OBJECT,
+                      DACL_SECURITY_INFORMATION,
+                      NULL,
+                      NULL,
+                      new_dacl,
+                      NULL)) {
+    error = GetLastError();
+    goto clean_dacl;
+  }
+
+  error = 0;
+
+clean_dacl:
+  LocalFree((HLOCAL) new_dacl);
+clean_sd:
+  LocalFree((HLOCAL) sd);
+clean_sid:
+  FreeSid(everyone);
+done:
+  return uv_translate_sys_error(error);
+}
diff --git a/third_party/libuv/src/win/poll.c b/third_party/libuv/src/win/poll.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/poll.c
@@ -0,0 +1,584 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+static const GUID uv_msafd_provider_ids[UV_MSAFD_PROVIDER_COUNT] = {
+  {0xe70f1aa0, 0xab8b, 0x11cf,
+      {0x8c, 0xa3, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}},
+  {0xf9eab0c0, 0x26d4, 0x11d0,
+      {0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}},
+  {0x9fc48064, 0x7298, 0x43e4,
+      {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}}
+};
+
+typedef struct uv_single_fd_set_s {
+  unsigned int fd_count;
+  SOCKET fd_array[1];
+} uv_single_fd_set_t;
+
+
+static OVERLAPPED overlapped_dummy_;
+static uv_once_t overlapped_dummy_init_guard_ = UV_ONCE_INIT;
+
+static AFD_POLL_INFO afd_poll_info_dummy_;
+
+
+static void uv__init_overlapped_dummy(void) {
+  HANDLE event;
+
+  event = CreateEvent(NULL, TRUE, TRUE, NULL);
+  if (event == NULL)
+    uv_fatal_error(GetLastError(), "CreateEvent");
+
+  memset(&overlapped_dummy_, 0, sizeof overlapped_dummy_);
+  overlapped_dummy_.hEvent = (HANDLE) ((uintptr_t) event | 1);
+}
+
+
+static OVERLAPPED* uv__get_overlapped_dummy(void) {
+  uv_once(&overlapped_dummy_init_guard_, uv__init_overlapped_dummy);
+  return &overlapped_dummy_;
+}
+
+
+static AFD_POLL_INFO* uv__get_afd_poll_info_dummy(void) {
+  return &afd_poll_info_dummy_;
+}
+
+
+static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
+  uv_req_t* req;
+  AFD_POLL_INFO* afd_poll_info;
+  int result;
+
+  /* Find a yet unsubmitted req to submit. */
+  if (handle->submitted_events_1 == 0) {
+    req = &handle->poll_req_1;
+    afd_poll_info = &handle->afd_poll_info_1;
+    handle->submitted_events_1 = handle->events;
+    handle->mask_events_1 = 0;
+    handle->mask_events_2 = handle->events;
+  } else if (handle->submitted_events_2 == 0) {
+    req = &handle->poll_req_2;
+    afd_poll_info = &handle->afd_poll_info_2;
+    handle->submitted_events_2 = handle->events;
+    handle->mask_events_1 = handle->events;
+    handle->mask_events_2 = 0;
+  } else {
+    /* Just wait until there's an unsubmitted req. This will happen almost
+     * immediately as one of the 2 outstanding requests is about to return.
+     * When this happens, uv__fast_poll_process_poll_req will be called, and
+     * the pending events, if needed, will be processed in a subsequent
+     * request. */
+    return;
+  }
+
+  /* Setting Exclusive to TRUE makes the other poll request return if there is
+   * any. */
+  afd_poll_info->Exclusive = TRUE;
+  afd_poll_info->NumberOfHandles = 1;
+  afd_poll_info->Timeout.QuadPart = INT64_MAX;
+  afd_poll_info->Handles[0].Handle = (HANDLE) handle->socket;
+  afd_poll_info->Handles[0].Status = 0;
+  afd_poll_info->Handles[0].Events = 0;
+
+  if (handle->events & UV_READABLE) {
+    afd_poll_info->Handles[0].Events |= AFD_POLL_RECEIVE |
+        AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT;
+  } else {
+    if (handle->events & UV_DISCONNECT) {
+      afd_poll_info->Handles[0].Events |= AFD_POLL_DISCONNECT;
+    }
+  }
+  if (handle->events & UV_WRITABLE) {
+    afd_poll_info->Handles[0].Events |= AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL;
+  }
+
+  memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped);
+
+  result = uv_msafd_poll((SOCKET) handle->peer_socket,
+                         afd_poll_info,
+                         afd_poll_info,
+                         &req->u.io.overlapped);
+  if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) {
+    /* Queue this req, reporting an error. */
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv_insert_pending_req(loop, req);
+  }
+}
+
+
+static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
+    uv_req_t* req) {
+  unsigned char mask_events;
+  AFD_POLL_INFO* afd_poll_info;
+
+  if (req == &handle->poll_req_1) {
+    afd_poll_info = &handle->afd_poll_info_1;
+    handle->submitted_events_1 = 0;
+    mask_events = handle->mask_events_1;
+  } else if (req == &handle->poll_req_2) {
+    afd_poll_info = &handle->afd_poll_info_2;
+    handle->submitted_events_2 = 0;
+    mask_events = handle->mask_events_2;
+  } else {
+    assert(0);
+    return;
+  }
+
+  /* Report an error unless the select was just interrupted. */
+  if (!REQ_SUCCESS(req)) {
+    DWORD error = GET_REQ_SOCK_ERROR(req);
+    if (error != WSAEINTR && handle->events != 0) {
+      handle->events = 0; /* Stop the watcher */
+      handle->poll_cb(handle, uv_translate_sys_error(error), 0);
+    }
+
+  } else if (afd_poll_info->NumberOfHandles >= 1) {
+    unsigned char events = 0;
+
+    if ((afd_poll_info->Handles[0].Events & (AFD_POLL_RECEIVE |
+        AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT)) != 0) {
+      events |= UV_READABLE;
+      if ((afd_poll_info->Handles[0].Events & AFD_POLL_DISCONNECT) != 0) {
+        events |= UV_DISCONNECT;
+      }
+    }
+    if ((afd_poll_info->Handles[0].Events & (AFD_POLL_SEND |
+        AFD_POLL_CONNECT_FAIL)) != 0) {
+      events |= UV_WRITABLE;
+    }
+
+    events &= handle->events & ~mask_events;
+
+    if (afd_poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) {
+      /* Stop polling. */
+      handle->events = 0;
+      if (uv__is_active(handle))
+        uv__handle_stop(handle);
+    }
+
+    if (events != 0) {
+      handle->poll_cb(handle, 0, events);
+    }
+  }
+
+  if ((handle->events & ~(handle->submitted_events_1 |
+      handle->submitted_events_2)) != 0) {
+    uv__fast_poll_submit_poll_req(loop, handle);
+  } else if ((handle->flags & UV_HANDLE_CLOSING) &&
+             handle->submitted_events_1 == 0 &&
+             handle->submitted_events_2 == 0) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+static SOCKET uv__fast_poll_create_peer_socket(HANDLE iocp,
+    WSAPROTOCOL_INFOW* protocol_info) {
+  SOCKET sock = 0;
+
+  sock = WSASocketW(protocol_info->iAddressFamily,
+                    protocol_info->iSocketType,
+                    protocol_info->iProtocol,
+                    protocol_info,
+                    0,
+                    WSA_FLAG_OVERLAPPED);
+  if (sock == INVALID_SOCKET) {
+    return INVALID_SOCKET;
+  }
+
+  if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) {
+    goto error;
+  };
+
+  if (CreateIoCompletionPort((HANDLE) sock,
+                             iocp,
+                             (ULONG_PTR) sock,
+                             0) == NULL) {
+    goto error;
+  }
+
+  return sock;
+
+ error:
+  closesocket(sock);
+  return INVALID_SOCKET;
+}
+
+
+static SOCKET uv__fast_poll_get_peer_socket(uv_loop_t* loop,
+    WSAPROTOCOL_INFOW* protocol_info) {
+  int index, i;
+  SOCKET peer_socket;
+
+  index = -1;
+  for (i = 0; (size_t) i < ARRAY_SIZE(uv_msafd_provider_ids); i++) {
+    if (memcmp((void*) &protocol_info->ProviderId,
+               (void*) &uv_msafd_provider_ids[i],
+               sizeof protocol_info->ProviderId) == 0) {
+      index = i;
+    }
+  }
+
+  /* Check if the protocol uses an msafd socket. */
+  if (index < 0) {
+    return INVALID_SOCKET;
+  }
+
+  /* If we didn't (try) to create a peer socket yet, try to make one. Don't try
+   * again if the peer socket creation failed earlier for the same protocol. */
+  peer_socket = loop->poll_peer_sockets[index];
+  if (peer_socket == 0) {
+    peer_socket = uv__fast_poll_create_peer_socket(loop->iocp, protocol_info);
+    loop->poll_peer_sockets[index] = peer_socket;
+  }
+
+  return peer_socket;
+}
+
+
+static DWORD WINAPI uv__slow_poll_thread_proc(void* arg) {
+  uv_req_t* req = (uv_req_t*) arg;
+  uv_poll_t* handle = (uv_poll_t*) req->data;
+  unsigned char reported_events;
+  int r;
+  uv_single_fd_set_t rfds, wfds, efds;
+  struct timeval timeout;
+
+  assert(handle->type == UV_POLL);
+  assert(req->type == UV_POLL_REQ);
+
+  if (handle->events & UV_READABLE) {
+    rfds.fd_count = 1;
+    rfds.fd_array[0] = handle->socket;
+  } else {
+    rfds.fd_count = 0;
+  }
+
+  if (handle->events & UV_WRITABLE) {
+    wfds.fd_count = 1;
+    wfds.fd_array[0] = handle->socket;
+    efds.fd_count = 1;
+    efds.fd_array[0] = handle->socket;
+  } else {
+    wfds.fd_count = 0;
+    efds.fd_count = 0;
+  }
+
+  /* Make the select() time out after 3 minutes. If select() hangs because the
+   * user closed the socket, we will at least not hang indefinitely. */
+  timeout.tv_sec = 3 * 60;
+  timeout.tv_usec = 0;
+
+  r = select(1, (fd_set*) &rfds, (fd_set*) &wfds, (fd_set*) &efds, &timeout);
+  if (r == SOCKET_ERROR) {
+    /* Queue this req, reporting an error. */
+    SET_REQ_ERROR(&handle->poll_req_1, WSAGetLastError());
+    POST_COMPLETION_FOR_REQ(handle->loop, req);
+    return 0;
+  }
+
+  reported_events = 0;
+
+  if (r > 0) {
+    if (rfds.fd_count > 0) {
+      assert(rfds.fd_count == 1);
+      assert(rfds.fd_array[0] == handle->socket);
+      reported_events |= UV_READABLE;
+    }
+
+    if (wfds.fd_count > 0) {
+      assert(wfds.fd_count == 1);
+      assert(wfds.fd_array[0] == handle->socket);
+      reported_events |= UV_WRITABLE;
+    } else if (efds.fd_count > 0) {
+      assert(efds.fd_count == 1);
+      assert(efds.fd_array[0] == handle->socket);
+      reported_events |= UV_WRITABLE;
+    }
+  }
+
+  SET_REQ_SUCCESS(req);
+  req->u.io.overlapped.InternalHigh = (DWORD) reported_events;
+  POST_COMPLETION_FOR_REQ(handle->loop, req);
+
+  return 0;
+}
+
+
+static void uv__slow_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
+  uv_req_t* req;
+
+  /* Find a yet unsubmitted req to submit. */
+  if (handle->submitted_events_1 == 0) {
+    req = &handle->poll_req_1;
+    handle->submitted_events_1 = handle->events;
+    handle->mask_events_1 = 0;
+    handle->mask_events_2 = handle->events;
+  } else if (handle->submitted_events_2 == 0) {
+    req = &handle->poll_req_2;
+    handle->submitted_events_2 = handle->events;
+    handle->mask_events_1 = handle->events;
+    handle->mask_events_2 = 0;
+  } else {
+    assert(0);
+    return;
+  }
+
+  if (!QueueUserWorkItem(uv__slow_poll_thread_proc,
+                         (void*) req,
+                         WT_EXECUTELONGFUNCTION)) {
+    /* Make this req pending, reporting an error. */
+    SET_REQ_ERROR(req, GetLastError());
+    uv_insert_pending_req(loop, req);
+  }
+}
+
+
+
+static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
+    uv_req_t* req) {
+  unsigned char mask_events;
+  int err;
+
+  if (req == &handle->poll_req_1) {
+    handle->submitted_events_1 = 0;
+    mask_events = handle->mask_events_1;
+  } else if (req == &handle->poll_req_2) {
+    handle->submitted_events_2 = 0;
+    mask_events = handle->mask_events_2;
+  } else {
+    assert(0);
+    return;
+  }
+
+  if (!REQ_SUCCESS(req)) {
+    /* Error. */
+    if (handle->events != 0) {
+      err = GET_REQ_ERROR(req);
+      handle->events = 0; /* Stop the watcher */
+      handle->poll_cb(handle, uv_translate_sys_error(err), 0);
+    }
+  } else {
+    /* Got some events. */
+    int events = req->u.io.overlapped.InternalHigh & handle->events & ~mask_events;
+    if (events != 0) {
+      handle->poll_cb(handle, 0, events);
+    }
+  }
+
+  if ((handle->events & ~(handle->submitted_events_1 |
+      handle->submitted_events_2)) != 0) {
+    uv__slow_poll_submit_poll_req(loop, handle);
+  } else if ((handle->flags & UV_HANDLE_CLOSING) &&
+             handle->submitted_events_1 == 0 &&
+             handle->submitted_events_2 == 0) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
+  return uv_poll_init_socket(loop, handle, (SOCKET) uv__get_osfhandle(fd));
+}
+
+
+int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
+    uv_os_sock_t socket) {
+  WSAPROTOCOL_INFOW protocol_info;
+  int len;
+  SOCKET peer_socket, base_socket;
+  DWORD bytes;
+  DWORD yes = 1;
+
+  /* Set the socket to nonblocking mode */
+  if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR)
+    return uv_translate_sys_error(WSAGetLastError());
+
+/* Try to obtain a base handle for the socket. This increases this chances that
+ * we find an AFD handle and are able to use the fast poll mechanism. This will
+ * always fail on windows XP/2k3, since they don't support the. SIO_BASE_HANDLE
+ * ioctl. */
+#ifndef NDEBUG
+  base_socket = INVALID_SOCKET;
+#endif
+
+  if (WSAIoctl(socket,
+               SIO_BASE_HANDLE,
+               NULL,
+               0,
+               &base_socket,
+               sizeof base_socket,
+               &bytes,
+               NULL,
+               NULL) == 0) {
+    assert(base_socket != 0 && base_socket != INVALID_SOCKET);
+    socket = base_socket;
+  }
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
+  handle->socket = socket;
+  handle->events = 0;
+
+  /* Obtain protocol information about the socket. */
+  len = sizeof protocol_info;
+  if (getsockopt(socket,
+                 SOL_SOCKET,
+                 SO_PROTOCOL_INFOW,
+                 (char*) &protocol_info,
+                 &len) != 0) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  /* Get the peer socket that is needed to enable fast poll. If the returned
+   * value is NULL, the protocol is not implemented by MSAFD and we'll have to
+   * use slow mode. */
+  peer_socket = uv__fast_poll_get_peer_socket(loop, &protocol_info);
+
+  if (peer_socket != INVALID_SOCKET) {
+    /* Initialize fast poll specific fields. */
+    handle->peer_socket = peer_socket;
+  } else {
+    /* Initialize slow poll specific fields. */
+    handle->flags |= UV_HANDLE_POLL_SLOW;
+  }
+
+  /* Initialize 2 poll reqs. */
+  handle->submitted_events_1 = 0;
+  UV_REQ_INIT(&handle->poll_req_1, UV_POLL_REQ);
+  handle->poll_req_1.data = handle;
+
+  handle->submitted_events_2 = 0;
+  UV_REQ_INIT(&handle->poll_req_2, UV_POLL_REQ);
+  handle->poll_req_2.data = handle;
+
+  return 0;
+}
+
+
+static int uv__poll_set(uv_poll_t* handle, int events, uv_poll_cb cb) {
+  int submitted_events;
+
+  assert(handle->type == UV_POLL);
+  assert(!(handle->flags & UV_HANDLE_CLOSING));
+  assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0);
+
+  handle->events = events;
+  handle->poll_cb = cb;
+
+  if (handle->events == 0) {
+    uv__handle_stop(handle);
+    return 0;
+  }
+
+  uv__handle_start(handle);
+  submitted_events = handle->submitted_events_1 | handle->submitted_events_2;
+
+  if (handle->events & ~submitted_events) {
+    if (handle->flags & UV_HANDLE_POLL_SLOW) {
+      uv__slow_poll_submit_poll_req(handle->loop, handle);
+    } else {
+      uv__fast_poll_submit_poll_req(handle->loop, handle);
+    }
+  }
+
+  return 0;
+}
+
+
+int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) {
+  return uv__poll_set(handle, events, cb);
+}
+
+
+int uv_poll_stop(uv_poll_t* handle) {
+  return uv__poll_set(handle, 0, handle->poll_cb);
+}
+
+
+void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) {
+  if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
+    uv__fast_poll_process_poll_req(loop, handle, req);
+  } else {
+    uv__slow_poll_process_poll_req(loop, handle, req);
+  }
+}
+
+
+int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
+  AFD_POLL_INFO afd_poll_info;
+  DWORD error;
+  int result;
+
+  handle->events = 0;
+  uv__handle_closing(handle);
+
+  if (handle->submitted_events_1 == 0 &&
+      handle->submitted_events_2 == 0) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+    return 0;
+  }
+
+  if (handle->flags & UV_HANDLE_POLL_SLOW)
+    return 0;
+
+  /* Cancel outstanding poll requests by executing another, unique poll
+   * request that forces the outstanding ones to return. */
+  afd_poll_info.Exclusive = TRUE;
+  afd_poll_info.NumberOfHandles = 1;
+  afd_poll_info.Timeout.QuadPart = INT64_MAX;
+  afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket;
+  afd_poll_info.Handles[0].Status = 0;
+  afd_poll_info.Handles[0].Events = AFD_POLL_ALL;
+
+  result = uv_msafd_poll(handle->socket,
+                         &afd_poll_info,
+                         uv__get_afd_poll_info_dummy(),
+                         uv__get_overlapped_dummy());
+
+  if (result == SOCKET_ERROR) {
+    error = WSAGetLastError();
+    if (error != WSA_IO_PENDING)
+      return uv_translate_sys_error(error);
+  }
+
+  return 0;
+}
+
+
+void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+  assert(handle->submitted_events_1 == 0);
+  assert(handle->submitted_events_2 == 0);
+
+  uv__handle_close(handle);
+}
diff --git a/third_party/libuv/src/win/process-stdio.c b/third_party/libuv/src/win/process-stdio.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/process-stdio.c
@@ -0,0 +1,512 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+
+
+/*
+ * The `child_stdio_buffer` buffer has the following layout:
+ *   int number_of_fds
+ *   unsigned char crt_flags[number_of_fds]
+ *   HANDLE os_handle[number_of_fds]
+ */
+#define CHILD_STDIO_SIZE(count)                     \
+    (sizeof(int) +                                  \
+     sizeof(unsigned char) * (count) +              \
+     sizeof(uintptr_t) * (count))
+
+#define CHILD_STDIO_COUNT(buffer)                   \
+    *((unsigned int*) (buffer))
+
+#define CHILD_STDIO_CRT_FLAGS(buffer, fd)           \
+    *((unsigned char*) (buffer) + sizeof(int) + fd)
+
+#define CHILD_STDIO_HANDLE(buffer, fd)              \
+    *((HANDLE*) ((unsigned char*) (buffer) +        \
+                 sizeof(int) +                      \
+                 sizeof(unsigned char) *            \
+                 CHILD_STDIO_COUNT((buffer)) +      \
+                 sizeof(HANDLE) * (fd)))
+
+
+/* CRT file descriptor mode flags */
+#define FOPEN       0x01
+#define FEOFLAG     0x02
+#define FCRLF       0x04
+#define FPIPE       0x08
+#define FNOINHERIT  0x10
+#define FAPPEND     0x20
+#define FDEV        0x40
+#define FTEXT       0x80
+
+
+/*
+ * Clear the HANDLE_FLAG_INHERIT flag from all HANDLEs that were inherited
+ * the parent process. Don't check for errors - the stdio handles may not be
+ * valid, or may be closed already. There is no guarantee that this function
+ * does a perfect job.
+ */
+void uv_disable_stdio_inheritance(void) {
+  HANDLE handle;
+  STARTUPINFOW si;
+
+  /* Make the windows stdio handles non-inheritable. */
+  handle = GetStdHandle(STD_INPUT_HANDLE);
+  if (handle != NULL && handle != INVALID_HANDLE_VALUE)
+    SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+
+  handle = GetStdHandle(STD_OUTPUT_HANDLE);
+  if (handle != NULL && handle != INVALID_HANDLE_VALUE)
+    SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+
+  handle = GetStdHandle(STD_ERROR_HANDLE);
+  if (handle != NULL && handle != INVALID_HANDLE_VALUE)
+    SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+
+  /* Make inherited CRT FDs non-inheritable. */
+  GetStartupInfoW(&si);
+  if (uv__stdio_verify(si.lpReserved2, si.cbReserved2))
+    uv__stdio_noinherit(si.lpReserved2);
+}
+
+
+static int uv__create_stdio_pipe_pair(uv_loop_t* loop,
+    uv_pipe_t* server_pipe, HANDLE* child_pipe_ptr, unsigned int flags) {
+  char pipe_name[64];
+  SECURITY_ATTRIBUTES sa;
+  DWORD server_access = 0;
+  DWORD client_access = 0;
+  HANDLE child_pipe = INVALID_HANDLE_VALUE;
+  int err;
+  int overlap;
+
+  if (flags & UV_READABLE_PIPE) {
+    /* The server needs inbound access too, otherwise CreateNamedPipe() won't
+     * give us the FILE_READ_ATTRIBUTES permission. We need that to probe the
+     * state of the write buffer when we're trying to shutdown the pipe. */
+    server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND;
+    client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
+  }
+  if (flags & UV_WRITABLE_PIPE) {
+    server_access |= PIPE_ACCESS_INBOUND;
+    client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES;
+  }
+
+  /* Create server pipe handle. */
+  err = uv_stdio_pipe_server(loop,
+                             server_pipe,
+                             server_access,
+                             pipe_name,
+                             sizeof(pipe_name));
+  if (err)
+    goto error;
+
+  /* Create child pipe handle. */
+  sa.nLength = sizeof sa;
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle = TRUE;
+
+  overlap = server_pipe->ipc || (flags & UV_OVERLAPPED_PIPE);
+  child_pipe = CreateFileA(pipe_name,
+                           client_access,
+                           0,
+                           &sa,
+                           OPEN_EXISTING,
+                           overlap ? FILE_FLAG_OVERLAPPED : 0,
+                           NULL);
+  if (child_pipe == INVALID_HANDLE_VALUE) {
+    err = GetLastError();
+    goto error;
+  }
+
+#ifndef NDEBUG
+  /* Validate that the pipe was opened in the right mode. */
+  {
+    DWORD mode;
+    BOOL r = GetNamedPipeHandleState(child_pipe,
+                                     &mode,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     0);
+    assert(r == TRUE);
+    assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT));
+  }
+#endif
+
+  /* Do a blocking ConnectNamedPipe. This should not block because we have both
+   * ends of the pipe created. */
+  if (!ConnectNamedPipe(server_pipe->handle, NULL)) {
+    if (GetLastError() != ERROR_PIPE_CONNECTED) {
+      err = GetLastError();
+      goto error;
+    }
+  }
+
+  /* The server end is now readable and/or writable. */
+  if (flags & UV_READABLE_PIPE)
+    server_pipe->flags |= UV_HANDLE_WRITABLE;
+  if (flags & UV_WRITABLE_PIPE)
+    server_pipe->flags |= UV_HANDLE_READABLE;
+
+  *child_pipe_ptr = child_pipe;
+  return 0;
+
+ error:
+  if (server_pipe->handle != INVALID_HANDLE_VALUE) {
+    uv_pipe_cleanup(loop, server_pipe);
+  }
+
+  if (child_pipe != INVALID_HANDLE_VALUE) {
+    CloseHandle(child_pipe);
+  }
+
+  return err;
+}
+
+
+static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) {
+  HANDLE current_process;
+
+
+  /* _get_osfhandle will sometimes return -2 in case of an error. This seems to
+   * happen when fd <= 2 and the process' corresponding stdio handle is set to
+   * NULL. Unfortunately DuplicateHandle will happily duplicate (HANDLE) -2, so
+   * this situation goes unnoticed until someone tries to use the duplicate.
+   * Therefore we filter out known-invalid handles here. */
+  if (handle == INVALID_HANDLE_VALUE ||
+      handle == NULL ||
+      handle == (HANDLE) -2) {
+    *dup = INVALID_HANDLE_VALUE;
+    return ERROR_INVALID_HANDLE;
+  }
+
+  current_process = GetCurrentProcess();
+
+  if (!DuplicateHandle(current_process,
+                       handle,
+                       current_process,
+                       dup,
+                       0,
+                       TRUE,
+                       DUPLICATE_SAME_ACCESS)) {
+    *dup = INVALID_HANDLE_VALUE;
+    return GetLastError();
+  }
+
+  return 0;
+}
+
+
+static int uv__duplicate_fd(uv_loop_t* loop, int fd, HANDLE* dup) {
+  HANDLE handle;
+
+  if (fd == -1) {
+    *dup = INVALID_HANDLE_VALUE;
+    return ERROR_INVALID_HANDLE;
+  }
+
+  handle = uv__get_osfhandle(fd);
+  return uv__duplicate_handle(loop, handle, dup);
+}
+
+
+int uv__create_nul_handle(HANDLE* handle_ptr,
+    DWORD access) {
+  HANDLE handle;
+  SECURITY_ATTRIBUTES sa;
+
+  sa.nLength = sizeof sa;
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle = TRUE;
+
+  handle = CreateFileW(L"NUL",
+                       access,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       &sa,
+                       OPEN_EXISTING,
+                       0,
+                       NULL);
+  if (handle == INVALID_HANDLE_VALUE) {
+    return GetLastError();
+  }
+
+  *handle_ptr = handle;
+  return 0;
+}
+
+
+int uv__stdio_create(uv_loop_t* loop,
+                     const uv_process_options_t* options,
+                     BYTE** buffer_ptr) {
+  BYTE* buffer;
+  int count, i;
+  int err;
+
+  count = options->stdio_count;
+
+  if (count < 0 || count > 255) {
+    /* Only support FDs 0-255 */
+    return ERROR_NOT_SUPPORTED;
+  } else if (count < 3) {
+    /* There should always be at least 3 stdio handles. */
+    count = 3;
+  }
+
+  /* Allocate the child stdio buffer */
+  buffer = (BYTE*) uv__malloc(CHILD_STDIO_SIZE(count));
+  if (buffer == NULL) {
+    return ERROR_OUTOFMEMORY;
+  }
+
+  /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can clean
+   * up on failure. */
+  CHILD_STDIO_COUNT(buffer) = count;
+  for (i = 0; i < count; i++) {
+    CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
+    CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
+  }
+
+  for (i = 0; i < count; i++) {
+    uv_stdio_container_t fdopt;
+    if (i < options->stdio_count) {
+      fdopt = options->stdio[i];
+    } else {
+      fdopt.flags = UV_IGNORE;
+    }
+
+    switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
+            UV_INHERIT_STREAM)) {
+      case UV_IGNORE:
+        /* Starting a process with no stdin/stout/stderr can confuse it. So no
+         * matter what the user specified, we make sure the first three FDs are
+         * always open in their typical modes, e. g. stdin be readable and
+         * stdout/err should be writable. For FDs > 2, don't do anything - all
+         * handles in the stdio buffer are initialized with.
+         * INVALID_HANDLE_VALUE, which should be okay. */
+        if (i <= 2) {
+          DWORD access = (i == 0) ? FILE_GENERIC_READ :
+                                    FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
+
+          err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
+                                      access);
+          if (err)
+            goto error;
+
+          CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+        }
+        break;
+
+      case UV_CREATE_PIPE: {
+        /* Create a pair of two connected pipe ends; one end is turned into an
+         * uv_pipe_t for use by the parent. The other one is given to the
+         * child. */
+        uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream;
+        HANDLE child_pipe = INVALID_HANDLE_VALUE;
+
+        /* Create a new, connected pipe pair. stdio[i]. stream should point to
+         * an uninitialized, but not connected pipe handle. */
+        assert(fdopt.data.stream->type == UV_NAMED_PIPE);
+        assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION));
+        assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER));
+
+        err = uv__create_stdio_pipe_pair(loop,
+                                         parent_pipe,
+                                         &child_pipe,
+                                         fdopt.flags);
+        if (err)
+          goto error;
+
+        CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
+        CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
+        break;
+      }
+
+      case UV_INHERIT_FD: {
+        /* Inherit a raw FD. */
+        HANDLE child_handle;
+
+        /* Make an inheritable duplicate of the handle. */
+        err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle);
+        if (err) {
+          /* If fdopt. data. fd is not valid and fd <= 2, then ignore the
+           * error. */
+          if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
+            CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
+            break;
+          }
+          goto error;
+        }
+
+        /* Figure out what the type is. */
+        switch (GetFileType(child_handle)) {
+          case FILE_TYPE_DISK:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
+            break;
+
+          case FILE_TYPE_PIPE:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
+            break;
+
+          case FILE_TYPE_CHAR:
+          case FILE_TYPE_REMOTE:
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+            break;
+
+          case FILE_TYPE_UNKNOWN:
+            if (GetLastError() != 0) {
+              err = GetLastError();
+              CloseHandle(child_handle);
+              goto error;
+            }
+            CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
+            break;
+
+          default:
+            assert(0);
+            return -1;
+        }
+
+        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
+        break;
+      }
+
+      case UV_INHERIT_STREAM: {
+        /* Use an existing stream as the stdio handle for the child. */
+        HANDLE stream_handle, child_handle;
+        unsigned char crt_flags;
+        uv_stream_t* stream = fdopt.data.stream;
+
+        /* Leech the handle out of the stream. */
+        if (stream->type == UV_TTY) {
+          stream_handle = ((uv_tty_t*) stream)->handle;
+          crt_flags = FOPEN | FDEV;
+        } else if (stream->type == UV_NAMED_PIPE &&
+                   stream->flags & UV_HANDLE_CONNECTION) {
+          stream_handle = ((uv_pipe_t*) stream)->handle;
+          crt_flags = FOPEN | FPIPE;
+        } else {
+          stream_handle = INVALID_HANDLE_VALUE;
+          crt_flags = 0;
+        }
+
+        if (stream_handle == NULL ||
+            stream_handle == INVALID_HANDLE_VALUE) {
+          /* The handle is already closed, or not yet created, or the stream
+           * type is not supported. */
+          err = ERROR_NOT_SUPPORTED;
+          goto error;
+        }
+
+        /* Make an inheritable copy of the handle. */
+        err = uv__duplicate_handle(loop, stream_handle, &child_handle);
+        if (err)
+          goto error;
+
+        CHILD_STDIO_HANDLE(buffer, i) = child_handle;
+        CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
+        break;
+      }
+
+      default:
+        assert(0);
+        return -1;
+    }
+  }
+
+  *buffer_ptr  = buffer;
+  return 0;
+
+ error:
+  uv__stdio_destroy(buffer);
+  return err;
+}
+
+
+void uv__stdio_destroy(BYTE* buffer) {
+  int i, count;
+
+  count = CHILD_STDIO_COUNT(buffer);
+  for (i = 0; i < count; i++) {
+    HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
+    if (handle != INVALID_HANDLE_VALUE) {
+      CloseHandle(handle);
+    }
+  }
+
+  uv__free(buffer);
+}
+
+
+void uv__stdio_noinherit(BYTE* buffer) {
+  int i, count;
+
+  count = CHILD_STDIO_COUNT(buffer);
+  for (i = 0; i < count; i++) {
+    HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
+    if (handle != INVALID_HANDLE_VALUE) {
+      SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
+    }
+  }
+}
+
+
+int uv__stdio_verify(BYTE* buffer, WORD size) {
+  unsigned int count;
+
+  /* Check the buffer pointer. */
+  if (buffer == NULL)
+    return 0;
+
+  /* Verify that the buffer is at least big enough to hold the count. */
+  if (size < CHILD_STDIO_SIZE(0))
+    return 0;
+
+  /* Verify if the count is within range. */
+  count = CHILD_STDIO_COUNT(buffer);
+  if (count > 256)
+    return 0;
+
+  /* Verify that the buffer size is big enough to hold info for N FDs. */
+  if (size < CHILD_STDIO_SIZE(count))
+    return 0;
+
+  return 1;
+}
+
+
+WORD uv__stdio_size(BYTE* buffer) {
+  return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)));
+}
+
+
+HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
+  return CHILD_STDIO_HANDLE(buffer, fd);
+}
diff --git a/third_party/libuv/src/win/process.c b/third_party/libuv/src/win/process.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/process.c
@@ -0,0 +1,1281 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <limits.h>
+#include <wchar.h>
+#include <malloc.h>    /* alloca */
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+#define SIGKILL         9
+
+
+typedef struct env_var {
+  const WCHAR* const wide;
+  const WCHAR* const wide_eq;
+  const size_t len; /* including null or '=' */
+} env_var_t;
+
+#define E_V(str) { L##str, L##str L"=", sizeof(str) }
+
+static const env_var_t required_vars[] = { /* keep me sorted */
+  E_V("HOMEDRIVE"),
+  E_V("HOMEPATH"),
+  E_V("LOGONSERVER"),
+  E_V("PATH"),
+  E_V("SYSTEMDRIVE"),
+  E_V("SYSTEMROOT"),
+  E_V("TEMP"),
+  E_V("USERDOMAIN"),
+  E_V("USERNAME"),
+  E_V("USERPROFILE"),
+  E_V("WINDIR"),
+};
+
+
+static HANDLE uv_global_job_handle_;
+static uv_once_t uv_global_job_handle_init_guard_ = UV_ONCE_INIT;
+
+
+static void uv__init_global_job_handle(void) {
+  /* Create a job object and set it up to kill all contained processes when
+   * it's closed. Since this handle is made non-inheritable and we're not
+   * giving it to anyone, we're the only process holding a reference to it.
+   * That means that if this process exits it is closed and all the processes
+   * it contains are killed. All processes created with uv_spawn that are not
+   * spawned with the UV_PROCESS_DETACHED flag are assigned to this job.
+   *
+   * We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the
+   * processes that we explicitly add are affected, and *their* subprocesses
+   * are not. This ensures that our child processes are not limited in their
+   * ability to use job control on Windows versions that don't deal with
+   * nested jobs (prior to Windows 8 / Server 2012). It also lets our child
+   * processes created detached processes without explicitly breaking away
+   * from job control (which uv_spawn doesn't, either).
+   */
+  SECURITY_ATTRIBUTES attr;
+  JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
+
+  memset(&attr, 0, sizeof attr);
+  attr.bInheritHandle = FALSE;
+
+  memset(&info, 0, sizeof info);
+  info.BasicLimitInformation.LimitFlags =
+      JOB_OBJECT_LIMIT_BREAKAWAY_OK |
+      JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK |
+      JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION |
+      JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+
+  uv_global_job_handle_ = CreateJobObjectW(&attr, NULL);
+  if (uv_global_job_handle_ == NULL)
+    uv_fatal_error(GetLastError(), "CreateJobObjectW");
+
+  if (!SetInformationJobObject(uv_global_job_handle_,
+                               JobObjectExtendedLimitInformation,
+                               &info,
+                               sizeof info))
+    uv_fatal_error(GetLastError(), "SetInformationJobObject");
+}
+
+
+static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
+  int ws_len, r;
+  WCHAR* ws;
+
+  ws_len = MultiByteToWideChar(CP_UTF8,
+                               0,
+                               s,
+                               -1,
+                               NULL,
+                               0);
+  if (ws_len <= 0) {
+    return GetLastError();
+  }
+
+  ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR));
+  if (ws == NULL) {
+    return ERROR_OUTOFMEMORY;
+  }
+
+  r = MultiByteToWideChar(CP_UTF8,
+                          0,
+                          s,
+                          -1,
+                          ws,
+                          ws_len);
+  assert(r == ws_len);
+
+  *ws_ptr = ws;
+  return 0;
+}
+
+
+static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
+  handle->exit_cb = NULL;
+  handle->pid = 0;
+  handle->exit_signal = 0;
+  handle->wait_handle = INVALID_HANDLE_VALUE;
+  handle->process_handle = INVALID_HANDLE_VALUE;
+  handle->child_stdio_buffer = NULL;
+  handle->exit_cb_pending = 0;
+
+  UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT);
+  handle->exit_req.data = handle;
+}
+
+
+/*
+ * Path search functions
+ */
+
+/*
+ * Helper function for search_path
+ */
+static WCHAR* search_path_join_test(const WCHAR* dir,
+                                    size_t dir_len,
+                                    const WCHAR* name,
+                                    size_t name_len,
+                                    const WCHAR* ext,
+                                    size_t ext_len,
+                                    const WCHAR* cwd,
+                                    size_t cwd_len) {
+  WCHAR *result, *result_pos;
+  DWORD attrs;
+  if (dir_len > 2 && dir[0] == L'\\' && dir[1] == L'\\') {
+    /* It's a UNC path so ignore cwd */
+    cwd_len = 0;
+  } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
+    /* It's a full path without drive letter, use cwd's drive letter only */
+    cwd_len = 2;
+  } else if (dir_len >= 2 && dir[1] == L':' &&
+      (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) {
+    /* It's a relative path with drive letter (ext.g. D:../some/file)
+     * Replace drive letter in dir by full cwd if it points to the same drive,
+     * otherwise use the dir only.
+     */
+    if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) {
+      cwd_len = 0;
+    } else {
+      dir += 2;
+      dir_len -= 2;
+    }
+  } else if (dir_len > 2 && dir[1] == L':') {
+    /* It's an absolute path with drive letter
+     * Don't use the cwd at all
+     */
+    cwd_len = 0;
+  }
+
+  /* Allocate buffer for output */
+  result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) *
+      (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1));
+
+  /* Copy cwd */
+  wcsncpy(result_pos, cwd, cwd_len);
+  result_pos += cwd_len;
+
+  /* Add a path separator if cwd didn't end with one */
+  if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
+    result_pos[0] = L'\\';
+    result_pos++;
+  }
+
+  /* Copy dir */
+  wcsncpy(result_pos, dir, dir_len);
+  result_pos += dir_len;
+
+  /* Add a separator if the dir didn't end with one */
+  if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
+    result_pos[0] = L'\\';
+    result_pos++;
+  }
+
+  /* Copy filename */
+  wcsncpy(result_pos, name, name_len);
+  result_pos += name_len;
+
+  if (ext_len) {
+    /* Add a dot if the filename didn't end with one */
+    if (name_len && result_pos[-1] != '.') {
+      result_pos[0] = L'.';
+      result_pos++;
+    }
+
+    /* Copy extension */
+    wcsncpy(result_pos, ext, ext_len);
+    result_pos += ext_len;
+  }
+
+  /* Null terminator */
+  result_pos[0] = L'\0';
+
+  attrs = GetFileAttributesW(result);
+
+  if (attrs != INVALID_FILE_ATTRIBUTES &&
+      !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
+    return result;
+  }
+
+  uv__free(result);
+  return NULL;
+}
+
+
+/*
+ * Helper function for search_path
+ */
+static WCHAR* path_search_walk_ext(const WCHAR *dir,
+                                   size_t dir_len,
+                                   const WCHAR *name,
+                                   size_t name_len,
+                                   WCHAR *cwd,
+                                   size_t cwd_len,
+                                   int name_has_ext) {
+  WCHAR* result;
+
+  /* If the name itself has a nonempty extension, try this extension first */
+  if (name_has_ext) {
+    result = search_path_join_test(dir, dir_len,
+                                   name, name_len,
+                                   L"", 0,
+                                   cwd, cwd_len);
+    if (result != NULL) {
+      return result;
+    }
+  }
+
+  /* Try .com extension */
+  result = search_path_join_test(dir, dir_len,
+                                 name, name_len,
+                                 L"com", 3,
+                                 cwd, cwd_len);
+  if (result != NULL) {
+    return result;
+  }
+
+  /* Try .exe extension */
+  result = search_path_join_test(dir, dir_len,
+                                 name, name_len,
+                                 L"exe", 3,
+                                 cwd, cwd_len);
+  if (result != NULL) {
+    return result;
+  }
+
+  return NULL;
+}
+
+
+/*
+ * search_path searches the system path for an executable filename -
+ * the windows API doesn't provide this as a standalone function nor as an
+ * option to CreateProcess.
+ *
+ * It tries to return an absolute filename.
+ *
+ * Furthermore, it tries to follow the semantics that cmd.exe, with this
+ * exception that PATHEXT environment variable isn't used. Since CreateProcess
+ * can start only .com and .exe files, only those extensions are tried. This
+ * behavior equals that of msvcrt's spawn functions.
+ *
+ * - Do not search the path if the filename already contains a path (either
+ *   relative or absolute).
+ *
+ * - If there's really only a filename, check the current directory for file,
+ *   then search all path directories.
+ *
+ * - If filename specified has *any* extension, search for the file with the
+ *   specified extension first.
+ *
+ * - If the literal filename is not found in a directory, try *appending*
+ *   (not replacing) .com first and then .exe.
+ *
+ * - The path variable may contain relative paths; relative paths are relative
+ *   to the cwd.
+ *
+ * - Directories in path may or may not end with a trailing backslash.
+ *
+ * - CMD does not trim leading/trailing whitespace from path/pathex entries
+ *   nor from the environment variables as a whole.
+ *
+ * - When cmd.exe cannot read a directory, it will just skip it and go on
+ *   searching. However, unlike posix-y systems, it will happily try to run a
+ *   file that is not readable/executable; if the spawn fails it will not
+ *   continue searching.
+ *
+ * UNC path support: we are dealing with UNC paths in both the path and the
+ * filename. This is a deviation from what cmd.exe does (it does not let you
+ * start a program by specifying an UNC path on the command line) but this is
+ * really a pointless restriction.
+ *
+ */
+static WCHAR* search_path(const WCHAR *file,
+                            WCHAR *cwd,
+                            const WCHAR *path) {
+  int file_has_dir;
+  WCHAR* result = NULL;
+  WCHAR *file_name_start;
+  WCHAR *dot;
+  const WCHAR *dir_start, *dir_end, *dir_path;
+  size_t dir_len;
+  int name_has_ext;
+
+  size_t file_len = wcslen(file);
+  size_t cwd_len = wcslen(cwd);
+
+  /* If the caller supplies an empty filename,
+   * we're not gonna return c:\windows\.exe -- GFY!
+   */
+  if (file_len == 0
+      || (file_len == 1 && file[0] == L'.')) {
+    return NULL;
+  }
+
+  /* Find the start of the filename so we can split the directory from the
+   * name. */
+  for (file_name_start = (WCHAR*)file + file_len;
+       file_name_start > file
+           && file_name_start[-1] != L'\\'
+           && file_name_start[-1] != L'/'
+           && file_name_start[-1] != L':';
+       file_name_start--);
+
+  file_has_dir = file_name_start != file;
+
+  /* Check if the filename includes an extension */
+  dot = wcschr(file_name_start, L'.');
+  name_has_ext = (dot != NULL && dot[1] != L'\0');
+
+  if (file_has_dir) {
+    /* The file has a path inside, don't use path */
+    result = path_search_walk_ext(
+        file, file_name_start - file,
+        file_name_start, file_len - (file_name_start - file),
+        cwd, cwd_len,
+        name_has_ext);
+
+  } else {
+    dir_end = path;
+
+    /* The file is really only a name; look in cwd first, then scan path */
+    result = path_search_walk_ext(L"", 0,
+                                  file, file_len,
+                                  cwd, cwd_len,
+                                  name_has_ext);
+
+    while (result == NULL) {
+      if (*dir_end == L'\0') {
+        break;
+      }
+
+      /* Skip the separator that dir_end now points to */
+      if (dir_end != path || *path == L';') {
+        dir_end++;
+      }
+
+      /* Next slice starts just after where the previous one ended */
+      dir_start = dir_end;
+
+      /* If path is quoted, find quote end */
+      if (*dir_start == L'"' || *dir_start == L'\'') {
+        dir_end = wcschr(dir_start + 1, *dir_start);
+        if (dir_end == NULL) {
+          dir_end = wcschr(dir_start, L'\0');
+        }
+      }
+      /* Slice until the next ; or \0 is found */
+      dir_end = wcschr(dir_end, L';');
+      if (dir_end == NULL) {
+        dir_end = wcschr(dir_start, L'\0');
+      }
+
+      /* If the slice is zero-length, don't bother */
+      if (dir_end - dir_start == 0) {
+        continue;
+      }
+
+      dir_path = dir_start;
+      dir_len = dir_end - dir_start;
+
+      /* Adjust if the path is quoted. */
+      if (dir_path[0] == '"' || dir_path[0] == '\'') {
+        ++dir_path;
+        --dir_len;
+      }
+
+      if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') {
+        --dir_len;
+      }
+
+      result = path_search_walk_ext(dir_path, dir_len,
+                                    file, file_len,
+                                    cwd, cwd_len,
+                                    name_has_ext);
+    }
+  }
+
+  return result;
+}
+
+
+/*
+ * Quotes command line arguments
+ * Returns a pointer to the end (next char to be written) of the buffer
+ */
+WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {
+  size_t len = wcslen(source);
+  size_t i;
+  int quote_hit;
+  WCHAR* start;
+
+  if (len == 0) {
+    /* Need double quotation for empty argument */
+    *(target++) = L'"';
+    *(target++) = L'"';
+    return target;
+  }
+
+  if (NULL == wcspbrk(source, L" \t\"")) {
+    /* No quotation needed */
+    wcsncpy(target, source, len);
+    target += len;
+    return target;
+  }
+
+  if (NULL == wcspbrk(source, L"\"\\")) {
+    /*
+     * No embedded double quotes or backlashes, so I can just wrap
+     * quote marks around the whole thing.
+     */
+    *(target++) = L'"';
+    wcsncpy(target, source, len);
+    target += len;
+    *(target++) = L'"';
+    return target;
+  }
+
+  /*
+   * Expected input/output:
+   *   input : hello"world
+   *   output: "hello\"world"
+   *   input : hello""world
+   *   output: "hello\"\"world"
+   *   input : hello\world
+   *   output: hello\world
+   *   input : hello\\world
+   *   output: hello\\world
+   *   input : hello\"world
+   *   output: "hello\\\"world"
+   *   input : hello\\"world
+   *   output: "hello\\\\\"world"
+   *   input : hello world\
+   *   output: "hello world\\"
+   */
+
+  *(target++) = L'"';
+  start = target;
+  quote_hit = 1;
+
+  for (i = len; i > 0; --i) {
+    *(target++) = source[i - 1];
+
+    if (quote_hit && source[i - 1] == L'\\') {
+      *(target++) = L'\\';
+    } else if(source[i - 1] == L'"') {
+      quote_hit = 1;
+      *(target++) = L'\\';
+    } else {
+      quote_hit = 0;
+    }
+  }
+  target[0] = L'\0';
+  wcsrev(start);
+  *(target++) = L'"';
+  return target;
+}
+
+
+int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) {
+  char** arg;
+  WCHAR* dst = NULL;
+  WCHAR* temp_buffer = NULL;
+  size_t dst_len = 0;
+  size_t temp_buffer_len = 0;
+  WCHAR* pos;
+  int arg_count = 0;
+  int err = 0;
+
+  /* Count the required size. */
+  for (arg = args; *arg; arg++) {
+    DWORD arg_len;
+
+    arg_len = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  *arg,
+                                  -1,
+                                  NULL,
+                                  0);
+    if (arg_len == 0) {
+      return GetLastError();
+    }
+
+    dst_len += arg_len;
+
+    if (arg_len > temp_buffer_len)
+      temp_buffer_len = arg_len;
+
+    arg_count++;
+  }
+
+  /* Adjust for potential quotes. Also assume the worst-case scenario that
+   * every character needs escaping, so we need twice as much space. */
+  dst_len = dst_len * 2 + arg_count * 2;
+
+  /* Allocate buffer for the final command line. */
+  dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR));
+  if (dst == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  /* Allocate temporary working buffer. */
+  temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR));
+  if (temp_buffer == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  pos = dst;
+  for (arg = args; *arg; arg++) {
+    DWORD arg_len;
+
+    /* Convert argument to wide char. */
+    arg_len = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  *arg,
+                                  -1,
+                                  temp_buffer,
+                                  (int) (dst + dst_len - pos));
+    if (arg_len == 0) {
+      err = GetLastError();
+      goto error;
+    }
+
+    if (verbatim_arguments) {
+      /* Copy verbatim. */
+      wcscpy(pos, temp_buffer);
+      pos += arg_len - 1;
+    } else {
+      /* Quote/escape, if needed. */
+      pos = quote_cmd_arg(temp_buffer, pos);
+    }
+
+    *pos++ = *(arg + 1) ? L' ' : L'\0';
+  }
+
+  uv__free(temp_buffer);
+
+  *dst_ptr = dst;
+  return 0;
+
+error:
+  uv__free(dst);
+  uv__free(temp_buffer);
+  return err;
+}
+
+
+int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
+  wchar_t* a_eq;
+  wchar_t* b_eq;
+  wchar_t* A;
+  wchar_t* B;
+  int nb;
+  int r;
+
+  if (na < 0) {
+    a_eq = wcschr(a, L'=');
+    assert(a_eq);
+    na = (int)(long)(a_eq - a);
+  } else {
+    na--;
+  }
+  b_eq = wcschr(b, L'=');
+  assert(b_eq);
+  nb = b_eq - b;
+
+  A = alloca((na+1) * sizeof(wchar_t));
+  B = alloca((nb+1) * sizeof(wchar_t));
+
+  r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
+  assert(r==na);
+  A[na] = L'\0';
+  r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
+  assert(r==nb);
+  B[nb] = L'\0';
+
+  while (1) {
+    wchar_t AA = *A++;
+    wchar_t BB = *B++;
+    if (AA < BB) {
+      return -1;
+    } else if (AA > BB) {
+      return 1;
+    } else if (!AA && !BB) {
+      return 0;
+    }
+  }
+}
+
+
+static int qsort_wcscmp(const void *a, const void *b) {
+  wchar_t* astr = *(wchar_t* const*)a;
+  wchar_t* bstr = *(wchar_t* const*)b;
+  return env_strncmp(astr, -1, bstr);
+}
+
+
+/*
+ * The way windows takes environment variables is different than what C does;
+ * Windows wants a contiguous block of null-terminated strings, terminated
+ * with an additional null.
+ *
+ * Windows has a few "essential" environment variables. winsock will fail
+ * to initialize if SYSTEMROOT is not defined; some APIs make reference to
+ * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that
+ * these get defined if the input environment block does not contain any
+ * values for them.
+ *
+ * Also add variables known to Cygwin to be required for correct
+ * subprocess operation in many cases:
+ * https://github.com/Alexpux/Cygwin/blob/b266b04fbbd3a595f02ea149e4306d3ab9b1fe3d/winsup/cygwin/environ.cc#L955
+ *
+ */
+int make_program_env(char* env_block[], WCHAR** dst_ptr) {
+  WCHAR* dst;
+  WCHAR* ptr;
+  char** env;
+  size_t env_len = 0;
+  int len;
+  size_t i;
+  DWORD var_size;
+  size_t env_block_count = 1; /* 1 for null-terminator */
+  WCHAR* dst_copy;
+  WCHAR** ptr_copy;
+  WCHAR** env_copy;
+  DWORD required_vars_value_len[ARRAY_SIZE(required_vars)];
+
+  /* first pass: determine size in UTF-16 */
+  for (env = env_block; *env; env++) {
+    int len;
+    if (strchr(*env, '=')) {
+      len = MultiByteToWideChar(CP_UTF8,
+                                0,
+                                *env,
+                                -1,
+                                NULL,
+                                0);
+      if (len <= 0) {
+        return GetLastError();
+      }
+      env_len += len;
+      env_block_count++;
+    }
+  }
+
+  /* second pass: copy to UTF-16 environment block */
+  dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR));
+  if (dst_copy == NULL && env_len > 0) {
+    return ERROR_OUTOFMEMORY;
+  }
+  env_copy = alloca(env_block_count * sizeof(WCHAR*));
+
+  ptr = dst_copy;
+  ptr_copy = env_copy;
+  for (env = env_block; *env; env++) {
+    if (strchr(*env, '=')) {
+      len = MultiByteToWideChar(CP_UTF8,
+                                0,
+                                *env,
+                                -1,
+                                ptr,
+                                (int) (env_len - (ptr - dst_copy)));
+      if (len <= 0) {
+        DWORD err = GetLastError();
+        uv__free(dst_copy);
+        return err;
+      }
+      *ptr_copy++ = ptr;
+      ptr += len;
+    }
+  }
+  *ptr_copy = NULL;
+  assert(env_len == 0 || env_len == (size_t) (ptr - dst_copy));
+
+  /* sort our (UTF-16) copy */
+  qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp);
+
+  /* third pass: check for required variables */
+  for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) {
+    int cmp;
+    if (!*ptr_copy) {
+      cmp = -1;
+    } else {
+      cmp = env_strncmp(required_vars[i].wide_eq,
+                       required_vars[i].len,
+                        *ptr_copy);
+    }
+    if (cmp < 0) {
+      /* missing required var */
+      var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0);
+      required_vars_value_len[i] = var_size;
+      if (var_size != 0) {
+        env_len += required_vars[i].len;
+        env_len += var_size;
+      }
+      i++;
+    } else {
+      ptr_copy++;
+      if (cmp == 0)
+        i++;
+    }
+  }
+
+  /* final pass: copy, in sort order, and inserting required variables */
+  dst = uv__malloc((1+env_len) * sizeof(WCHAR));
+  if (!dst) {
+    uv__free(dst_copy);
+    return ERROR_OUTOFMEMORY;
+  }
+
+  for (ptr = dst, ptr_copy = env_copy, i = 0;
+       *ptr_copy || i < ARRAY_SIZE(required_vars);
+       ptr += len) {
+    int cmp;
+    if (i >= ARRAY_SIZE(required_vars)) {
+      cmp = 1;
+    } else if (!*ptr_copy) {
+      cmp = -1;
+    } else {
+      cmp = env_strncmp(required_vars[i].wide_eq,
+                        required_vars[i].len,
+                        *ptr_copy);
+    }
+    if (cmp < 0) {
+      /* missing required var */
+      len = required_vars_value_len[i];
+      if (len) {
+        wcscpy(ptr, required_vars[i].wide_eq);
+        ptr += required_vars[i].len;
+        var_size = GetEnvironmentVariableW(required_vars[i].wide,
+                                           ptr,
+                                           (int) (env_len - (ptr - dst)));
+        if (var_size != (DWORD) (len - 1)) { /* TODO: handle race condition? */
+          uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
+        }
+      }
+      i++;
+    } else {
+      /* copy var from env_block */
+      len = wcslen(*ptr_copy) + 1;
+      wmemcpy(ptr, *ptr_copy, len);
+      ptr_copy++;
+      if (cmp == 0)
+        i++;
+    }
+  }
+
+  /* Terminate with an extra NULL. */
+  assert(env_len == (size_t) (ptr - dst));
+  *ptr = L'\0';
+
+  uv__free(dst_copy);
+  *dst_ptr = dst;
+  return 0;
+}
+
+/*
+ * Attempt to find the value of the PATH environment variable in the child's
+ * preprocessed environment.
+ *
+ * If found, a pointer into `env` is returned. If not found, NULL is returned.
+ */
+static WCHAR* find_path(WCHAR *env) {
+  for (; env != NULL && *env != 0; env += wcslen(env) + 1) {
+    if ((env[0] == L'P' || env[0] == L'p') &&
+        (env[1] == L'A' || env[1] == L'a') &&
+        (env[2] == L'T' || env[2] == L't') &&
+        (env[3] == L'H' || env[3] == L'h') &&
+        (env[4] == L'=')) {
+      return &env[5];
+    }
+  }
+
+  return NULL;
+}
+
+/*
+ * Called on Windows thread-pool thread to indicate that
+ * a child process has exited.
+ */
+static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
+  uv_process_t* process = (uv_process_t*) data;
+  uv_loop_t* loop = process->loop;
+
+  assert(didTimeout == FALSE);
+  assert(process);
+  assert(!process->exit_cb_pending);
+
+  process->exit_cb_pending = 1;
+
+  /* Post completed */
+  POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
+}
+
+
+/* Called on main thread after a child process has exited. */
+void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
+  int64_t exit_code;
+  DWORD status;
+
+  assert(handle->exit_cb_pending);
+  handle->exit_cb_pending = 0;
+
+  /* If we're closing, don't call the exit callback. Just schedule a close
+   * callback now. */
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+    return;
+  }
+
+  /* Unregister from process notification. */
+  if (handle->wait_handle != INVALID_HANDLE_VALUE) {
+    UnregisterWait(handle->wait_handle);
+    handle->wait_handle = INVALID_HANDLE_VALUE;
+  }
+
+  /* Set the handle to inactive: no callbacks will be made after the exit
+   * callback. */
+  uv__handle_stop(handle);
+
+  if (GetExitCodeProcess(handle->process_handle, &status)) {
+    exit_code = status;
+  } else {
+    /* Unable to obtain the exit code. This should never happen. */
+    exit_code = uv_translate_sys_error(GetLastError());
+  }
+
+  /* Fire the exit callback. */
+  if (handle->exit_cb) {
+    handle->exit_cb(handle, exit_code, handle->exit_signal);
+  }
+}
+
+
+void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
+  uv__handle_closing(handle);
+
+  if (handle->wait_handle != INVALID_HANDLE_VALUE) {
+    /* This blocks until either the wait was cancelled, or the callback has
+     * completed. */
+    BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE);
+    if (!r) {
+      /* This should never happen, and if it happens, we can't recover... */
+      uv_fatal_error(GetLastError(), "UnregisterWaitEx");
+    }
+
+    handle->wait_handle = INVALID_HANDLE_VALUE;
+  }
+
+  if (!handle->exit_cb_pending) {
+    uv_want_endgame(loop, (uv_handle_t*)handle);
+  }
+}
+
+
+void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) {
+  assert(!handle->exit_cb_pending);
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+  /* Clean-up the process handle. */
+  CloseHandle(handle->process_handle);
+
+  uv__handle_close(handle);
+}
+
+
+int uv_spawn(uv_loop_t* loop,
+             uv_process_t* process,
+             const uv_process_options_t* options) {
+  int i;
+  int err = 0;
+  WCHAR* path = NULL, *alloc_path = NULL;
+  BOOL result;
+  WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
+         *env = NULL, *cwd = NULL;
+  STARTUPINFOW startup;
+  PROCESS_INFORMATION info;
+  DWORD process_flags;
+
+  uv_process_init(loop, process);
+  process->exit_cb = options->exit_cb;
+
+  if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
+    return UV_ENOTSUP;
+  }
+
+  if (options->file == NULL ||
+      options->args == NULL) {
+    return UV_EINVAL;
+  }
+
+  assert(options->file != NULL);
+  assert(!(options->flags & ~(UV_PROCESS_DETACHED |
+                              UV_PROCESS_SETGID |
+                              UV_PROCESS_SETUID |
+                              UV_PROCESS_WINDOWS_HIDE |
+                              UV_PROCESS_WINDOWS_HIDE_CONSOLE |
+                              UV_PROCESS_WINDOWS_HIDE_GUI |
+                              UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
+
+  err = uv_utf8_to_utf16_alloc(options->file, &application);
+  if (err)
+    goto done;
+
+  err = make_program_args(
+      options->args,
+      options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
+      &arguments);
+  if (err)
+    goto done;
+
+  if (options->env) {
+     err = make_program_env(options->env, &env);
+     if (err)
+       goto done;
+  }
+
+  if (options->cwd) {
+    /* Explicit cwd */
+    err = uv_utf8_to_utf16_alloc(options->cwd, &cwd);
+    if (err)
+      goto done;
+
+  } else {
+    /* Inherit cwd */
+    DWORD cwd_len, r;
+
+    cwd_len = GetCurrentDirectoryW(0, NULL);
+    if (!cwd_len) {
+      err = GetLastError();
+      goto done;
+    }
+
+    cwd = (WCHAR*) uv__malloc(cwd_len * sizeof(WCHAR));
+    if (cwd == NULL) {
+      err = ERROR_OUTOFMEMORY;
+      goto done;
+    }
+
+    r = GetCurrentDirectoryW(cwd_len, cwd);
+    if (r == 0 || r >= cwd_len) {
+      err = GetLastError();
+      goto done;
+    }
+  }
+
+  /* Get PATH environment variable. */
+  path = find_path(env);
+  if (path == NULL) {
+    DWORD path_len, r;
+
+    path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
+    if (path_len == 0) {
+      err = GetLastError();
+      goto done;
+    }
+
+    alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR));
+    if (alloc_path == NULL) {
+      err = ERROR_OUTOFMEMORY;
+      goto done;
+    }
+    path = alloc_path;
+
+    r = GetEnvironmentVariableW(L"PATH", path, path_len);
+    if (r == 0 || r >= path_len) {
+      err = GetLastError();
+      goto done;
+    }
+  }
+
+  err = uv__stdio_create(loop, options, &process->child_stdio_buffer);
+  if (err)
+    goto done;
+
+  application_path = search_path(application,
+                                 cwd,
+                                 path);
+  if (application_path == NULL) {
+    /* Not found. */
+    err = ERROR_FILE_NOT_FOUND;
+    goto done;
+  }
+
+  startup.cb = sizeof(startup);
+  startup.lpReserved = NULL;
+  startup.lpDesktop = NULL;
+  startup.lpTitle = NULL;
+  startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+
+  startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
+  startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;
+
+  startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
+  startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
+  startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);
+
+  process_flags = CREATE_UNICODE_ENVIRONMENT;
+
+  if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) ||
+      (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
+    /* Avoid creating console window if stdio is not inherited. */
+    for (i = 0; i < options->stdio_count; i++) {
+      if (options->stdio[i].flags & UV_INHERIT_FD)
+        break;
+      if (i == options->stdio_count - 1)
+        process_flags |= CREATE_NO_WINDOW;
+    }
+  }
+  if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) ||
+      (options->flags & UV_PROCESS_WINDOWS_HIDE)) {
+    /* Use SW_HIDE to avoid any potential process window. */
+    startup.wShowWindow = SW_HIDE;
+  } else {
+    startup.wShowWindow = SW_SHOWDEFAULT;
+  }
+
+  if (options->flags & UV_PROCESS_DETACHED) {
+    /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That
+     * means that libuv might not let you create a fully daemonized process
+     * when run under job control. However the type of job control that libuv
+     * itself creates doesn't trickle down to subprocesses so they can still
+     * daemonize.
+     *
+     * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the
+     * CreateProcess call fail if we're under job control that doesn't allow
+     * breakaway.
+     */
+    process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
+  }
+
+  if (!CreateProcessW(application_path,
+                     arguments,
+                     NULL,
+                     NULL,
+                     1,
+                     process_flags,
+                     env,
+                     cwd,
+                     &startup,
+                     &info)) {
+    /* CreateProcessW failed. */
+    err = GetLastError();
+    goto done;
+  }
+
+  /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
+
+  process->process_handle = info.hProcess;
+  process->pid = info.dwProcessId;
+
+  /* If the process isn't spawned as detached, assign to the global job object
+   * so windows will kill it when the parent process dies. */
+  if (!(options->flags & UV_PROCESS_DETACHED)) {
+    uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle);
+
+    if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) {
+      /* AssignProcessToJobObject might fail if this process is under job
+       * control and the job doesn't have the
+       * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version
+       * that doesn't support nested jobs.
+       *
+       * When that happens we just swallow the error and continue without
+       * establishing a kill-child-on-parent-exit relationship, otherwise
+       * there would be no way for libuv applications run under job control
+       * to spawn processes at all.
+       */
+      DWORD err = GetLastError();
+      if (err != ERROR_ACCESS_DENIED)
+        uv_fatal_error(err, "AssignProcessToJobObject");
+    }
+  }
+
+  /* Set IPC pid to all IPC pipes. */
+  for (i = 0; i < options->stdio_count; i++) {
+    const uv_stdio_container_t* fdopt = &options->stdio[i];
+    if (fdopt->flags & UV_CREATE_PIPE &&
+        fdopt->data.stream->type == UV_NAMED_PIPE &&
+        ((uv_pipe_t*) fdopt->data.stream)->ipc) {
+      ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_remote_pid =
+          info.dwProcessId;
+    }
+  }
+
+  /* Setup notifications for when the child process exits. */
+  result = RegisterWaitForSingleObject(&process->wait_handle,
+      process->process_handle, exit_wait_callback, (void*)process, INFINITE,
+      WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
+  if (!result) {
+    uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
+  }
+
+  CloseHandle(info.hThread);
+
+  assert(!err);
+
+  /* Make the handle active. It will remain active until the exit callback is
+   * made or the handle is closed, whichever happens first. */
+  uv__handle_start(process);
+
+  /* Cleanup, whether we succeeded or failed. */
+ done:
+  uv__free(application);
+  uv__free(application_path);
+  uv__free(arguments);
+  uv__free(cwd);
+  uv__free(env);
+  uv__free(alloc_path);
+
+  if (process->child_stdio_buffer != NULL) {
+    /* Clean up child stdio handles. */
+    uv__stdio_destroy(process->child_stdio_buffer);
+    process->child_stdio_buffer = NULL;
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+static int uv__kill(HANDLE process_handle, int signum) {
+  if (signum < 0 || signum >= NSIG) {
+    return UV_EINVAL;
+  }
+
+  switch (signum) {
+    case SIGTERM:
+    case SIGKILL:
+    case SIGINT: {
+      /* Unconditionally terminate the process. On Windows, killed processes
+       * normally return 1. */
+      DWORD status;
+      int err;
+
+      if (TerminateProcess(process_handle, 1))
+        return 0;
+
+      /* If the process already exited before TerminateProcess was called,.
+       * TerminateProcess will fail with ERROR_ACCESS_DENIED. */
+      err = GetLastError();
+      if (err == ERROR_ACCESS_DENIED &&
+          GetExitCodeProcess(process_handle, &status) &&
+          status != STILL_ACTIVE) {
+        return UV_ESRCH;
+      }
+
+      return uv_translate_sys_error(err);
+    }
+
+    case 0: {
+      /* Health check: is the process still alive? */
+      DWORD status;
+
+      if (!GetExitCodeProcess(process_handle, &status))
+        return uv_translate_sys_error(GetLastError());
+
+      if (status != STILL_ACTIVE)
+        return UV_ESRCH;
+
+      return 0;
+    }
+
+    default:
+      /* Unsupported signal. */
+      return UV_ENOSYS;
+  }
+}
+
+
+int uv_process_kill(uv_process_t* process, int signum) {
+  int err;
+
+  if (process->process_handle == INVALID_HANDLE_VALUE) {
+    return UV_EINVAL;
+  }
+
+  err = uv__kill(process->process_handle, signum);
+  if (err) {
+    return err;  /* err is already translated. */
+  }
+
+  process->exit_signal = signum;
+
+  return 0;
+}
+
+
+int uv_kill(int pid, int signum) {
+  int err;
+  HANDLE process_handle;
+
+  if (pid == 0) {
+    process_handle = GetCurrentProcess();
+  } else {
+    process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
+                                 FALSE,
+                                 pid);
+  }
+
+  if (process_handle == NULL) {
+    err = GetLastError();
+    if (err == ERROR_INVALID_PARAMETER) {
+      return UV_ESRCH;
+    } else {
+      return uv_translate_sys_error(err);
+    }
+  }
+
+  err = uv__kill(process_handle, signum);
+  CloseHandle(process_handle);
+
+  return err;  /* err is already translated. */
+}
diff --git a/third_party/libuv/src/win/req-inl.h b/third_party/libuv/src/win/req-inl.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/req-inl.h
@@ -0,0 +1,221 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_REQ_INL_H_
+#define UV_WIN_REQ_INL_H_
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+#define SET_REQ_STATUS(req, status)                                     \
+   (req)->u.io.overlapped.Internal = (ULONG_PTR) (status)
+
+#define SET_REQ_ERROR(req, error)                                       \
+  SET_REQ_STATUS((req), NTSTATUS_FROM_WIN32((error)))
+
+/* Note: used open-coded in UV_REQ_INIT() because of a circular dependency
+ * between src/uv-common.h and src/win/internal.h.
+ */
+#define SET_REQ_SUCCESS(req)                                            \
+  SET_REQ_STATUS((req), STATUS_SUCCESS)
+
+#define GET_REQ_STATUS(req)                                             \
+  ((NTSTATUS) (req)->u.io.overlapped.Internal)
+
+#define REQ_SUCCESS(req)                                                \
+  (NT_SUCCESS(GET_REQ_STATUS((req))))
+
+#define GET_REQ_ERROR(req)                                              \
+  (pRtlNtStatusToDosError(GET_REQ_STATUS((req))))
+
+#define GET_REQ_SOCK_ERROR(req)                                         \
+  (uv_ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
+
+
+#define REGISTER_HANDLE_REQ(loop, handle, req)                          \
+  do {                                                                  \
+    INCREASE_ACTIVE_COUNT((loop), (handle));                            \
+    uv__req_register((loop), (req));                                    \
+  } while (0)
+
+#define UNREGISTER_HANDLE_REQ(loop, handle, req)                        \
+  do {                                                                  \
+    DECREASE_ACTIVE_COUNT((loop), (handle));                            \
+    uv__req_unregister((loop), (req));                                  \
+  } while (0)
+
+
+#define UV_SUCCEEDED_WITHOUT_IOCP(result)                               \
+  ((result) && (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP))
+
+#define UV_SUCCEEDED_WITH_IOCP(result)                                  \
+  ((result) || (GetLastError() == ERROR_IO_PENDING))
+
+
+#define POST_COMPLETION_FOR_REQ(loop, req)                              \
+  if (!PostQueuedCompletionStatus((loop)->iocp,                         \
+                                  0,                                    \
+                                  0,                                    \
+                                  &((req)->u.io.overlapped))) {         \
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");       \
+  }
+
+
+INLINE static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) {
+  return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped);
+}
+
+
+INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
+  req->next_req = NULL;
+  if (loop->pending_reqs_tail) {
+#ifdef _DEBUG
+    /* Ensure the request is not already in the queue, or the queue
+     * will get corrupted.
+     */
+    uv_req_t* current = loop->pending_reqs_tail;
+    do {
+      assert(req != current);
+      current = current->next_req;
+    } while(current != loop->pending_reqs_tail);
+#endif
+
+    req->next_req = loop->pending_reqs_tail->next_req;
+    loop->pending_reqs_tail->next_req = req;
+    loop->pending_reqs_tail = req;
+  } else {
+    req->next_req = req;
+    loop->pending_reqs_tail = req;
+  }
+}
+
+
+#define DELEGATE_STREAM_REQ(loop, req, method, handle_at)                     \
+  do {                                                                        \
+    switch (((uv_handle_t*) (req)->handle_at)->type) {                        \
+      case UV_TCP:                                                            \
+        uv_process_tcp_##method##_req(loop,                                   \
+                                      (uv_tcp_t*) ((req)->handle_at),         \
+                                      req);                                   \
+        break;                                                                \
+                                                                              \
+      case UV_NAMED_PIPE:                                                     \
+        uv_process_pipe_##method##_req(loop,                                  \
+                                       (uv_pipe_t*) ((req)->handle_at),       \
+                                       req);                                  \
+        break;                                                                \
+                                                                              \
+      case UV_TTY:                                                            \
+        uv_process_tty_##method##_req(loop,                                   \
+                                      (uv_tty_t*) ((req)->handle_at),         \
+                                      req);                                   \
+        break;                                                                \
+                                                                              \
+      default:                                                                \
+        assert(0);                                                            \
+    }                                                                         \
+  } while (0)
+
+
+INLINE static int uv_process_reqs(uv_loop_t* loop) {
+  uv_req_t* req;
+  uv_req_t* first;
+  uv_req_t* next;
+
+  if (loop->pending_reqs_tail == NULL)
+    return 0;
+
+  first = loop->pending_reqs_tail->next_req;
+  next = first;
+  loop->pending_reqs_tail = NULL;
+
+  while (next != NULL) {
+    req = next;
+    next = req->next_req != first ? req->next_req : NULL;
+
+    switch (req->type) {
+      case UV_READ:
+        DELEGATE_STREAM_REQ(loop, req, read, data);
+        break;
+
+      case UV_WRITE:
+        DELEGATE_STREAM_REQ(loop, (uv_write_t*) req, write, handle);
+        break;
+
+      case UV_ACCEPT:
+        DELEGATE_STREAM_REQ(loop, req, accept, data);
+        break;
+
+      case UV_CONNECT:
+        DELEGATE_STREAM_REQ(loop, (uv_connect_t*) req, connect, handle);
+        break;
+
+      case UV_SHUTDOWN:
+        /* Tcp shutdown requests don't come here. */
+        assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE);
+        uv_process_pipe_shutdown_req(
+            loop,
+            (uv_pipe_t*) ((uv_shutdown_t*) req)->handle,
+            (uv_shutdown_t*) req);
+        break;
+
+      case UV_UDP_RECV:
+        uv_process_udp_recv_req(loop, (uv_udp_t*) req->data, req);
+        break;
+
+      case UV_UDP_SEND:
+        uv_process_udp_send_req(loop,
+                                ((uv_udp_send_t*) req)->handle,
+                                (uv_udp_send_t*) req);
+        break;
+
+      case UV_WAKEUP:
+        uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req);
+        break;
+
+      case UV_SIGNAL_REQ:
+        uv_process_signal_req(loop, (uv_signal_t*) req->data, req);
+        break;
+
+      case UV_POLL_REQ:
+        uv_process_poll_req(loop, (uv_poll_t*) req->data, req);
+        break;
+
+      case UV_PROCESS_EXIT:
+        uv_process_proc_exit(loop, (uv_process_t*) req->data);
+        break;
+
+      case UV_FS_EVENT_REQ:
+        uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data);
+        break;
+
+      default:
+        assert(0);
+    }
+  }
+
+  return 1;
+}
+
+#endif /* UV_WIN_REQ_INL_H_ */
diff --git a/third_party/libuv/src/win/signal.c b/third_party/libuv/src/win/signal.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/signal.c
@@ -0,0 +1,282 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <signal.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+RB_HEAD(uv_signal_tree_s, uv_signal_s);
+
+static struct uv_signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree);
+static CRITICAL_SECTION uv__signal_lock;
+
+static BOOL WINAPI uv__signal_control_handler(DWORD type);
+
+int uv__signal_start(uv_signal_t* handle,
+                     uv_signal_cb signal_cb,
+                     int signum,
+                     int oneshot);
+
+void uv_signals_init(void) {
+  InitializeCriticalSection(&uv__signal_lock);
+  if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE))
+    abort();
+}
+
+
+void uv__signal_cleanup(void) {
+  /* TODO(bnoordhuis) Undo effects of uv_signal_init()? */
+}
+
+
+static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) {
+  /* Compare signums first so all watchers with the same signnum end up
+   * adjacent. */
+  if (w1->signum < w2->signum) return -1;
+  if (w1->signum > w2->signum) return 1;
+
+  /* Sort by loop pointer, so we can easily look up the first item after
+   * { .signum = x, .loop = NULL }. */
+  if ((uintptr_t) w1->loop < (uintptr_t) w2->loop) return -1;
+  if ((uintptr_t) w1->loop > (uintptr_t) w2->loop) return 1;
+
+  if ((uintptr_t) w1 < (uintptr_t) w2) return -1;
+  if ((uintptr_t) w1 > (uintptr_t) w2) return 1;
+
+  return 0;
+}
+
+
+RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare)
+
+
+/*
+ * Dispatches signal {signum} to all active uv_signal_t watchers in all loops.
+ * Returns 1 if the signal was dispatched to any watcher, or 0 if there were
+ * no active signal watchers observing this signal.
+ */
+int uv__signal_dispatch(int signum) {
+  uv_signal_t lookup;
+  uv_signal_t* handle;
+  int dispatched;
+
+  dispatched = 0;
+
+  EnterCriticalSection(&uv__signal_lock);
+
+  lookup.signum = signum;
+  lookup.loop = NULL;
+
+  for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
+       handle != NULL && handle->signum == signum;
+       handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) {
+    unsigned long previous = InterlockedExchange(
+            (volatile LONG*) &handle->pending_signum, signum);
+
+    if (handle->flags & UV_SIGNAL_ONE_SHOT_DISPATCHED)
+      continue;
+
+    if (!previous) {
+      POST_COMPLETION_FOR_REQ(handle->loop, &handle->signal_req);
+    }
+
+    dispatched = 1;
+    if (handle->flags & UV_SIGNAL_ONE_SHOT)
+      handle->flags |= UV_SIGNAL_ONE_SHOT_DISPATCHED;
+  }
+
+  LeaveCriticalSection(&uv__signal_lock);
+
+  return dispatched;
+}
+
+
+static BOOL WINAPI uv__signal_control_handler(DWORD type) {
+  switch (type) {
+    case CTRL_C_EVENT:
+      return uv__signal_dispatch(SIGINT);
+
+    case CTRL_BREAK_EVENT:
+      return uv__signal_dispatch(SIGBREAK);
+
+    case CTRL_CLOSE_EVENT:
+      if (uv__signal_dispatch(SIGHUP)) {
+        /* Windows will terminate the process after the control handler
+         * returns. After that it will just terminate our process. Therefore
+         * block the signal handler so the main loop has some time to pick up
+         * the signal and do something for a few seconds. */
+        Sleep(INFINITE);
+        return TRUE;
+      }
+      return FALSE;
+
+    case CTRL_LOGOFF_EVENT:
+    case CTRL_SHUTDOWN_EVENT:
+      /* These signals are only sent to services. Services have their own
+       * notification mechanism, so there's no point in handling these. */
+
+    default:
+      /* We don't handle these. */
+      return FALSE;
+  }
+}
+
+
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
+  handle->pending_signum = 0;
+  handle->signum = 0;
+  handle->signal_cb = NULL;
+
+  UV_REQ_INIT(&handle->signal_req, UV_SIGNAL_REQ);
+  handle->signal_req.data = handle;
+
+  return 0;
+}
+
+
+int uv_signal_stop(uv_signal_t* handle) {
+  uv_signal_t* removed_handle;
+
+  /* If the watcher wasn't started, this is a no-op. */
+  if (handle->signum == 0)
+    return 0;
+
+  EnterCriticalSection(&uv__signal_lock);
+
+  removed_handle = RB_REMOVE(uv_signal_tree_s, &uv__signal_tree, handle);
+  assert(removed_handle == handle);
+
+  LeaveCriticalSection(&uv__signal_lock);
+
+  handle->signum = 0;
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
+  return uv__signal_start(handle, signal_cb, signum, 0);
+}
+
+
+int uv_signal_start_oneshot(uv_signal_t* handle,
+                            uv_signal_cb signal_cb,
+                            int signum) {
+  return uv__signal_start(handle, signal_cb, signum, 1);
+}
+
+
+int uv__signal_start(uv_signal_t* handle,
+                            uv_signal_cb signal_cb,
+                            int signum,
+                            int oneshot) {
+  /* Test for invalid signal values. */
+  if (signum <= 0 || signum >= NSIG)
+    return UV_EINVAL;
+
+  /* Short circuit: if the signal watcher is already watching {signum} don't go
+   * through the process of deregistering and registering the handler.
+   * Additionally, this avoids pending signals getting lost in the (small) time
+   * frame that handle->signum == 0. */
+  if (signum == handle->signum) {
+    handle->signal_cb = signal_cb;
+    return 0;
+  }
+
+  /* If the signal handler was already active, stop it first. */
+  if (handle->signum != 0) {
+    int r = uv_signal_stop(handle);
+    /* uv_signal_stop is infallible. */
+    assert(r == 0);
+  }
+
+  EnterCriticalSection(&uv__signal_lock);
+
+  handle->signum = signum;
+  if (oneshot)
+    handle->flags |= UV_SIGNAL_ONE_SHOT;
+
+  RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle);
+
+  LeaveCriticalSection(&uv__signal_lock);
+
+  handle->signal_cb = signal_cb;
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+    uv_req_t* req) {
+  long dispatched_signum;
+
+  assert(handle->type == UV_SIGNAL);
+  assert(req->type == UV_SIGNAL_REQ);
+
+  dispatched_signum = InterlockedExchange(
+          (volatile LONG*) &handle->pending_signum, 0);
+  assert(dispatched_signum != 0);
+
+  /* Check if the pending signal equals the signum that we are watching for.
+   * These can get out of sync when the handler is stopped and restarted while
+   * the signal_req is pending. */
+  if (dispatched_signum == handle->signum)
+    handle->signal_cb(handle, dispatched_signum);
+
+  if (handle->flags & UV_SIGNAL_ONE_SHOT)
+    uv_signal_stop(handle);
+
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    /* When it is closing, it must be stopped at this point. */
+    assert(handle->signum == 0);
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) {
+  uv_signal_stop(handle);
+  uv__handle_closing(handle);
+
+  if (handle->pending_signum == 0) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+  assert(handle->signum == 0);
+  assert(handle->pending_signum == 0);
+
+  handle->flags |= UV_HANDLE_CLOSED;
+
+  uv__handle_close(handle);
+}
diff --git a/third_party/libuv/src/win/snprintf.c b/third_party/libuv/src/win/snprintf.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/snprintf.c
@@ -0,0 +1,42 @@
+/* Copyright the libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/* Emulate snprintf() on MSVC<2015, _snprintf() doesn't zero-terminate the buffer
+ * on overflow...
+ */
+int snprintf(char* buf, size_t len, const char* fmt, ...) {
+  int n;
+  va_list ap;
+  va_start(ap, fmt);
+
+  n = _vscprintf(fmt, ap);
+  vsnprintf_s(buf, len, _TRUNCATE, fmt, ap);
+
+  va_end(ap);
+  return n;
+}
+
+#endif
diff --git a/third_party/libuv/src/win/stream-inl.h b/third_party/libuv/src/win/stream-inl.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/stream-inl.h
@@ -0,0 +1,54 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_STREAM_INL_H_
+#define UV_WIN_STREAM_INL_H_
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+INLINE static void uv_stream_init(uv_loop_t* loop,
+                                  uv_stream_t* handle,
+                                  uv_handle_type type) {
+  uv__handle_init(loop, (uv_handle_t*) handle, type);
+  handle->write_queue_size = 0;
+  handle->activecnt = 0;
+  handle->stream.conn.shutdown_req = NULL;
+  handle->stream.conn.write_reqs_pending = 0;
+
+  UV_REQ_INIT(&handle->read_req, UV_READ);
+  handle->read_req.event_handle = NULL;
+  handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+  handle->read_req.data = handle;
+}
+
+
+INLINE static void uv_connection_init(uv_stream_t* handle) {
+  handle->flags |= UV_HANDLE_CONNECTION;
+}
+
+
+#endif /* UV_WIN_STREAM_INL_H_ */
diff --git a/third_party/libuv/src/win/stream.c b/third_party/libuv/src/win/stream.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/stream.c
@@ -0,0 +1,243 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "req-inl.h"
+
+
+int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
+  int err;
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (stream->type) {
+    case UV_TCP:
+      err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_accept(uv_stream_t* server, uv_stream_t* client) {
+  int err;
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (server->type) {
+    case UV_TCP:
+      err = uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv_pipe_accept((uv_pipe_t*)server, client);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb) {
+  int err;
+
+  if (handle->flags & UV_HANDLE_READING) {
+    return UV_EALREADY;
+  }
+
+  if (!(handle->flags & UV_HANDLE_READABLE)) {
+    return UV_ENOTCONN;
+  }
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (handle->type) {
+    case UV_TCP:
+      err = uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb);
+      break;
+    case UV_TTY:
+      err = uv_tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_read_stop(uv_stream_t* handle) {
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_READING))
+    return 0;
+
+  err = 0;
+  if (handle->type == UV_TTY) {
+    err = uv_tty_read_stop((uv_tty_t*) handle);
+  } else if (handle->type == UV_NAMED_PIPE) {
+    uv__pipe_read_stop((uv_pipe_t*) handle);
+  } else {
+    handle->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(handle->loop, handle);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_write(uv_write_t* req,
+             uv_stream_t* handle,
+             const uv_buf_t bufs[],
+             unsigned int nbufs,
+             uv_write_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    return UV_EPIPE;
+  }
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (handle->type) {
+    case UV_TCP:
+      err = uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb);
+      break;
+    case UV_NAMED_PIPE:
+      err = uv__pipe_write(
+          loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
+      break;
+    case UV_TTY:
+      err = uv_tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
+      break;
+    default:
+      assert(0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_write2(uv_write_t* req,
+              uv_stream_t* handle,
+              const uv_buf_t bufs[],
+              unsigned int nbufs,
+              uv_stream_t* send_handle,
+              uv_write_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  int err;
+
+  if (send_handle == NULL) {
+    return uv_write(req, handle, bufs, nbufs, cb);
+  }
+
+  if (handle->type != UV_NAMED_PIPE || !((uv_pipe_t*) handle)->ipc) {
+    return UV_EINVAL;
+  } else if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    return UV_EPIPE;
+  }
+
+  err = uv__pipe_write(
+      loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb);
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_try_write(uv_stream_t* stream,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs) {
+  if (stream->flags & UV_HANDLE_CLOSING)
+    return UV_EBADF;
+  if (!(stream->flags & UV_HANDLE_WRITABLE))
+    return UV_EPIPE;
+
+  switch (stream->type) {
+    case UV_TCP:
+      return uv__tcp_try_write((uv_tcp_t*) stream, bufs, nbufs);
+    case UV_TTY:
+      return uv__tty_try_write((uv_tty_t*) stream, bufs, nbufs);
+    case UV_NAMED_PIPE:
+      return UV_EAGAIN;
+    default:
+      assert(0);
+      return UV_ENOSYS;
+  }
+}
+
+
+int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
+  uv_loop_t* loop = handle->loop;
+
+  if (!(handle->flags & UV_HANDLE_WRITABLE) ||
+      handle->flags & UV_HANDLE_SHUTTING ||
+      uv__is_closing(handle)) {
+    return UV_ENOTCONN;
+  }
+
+  UV_REQ_INIT(req, UV_SHUTDOWN);
+  req->handle = handle;
+  req->cb = cb;
+
+  handle->flags &= ~UV_HANDLE_WRITABLE;
+  handle->flags |= UV_HANDLE_SHUTTING;
+  handle->stream.conn.shutdown_req = req;
+  handle->reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+
+  uv_want_endgame(loop, (uv_handle_t*)handle);
+
+  return 0;
+}
+
+
+int uv_is_readable(const uv_stream_t* handle) {
+  return !!(handle->flags & UV_HANDLE_READABLE);
+}
+
+
+int uv_is_writable(const uv_stream_t* handle) {
+  return !!(handle->flags & UV_HANDLE_WRITABLE);
+}
+
+
+int uv_stream_set_blocking(uv_stream_t* handle, int blocking) {
+  if (handle->type != UV_NAMED_PIPE)
+    return UV_EINVAL;
+
+  if (blocking != 0)
+    handle->flags |= UV_HANDLE_BLOCKING_WRITES;
+  else
+    handle->flags &= ~UV_HANDLE_BLOCKING_WRITES;
+
+  return 0;
+}
diff --git a/third_party/libuv/src/win/tcp.c b/third_party/libuv/src/win/tcp.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/tcp.c
@@ -0,0 +1,1573 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "stream-inl.h"
+#include "req-inl.h"
+
+
+/*
+ * Threshold of active tcp streams for which to preallocate tcp read buffers.
+ * (Due to node slab allocator performing poorly under this pattern,
+ *  the optimization is temporarily disabled (threshold=0).  This will be
+ *  revisited once node allocator is improved.)
+ */
+const unsigned int uv_active_tcp_streams_threshold = 0;
+
+/*
+ * Number of simultaneous pending AcceptEx calls.
+ */
+const unsigned int uv_simultaneous_server_accepts = 32;
+
+/* A zero-size buffer for use by uv_tcp_read */
+static char uv_zero_[] = "";
+
+static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) {
+  if (setsockopt(socket,
+                 IPPROTO_TCP,
+                 TCP_NODELAY,
+                 (const char*)&enable,
+                 sizeof enable) == -1) {
+    return WSAGetLastError();
+  }
+  return 0;
+}
+
+
+static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) {
+  if (setsockopt(socket,
+                 SOL_SOCKET,
+                 SO_KEEPALIVE,
+                 (const char*)&enable,
+                 sizeof enable) == -1) {
+    return WSAGetLastError();
+  }
+
+  if (enable && setsockopt(socket,
+                           IPPROTO_TCP,
+                           TCP_KEEPALIVE,
+                           (const char*)&delay,
+                           sizeof delay) == -1) {
+    return WSAGetLastError();
+  }
+
+  return 0;
+}
+
+
+static int uv_tcp_set_socket(uv_loop_t* loop,
+                             uv_tcp_t* handle,
+                             SOCKET socket,
+                             int family,
+                             int imported) {
+  DWORD yes = 1;
+  int non_ifs_lsp;
+  int err;
+
+  if (handle->socket != INVALID_SOCKET)
+    return UV_EBUSY;
+
+  /* Set the socket to nonblocking mode */
+  if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  /* Make the socket non-inheritable */
+  if (!SetHandleInformation((HANDLE) socket, HANDLE_FLAG_INHERIT, 0))
+    return GetLastError();
+
+  /* Associate it with the I/O completion port. Use uv_handle_t pointer as
+   * completion key. */
+  if (CreateIoCompletionPort((HANDLE)socket,
+                             loop->iocp,
+                             (ULONG_PTR)socket,
+                             0) == NULL) {
+    if (imported) {
+      handle->flags |= UV_HANDLE_EMULATE_IOCP;
+    } else {
+      return GetLastError();
+    }
+  }
+
+  if (family == AF_INET6) {
+    non_ifs_lsp = uv_tcp_non_ifs_lsp_ipv6;
+  } else {
+    non_ifs_lsp = uv_tcp_non_ifs_lsp_ipv4;
+  }
+
+  if (!(handle->flags & UV_HANDLE_EMULATE_IOCP) && !non_ifs_lsp) {
+    UCHAR sfcnm_flags =
+        FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
+    if (!SetFileCompletionNotificationModes((HANDLE) socket, sfcnm_flags))
+      return GetLastError();
+    handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
+  }
+
+  if (handle->flags & UV_HANDLE_TCP_NODELAY) {
+    err = uv__tcp_nodelay(handle, socket, 1);
+    if (err)
+      return err;
+  }
+
+  /* TODO: Use stored delay. */
+  if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) {
+    err = uv__tcp_keepalive(handle, socket, 1, 60);
+    if (err)
+      return err;
+  }
+
+  handle->socket = socket;
+
+  if (family == AF_INET6) {
+    handle->flags |= UV_HANDLE_IPV6;
+  } else {
+    assert(!(handle->flags & UV_HANDLE_IPV6));
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) {
+  int domain;
+
+  /* Use the lower 8 bits for the domain */
+  domain = flags & 0xFF;
+  if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
+    return UV_EINVAL;
+
+  if (flags & ~0xFF)
+    return UV_EINVAL;
+
+  uv_stream_init(loop, (uv_stream_t*) handle, UV_TCP);
+  handle->tcp.serv.accept_reqs = NULL;
+  handle->tcp.serv.pending_accepts = NULL;
+  handle->socket = INVALID_SOCKET;
+  handle->reqs_pending = 0;
+  handle->tcp.serv.func_acceptex = NULL;
+  handle->tcp.conn.func_connectex = NULL;
+  handle->tcp.serv.processed_accepts = 0;
+  handle->delayed_error = 0;
+
+  /* If anything fails beyond this point we need to remove the handle from
+   * the handle queue, since it was added by uv__handle_init in uv_stream_init.
+   */
+
+  if (domain != AF_UNSPEC) {
+    SOCKET sock;
+    DWORD err;
+
+    sock = socket(domain, SOCK_STREAM, 0);
+    if (sock == INVALID_SOCKET) {
+      err = WSAGetLastError();
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+
+    err = uv_tcp_set_socket(handle->loop, handle, sock, domain, 0);
+    if (err) {
+      closesocket(sock);
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) {
+  return uv_tcp_init_ex(loop, handle, AF_UNSPEC);
+}
+
+
+void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
+  int err;
+  unsigned int i;
+  uv_tcp_accept_t* req;
+
+  if (handle->flags & UV_HANDLE_CONNECTION &&
+      handle->stream.conn.shutdown_req != NULL &&
+      handle->stream.conn.write_reqs_pending == 0) {
+
+    UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req);
+
+    err = 0;
+    if (handle->flags & UV_HANDLE_CLOSING) {
+      err = ERROR_OPERATION_ABORTED;
+    } else if (shutdown(handle->socket, SD_SEND) == SOCKET_ERROR) {
+      err = WSAGetLastError();
+    }
+
+    if (handle->stream.conn.shutdown_req->cb) {
+      handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req,
+                               uv_translate_sys_error(err));
+    }
+
+    handle->stream.conn.shutdown_req = NULL;
+    DECREASE_PENDING_REQ_COUNT(handle);
+    return;
+  }
+
+  if (handle->flags & UV_HANDLE_CLOSING &&
+      handle->reqs_pending == 0) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+
+    if (!(handle->flags & UV_HANDLE_TCP_SOCKET_CLOSED)) {
+      closesocket(handle->socket);
+      handle->socket = INVALID_SOCKET;
+      handle->flags |= UV_HANDLE_TCP_SOCKET_CLOSED;
+    }
+
+    if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) {
+      if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+        for (i = 0; i < uv_simultaneous_server_accepts; i++) {
+          req = &handle->tcp.serv.accept_reqs[i];
+          if (req->wait_handle != INVALID_HANDLE_VALUE) {
+            UnregisterWait(req->wait_handle);
+            req->wait_handle = INVALID_HANDLE_VALUE;
+          }
+          if (req->event_handle != NULL) {
+            CloseHandle(req->event_handle);
+            req->event_handle = NULL;
+          }
+        }
+      }
+
+      uv__free(handle->tcp.serv.accept_reqs);
+      handle->tcp.serv.accept_reqs = NULL;
+    }
+
+    if (handle->flags & UV_HANDLE_CONNECTION &&
+        handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
+        UnregisterWait(handle->read_req.wait_handle);
+        handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+      }
+      if (handle->read_req.event_handle != NULL) {
+        CloseHandle(handle->read_req.event_handle);
+        handle->read_req.event_handle = NULL;
+      }
+    }
+
+    uv__handle_close(handle);
+    loop->active_tcp_streams--;
+  }
+}
+
+
+/* Unlike on Unix, here we don't set SO_REUSEADDR, because it doesn't just
+ * allow binding to addresses that are in use by sockets in TIME_WAIT, it
+ * effectively allows 'stealing' a port which is in use by another application.
+ *
+ * SO_EXCLUSIVEADDRUSE is also not good here because it does check all sockets,
+ * regardless of state, so we'd get an error even if the port is in use by a
+ * socket in TIME_WAIT state.
+ *
+ * See issue #1360.
+ *
+ */
+static int uv_tcp_try_bind(uv_tcp_t* handle,
+                           const struct sockaddr* addr,
+                           unsigned int addrlen,
+                           unsigned int flags) {
+  DWORD err;
+  int r;
+
+  if (handle->socket == INVALID_SOCKET) {
+    SOCKET sock;
+
+    /* Cannot set IPv6-only mode on non-IPv6 socket. */
+    if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
+      return ERROR_INVALID_PARAMETER;
+
+    sock = socket(addr->sa_family, SOCK_STREAM, 0);
+    if (sock == INVALID_SOCKET) {
+      return WSAGetLastError();
+    }
+
+    err = uv_tcp_set_socket(handle->loop, handle, sock, addr->sa_family, 0);
+    if (err) {
+      closesocket(sock);
+      return err;
+    }
+  }
+
+#ifdef IPV6_V6ONLY
+  if (addr->sa_family == AF_INET6) {
+    int on;
+
+    on = (flags & UV_TCP_IPV6ONLY) != 0;
+
+    /* TODO: how to handle errors? This may fail if there is no ipv4 stack
+     * available, or when run on XP/2003 which have no support for dualstack
+     * sockets. For now we're silently ignoring the error. */
+    setsockopt(handle->socket,
+               IPPROTO_IPV6,
+               IPV6_V6ONLY,
+               (const char*)&on,
+               sizeof on);
+  }
+#endif
+
+  r = bind(handle->socket, addr, addrlen);
+
+  if (r == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    if (err == WSAEADDRINUSE) {
+      /* Some errors are not to be reported until connect() or listen() */
+      handle->delayed_error = err;
+    } else {
+      return err;
+    }
+  }
+
+  handle->flags |= UV_HANDLE_BOUND;
+
+  return 0;
+}
+
+
+static void CALLBACK post_completion(void* context, BOOLEAN timed_out) {
+  uv_req_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_req_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->data;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+static void CALLBACK post_write_completion(void* context, BOOLEAN timed_out) {
+  uv_write_t* req;
+  uv_tcp_t* handle;
+
+  req = (uv_write_t*) context;
+  assert(req != NULL);
+  handle = (uv_tcp_t*)req->handle;
+  assert(handle != NULL);
+  assert(!timed_out);
+
+  if (!PostQueuedCompletionStatus(handle->loop->iocp,
+                                  req->u.io.overlapped.InternalHigh,
+                                  0,
+                                  &req->u.io.overlapped)) {
+    uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus");
+  }
+}
+
+
+void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
+  uv_loop_t* loop = handle->loop;
+  BOOL success;
+  DWORD bytes;
+  SOCKET accept_socket;
+  short family;
+
+  assert(handle->flags & UV_HANDLE_LISTENING);
+  assert(req->accept_socket == INVALID_SOCKET);
+
+  /* choose family and extension function */
+  if (handle->flags & UV_HANDLE_IPV6) {
+    family = AF_INET6;
+  } else {
+    family = AF_INET;
+  }
+
+  /* Open a socket for the accepted connection. */
+  accept_socket = socket(family, SOCK_STREAM, 0);
+  if (accept_socket == INVALID_SOCKET) {
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+    handle->reqs_pending++;
+    return;
+  }
+
+  /* Make the socket non-inheritable */
+  if (!SetHandleInformation((HANDLE) accept_socket, HANDLE_FLAG_INHERIT, 0)) {
+    SET_REQ_ERROR(req, GetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+    handle->reqs_pending++;
+    closesocket(accept_socket);
+    return;
+  }
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    assert(req->event_handle != NULL);
+    req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1);
+  }
+
+  success = handle->tcp.serv.func_acceptex(handle->socket,
+                                          accept_socket,
+                                          (void*)req->accept_buffer,
+                                          0,
+                                          sizeof(struct sockaddr_storage),
+                                          sizeof(struct sockaddr_storage),
+                                          &bytes,
+                                          &req->u.io.overlapped);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
+    /* Process the req without IOCP. */
+    req->accept_socket = accept_socket;
+    handle->reqs_pending++;
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(success)) {
+    /* The req will be processed with IOCP. */
+    req->accept_socket = accept_socket;
+    handle->reqs_pending++;
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        req->wait_handle == INVALID_HANDLE_VALUE &&
+        !RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_completion, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD)) {
+      SET_REQ_ERROR(req, GetLastError());
+      uv_insert_pending_req(loop, (uv_req_t*)req);
+    }
+  } else {
+    /* Make this req pending reporting an error. */
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+    handle->reqs_pending++;
+    /* Destroy the preallocated client socket. */
+    closesocket(accept_socket);
+    /* Destroy the event handle */
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+    }
+  }
+}
+
+
+static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) {
+  uv_read_t* req;
+  uv_buf_t buf;
+  int result;
+  DWORD bytes, flags;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  req = &handle->read_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  /*
+   * Preallocate a read buffer if the number of active streams is below
+   * the threshold.
+  */
+  if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) {
+    handle->flags &= ~UV_HANDLE_ZERO_READ;
+    handle->tcp.conn.read_buffer = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer);
+    if (handle->tcp.conn.read_buffer.base == NULL ||
+        handle->tcp.conn.read_buffer.len == 0) {
+      handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer);
+      return;
+    }
+    assert(handle->tcp.conn.read_buffer.base != NULL);
+    buf = handle->tcp.conn.read_buffer;
+  } else {
+    handle->flags |= UV_HANDLE_ZERO_READ;
+    buf.base = (char*) &uv_zero_;
+    buf.len = 0;
+  }
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    assert(req->event_handle != NULL);
+    req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1);
+  }
+
+  flags = 0;
+  result = WSARecv(handle->socket,
+                   (WSABUF*)&buf,
+                   1,
+                   &bytes,
+                   &flags,
+                   &req->u.io.overlapped,
+                   NULL);
+
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+    /* Process the req without IOCP. */
+    req->u.io.overlapped.InternalHigh = bytes;
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+    /* The req will be processed with IOCP. */
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        req->wait_handle == INVALID_HANDLE_VALUE &&
+        !RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_completion, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD)) {
+      SET_REQ_ERROR(req, GetLastError());
+      uv_insert_pending_req(loop, (uv_req_t*)req);
+    }
+  } else {
+    /* Make this req pending reporting an error. */
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+  }
+}
+
+
+int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
+  struct linger l = { 1, 0 };
+
+  /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */
+  if (handle->flags & UV_HANDLE_SHUTTING)
+    return UV_EINVAL;
+
+  if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l)))
+    return uv_translate_sys_error(WSAGetLastError());
+
+  uv_close((uv_handle_t*) handle, close_cb);
+  return 0;
+}
+
+
+int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
+  unsigned int i, simultaneous_accepts;
+  uv_tcp_accept_t* req;
+  int err;
+
+  assert(backlog > 0);
+
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->stream.serv.connection_cb = cb;
+  }
+
+  if (handle->flags & UV_HANDLE_READING) {
+    return WSAEISCONN;
+  }
+
+  if (handle->delayed_error) {
+    return handle->delayed_error;
+  }
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    err = uv_tcp_try_bind(handle,
+                          (const struct sockaddr*) &uv_addr_ip4_any_,
+                          sizeof(uv_addr_ip4_any_),
+                          0);
+    if (err)
+      return err;
+    if (handle->delayed_error)
+      return handle->delayed_error;
+  }
+
+  if (!handle->tcp.serv.func_acceptex) {
+    if (!uv_get_acceptex_function(handle->socket, &handle->tcp.serv.func_acceptex)) {
+      return WSAEAFNOSUPPORT;
+    }
+  }
+
+  if (!(handle->flags & UV_HANDLE_SHARED_TCP_SOCKET) &&
+      listen(handle->socket, backlog) == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  handle->flags |= UV_HANDLE_LISTENING;
+  handle->stream.serv.connection_cb = cb;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+
+  simultaneous_accepts = handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT ? 1
+    : uv_simultaneous_server_accepts;
+
+  if (handle->tcp.serv.accept_reqs == NULL) {
+    handle->tcp.serv.accept_reqs =
+      uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t));
+    if (!handle->tcp.serv.accept_reqs) {
+      uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+    }
+
+    for (i = 0; i < simultaneous_accepts; i++) {
+      req = &handle->tcp.serv.accept_reqs[i];
+      UV_REQ_INIT(req, UV_ACCEPT);
+      req->accept_socket = INVALID_SOCKET;
+      req->data = handle;
+
+      req->wait_handle = INVALID_HANDLE_VALUE;
+      if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+        req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+        if (req->event_handle == NULL) {
+          uv_fatal_error(GetLastError(), "CreateEvent");
+        }
+      } else {
+        req->event_handle = NULL;
+      }
+
+      uv_tcp_queue_accept(handle, req);
+    }
+
+    /* Initialize other unused requests too, because uv_tcp_endgame doesn't
+     * know how many requests were initialized, so it will try to clean up
+     * {uv_simultaneous_server_accepts} requests. */
+    for (i = simultaneous_accepts; i < uv_simultaneous_server_accepts; i++) {
+      req = &handle->tcp.serv.accept_reqs[i];
+      UV_REQ_INIT(req, UV_ACCEPT);
+      req->accept_socket = INVALID_SOCKET;
+      req->data = handle;
+      req->wait_handle = INVALID_HANDLE_VALUE;
+      req->event_handle = NULL;
+    }
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
+  uv_loop_t* loop = server->loop;
+  int err = 0;
+  int family;
+
+  uv_tcp_accept_t* req = server->tcp.serv.pending_accepts;
+
+  if (!req) {
+    /* No valid connections found, so we error out. */
+    return WSAEWOULDBLOCK;
+  }
+
+  if (req->accept_socket == INVALID_SOCKET) {
+    return WSAENOTCONN;
+  }
+
+  if (server->flags & UV_HANDLE_IPV6) {
+    family = AF_INET6;
+  } else {
+    family = AF_INET;
+  }
+
+  err = uv_tcp_set_socket(client->loop,
+                          client,
+                          req->accept_socket,
+                          family,
+                          0);
+  if (err) {
+    closesocket(req->accept_socket);
+  } else {
+    uv_connection_init((uv_stream_t*) client);
+    /* AcceptEx() implicitly binds the accepted socket. */
+    client->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+  }
+
+  /* Prepare the req to pick up a new connection */
+  server->tcp.serv.pending_accepts = req->next_pending;
+  req->next_pending = NULL;
+  req->accept_socket = INVALID_SOCKET;
+
+  if (!(server->flags & UV_HANDLE_CLOSING)) {
+    /* Check if we're in a middle of changing the number of pending accepts. */
+    if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) {
+      uv_tcp_queue_accept(server, req);
+    } else {
+      /* We better be switching to a single pending accept. */
+      assert(server->flags & UV_HANDLE_TCP_SINGLE_ACCEPT);
+
+      server->tcp.serv.processed_accepts++;
+
+      if (server->tcp.serv.processed_accepts >= uv_simultaneous_server_accepts) {
+        server->tcp.serv.processed_accepts = 0;
+        /*
+         * All previously queued accept requests are now processed.
+         * We now switch to queueing just a single accept.
+         */
+        uv_tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]);
+        server->flags &= ~UV_HANDLE_TCP_ACCEPT_STATE_CHANGING;
+        server->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
+      }
+    }
+  }
+
+  loop->active_tcp_streams++;
+
+  return err;
+}
+
+
+int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb) {
+  uv_loop_t* loop = handle->loop;
+
+  handle->flags |= UV_HANDLE_READING;
+  handle->read_cb = read_cb;
+  handle->alloc_cb = alloc_cb;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+
+  /* If reading was stopped and then started again, there could still be a read
+   * request pending. */
+  if (!(handle->flags & UV_HANDLE_READ_PENDING)) {
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        handle->read_req.event_handle == NULL) {
+      handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
+      if (handle->read_req.event_handle == NULL) {
+        uv_fatal_error(GetLastError(), "CreateEvent");
+      }
+    }
+    uv_tcp_queue_read(loop, handle);
+  }
+
+  return 0;
+}
+
+static int uv__is_loopback(const struct sockaddr_storage* storage) {
+  const struct sockaddr_in* in4;
+  const struct sockaddr_in6* in6;
+  int i;
+
+  if (storage->ss_family == AF_INET) {
+    in4 = (const struct sockaddr_in*) storage;
+    return in4->sin_addr.S_un.S_un_b.s_b1 == 127;
+  }
+  if (storage->ss_family == AF_INET6) {
+    in6 = (const struct sockaddr_in6*) storage;
+    for (i = 0; i < 7; ++i) {
+      if (in6->sin6_addr.u.Word[i] != 0)
+        return 0;
+    }
+    return in6->sin6_addr.u.Word[7] == htons(1);
+  }
+  return 0;
+}
+
+// Check if Windows version is 10.0.16299 or later
+static int uv__is_fast_loopback_fail_supported() {
+  OSVERSIONINFOW os_info;
+  if (!pRtlGetVersion)
+    return 0;
+  pRtlGetVersion(&os_info);
+  if (os_info.dwMajorVersion < 10)
+    return 0;
+  if (os_info.dwMajorVersion > 10)
+    return 1;
+  if (os_info.dwMinorVersion > 0)
+    return 1;
+  return os_info.dwBuildNumber >= 16299;
+}
+
+static int uv_tcp_try_connect(uv_connect_t* req,
+                              uv_tcp_t* handle,
+                              const struct sockaddr* addr,
+                              unsigned int addrlen,
+                              uv_connect_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  TCP_INITIAL_RTO_PARAMETERS retransmit_ioctl;
+  const struct sockaddr* bind_addr;
+  struct sockaddr_storage converted;
+  BOOL success;
+  DWORD bytes;
+  int err;
+
+  err = uv__convert_to_localhost_if_unspecified(addr, &converted);
+  if (err)
+    return err;
+
+  if (handle->delayed_error) {
+    return handle->delayed_error;
+  }
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_)) {
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    } else if (addrlen == sizeof(uv_addr_ip6_any_)) {
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    } else {
+      abort();
+    }
+    err = uv_tcp_try_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return err;
+    if (handle->delayed_error)
+      return handle->delayed_error;
+  }
+
+  if (!handle->tcp.conn.func_connectex) {
+    if (!uv_get_connectex_function(handle->socket, &handle->tcp.conn.func_connectex)) {
+      return WSAEAFNOSUPPORT;
+    }
+  }
+
+  /* This makes connect() fail instantly if the target port on the localhost
+   * is not reachable, instead of waiting for 2s. We do not care if this fails.
+   * This only works on Windows version 10.0.16299 and later.
+   */
+  if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) {
+    memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl));
+    retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
+    retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
+    WSAIoctl(handle->socket,
+             SIO_TCP_INITIAL_RTO,
+             &retransmit_ioctl,
+             sizeof(retransmit_ioctl),
+             NULL,
+             0,
+             &bytes,
+             NULL,
+             NULL);
+  }
+
+  UV_REQ_INIT(req, UV_CONNECT);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  success = handle->tcp.conn.func_connectex(handle->socket,
+                                            (const struct sockaddr*) &converted,
+                                            addrlen,
+                                            NULL,
+                                            0,
+                                            &bytes,
+                                            &req->u.io.overlapped);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
+    /* Process the req without IOCP. */
+    handle->reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(success)) {
+    /* The req will be processed with IOCP. */
+    handle->reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+  } else {
+    return WSAGetLastError();
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_getsockname(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             handle->delayed_error);
+}
+
+
+int uv_tcp_getpeername(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             handle->delayed_error);
+}
+
+
+int uv_tcp_write(uv_loop_t* loop,
+                 uv_write_t* req,
+                 uv_tcp_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 uv_write_cb cb) {
+  int result;
+  DWORD bytes;
+
+  UV_REQ_INIT(req, UV_WRITE);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+
+  /* Prepare the overlapped structure. */
+  memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped));
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+    if (req->event_handle == NULL) {
+      uv_fatal_error(GetLastError(), "CreateEvent");
+    }
+    req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1);
+    req->wait_handle = INVALID_HANDLE_VALUE;
+  }
+
+  result = WSASend(handle->socket,
+                   (WSABUF*) bufs,
+                   nbufs,
+                   &bytes,
+                   0,
+                   &req->u.io.overlapped,
+                   NULL);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+    /* Request completed immediately. */
+    req->u.io.queued_bytes = 0;
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    uv_insert_pending_req(loop, (uv_req_t*) req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+    /* Request queued by the kernel. */
+    req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    handle->write_queue_size += req->u.io.queued_bytes;
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
+        !RegisterWaitForSingleObject(&req->wait_handle,
+          req->event_handle, post_write_completion, (void*) req,
+          INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
+      SET_REQ_ERROR(req, GetLastError());
+      uv_insert_pending_req(loop, (uv_req_t*)req);
+    }
+  } else {
+    /* Send failed due to an error, report it later */
+    req->u.io.queued_bytes = 0;
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    SET_REQ_ERROR(req, WSAGetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*) req);
+  }
+
+  return 0;
+}
+
+
+int uv__tcp_try_write(uv_tcp_t* handle,
+                     const uv_buf_t bufs[],
+                     unsigned int nbufs) {
+  int result;
+  DWORD bytes;
+
+  if (handle->stream.conn.write_reqs_pending > 0)
+    return UV_EAGAIN;
+
+  result = WSASend(handle->socket,
+                   (WSABUF*) bufs,
+                   nbufs,
+                   &bytes,
+                   0,
+                   NULL,
+                   NULL);
+
+  if (result == SOCKET_ERROR)
+    return uv_translate_sys_error(WSAGetLastError());
+  else
+    return bytes;
+}
+
+
+void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_req_t* req) {
+  DWORD bytes, flags, err;
+  uv_buf_t buf;
+  int count;
+
+  assert(handle->type == UV_TCP);
+
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+
+  if (!REQ_SUCCESS(req)) {
+    /* An error occurred doing the read. */
+    if ((handle->flags & UV_HANDLE_READING) ||
+        !(handle->flags & UV_HANDLE_ZERO_READ)) {
+      handle->flags &= ~UV_HANDLE_READING;
+      DECREASE_ACTIVE_COUNT(loop, handle);
+      buf = (handle->flags & UV_HANDLE_ZERO_READ) ?
+            uv_buf_init(NULL, 0) : handle->tcp.conn.read_buffer;
+
+      err = GET_REQ_SOCK_ERROR(req);
+
+      if (err == WSAECONNABORTED) {
+        /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with Unix.
+         */
+        err = WSAECONNRESET;
+      }
+
+      handle->read_cb((uv_stream_t*)handle,
+                      uv_translate_sys_error(err),
+                      &buf);
+    }
+  } else {
+    if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
+      /* The read was done with a non-zero buffer length. */
+      if (req->u.io.overlapped.InternalHigh > 0) {
+        /* Successful read */
+        handle->read_cb((uv_stream_t*)handle,
+                        req->u.io.overlapped.InternalHigh,
+                        &handle->tcp.conn.read_buffer);
+        /* Read again only if bytes == buf.len */
+        if (req->u.io.overlapped.InternalHigh < handle->tcp.conn.read_buffer.len) {
+          goto done;
+        }
+      } else {
+        /* Connection closed */
+        if (handle->flags & UV_HANDLE_READING) {
+          handle->flags &= ~UV_HANDLE_READING;
+          DECREASE_ACTIVE_COUNT(loop, handle);
+        }
+        handle->flags &= ~UV_HANDLE_READABLE;
+
+        buf.base = 0;
+        buf.len = 0;
+        handle->read_cb((uv_stream_t*)handle, UV_EOF, &handle->tcp.conn.read_buffer);
+        goto done;
+      }
+    }
+
+    /* Do nonblocking reads until the buffer is empty */
+    count = 32;
+    while ((handle->flags & UV_HANDLE_READING) && (count-- > 0)) {
+      buf = uv_buf_init(NULL, 0);
+      handle->alloc_cb((uv_handle_t*) handle, 65536, &buf);
+      if (buf.base == NULL || buf.len == 0) {
+        handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
+        break;
+      }
+      assert(buf.base != NULL);
+
+      flags = 0;
+      if (WSARecv(handle->socket,
+                  (WSABUF*)&buf,
+                  1,
+                  &bytes,
+                  &flags,
+                  NULL,
+                  NULL) != SOCKET_ERROR) {
+        if (bytes > 0) {
+          /* Successful read */
+          handle->read_cb((uv_stream_t*)handle, bytes, &buf);
+          /* Read again only if bytes == buf.len */
+          if (bytes < buf.len) {
+            break;
+          }
+        } else {
+          /* Connection closed */
+          handle->flags &= ~(UV_HANDLE_READING | UV_HANDLE_READABLE);
+          DECREASE_ACTIVE_COUNT(loop, handle);
+
+          handle->read_cb((uv_stream_t*)handle, UV_EOF, &buf);
+          break;
+        }
+      } else {
+        err = WSAGetLastError();
+        if (err == WSAEWOULDBLOCK) {
+          /* Read buffer was completely empty, report a 0-byte read. */
+          handle->read_cb((uv_stream_t*)handle, 0, &buf);
+        } else {
+          /* Ouch! serious error. */
+          handle->flags &= ~UV_HANDLE_READING;
+          DECREASE_ACTIVE_COUNT(loop, handle);
+
+          if (err == WSAECONNABORTED) {
+            /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with
+             * Unix. */
+            err = WSAECONNRESET;
+          }
+
+          handle->read_cb((uv_stream_t*)handle,
+                          uv_translate_sys_error(err),
+                          &buf);
+        }
+        break;
+      }
+    }
+
+done:
+    /* Post another read if still reading and not closing. */
+    if ((handle->flags & UV_HANDLE_READING) &&
+        !(handle->flags & UV_HANDLE_READ_PENDING)) {
+      uv_tcp_queue_read(loop, handle);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_write_t* req) {
+  int err;
+
+  assert(handle->type == UV_TCP);
+
+  assert(handle->write_queue_size >= req->u.io.queued_bytes);
+  handle->write_queue_size -= req->u.io.queued_bytes;
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    if (req->wait_handle != INVALID_HANDLE_VALUE) {
+      UnregisterWait(req->wait_handle);
+      req->wait_handle = INVALID_HANDLE_VALUE;
+    }
+    if (req->event_handle != NULL) {
+      CloseHandle(req->event_handle);
+      req->event_handle = NULL;
+    }
+  }
+
+  if (req->cb) {
+    err = uv_translate_sys_error(GET_REQ_SOCK_ERROR(req));
+    if (err == UV_ECONNABORTED) {
+      /* use UV_ECANCELED for consistency with Unix */
+      err = UV_ECANCELED;
+    }
+    req->cb(req, err);
+  }
+
+  handle->stream.conn.write_reqs_pending--;
+  if (handle->stream.conn.shutdown_req != NULL &&
+      handle->stream.conn.write_reqs_pending == 0) {
+    uv_want_endgame(loop, (uv_handle_t*)handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_req_t* raw_req) {
+  uv_tcp_accept_t* req = (uv_tcp_accept_t*) raw_req;
+  int err;
+
+  assert(handle->type == UV_TCP);
+
+  /* If handle->accepted_socket is not a valid socket, then uv_queue_accept
+   * must have failed. This is a serious error. We stop accepting connections
+   * and report this error to the connection callback. */
+  if (req->accept_socket == INVALID_SOCKET) {
+    if (handle->flags & UV_HANDLE_LISTENING) {
+      handle->flags &= ~UV_HANDLE_LISTENING;
+      DECREASE_ACTIVE_COUNT(loop, handle);
+      if (handle->stream.serv.connection_cb) {
+        err = GET_REQ_SOCK_ERROR(req);
+        handle->stream.serv.connection_cb((uv_stream_t*)handle,
+                                      uv_translate_sys_error(err));
+      }
+    }
+  } else if (REQ_SUCCESS(req) &&
+      setsockopt(req->accept_socket,
+                  SOL_SOCKET,
+                  SO_UPDATE_ACCEPT_CONTEXT,
+                  (char*)&handle->socket,
+                  sizeof(handle->socket)) == 0) {
+    req->next_pending = handle->tcp.serv.pending_accepts;
+    handle->tcp.serv.pending_accepts = req;
+
+    /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */
+    if (handle->stream.serv.connection_cb) {
+      handle->stream.serv.connection_cb((uv_stream_t*)handle, 0);
+    }
+  } else {
+    /* Error related to accepted socket is ignored because the server socket
+     * may still be healthy. If the server socket is broken uv_queue_accept
+     * will detect it. */
+    closesocket(req->accept_socket);
+    req->accept_socket = INVALID_SOCKET;
+    if (handle->flags & UV_HANDLE_LISTENING) {
+      uv_tcp_queue_accept(handle, req);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
+    uv_connect_t* req) {
+  int err;
+
+  assert(handle->type == UV_TCP);
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  err = 0;
+  if (REQ_SUCCESS(req)) {
+    if (handle->flags & UV_HANDLE_CLOSING) {
+      /* use UV_ECANCELED for consistency with Unix */
+      err = ERROR_OPERATION_ABORTED;
+    } else if (setsockopt(handle->socket,
+                          SOL_SOCKET,
+                          SO_UPDATE_CONNECT_CONTEXT,
+                          NULL,
+                          0) == 0) {
+      uv_connection_init((uv_stream_t*)handle);
+      handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+      loop->active_tcp_streams++;
+    } else {
+      err = WSAGetLastError();
+    }
+  } else {
+    err = GET_REQ_SOCK_ERROR(req);
+  }
+  req->cb(req, uv_translate_sys_error(err));
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+int uv__tcp_xfer_export(uv_tcp_t* handle,
+                        int target_pid,
+                        uv__ipc_socket_xfer_type_t* xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info) {
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    *xfer_type = UV__IPC_SOCKET_XFER_TCP_CONNECTION;
+  } else {
+    *xfer_type = UV__IPC_SOCKET_XFER_TCP_SERVER;
+    /* We're about to share the socket with another process. Because this is a
+     * listening socket, we assume that the other process will be accepting
+     * connections on it. Thus, before sharing the socket with another process,
+     * we call listen here in the parent process. */
+    if (!(handle->flags & UV_HANDLE_LISTENING)) {
+      if (!(handle->flags & UV_HANDLE_BOUND)) {
+        return ERROR_NOT_SUPPORTED;
+      }
+      if (handle->delayed_error == 0 &&
+          listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) {
+        handle->delayed_error = WSAGetLastError();
+      }
+    }
+  }
+
+  if (WSADuplicateSocketW(handle->socket, target_pid, &xfer_info->socket_info))
+    return WSAGetLastError();
+  xfer_info->delayed_error = handle->delayed_error;
+
+  /* Mark the local copy of the handle as 'shared' so we behave in a way that's
+   * friendly to the process(es) that we share the socket with. */
+  handle->flags |= UV_HANDLE_SHARED_TCP_SOCKET;
+
+  return 0;
+}
+
+
+int uv__tcp_xfer_import(uv_tcp_t* tcp,
+                        uv__ipc_socket_xfer_type_t xfer_type,
+                        uv__ipc_socket_xfer_info_t* xfer_info) {
+  int err;
+  SOCKET socket;
+
+  assert(xfer_type == UV__IPC_SOCKET_XFER_TCP_SERVER ||
+         xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION);
+
+  socket = WSASocketW(FROM_PROTOCOL_INFO,
+                      FROM_PROTOCOL_INFO,
+                      FROM_PROTOCOL_INFO,
+                      &xfer_info->socket_info,
+                      0,
+                      WSA_FLAG_OVERLAPPED);
+
+  if (socket == INVALID_SOCKET) {
+    return WSAGetLastError();
+  }
+
+  err = uv_tcp_set_socket(
+      tcp->loop, tcp, socket, xfer_info->socket_info.iAddressFamily, 1);
+  if (err) {
+    closesocket(socket);
+    return err;
+  }
+
+  tcp->delayed_error = xfer_info->delayed_error;
+  tcp->flags |= UV_HANDLE_BOUND | UV_HANDLE_SHARED_TCP_SOCKET;
+
+  if (xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION) {
+    uv_connection_init((uv_stream_t*)tcp);
+    tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+  }
+
+  tcp->loop->active_tcp_streams++;
+  return 0;
+}
+
+
+int uv_tcp_nodelay(uv_tcp_t* handle, int enable) {
+  int err;
+
+  if (handle->socket != INVALID_SOCKET) {
+    err = uv__tcp_nodelay(handle, handle->socket, enable);
+    if (err)
+      return err;
+  }
+
+  if (enable) {
+    handle->flags |= UV_HANDLE_TCP_NODELAY;
+  } else {
+    handle->flags &= ~UV_HANDLE_TCP_NODELAY;
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) {
+  int err;
+
+  if (handle->socket != INVALID_SOCKET) {
+    err = uv__tcp_keepalive(handle, handle->socket, enable, delay);
+    if (err)
+      return err;
+  }
+
+  if (enable) {
+    handle->flags |= UV_HANDLE_TCP_KEEPALIVE;
+  } else {
+    handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
+  }
+
+  /* TODO: Store delay if handle->socket isn't created yet. */
+
+  return 0;
+}
+
+
+int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    return UV_EINVAL;
+  }
+
+  /* Check if we're already in the desired mode. */
+  if ((enable && !(handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) ||
+      (!enable && handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {
+    return 0;
+  }
+
+  /* Don't allow switching from single pending accept to many. */
+  if (enable) {
+    return UV_ENOTSUP;
+  }
+
+  /* Check if we're in a middle of changing the number of pending accepts. */
+  if (handle->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING) {
+    return 0;
+  }
+
+  handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
+
+  /* Flip the changing flag if we have already queued multiple accepts. */
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->flags |= UV_HANDLE_TCP_ACCEPT_STATE_CHANGING;
+  }
+
+  return 0;
+}
+
+
+static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) {
+  SOCKET socket = tcp->socket;
+  int non_ifs_lsp;
+
+  /* Check if we have any non-IFS LSPs stacked on top of TCP */
+  non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 :
+                                                uv_tcp_non_ifs_lsp_ipv4;
+
+  /* If there are non-ifs LSPs then try to obtain a base handle for the socket.
+   * This will always fail on Windows XP/3k. */
+  if (non_ifs_lsp) {
+    DWORD bytes;
+    if (WSAIoctl(socket,
+                 SIO_BASE_HANDLE,
+                 NULL,
+                 0,
+                 &socket,
+                 sizeof socket,
+                 &bytes,
+                 NULL,
+                 NULL) != 0) {
+      /* Failed. We can't do CancelIo. */
+      return -1;
+    }
+  }
+
+  assert(socket != 0 && socket != INVALID_SOCKET);
+
+  if (!CancelIo((HANDLE) socket)) {
+    return GetLastError();
+  }
+
+  /* It worked. */
+  return 0;
+}
+
+
+void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
+  int close_socket = 1;
+
+  if (tcp->flags & UV_HANDLE_READ_PENDING) {
+    /* In order for winsock to do a graceful close there must not be any any
+     * pending reads, or the socket must be shut down for writing */
+    if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) {
+      /* Just do shutdown on non-shared sockets, which ensures graceful close. */
+      shutdown(tcp->socket, SD_SEND);
+
+    } else if (uv_tcp_try_cancel_io(tcp) == 0) {
+      /* In case of a shared socket, we try to cancel all outstanding I/O,. If
+       * that works, don't close the socket yet - wait for the read req to
+       * return and close the socket in uv_tcp_endgame. */
+      close_socket = 0;
+
+    } else {
+      /* When cancelling isn't possible - which could happen when an LSP is
+       * present on an old Windows version, we will have to close the socket
+       * with a read pending. That is not nice because trailing sent bytes may
+       * not make it to the other side. */
+    }
+
+  } else if ((tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET) &&
+             tcp->tcp.serv.accept_reqs != NULL) {
+    /* Under normal circumstances closesocket() will ensure that all pending
+     * accept reqs are canceled. However, when the socket is shared the
+     * presence of another reference to the socket in another process will keep
+     * the accept reqs going, so we have to ensure that these are canceled. */
+    if (uv_tcp_try_cancel_io(tcp) != 0) {
+      /* When cancellation is not possible, there is another option: we can
+       * close the incoming sockets, which will also cancel the accept
+       * operations. However this is not cool because we might inadvertently
+       * close a socket that just accepted a new connection, which will cause
+       * the connection to be aborted. */
+      unsigned int i;
+      for (i = 0; i < uv_simultaneous_server_accepts; i++) {
+        uv_tcp_accept_t* req = &tcp->tcp.serv.accept_reqs[i];
+        if (req->accept_socket != INVALID_SOCKET &&
+            !HasOverlappedIoCompleted(&req->u.io.overlapped)) {
+          closesocket(req->accept_socket);
+          req->accept_socket = INVALID_SOCKET;
+        }
+      }
+    }
+  }
+
+  if (tcp->flags & UV_HANDLE_READING) {
+    tcp->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(loop, tcp);
+  }
+
+  if (tcp->flags & UV_HANDLE_LISTENING) {
+    tcp->flags &= ~UV_HANDLE_LISTENING;
+    DECREASE_ACTIVE_COUNT(loop, tcp);
+  }
+
+  if (close_socket) {
+    closesocket(tcp->socket);
+    tcp->socket = INVALID_SOCKET;
+    tcp->flags |= UV_HANDLE_TCP_SOCKET_CLOSED;
+  }
+
+  tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  uv__handle_closing(tcp);
+
+  if (tcp->reqs_pending == 0) {
+    uv_want_endgame(tcp->loop, (uv_handle_t*)tcp);
+  }
+}
+
+
+int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
+  WSAPROTOCOL_INFOW protocol_info;
+  int opt_len;
+  int err;
+  struct sockaddr_storage saddr;
+  int saddr_len;
+
+  /* Detect the address family of the socket. */
+  opt_len = (int) sizeof protocol_info;
+  if (getsockopt(sock,
+                 SOL_SOCKET,
+                 SO_PROTOCOL_INFOW,
+                 (char*) &protocol_info,
+                 &opt_len) == SOCKET_ERROR) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  err = uv_tcp_set_socket(handle->loop,
+                          handle,
+                          sock,
+                          protocol_info.iAddressFamily,
+                          1);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  /* Support already active socket. */
+  saddr_len = sizeof(saddr);
+  if (!uv_tcp_getsockname(handle, (struct sockaddr*) &saddr, &saddr_len)) {
+    /* Socket is already bound. */
+    handle->flags |= UV_HANDLE_BOUND;
+    saddr_len = sizeof(saddr);
+    if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) {
+      /* Socket is already connected. */
+      uv_connection_init((uv_stream_t*) handle);
+      handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+    }
+  }
+
+  return 0;
+}
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__tcp_bind(uv_tcp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+
+  err = uv_tcp_try_bind(handle, addr, addrlen, flags);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__tcp_connect(uv_connect_t* req,
+                    uv_tcp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen,
+                    uv_connect_cb cb) {
+  int err;
+
+  err = uv_tcp_try_connect(req, handle, addr, addrlen, cb);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
diff --git a/third_party/libuv/src/win/thread.c b/third_party/libuv/src/win/thread.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/thread.c
@@ -0,0 +1,520 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#if defined(__MINGW64_VERSION_MAJOR)
+/* MemoryBarrier expands to __mm_mfence in some cases (x86+sse2), which may
+ * require this header in some versions of mingw64. */
+#include <intrin.h>
+#endif
+
+#include "uv.h"
+#include "internal.h"
+
+static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
+  DWORD result;
+  HANDLE existing_event, created_event;
+
+  created_event = CreateEvent(NULL, 1, 0, NULL);
+  if (created_event == 0) {
+    /* Could fail in a low-memory situation? */
+    uv_fatal_error(GetLastError(), "CreateEvent");
+  }
+
+  existing_event = InterlockedCompareExchangePointer(&guard->event,
+                                                     created_event,
+                                                     NULL);
+
+  if (existing_event == NULL) {
+    /* We won the race */
+    callback();
+
+    result = SetEvent(created_event);
+    assert(result);
+    guard->ran = 1;
+
+  } else {
+    /* We lost the race. Destroy the event we created and wait for the existing
+     * one to become signaled. */
+    CloseHandle(created_event);
+    result = WaitForSingleObject(existing_event, INFINITE);
+    assert(result == WAIT_OBJECT_0);
+  }
+}
+
+
+void uv_once(uv_once_t* guard, void (*callback)(void)) {
+  /* Fast case - avoid WaitForSingleObject. */
+  if (guard->ran) {
+    return;
+  }
+
+  uv__once_inner(guard, callback);
+}
+
+
+/* Verify that uv_thread_t can be stored in a TLS slot. */
+STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
+
+static uv_key_t uv__current_thread_key;
+static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT;
+
+
+static void uv__init_current_thread_key(void) {
+  if (uv_key_create(&uv__current_thread_key))
+    abort();
+}
+
+
+struct thread_ctx {
+  void (*entry)(void* arg);
+  void* arg;
+  uv_thread_t self;
+};
+
+
+static UINT __stdcall uv__thread_start(void* arg) {
+  struct thread_ctx *ctx_p;
+  struct thread_ctx ctx;
+
+  ctx_p = arg;
+  ctx = *ctx_p;
+  uv__free(ctx_p);
+
+  uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
+  uv_key_set(&uv__current_thread_key, (void*) ctx.self);
+
+  ctx.entry(ctx.arg);
+
+  return 0;
+}
+
+
+int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  uv_thread_options_t params;
+  params.flags = UV_THREAD_NO_FLAGS;
+  return uv_thread_create_ex(tid, &params, entry, arg);
+}
+
+int uv_thread_create_ex(uv_thread_t* tid,
+                        const uv_thread_options_t* params,
+                        void (*entry)(void *arg),
+                        void *arg) {
+  struct thread_ctx* ctx;
+  int err;
+  HANDLE thread;
+  SYSTEM_INFO sysinfo;
+  size_t stack_size;
+  size_t pagesize;
+
+  stack_size =
+      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
+
+  if (stack_size != 0) {
+    GetNativeSystemInfo(&sysinfo);
+    pagesize = (size_t)sysinfo.dwPageSize;
+    /* Round up to the nearest page boundary. */
+    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
+
+    if ((unsigned)stack_size != stack_size)
+      return UV_EINVAL;
+  }
+
+  ctx = uv__malloc(sizeof(*ctx));
+  if (ctx == NULL)
+    return UV_ENOMEM;
+
+  ctx->entry = entry;
+  ctx->arg = arg;
+
+  /* Create the thread in suspended state so we have a chance to pass
+   * its own creation handle to it */
+  thread = (HANDLE) _beginthreadex(NULL,
+                                   (unsigned)stack_size,
+                                   uv__thread_start,
+                                   ctx,
+                                   CREATE_SUSPENDED,
+                                   NULL);
+  if (thread == NULL) {
+    err = errno;
+    uv__free(ctx);
+  } else {
+    err = 0;
+    *tid = thread;
+    ctx->self = thread;
+    ResumeThread(thread);
+  }
+
+  switch (err) {
+    case 0:
+      return 0;
+    case EACCES:
+      return UV_EACCES;
+    case EAGAIN:
+      return UV_EAGAIN;
+    case EINVAL:
+      return UV_EINVAL;
+  }
+
+  return UV_EIO;
+}
+
+
+uv_thread_t uv_thread_self(void) {
+  uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
+  return (uv_thread_t) uv_key_get(&uv__current_thread_key);
+}
+
+
+int uv_thread_join(uv_thread_t *tid) {
+  if (WaitForSingleObject(*tid, INFINITE))
+    return uv_translate_sys_error(GetLastError());
+  else {
+    CloseHandle(*tid);
+    *tid = 0;
+    MemoryBarrier();  /* For feature parity with pthread_join(). */
+    return 0;
+  }
+}
+
+
+int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
+  return *t1 == *t2;
+}
+
+
+int uv_mutex_init(uv_mutex_t* mutex) {
+  InitializeCriticalSection(mutex);
+  return 0;
+}
+
+
+int uv_mutex_init_recursive(uv_mutex_t* mutex) {
+  return uv_mutex_init(mutex);
+}
+
+
+void uv_mutex_destroy(uv_mutex_t* mutex) {
+  DeleteCriticalSection(mutex);
+}
+
+
+void uv_mutex_lock(uv_mutex_t* mutex) {
+  EnterCriticalSection(mutex);
+}
+
+
+int uv_mutex_trylock(uv_mutex_t* mutex) {
+  if (TryEnterCriticalSection(mutex))
+    return 0;
+  else
+    return UV_EBUSY;
+}
+
+
+void uv_mutex_unlock(uv_mutex_t* mutex) {
+  LeaveCriticalSection(mutex);
+}
+
+
+int uv_rwlock_init(uv_rwlock_t* rwlock) {
+  /* Initialize the semaphore that acts as the write lock. */
+  HANDLE handle = CreateSemaphoreW(NULL, 1, 1, NULL);
+  if (handle == NULL)
+    return uv_translate_sys_error(GetLastError());
+  rwlock->state_.write_semaphore_ = handle;
+
+  /* Initialize the critical section protecting the reader count. */
+  InitializeCriticalSection(&rwlock->state_.num_readers_lock_);
+
+  /* Initialize the reader count. */
+  rwlock->state_.num_readers_ = 0;
+
+  return 0;
+}
+
+
+void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
+  DeleteCriticalSection(&rwlock->state_.num_readers_lock_);
+  CloseHandle(rwlock->state_.write_semaphore_);
+}
+
+
+void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
+  /* Acquire the lock that protects the reader count. */
+  EnterCriticalSection(&rwlock->state_.num_readers_lock_);
+
+  /* Increase the reader count, and lock for write if this is the first
+   * reader.
+   */
+  if (++rwlock->state_.num_readers_ == 1) {
+    DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE);
+    if (r != WAIT_OBJECT_0)
+      uv_fatal_error(GetLastError(), "WaitForSingleObject");
+  }
+
+  /* Release the lock that protects the reader count. */
+  LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
+}
+
+
+int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
+  int err;
+
+  if (!TryEnterCriticalSection(&rwlock->state_.num_readers_lock_))
+    return UV_EBUSY;
+
+  err = 0;
+
+  if (rwlock->state_.num_readers_ == 0) {
+    /* Currently there are no other readers, which means that the write lock
+     * needs to be acquired.
+     */
+    DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0);
+    if (r == WAIT_OBJECT_0)
+      rwlock->state_.num_readers_++;
+    else if (r == WAIT_TIMEOUT)
+      err = UV_EBUSY;
+    else if (r == WAIT_FAILED)
+      uv_fatal_error(GetLastError(), "WaitForSingleObject");
+
+  } else {
+    /* The write lock has already been acquired because there are other
+     * active readers.
+     */
+    rwlock->state_.num_readers_++;
+  }
+
+  LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
+  return err;
+}
+
+
+void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
+  EnterCriticalSection(&rwlock->state_.num_readers_lock_);
+
+  if (--rwlock->state_.num_readers_ == 0) {
+    if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL))
+      uv_fatal_error(GetLastError(), "ReleaseSemaphore");
+  }
+
+  LeaveCriticalSection(&rwlock->state_.num_readers_lock_);
+}
+
+
+void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
+  DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE);
+  if (r != WAIT_OBJECT_0)
+    uv_fatal_error(GetLastError(), "WaitForSingleObject");
+}
+
+
+int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
+  DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0);
+  if (r == WAIT_OBJECT_0)
+    return 0;
+  else if (r == WAIT_TIMEOUT)
+    return UV_EBUSY;
+  else
+    uv_fatal_error(GetLastError(), "WaitForSingleObject");
+}
+
+
+void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
+  if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL))
+    uv_fatal_error(GetLastError(), "ReleaseSemaphore");
+}
+
+
+int uv_sem_init(uv_sem_t* sem, unsigned int value) {
+  *sem = CreateSemaphore(NULL, value, INT_MAX, NULL);
+  if (*sem == NULL)
+    return uv_translate_sys_error(GetLastError());
+  else
+    return 0;
+}
+
+
+void uv_sem_destroy(uv_sem_t* sem) {
+  if (!CloseHandle(*sem))
+    abort();
+}
+
+
+void uv_sem_post(uv_sem_t* sem) {
+  if (!ReleaseSemaphore(*sem, 1, NULL))
+    abort();
+}
+
+
+void uv_sem_wait(uv_sem_t* sem) {
+  if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0)
+    abort();
+}
+
+
+int uv_sem_trywait(uv_sem_t* sem) {
+  DWORD r = WaitForSingleObject(*sem, 0);
+
+  if (r == WAIT_OBJECT_0)
+    return 0;
+
+  if (r == WAIT_TIMEOUT)
+    return UV_EAGAIN;
+
+  abort();
+  return -1; /* Satisfy the compiler. */
+}
+
+
+int uv_cond_init(uv_cond_t* cond) {
+  InitializeConditionVariable(&cond->cond_var);
+  return 0;
+}
+
+
+void uv_cond_destroy(uv_cond_t* cond) {
+  /* nothing to do */
+  (void) &cond;
+}
+
+
+void uv_cond_signal(uv_cond_t* cond) {
+  WakeConditionVariable(&cond->cond_var);
+}
+
+
+void uv_cond_broadcast(uv_cond_t* cond) {
+  WakeAllConditionVariable(&cond->cond_var);
+}
+
+
+void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
+  if (!SleepConditionVariableCS(&cond->cond_var, mutex, INFINITE))
+    abort();
+}
+
+int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
+  if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
+    return 0;
+  if (GetLastError() != ERROR_TIMEOUT)
+    abort();
+  return UV_ETIMEDOUT;
+}
+
+
+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+  int err;
+
+  barrier->n = count;
+  barrier->count = 0;
+
+  err = uv_mutex_init(&barrier->mutex);
+  if (err)
+    return err;
+
+  err = uv_sem_init(&barrier->turnstile1, 0);
+  if (err)
+    goto error2;
+
+  err = uv_sem_init(&barrier->turnstile2, 1);
+  if (err)
+    goto error;
+
+  return 0;
+
+error:
+  uv_sem_destroy(&barrier->turnstile1);
+error2:
+  uv_mutex_destroy(&barrier->mutex);
+  return err;
+
+}
+
+
+void uv_barrier_destroy(uv_barrier_t* barrier) {
+  uv_sem_destroy(&barrier->turnstile2);
+  uv_sem_destroy(&barrier->turnstile1);
+  uv_mutex_destroy(&barrier->mutex);
+}
+
+
+int uv_barrier_wait(uv_barrier_t* barrier) {
+  int serial_thread;
+
+  uv_mutex_lock(&barrier->mutex);
+  if (++barrier->count == barrier->n) {
+    uv_sem_wait(&barrier->turnstile2);
+    uv_sem_post(&barrier->turnstile1);
+  }
+  uv_mutex_unlock(&barrier->mutex);
+
+  uv_sem_wait(&barrier->turnstile1);
+  uv_sem_post(&barrier->turnstile1);
+
+  uv_mutex_lock(&barrier->mutex);
+  serial_thread = (--barrier->count == 0);
+  if (serial_thread) {
+    uv_sem_wait(&barrier->turnstile1);
+    uv_sem_post(&barrier->turnstile2);
+  }
+  uv_mutex_unlock(&barrier->mutex);
+
+  uv_sem_wait(&barrier->turnstile2);
+  uv_sem_post(&barrier->turnstile2);
+  return serial_thread;
+}
+
+
+int uv_key_create(uv_key_t* key) {
+  key->tls_index = TlsAlloc();
+  if (key->tls_index == TLS_OUT_OF_INDEXES)
+    return UV_ENOMEM;
+  return 0;
+}
+
+
+void uv_key_delete(uv_key_t* key) {
+  if (TlsFree(key->tls_index) == FALSE)
+    abort();
+  key->tls_index = TLS_OUT_OF_INDEXES;
+}
+
+
+void* uv_key_get(uv_key_t* key) {
+  void* value;
+
+  value = TlsGetValue(key->tls_index);
+  if (value == NULL)
+    if (GetLastError() != ERROR_SUCCESS)
+      abort();
+
+  return value;
+}
+
+
+void uv_key_set(uv_key_t* key, void* value) {
+  if (TlsSetValue(key->tls_index, value) == FALSE)
+    abort();
+}
diff --git a/third_party/libuv/src/win/tty.c b/third_party/libuv/src/win/tty.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/tty.c
@@ -0,0 +1,2453 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <io.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "uv/stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#ifndef COMMON_LVB_REVERSE_VIDEO
+# define COMMON_LVB_REVERSE_VIDEO 0x4000
+#endif
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "stream-inl.h"
+#include "req-inl.h"
+
+#ifndef InterlockedOr
+# define InterlockedOr _InterlockedOr
+#endif
+
+#define UNICODE_REPLACEMENT_CHARACTER (0xfffd)
+
+#define ANSI_NORMAL           0x0000
+#define ANSI_ESCAPE_SEEN      0x0002
+#define ANSI_CSI              0x0004
+#define ANSI_ST_CONTROL       0x0008
+#define ANSI_IGNORE           0x0010
+#define ANSI_IN_ARG           0x0020
+#define ANSI_IN_STRING        0x0040
+#define ANSI_BACKSLASH_SEEN   0x0080
+#define ANSI_EXTENSION        0x0100
+#define ANSI_DECSCUSR         0x0200
+
+#define MAX_INPUT_BUFFER_LENGTH 8192
+#define MAX_CONSOLE_CHAR 8192
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#define CURSOR_SIZE_SMALL     25
+#define CURSOR_SIZE_LARGE     100
+
+static void uv_tty_capture_initial_style(
+    CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
+    CONSOLE_CURSOR_INFO* cursor_info);
+static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info);
+static int uv__cancel_read_console(uv_tty_t* handle);
+
+
+/* Null uv_buf_t */
+static const uv_buf_t uv_null_buf_ = { 0, NULL };
+
+enum uv__read_console_status_e {
+  NOT_STARTED,
+  IN_PROGRESS,
+  TRAP_REQUESTED,
+  COMPLETED
+};
+
+static volatile LONG uv__read_console_status = NOT_STARTED;
+static volatile LONG uv__restore_screen_state;
+static CONSOLE_SCREEN_BUFFER_INFO uv__saved_screen_state;
+
+
+/*
+ * The console virtual window.
+ *
+ * Normally cursor movement in windows is relative to the console screen buffer,
+ * e.g. the application is allowed to overwrite the 'history'. This is very
+ * inconvenient, it makes absolute cursor movement pretty useless. There is
+ * also the concept of 'client rect' which is defined by the actual size of
+ * the console window and the scroll position of the screen buffer, but it's
+ * very volatile because it changes when the user scrolls.
+ *
+ * To make cursor movement behave sensibly we define a virtual window to which
+ * cursor movement is confined. The virtual window is always as wide as the
+ * console screen buffer, but it's height is defined by the size of the
+ * console window. The top of the virtual window aligns with the position
+ * of the caret when the first stdout/err handle is created, unless that would
+ * mean that it would extend beyond the bottom of the screen buffer -  in that
+ * that case it's located as far down as possible.
+ *
+ * When the user writes a long text or many newlines, such that the output
+ * reaches beyond the bottom of the virtual window, the virtual window is
+ * shifted downwards, but not resized.
+ *
+ * Since all tty i/o happens on the same console, this window is shared
+ * between all stdout/stderr handles.
+ */
+
+static int uv_tty_virtual_offset = -1;
+static int uv_tty_virtual_height = -1;
+static int uv_tty_virtual_width = -1;
+
+/* The console window size
+ * We keep this separate from uv_tty_virtual_*. We use those values to only
+ * handle signalling SIGWINCH
+ */
+
+static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE;
+static int uv__tty_console_height = -1;
+static int uv__tty_console_width = -1;
+static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE;
+static uv_mutex_t uv__tty_console_resize_mutex;
+
+static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param);
+static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
+                                                  DWORD event,
+                                                  HWND hwnd,
+                                                  LONG idObject,
+                                                  LONG idChild,
+                                                  DWORD dwEventThread,
+                                                  DWORD dwmsEventTime);
+static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param);
+static void uv__tty_console_signal_resize(void);
+
+/* We use a semaphore rather than a mutex or critical section because in some
+   cases (uv__cancel_read_console) we need take the lock in the main thread and
+   release it in another thread. Using a semaphore ensures that in such
+   scenario the main thread will still block when trying to acquire the lock. */
+static uv_sem_t uv_tty_output_lock;
+
+static WORD uv_tty_default_text_attributes =
+    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+
+static char uv_tty_default_fg_color = 7;
+static char uv_tty_default_bg_color = 0;
+static char uv_tty_default_fg_bright = 0;
+static char uv_tty_default_bg_bright = 0;
+static char uv_tty_default_inverse = 0;
+
+static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info;
+
+/* Determine whether or not ANSI support is enabled. */
+static BOOL uv__need_check_vterm_state = TRUE;
+static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED;
+static void uv__determine_vterm_state(HANDLE handle);
+
+void uv_console_init(void) {
+  if (uv_sem_init(&uv_tty_output_lock, 1))
+    abort();
+  uv__tty_console_handle = CreateFileW(L"CONOUT$",
+                                       GENERIC_READ | GENERIC_WRITE,
+                                       FILE_SHARE_WRITE,
+                                       0,
+                                       OPEN_EXISTING,
+                                       0,
+                                       0);
+  if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
+    CONSOLE_SCREEN_BUFFER_INFO sb_info;
+    QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
+                      NULL,
+                      WT_EXECUTELONGFUNCTION);
+    uv_mutex_init(&uv__tty_console_resize_mutex);
+    if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) {
+      uv__tty_console_width = sb_info.dwSize.X;
+      uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
+    }
+  }
+}
+
+
+int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
+  BOOL readable;
+  DWORD NumberOfEvents;
+  HANDLE handle;
+  CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
+  CONSOLE_CURSOR_INFO cursor_info;
+  (void)unused;
+
+  uv__once_init();
+  handle = (HANDLE) uv__get_osfhandle(fd);
+  if (handle == INVALID_HANDLE_VALUE)
+    return UV_EBADF;
+
+  if (fd <= 2) {
+    /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
+     * underlying OS handle and forget about the original fd.
+     * We could also opt to use the original OS handle and just never close it,
+     * but then there would be no reliable way to cancel pending read operations
+     * upon close.
+     */
+    if (!DuplicateHandle(INVALID_HANDLE_VALUE,
+                         handle,
+                         INVALID_HANDLE_VALUE,
+                         &handle,
+                         0,
+                         FALSE,
+                         DUPLICATE_SAME_ACCESS))
+      return uv_translate_sys_error(GetLastError());
+    fd = -1;
+  }
+
+  readable = GetNumberOfConsoleInputEvents(handle, &NumberOfEvents);
+  if (!readable) {
+    /* Obtain the screen buffer info with the output handle. */
+    if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) {
+      return uv_translate_sys_error(GetLastError());
+    }
+
+    /* Obtain the cursor info with the output handle. */
+    if (!GetConsoleCursorInfo(handle, &cursor_info)) {
+      return uv_translate_sys_error(GetLastError());
+    }
+
+    /* Obtain the tty_output_lock because the virtual window state is shared
+     * between all uv_tty_t handles. */
+    uv_sem_wait(&uv_tty_output_lock);
+
+    if (uv__need_check_vterm_state)
+      uv__determine_vterm_state(handle);
+
+    /* Remember the original console text attributes and cursor info. */
+    uv_tty_capture_initial_style(&screen_buffer_info, &cursor_info);
+
+    uv_tty_update_virtual_window(&screen_buffer_info);
+
+    uv_sem_post(&uv_tty_output_lock);
+  }
+
+
+  uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY);
+  uv_connection_init((uv_stream_t*) tty);
+
+  tty->handle = handle;
+  tty->u.fd = fd;
+  tty->reqs_pending = 0;
+  tty->flags |= UV_HANDLE_BOUND;
+
+  if (readable) {
+    /* Initialize TTY input specific fields. */
+    tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
+    /* TODO: remove me in v2.x. */
+    tty->tty.rd.unused_ = NULL;
+    tty->tty.rd.read_line_buffer = uv_null_buf_;
+    tty->tty.rd.read_raw_wait = NULL;
+
+    /* Init keycode-to-vt100 mapper state. */
+    tty->tty.rd.last_key_len = 0;
+    tty->tty.rd.last_key_offset = 0;
+    tty->tty.rd.last_utf16_high_surrogate = 0;
+    memset(&tty->tty.rd.last_input_record, 0, sizeof tty->tty.rd.last_input_record);
+  } else {
+    /* TTY output specific fields. */
+    tty->flags |= UV_HANDLE_WRITABLE;
+
+    /* Init utf8-to-utf16 conversion state. */
+    tty->tty.wr.utf8_bytes_left = 0;
+    tty->tty.wr.utf8_codepoint = 0;
+
+    /* Initialize eol conversion state */
+    tty->tty.wr.previous_eol = 0;
+
+    /* Init ANSI parser state. */
+    tty->tty.wr.ansi_parser_state = ANSI_NORMAL;
+  }
+
+  return 0;
+}
+
+
+/* Set the default console text attributes based on how the console was
+ * configured when libuv started.
+ */
+static void uv_tty_capture_initial_style(
+    CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
+    CONSOLE_CURSOR_INFO* cursor_info) {
+  static int style_captured = 0;
+
+  /* Only do this once.
+     Assumption: Caller has acquired uv_tty_output_lock. */
+  if (style_captured)
+    return;
+
+  /* Save raw win32 attributes. */
+  uv_tty_default_text_attributes = screen_buffer_info->wAttributes;
+
+  /* Convert black text on black background to use white text. */
+  if (uv_tty_default_text_attributes == 0)
+    uv_tty_default_text_attributes = 7;
+
+  /* Convert Win32 attributes to ANSI colors. */
+  uv_tty_default_fg_color = 0;
+  uv_tty_default_bg_color = 0;
+  uv_tty_default_fg_bright = 0;
+  uv_tty_default_bg_bright = 0;
+  uv_tty_default_inverse = 0;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_RED)
+    uv_tty_default_fg_color |= 1;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_GREEN)
+    uv_tty_default_fg_color |= 2;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_BLUE)
+    uv_tty_default_fg_color |= 4;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_RED)
+    uv_tty_default_bg_color |= 1;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_GREEN)
+    uv_tty_default_bg_color |= 2;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_BLUE)
+    uv_tty_default_bg_color |= 4;
+
+  if (uv_tty_default_text_attributes & FOREGROUND_INTENSITY)
+    uv_tty_default_fg_bright = 1;
+
+  if (uv_tty_default_text_attributes & BACKGROUND_INTENSITY)
+    uv_tty_default_bg_bright = 1;
+
+  if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO)
+    uv_tty_default_inverse = 1;
+
+  /* Save the cursor size and the cursor state. */
+  uv_tty_default_cursor_info = *cursor_info;
+
+  style_captured = 1;
+}
+
+
+int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
+  DWORD flags;
+  unsigned char was_reading;
+  uv_alloc_cb alloc_cb;
+  uv_read_cb read_cb;
+  int err;
+
+  if (!(tty->flags & UV_HANDLE_TTY_READABLE)) {
+    return UV_EINVAL;
+  }
+
+  if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
+    return 0;
+  }
+
+  switch (mode) {
+    case UV_TTY_MODE_NORMAL:
+      flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+      break;
+    case UV_TTY_MODE_RAW:
+      flags = ENABLE_WINDOW_INPUT;
+      break;
+    case UV_TTY_MODE_IO:
+      return UV_ENOTSUP;
+    default:
+      return UV_EINVAL;
+  }
+
+  /* If currently reading, stop, and restart reading. */
+  if (tty->flags & UV_HANDLE_READING) {
+    was_reading = 1;
+    alloc_cb = tty->alloc_cb;
+    read_cb = tty->read_cb;
+    err = uv_tty_read_stop(tty);
+    if (err) {
+      return uv_translate_sys_error(err);
+    }
+  } else {
+    was_reading = 0;
+    alloc_cb = NULL;
+    read_cb = NULL;
+  }
+
+  uv_sem_wait(&uv_tty_output_lock);
+  if (!SetConsoleMode(tty->handle, flags)) {
+    err = uv_translate_sys_error(GetLastError());
+    uv_sem_post(&uv_tty_output_lock);
+    return err;
+  }
+  uv_sem_post(&uv_tty_output_lock);
+
+  /* Update flag. */
+  tty->flags &= ~UV_HANDLE_TTY_RAW;
+  tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0;
+
+  /* If we just stopped reading, restart. */
+  if (was_reading) {
+    err = uv_tty_read_start(tty, alloc_cb, read_cb);
+    if (err) {
+      return uv_translate_sys_error(err);
+    }
+  }
+
+  return 0;
+}
+
+
+int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+
+  if (!GetConsoleScreenBufferInfo(tty->handle, &info)) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  uv_sem_wait(&uv_tty_output_lock);
+  uv_tty_update_virtual_window(&info);
+  uv_sem_post(&uv_tty_output_lock);
+
+  *width = uv_tty_virtual_width;
+  *height = uv_tty_virtual_height;
+
+  return 0;
+}
+
+
+static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) {
+  uv_loop_t* loop;
+  uv_tty_t* handle;
+  uv_req_t* req;
+
+  assert(data);
+  assert(!didTimeout);
+
+  req = (uv_req_t*) data;
+  handle = (uv_tty_t*) req->data;
+  loop = handle->loop;
+
+  UnregisterWait(handle->tty.rd.read_raw_wait);
+  handle->tty.rd.read_raw_wait = NULL;
+
+  SET_REQ_SUCCESS(req);
+  POST_COMPLETION_FOR_REQ(loop, req);
+}
+
+
+static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
+  uv_read_t* req;
+  BOOL r;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
+
+  handle->tty.rd.read_line_buffer = uv_null_buf_;
+
+  req = &handle->read_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait,
+                                  handle->handle,
+                                  uv_tty_post_raw_read,
+                                  (void*) req,
+                                  INFINITE,
+                                  WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
+  if (!r) {
+    handle->tty.rd.read_raw_wait = NULL;
+    SET_REQ_ERROR(req, GetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+  }
+
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+}
+
+
+static DWORD CALLBACK uv_tty_line_read_thread(void* data) {
+  uv_loop_t* loop;
+  uv_tty_t* handle;
+  uv_req_t* req;
+  DWORD bytes, read_bytes;
+  WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3];
+  DWORD chars, read_chars;
+  LONG status;
+  COORD pos;
+  BOOL read_console_success;
+
+  assert(data);
+
+  req = (uv_req_t*) data;
+  handle = (uv_tty_t*) req->data;
+  loop = handle->loop;
+
+  assert(handle->tty.rd.read_line_buffer.base != NULL);
+  assert(handle->tty.rd.read_line_buffer.len > 0);
+
+  /* ReadConsole can't handle big buffers. */
+  if (handle->tty.rd.read_line_buffer.len < MAX_INPUT_BUFFER_LENGTH) {
+    bytes = handle->tty.rd.read_line_buffer.len;
+  } else {
+    bytes = MAX_INPUT_BUFFER_LENGTH;
+  }
+
+  /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8
+   * codeunits to encode. */
+  chars = bytes / 3;
+
+  status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS);
+  if (status == TRAP_REQUESTED) {
+    SET_REQ_SUCCESS(req);
+    InterlockedExchange(&uv__read_console_status, COMPLETED);
+    req->u.io.overlapped.InternalHigh = 0;
+    POST_COMPLETION_FOR_REQ(loop, req);
+    return 0;
+  }
+
+  read_console_success = ReadConsoleW(handle->handle,
+                                      (void*) utf16,
+                                      chars,
+                                      &read_chars,
+                                      NULL);
+
+  if (read_console_success) {
+    read_bytes = WideCharToMultiByte(CP_UTF8,
+                                     0,
+                                     utf16,
+                                     read_chars,
+                                     handle->tty.rd.read_line_buffer.base,
+                                     bytes,
+                                     NULL,
+                                     NULL);
+    SET_REQ_SUCCESS(req);
+    req->u.io.overlapped.InternalHigh = read_bytes;
+  } else {
+    SET_REQ_ERROR(req, GetLastError());
+  }
+
+  status = InterlockedExchange(&uv__read_console_status, COMPLETED);
+
+  if (status ==  TRAP_REQUESTED) {
+    /* If we canceled the read by sending a VK_RETURN event, restore the
+       screen state to undo the visual effect of the VK_RETURN */
+    if (read_console_success && InterlockedOr(&uv__restore_screen_state, 0)) {
+      HANDLE active_screen_buffer;
+      active_screen_buffer = CreateFileA("conout$",
+                                         GENERIC_READ | GENERIC_WRITE,
+                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                         NULL,
+                                         OPEN_EXISTING,
+                                         FILE_ATTRIBUTE_NORMAL,
+                                         NULL);
+      if (active_screen_buffer != INVALID_HANDLE_VALUE) {
+        pos = uv__saved_screen_state.dwCursorPosition;
+
+        /* If the cursor was at the bottom line of the screen buffer, the
+           VK_RETURN would have caused the buffer contents to scroll up by one
+           line. The right position to reset the cursor to is therefore one line
+           higher */
+        if (pos.Y == uv__saved_screen_state.dwSize.Y - 1)
+          pos.Y--;
+
+        SetConsoleCursorPosition(active_screen_buffer, pos);
+        CloseHandle(active_screen_buffer);
+      }
+    }
+    uv_sem_post(&uv_tty_output_lock);
+  }
+  POST_COMPLETION_FOR_REQ(loop, req);
+  return 0;
+}
+
+
+static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
+  uv_read_t* req;
+  BOOL r;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+  assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
+
+  req = &handle->read_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  handle->tty.rd.read_line_buffer = uv_buf_init(NULL, 0);
+  handle->alloc_cb((uv_handle_t*) handle, 8192, &handle->tty.rd.read_line_buffer);
+  if (handle->tty.rd.read_line_buffer.base == NULL ||
+      handle->tty.rd.read_line_buffer.len == 0) {
+    handle->read_cb((uv_stream_t*) handle,
+                    UV_ENOBUFS,
+                    &handle->tty.rd.read_line_buffer);
+    return;
+  }
+  assert(handle->tty.rd.read_line_buffer.base != NULL);
+
+  /* Reset flags  No locking is required since there cannot be a line read
+     in progress. We are also relying on the memory barrier provided by
+     QueueUserWorkItem*/
+  uv__restore_screen_state = FALSE;
+  uv__read_console_status = NOT_STARTED;
+  r = QueueUserWorkItem(uv_tty_line_read_thread,
+                        (void*) req,
+                        WT_EXECUTELONGFUNCTION);
+  if (!r) {
+    SET_REQ_ERROR(req, GetLastError());
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+  }
+
+  handle->flags |= UV_HANDLE_READ_PENDING;
+  handle->reqs_pending++;
+}
+
+
+static void uv_tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) {
+  if (handle->flags & UV_HANDLE_TTY_RAW) {
+    uv_tty_queue_read_raw(loop, handle);
+  } else {
+    uv_tty_queue_read_line(loop, handle);
+  }
+}
+
+
+static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl,
+    size_t* len) {
+#define VK_CASE(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str)          \
+    case (vk):                                                                \
+      if (shift && ctrl) {                                                    \
+        *len = sizeof shift_ctrl_str;                                         \
+        return "\033" shift_ctrl_str;                                         \
+      } else if (shift) {                                                     \
+        *len = sizeof shift_str ;                                             \
+        return "\033" shift_str;                                              \
+      } else if (ctrl) {                                                      \
+        *len = sizeof ctrl_str;                                               \
+        return "\033" ctrl_str;                                               \
+      } else {                                                                \
+        *len = sizeof normal_str;                                             \
+        return "\033" normal_str;                                             \
+      }
+
+  switch (code) {
+    /* These mappings are the same as Cygwin's. Unmodified and alt-modified
+     * keypad keys comply with linux console, modifiers comply with xterm
+     * modifier usage. F1. f12 and shift-f1. f10 comply with linux console, f6.
+     * f12 with and without modifiers comply with rxvt. */
+    VK_CASE(VK_INSERT,  "[2~",  "[2;2~", "[2;5~", "[2;6~")
+    VK_CASE(VK_END,     "[4~",  "[4;2~", "[4;5~", "[4;6~")
+    VK_CASE(VK_DOWN,    "[B",   "[1;2B", "[1;5B", "[1;6B")
+    VK_CASE(VK_NEXT,    "[6~",  "[6;2~", "[6;5~", "[6;6~")
+    VK_CASE(VK_LEFT,    "[D",   "[1;2D", "[1;5D", "[1;6D")
+    VK_CASE(VK_CLEAR,   "[G",   "[1;2G", "[1;5G", "[1;6G")
+    VK_CASE(VK_RIGHT,   "[C",   "[1;2C", "[1;5C", "[1;6C")
+    VK_CASE(VK_UP,      "[A",   "[1;2A", "[1;5A", "[1;6A")
+    VK_CASE(VK_HOME,    "[1~",  "[1;2~", "[1;5~", "[1;6~")
+    VK_CASE(VK_PRIOR,   "[5~",  "[5;2~", "[5;5~", "[5;6~")
+    VK_CASE(VK_DELETE,  "[3~",  "[3;2~", "[3;5~", "[3;6~")
+    VK_CASE(VK_NUMPAD0, "[2~",  "[2;2~", "[2;5~", "[2;6~")
+    VK_CASE(VK_NUMPAD1, "[4~",  "[4;2~", "[4;5~", "[4;6~")
+    VK_CASE(VK_NUMPAD2, "[B",   "[1;2B", "[1;5B", "[1;6B")
+    VK_CASE(VK_NUMPAD3, "[6~",  "[6;2~", "[6;5~", "[6;6~")
+    VK_CASE(VK_NUMPAD4, "[D",   "[1;2D", "[1;5D", "[1;6D")
+    VK_CASE(VK_NUMPAD5, "[G",   "[1;2G", "[1;5G", "[1;6G")
+    VK_CASE(VK_NUMPAD6, "[C",   "[1;2C", "[1;5C", "[1;6C")
+    VK_CASE(VK_NUMPAD7, "[A",   "[1;2A", "[1;5A", "[1;6A")
+    VK_CASE(VK_NUMPAD8, "[1~",  "[1;2~", "[1;5~", "[1;6~")
+    VK_CASE(VK_NUMPAD9, "[5~",  "[5;2~", "[5;5~", "[5;6~")
+    VK_CASE(VK_DECIMAL, "[3~",  "[3;2~", "[3;5~", "[3;6~")
+    VK_CASE(VK_F1,      "[[A",  "[23~",  "[11^",  "[23^" )
+    VK_CASE(VK_F2,      "[[B",  "[24~",  "[12^",  "[24^" )
+    VK_CASE(VK_F3,      "[[C",  "[25~",  "[13^",  "[25^" )
+    VK_CASE(VK_F4,      "[[D",  "[26~",  "[14^",  "[26^" )
+    VK_CASE(VK_F5,      "[[E",  "[28~",  "[15^",  "[28^" )
+    VK_CASE(VK_F6,      "[17~", "[29~",  "[17^",  "[29^" )
+    VK_CASE(VK_F7,      "[18~", "[31~",  "[18^",  "[31^" )
+    VK_CASE(VK_F8,      "[19~", "[32~",  "[19^",  "[32^" )
+    VK_CASE(VK_F9,      "[20~", "[33~",  "[20^",  "[33^" )
+    VK_CASE(VK_F10,     "[21~", "[34~",  "[21^",  "[34^" )
+    VK_CASE(VK_F11,     "[23~", "[23$",  "[23^",  "[23@" )
+    VK_CASE(VK_F12,     "[24~", "[24$",  "[24^",  "[24@" )
+
+    default:
+      *len = 0;
+      return NULL;
+  }
+#undef VK_CASE
+}
+
+
+void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req) {
+  /* Shortcut for handle->tty.rd.last_input_record.Event.KeyEvent. */
+#define KEV handle->tty.rd.last_input_record.Event.KeyEvent
+
+  DWORD records_left, records_read;
+  uv_buf_t buf;
+  off_t buf_used;
+
+  assert(handle->type == UV_TTY);
+  assert(handle->flags & UV_HANDLE_TTY_READABLE);
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+
+  if (!(handle->flags & UV_HANDLE_READING) ||
+      !(handle->flags & UV_HANDLE_TTY_RAW)) {
+    goto out;
+  }
+
+  if (!REQ_SUCCESS(req)) {
+    /* An error occurred while waiting for the event. */
+    if ((handle->flags & UV_HANDLE_READING)) {
+      handle->flags &= ~UV_HANDLE_READING;
+      handle->read_cb((uv_stream_t*)handle,
+                      uv_translate_sys_error(GET_REQ_ERROR(req)),
+                      &uv_null_buf_);
+    }
+    goto out;
+  }
+
+  /* Fetch the number of events  */
+  if (!GetNumberOfConsoleInputEvents(handle->handle, &records_left)) {
+    handle->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+    handle->read_cb((uv_stream_t*)handle,
+                    uv_translate_sys_error(GetLastError()),
+                    &uv_null_buf_);
+    goto out;
+  }
+
+  /* Windows sends a lot of events that we're not interested in, so buf will be
+   * allocated on demand, when there's actually something to emit. */
+  buf = uv_null_buf_;
+  buf_used = 0;
+
+  while ((records_left > 0 || handle->tty.rd.last_key_len > 0) &&
+         (handle->flags & UV_HANDLE_READING)) {
+    if (handle->tty.rd.last_key_len == 0) {
+      /* Read the next input record */
+      if (!ReadConsoleInputW(handle->handle,
+                             &handle->tty.rd.last_input_record,
+                             1,
+                             &records_read)) {
+        handle->flags &= ~UV_HANDLE_READING;
+        DECREASE_ACTIVE_COUNT(loop, handle);
+        handle->read_cb((uv_stream_t*) handle,
+                        uv_translate_sys_error(GetLastError()),
+                        &buf);
+        goto out;
+      }
+      records_left--;
+
+      /* We might be not subscribed to EVENT_CONSOLE_LAYOUT or we might be
+       * running under some TTY emulator that does not send those events. */
+      if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
+        uv__tty_console_signal_resize();
+      }
+
+      /* Ignore other events that are not key events. */
+      if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) {
+        continue;
+      }
+
+      /* Ignore keyup events, unless the left alt key was held and a valid
+       * unicode character was emitted. */
+      if (!KEV.bKeyDown &&
+          (KEV.wVirtualKeyCode != VK_MENU ||
+           KEV.uChar.UnicodeChar == 0)) {
+        continue;
+      }
+
+      /* Ignore keypresses to numpad number keys if the left alt is held
+       * because the user is composing a character, or windows simulating this.
+       */
+      if ((KEV.dwControlKeyState & LEFT_ALT_PRESSED) &&
+          !(KEV.dwControlKeyState & ENHANCED_KEY) &&
+          (KEV.wVirtualKeyCode == VK_INSERT ||
+          KEV.wVirtualKeyCode == VK_END ||
+          KEV.wVirtualKeyCode == VK_DOWN ||
+          KEV.wVirtualKeyCode == VK_NEXT ||
+          KEV.wVirtualKeyCode == VK_LEFT ||
+          KEV.wVirtualKeyCode == VK_CLEAR ||
+          KEV.wVirtualKeyCode == VK_RIGHT ||
+          KEV.wVirtualKeyCode == VK_HOME ||
+          KEV.wVirtualKeyCode == VK_UP ||
+          KEV.wVirtualKeyCode == VK_PRIOR ||
+          KEV.wVirtualKeyCode == VK_NUMPAD0 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD1 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD2 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD3 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD4 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD5 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD6 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD7 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD8 ||
+          KEV.wVirtualKeyCode == VK_NUMPAD9)) {
+        continue;
+      }
+
+      if (KEV.uChar.UnicodeChar != 0) {
+        int prefix_len, char_len;
+
+        /* Character key pressed */
+        if (KEV.uChar.UnicodeChar >= 0xD800 &&
+            KEV.uChar.UnicodeChar < 0xDC00) {
+          /* UTF-16 high surrogate */
+          handle->tty.rd.last_utf16_high_surrogate = KEV.uChar.UnicodeChar;
+          continue;
+        }
+
+        /* Prefix with \u033 if alt was held, but alt was not used as part a
+         * compose sequence. */
+        if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+            && !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED |
+            RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) {
+          handle->tty.rd.last_key[0] = '\033';
+          prefix_len = 1;
+        } else {
+          prefix_len = 0;
+        }
+
+        if (KEV.uChar.UnicodeChar >= 0xDC00 &&
+            KEV.uChar.UnicodeChar < 0xE000) {
+          /* UTF-16 surrogate pair */
+          WCHAR utf16_buffer[2];
+          utf16_buffer[0] = handle->tty.rd.last_utf16_high_surrogate;
+          utf16_buffer[1] = KEV.uChar.UnicodeChar;
+          char_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         utf16_buffer,
+                                         2,
+                                         &handle->tty.rd.last_key[prefix_len],
+                                         sizeof handle->tty.rd.last_key,
+                                         NULL,
+                                         NULL);
+        } else {
+          /* Single UTF-16 character */
+          char_len = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         &KEV.uChar.UnicodeChar,
+                                         1,
+                                         &handle->tty.rd.last_key[prefix_len],
+                                         sizeof handle->tty.rd.last_key,
+                                         NULL,
+                                         NULL);
+        }
+
+        /* Whatever happened, the last character wasn't a high surrogate. */
+        handle->tty.rd.last_utf16_high_surrogate = 0;
+
+        /* If the utf16 character(s) couldn't be converted something must be
+         * wrong. */
+        if (!char_len) {
+          handle->flags &= ~UV_HANDLE_READING;
+          DECREASE_ACTIVE_COUNT(loop, handle);
+          handle->read_cb((uv_stream_t*) handle,
+                          uv_translate_sys_error(GetLastError()),
+                          &buf);
+          goto out;
+        }
+
+        handle->tty.rd.last_key_len = (unsigned char) (prefix_len + char_len);
+        handle->tty.rd.last_key_offset = 0;
+        continue;
+
+      } else {
+        /* Function key pressed */
+        const char* vt100;
+        size_t prefix_len, vt100_len;
+
+        vt100 = get_vt100_fn_key(KEV.wVirtualKeyCode,
+                                  !!(KEV.dwControlKeyState & SHIFT_PRESSED),
+                                  !!(KEV.dwControlKeyState & (
+                                    LEFT_CTRL_PRESSED |
+                                    RIGHT_CTRL_PRESSED)),
+                                  &vt100_len);
+
+        /* If we were unable to map to a vt100 sequence, just ignore. */
+        if (!vt100) {
+          continue;
+        }
+
+        /* Prefix with \x033 when the alt key was held. */
+        if (KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
+          handle->tty.rd.last_key[0] = '\033';
+          prefix_len = 1;
+        } else {
+          prefix_len = 0;
+        }
+
+        /* Copy the vt100 sequence to the handle buffer. */
+        assert(prefix_len + vt100_len < sizeof handle->tty.rd.last_key);
+        memcpy(&handle->tty.rd.last_key[prefix_len], vt100, vt100_len);
+
+        handle->tty.rd.last_key_len = (unsigned char) (prefix_len + vt100_len);
+        handle->tty.rd.last_key_offset = 0;
+        continue;
+      }
+    } else {
+      /* Copy any bytes left from the last keypress to the user buffer. */
+      if (handle->tty.rd.last_key_offset < handle->tty.rd.last_key_len) {
+        /* Allocate a buffer if needed */
+        if (buf_used == 0) {
+          buf = uv_buf_init(NULL, 0);
+          handle->alloc_cb((uv_handle_t*) handle, 1024, &buf);
+          if (buf.base == NULL || buf.len == 0) {
+            handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
+            goto out;
+          }
+          assert(buf.base != NULL);
+        }
+
+        buf.base[buf_used++] = handle->tty.rd.last_key[handle->tty.rd.last_key_offset++];
+
+        /* If the buffer is full, emit it */
+        if ((size_t) buf_used == buf.len) {
+          handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
+          buf = uv_null_buf_;
+          buf_used = 0;
+        }
+
+        continue;
+      }
+
+      /* Apply dwRepeat from the last input record. */
+      if (--KEV.wRepeatCount > 0) {
+        handle->tty.rd.last_key_offset = 0;
+        continue;
+      }
+
+      handle->tty.rd.last_key_len = 0;
+      continue;
+    }
+  }
+
+  /* Send the buffer back to the user */
+  if (buf_used > 0) {
+    handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
+  }
+
+ out:
+  /* Wait for more input events. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv_tty_queue_read(loop, handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+
+#undef KEV
+}
+
+
+
+void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req) {
+  uv_buf_t buf;
+
+  assert(handle->type == UV_TTY);
+  assert(handle->flags & UV_HANDLE_TTY_READABLE);
+
+  buf = handle->tty.rd.read_line_buffer;
+
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+  handle->tty.rd.read_line_buffer = uv_null_buf_;
+
+  if (!REQ_SUCCESS(req)) {
+    /* Read was not successful */
+    if (handle->flags & UV_HANDLE_READING) {
+      /* Real error */
+      handle->flags &= ~UV_HANDLE_READING;
+      DECREASE_ACTIVE_COUNT(loop, handle);
+      handle->read_cb((uv_stream_t*) handle,
+                      uv_translate_sys_error(GET_REQ_ERROR(req)),
+                      &buf);
+    }
+  } else {
+    if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING) &&
+        req->u.io.overlapped.InternalHigh != 0) {
+      /* Read successful. TODO: read unicode, convert to utf-8 */
+      DWORD bytes = req->u.io.overlapped.InternalHigh;
+      handle->read_cb((uv_stream_t*) handle, bytes, &buf);
+    }
+    handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING;
+  }
+
+  /* Wait for more input events. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv_tty_queue_read(loop, handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* req) {
+  assert(handle->type == UV_TTY);
+  assert(handle->flags & UV_HANDLE_TTY_READABLE);
+
+  /* If the read_line_buffer member is zero, it must have been an raw read.
+   * Otherwise it was a line-buffered read. FIXME: This is quite obscure. Use a
+   * flag or something. */
+  if (handle->tty.rd.read_line_buffer.len == 0) {
+    uv_process_tty_read_raw_req(loop, handle, req);
+  } else {
+    uv_process_tty_read_line_req(loop, handle, req);
+  }
+}
+
+
+int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
+    uv_read_cb read_cb) {
+  uv_loop_t* loop = handle->loop;
+
+  if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  handle->flags |= UV_HANDLE_READING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  handle->read_cb = read_cb;
+  handle->alloc_cb = alloc_cb;
+
+  /* If reading was stopped and then started again, there could still be a read
+   * request pending. */
+  if (handle->flags & UV_HANDLE_READ_PENDING) {
+    return 0;
+  }
+
+  /* Maybe the user stopped reading half-way while processing key events.
+   * Short-circuit if this could be the case. */
+  if (handle->tty.rd.last_key_len > 0) {
+    SET_REQ_SUCCESS(&handle->read_req);
+    uv_insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req);
+    /* Make sure no attempt is made to insert it again until it's handled. */
+    handle->flags |= UV_HANDLE_READ_PENDING;
+    handle->reqs_pending++;
+    return 0;
+  }
+
+  uv_tty_queue_read(loop, handle);
+
+  return 0;
+}
+
+
+int uv_tty_read_stop(uv_tty_t* handle) {
+  INPUT_RECORD record;
+  DWORD written, err;
+
+  handle->flags &= ~UV_HANDLE_READING;
+  DECREASE_ACTIVE_COUNT(handle->loop, handle);
+
+  if (!(handle->flags & UV_HANDLE_READ_PENDING))
+    return 0;
+
+  if (handle->flags & UV_HANDLE_TTY_RAW) {
+    /* Cancel raw read. Write some bullshit event to force the console wait to
+     * return. */
+    memset(&record, 0, sizeof record);
+    record.EventType = FOCUS_EVENT;
+    if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) {
+      return GetLastError();
+    }
+  } else if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
+    /* Cancel line-buffered read if not already pending */
+    err = uv__cancel_read_console(handle);
+    if (err)
+      return err;
+
+    handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
+  }
+
+  return 0;
+}
+
+static int uv__cancel_read_console(uv_tty_t* handle) {
+  HANDLE active_screen_buffer = INVALID_HANDLE_VALUE;
+  INPUT_RECORD record;
+  DWORD written;
+  DWORD err = 0;
+  LONG status;
+
+  assert(!(handle->flags & UV_HANDLE_CANCELLATION_PENDING));
+
+  /* Hold the output lock during the cancellation, to ensure that further
+     writes don't interfere with the screen state. It will be the ReadConsole
+     thread's responsibility to release the lock. */
+  uv_sem_wait(&uv_tty_output_lock);
+  status = InterlockedExchange(&uv__read_console_status, TRAP_REQUESTED);
+  if (status != IN_PROGRESS) {
+    /* Either we have managed to set a trap for the other thread before
+       ReadConsole is called, or ReadConsole has returned because the user
+       has pressed ENTER. In either case, there is nothing else to do. */
+    uv_sem_post(&uv_tty_output_lock);
+    return 0;
+  }
+
+  /* Save screen state before sending the VK_RETURN event */
+  active_screen_buffer = CreateFileA("conout$",
+                                     GENERIC_READ | GENERIC_WRITE,
+                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                     NULL,
+                                     OPEN_EXISTING,
+                                     FILE_ATTRIBUTE_NORMAL,
+                                     NULL);
+
+  if (active_screen_buffer != INVALID_HANDLE_VALUE &&
+      GetConsoleScreenBufferInfo(active_screen_buffer,
+                                 &uv__saved_screen_state)) {
+    InterlockedOr(&uv__restore_screen_state, 1);
+  }
+
+  /* Write enter key event to force the console wait to return. */
+  record.EventType = KEY_EVENT;
+  record.Event.KeyEvent.bKeyDown = TRUE;
+  record.Event.KeyEvent.wRepeatCount = 1;
+  record.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
+  record.Event.KeyEvent.wVirtualScanCode =
+    MapVirtualKeyW(VK_RETURN, MAPVK_VK_TO_VSC);
+  record.Event.KeyEvent.uChar.UnicodeChar = L'\r';
+  record.Event.KeyEvent.dwControlKeyState = 0;
+  if (!WriteConsoleInputW(handle->handle, &record, 1, &written))
+    err = GetLastError();
+
+  if (active_screen_buffer != INVALID_HANDLE_VALUE)
+    CloseHandle(active_screen_buffer);
+
+  return err;
+}
+
+
+static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) {
+  uv_tty_virtual_width = info->dwSize.X;
+  uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1;
+
+  /* Recompute virtual window offset row. */
+  if (uv_tty_virtual_offset == -1) {
+    uv_tty_virtual_offset = info->dwCursorPosition.Y;
+  } else if (uv_tty_virtual_offset < info->dwCursorPosition.Y -
+             uv_tty_virtual_height + 1) {
+    /* If suddenly find the cursor outside of the virtual window, it must have
+     * somehow scrolled. Update the virtual window offset. */
+    uv_tty_virtual_offset = info->dwCursorPosition.Y -
+                            uv_tty_virtual_height + 1;
+  }
+  if (uv_tty_virtual_offset + uv_tty_virtual_height > info->dwSize.Y) {
+    uv_tty_virtual_offset = info->dwSize.Y - uv_tty_virtual_height;
+  }
+  if (uv_tty_virtual_offset < 0) {
+    uv_tty_virtual_offset = 0;
+  }
+}
+
+
+static COORD uv_tty_make_real_coord(uv_tty_t* handle,
+    CONSOLE_SCREEN_BUFFER_INFO* info, int x, unsigned char x_relative, int y,
+    unsigned char y_relative) {
+  COORD result;
+
+  uv_tty_update_virtual_window(info);
+
+  /* Adjust y position */
+  if (y_relative) {
+    y = info->dwCursorPosition.Y + y;
+  } else {
+    y = uv_tty_virtual_offset + y;
+  }
+  /* Clip y to virtual client rectangle */
+  if (y < uv_tty_virtual_offset) {
+    y = uv_tty_virtual_offset;
+  } else if (y >= uv_tty_virtual_offset + uv_tty_virtual_height) {
+    y = uv_tty_virtual_offset + uv_tty_virtual_height - 1;
+  }
+
+  /* Adjust x */
+  if (x_relative) {
+    x = info->dwCursorPosition.X + x;
+  }
+  /* Clip x */
+  if (x < 0) {
+    x = 0;
+  } else if (x >= uv_tty_virtual_width) {
+    x = uv_tty_virtual_width - 1;
+  }
+
+  result.X = (unsigned short) x;
+  result.Y = (unsigned short) y;
+  return result;
+}
+
+
+static int uv_tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length,
+    DWORD* error) {
+  DWORD written;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (!WriteConsoleW(handle->handle,
+                     (void*) buffer,
+                     length,
+                     &written,
+                     NULL)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative,
+    int y, unsigned char y_relative, DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+  COORD pos;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+ retry:
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+  }
+
+  pos = uv_tty_make_real_coord(handle, &info, x, x_relative, y, y_relative);
+
+  if (!SetConsoleCursorPosition(handle->handle, pos)) {
+    if (GetLastError() == ERROR_INVALID_PARAMETER) {
+      /* The console may be resized - retry */
+      goto retry;
+    } else {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+
+static int uv_tty_reset(uv_tty_t* handle, DWORD* error) {
+  const COORD origin = {0, 0};
+  const WORD char_attrs = uv_tty_default_text_attributes;
+  CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
+  DWORD count, written;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  /* Reset original text attributes. */
+  if (!SetConsoleTextAttribute(handle->handle, char_attrs)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  /* Move the cursor position to (0, 0). */
+  if (!SetConsoleCursorPosition(handle->handle, origin)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  /* Clear the screen buffer. */
+ retry:
+   if (!GetConsoleScreenBufferInfo(handle->handle, &screen_buffer_info)) {
+     *error = GetLastError();
+     return -1;
+  }
+
+  count = screen_buffer_info.dwSize.X * screen_buffer_info.dwSize.Y;
+
+  if (!(FillConsoleOutputCharacterW(handle->handle,
+                                    L'\x20',
+                                    count,
+                                    origin,
+                                    &written) &&
+        FillConsoleOutputAttribute(handle->handle,
+                                   char_attrs,
+                                   written,
+                                   origin,
+                                   &written))) {
+    if (GetLastError() == ERROR_INVALID_PARAMETER) {
+      /* The console may be resized - retry */
+      goto retry;
+    } else {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  /* Move the virtual window up to the top. */
+  uv_tty_virtual_offset = 0;
+  uv_tty_update_virtual_window(&screen_buffer_info);
+
+  /* Reset the cursor size and the cursor state. */
+  if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen,
+    DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+  COORD start, end;
+  DWORD count, written;
+
+  int x1, x2, y1, y2;
+  int x1r, x2r, y1r, y2r;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (dir == 0) {
+    /* Clear from current position */
+    x1 = 0;
+    x1r = 1;
+  } else {
+    /* Clear from column 0 */
+    x1 = 0;
+    x1r = 0;
+  }
+
+  if (dir == 1) {
+    /* Clear to current position */
+    x2 = 0;
+    x2r = 1;
+  } else {
+    /* Clear to end of row. We pretend the console is 65536 characters wide,
+     * uv_tty_make_real_coord will clip it to the actual console width. */
+    x2 = 0xffff;
+    x2r = 0;
+  }
+
+  if (!entire_screen) {
+    /* Stay on our own row */
+    y1 = y2 = 0;
+    y1r = y2r = 1;
+  } else {
+    /* Apply columns direction to row */
+    y1 = x1;
+    y1r = x1r;
+    y2 = x2;
+    y2r = x2r;
+  }
+
+ retry:
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  start = uv_tty_make_real_coord(handle, &info, x1, x1r, y1, y1r);
+  end = uv_tty_make_real_coord(handle, &info, x2, x2r, y2, y2r);
+  count = (end.Y * info.dwSize.X + end.X) -
+          (start.Y * info.dwSize.X + start.X) + 1;
+
+  if (!(FillConsoleOutputCharacterW(handle->handle,
+                              L'\x20',
+                              count,
+                              start,
+                              &written) &&
+        FillConsoleOutputAttribute(handle->handle,
+                                   info.wAttributes,
+                                   written,
+                                   start,
+                                   &written))) {
+    if (GetLastError() == ERROR_INVALID_PARAMETER) {
+      /* The console may be resized - retry */
+      goto retry;
+    } else {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+#define FLIP_FGBG                                                             \
+    do {                                                                      \
+      WORD fg = info.wAttributes & 0xF;                                       \
+      WORD bg = info.wAttributes & 0xF0;                                      \
+      info.wAttributes &= 0xFF00;                                             \
+      info.wAttributes |= fg << 4;                                            \
+      info.wAttributes |= bg >> 4;                                            \
+    } while (0)
+
+static int uv_tty_set_style(uv_tty_t* handle, DWORD* error) {
+  unsigned short argc = handle->tty.wr.ansi_csi_argc;
+  unsigned short* argv = handle->tty.wr.ansi_csi_argv;
+  int i;
+  CONSOLE_SCREEN_BUFFER_INFO info;
+
+  char fg_color = -1, bg_color = -1;
+  char fg_bright = -1, bg_bright = -1;
+  char inverse = -1;
+
+  if (argc == 0) {
+    /* Reset mode */
+    fg_color = uv_tty_default_fg_color;
+    bg_color = uv_tty_default_bg_color;
+    fg_bright = uv_tty_default_fg_bright;
+    bg_bright = uv_tty_default_bg_bright;
+    inverse = uv_tty_default_inverse;
+  }
+
+  for (i = 0; i < argc; i++) {
+    short arg = argv[i];
+
+    if (arg == 0) {
+      /* Reset mode */
+      fg_color = uv_tty_default_fg_color;
+      bg_color = uv_tty_default_bg_color;
+      fg_bright = uv_tty_default_fg_bright;
+      bg_bright = uv_tty_default_bg_bright;
+      inverse = uv_tty_default_inverse;
+
+    } else if (arg == 1) {
+      /* Foreground bright on */
+      fg_bright = 1;
+
+    } else if (arg == 2) {
+      /* Both bright off */
+      fg_bright = 0;
+      bg_bright = 0;
+
+    } else if (arg == 5) {
+      /* Background bright on */
+      bg_bright = 1;
+
+    } else if (arg == 7) {
+      /* Inverse: on */
+      inverse = 1;
+
+    } else if (arg == 21 || arg == 22) {
+      /* Foreground bright off */
+      fg_bright = 0;
+
+    } else if (arg == 25) {
+      /* Background bright off */
+      bg_bright = 0;
+
+    } else if (arg == 27) {
+      /* Inverse: off */
+      inverse = 0;
+
+    } else if (arg >= 30 && arg <= 37) {
+      /* Set foreground color */
+      fg_color = arg - 30;
+
+    } else if (arg == 39) {
+      /* Default text color */
+      fg_color = uv_tty_default_fg_color;
+      fg_bright = uv_tty_default_fg_bright;
+
+    } else if (arg >= 40 && arg <= 47) {
+      /* Set background color */
+      bg_color = arg - 40;
+
+    } else if (arg ==  49) {
+      /* Default background color */
+      bg_color = uv_tty_default_bg_color;
+      bg_bright = uv_tty_default_bg_bright;
+
+    } else if (arg >= 90 && arg <= 97) {
+      /* Set bold foreground color */
+      fg_bright = 1;
+      fg_color = arg - 90;
+
+    } else if (arg >= 100 && arg <= 107) {
+      /* Set bold background color */
+      bg_bright = 1;
+      bg_color = arg - 100;
+
+    }
+  }
+
+  if (fg_color == -1 && bg_color == -1 && fg_bright == -1 &&
+      bg_bright == -1 && inverse == -1) {
+    /* Nothing changed */
+    return 0;
+  }
+
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
+    FLIP_FGBG;
+  }
+
+  if (fg_color != -1) {
+    info.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+    if (fg_color & 1) info.wAttributes |= FOREGROUND_RED;
+    if (fg_color & 2) info.wAttributes |= FOREGROUND_GREEN;
+    if (fg_color & 4) info.wAttributes |= FOREGROUND_BLUE;
+  }
+
+  if (fg_bright != -1) {
+    if (fg_bright) {
+      info.wAttributes |= FOREGROUND_INTENSITY;
+    } else {
+      info.wAttributes &= ~FOREGROUND_INTENSITY;
+    }
+  }
+
+  if (bg_color != -1) {
+    info.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+    if (bg_color & 1) info.wAttributes |= BACKGROUND_RED;
+    if (bg_color & 2) info.wAttributes |= BACKGROUND_GREEN;
+    if (bg_color & 4) info.wAttributes |= BACKGROUND_BLUE;
+  }
+
+  if (bg_bright != -1) {
+    if (bg_bright) {
+      info.wAttributes |= BACKGROUND_INTENSITY;
+    } else {
+      info.wAttributes &= ~BACKGROUND_INTENSITY;
+    }
+  }
+
+  if (inverse != -1) {
+    if (inverse) {
+      info.wAttributes |= COMMON_LVB_REVERSE_VIDEO;
+    } else {
+      info.wAttributes &= ~COMMON_LVB_REVERSE_VIDEO;
+    }
+  }
+
+  if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
+    FLIP_FGBG;
+  }
+
+  if (!SetConsoleTextAttribute(handle->handle, info.wAttributes)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
+    DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  uv_tty_update_virtual_window(&info);
+
+  handle->tty.wr.saved_position.X = info.dwCursorPosition.X;
+  handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y - uv_tty_virtual_offset;
+  handle->flags |= UV_HANDLE_TTY_SAVED_POSITION;
+
+  if (save_attributes) {
+    handle->tty.wr.saved_attributes = info.wAttributes &
+        (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
+    handle->flags |= UV_HANDLE_TTY_SAVED_ATTRIBUTES;
+  }
+
+  return 0;
+}
+
+
+static int uv_tty_restore_state(uv_tty_t* handle,
+    unsigned char restore_attributes, DWORD* error) {
+  CONSOLE_SCREEN_BUFFER_INFO info;
+  WORD new_attributes;
+
+  if (*error != ERROR_SUCCESS) {
+    return -1;
+  }
+
+  if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) {
+    if (uv_tty_move_caret(handle,
+                          handle->tty.wr.saved_position.X,
+                          0,
+                          handle->tty.wr.saved_position.Y,
+                          0,
+                          error) != 0) {
+      return -1;
+    }
+  }
+
+  if (restore_attributes &&
+      (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) {
+    if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
+      *error = GetLastError();
+      return -1;
+    }
+
+    new_attributes = info.wAttributes;
+    new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
+    new_attributes |= handle->tty.wr.saved_attributes;
+
+    if (!SetConsoleTextAttribute(handle->handle, new_attributes)) {
+      *error = GetLastError();
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+static int uv_tty_set_cursor_visibility(uv_tty_t* handle,
+                                        BOOL visible,
+                                        DWORD* error) {
+  CONSOLE_CURSOR_INFO cursor_info;
+
+  if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  cursor_info.bVisible = visible;
+
+  if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
+  CONSOLE_CURSOR_INFO cursor_info;
+
+  if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  if (style == 0) {
+    cursor_info.dwSize = uv_tty_default_cursor_info.dwSize;
+  } else if (style <= 2) {
+    cursor_info.dwSize = CURSOR_SIZE_LARGE;
+  } else {
+    cursor_info.dwSize = CURSOR_SIZE_SMALL;
+  }
+
+  if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  return 0;
+}
+
+
+static int uv_tty_write_bufs(uv_tty_t* handle,
+                             const uv_buf_t bufs[],
+                             unsigned int nbufs,
+                             DWORD* error) {
+  /* We can only write 8k characters at a time. Windows can't handle much more
+   * characters in a single console write anyway. */
+  WCHAR utf16_buf[MAX_CONSOLE_CHAR];
+  DWORD utf16_buf_used = 0;
+  unsigned int i;
+
+#define FLUSH_TEXT()                                                \
+  do {                                                              \
+    if (utf16_buf_used > 0) {                                       \
+      uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error);   \
+      utf16_buf_used = 0;                                           \
+    }                                                               \
+  } while (0)
+
+#define ENSURE_BUFFER_SPACE(wchars_needed)                          \
+  if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) {     \
+    FLUSH_TEXT();                                                   \
+  }
+
+  /* Cache for fast access */
+  unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left;
+  unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint;
+  unsigned char previous_eol = handle->tty.wr.previous_eol;
+  unsigned short ansi_parser_state = handle->tty.wr.ansi_parser_state;
+
+  /* Store the error here. If we encounter an error, stop trying to do i/o but
+   * keep parsing the buffer so we leave the parser in a consistent state. */
+  *error = ERROR_SUCCESS;
+
+  uv_sem_wait(&uv_tty_output_lock);
+
+  for (i = 0; i < nbufs; i++) {
+    uv_buf_t buf = bufs[i];
+    unsigned int j;
+
+    for (j = 0; j < buf.len; j++) {
+      unsigned char c = buf.base[j];
+
+      /* Run the character through the utf8 decoder We happily accept non
+       * shortest form encodings and invalid code points - there's no real harm
+       * that can be done. */
+      if (utf8_bytes_left == 0) {
+        /* Read utf-8 start byte */
+        DWORD first_zero_bit;
+        unsigned char not_c = ~c;
+#ifdef _MSC_VER /* msvc */
+        if (_BitScanReverse(&first_zero_bit, not_c)) {
+#else /* assume gcc */
+        if (c != 0) {
+          first_zero_bit = (sizeof(int) * 8) - 1 - __builtin_clz(not_c);
+#endif
+          if (first_zero_bit == 7) {
+            /* Ascii - pass right through */
+            utf8_codepoint = (unsigned int) c;
+
+          } else if (first_zero_bit <= 5) {
+            /* Multibyte sequence */
+            utf8_codepoint = (0xff >> (8 - first_zero_bit)) & c;
+            utf8_bytes_left = (char) (6 - first_zero_bit);
+
+          } else {
+            /* Invalid continuation */
+            utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
+          }
+
+        } else {
+          /* 0xff -- invalid */
+          utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
+        }
+
+      } else if ((c & 0xc0) == 0x80) {
+        /* Valid continuation of utf-8 multibyte sequence */
+        utf8_bytes_left--;
+        utf8_codepoint <<= 6;
+        utf8_codepoint |= ((unsigned int) c & 0x3f);
+
+      } else {
+        /* Start byte where continuation was expected. */
+        utf8_bytes_left = 0;
+        utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
+        /* Patch buf offset so this character will be parsed again as a start
+         * byte. */
+        j--;
+      }
+
+      /* Maybe we need to parse more bytes to find a character. */
+      if (utf8_bytes_left != 0) {
+        continue;
+      }
+
+      /* Parse vt100/ansi escape codes */
+      if (uv__vterm_state == UV_TTY_SUPPORTED) {
+        /* Pass through escape codes if conhost supports them. */
+      } else if (ansi_parser_state == ANSI_NORMAL) {
+        switch (utf8_codepoint) {
+          case '\033':
+            ansi_parser_state = ANSI_ESCAPE_SEEN;
+            continue;
+
+          case 0233:
+            ansi_parser_state = ANSI_CSI;
+            handle->tty.wr.ansi_csi_argc = 0;
+            continue;
+        }
+
+      } else if (ansi_parser_state == ANSI_ESCAPE_SEEN) {
+        switch (utf8_codepoint) {
+          case '[':
+            ansi_parser_state = ANSI_CSI;
+            handle->tty.wr.ansi_csi_argc = 0;
+            continue;
+
+          case '^':
+          case '_':
+          case 'P':
+          case ']':
+            /* Not supported, but we'll have to parse until we see a stop code,
+             * e. g. ESC \ or BEL. */
+            ansi_parser_state = ANSI_ST_CONTROL;
+            continue;
+
+          case '\033':
+            /* Ignore double escape. */
+            continue;
+
+          case 'c':
+            /* Full console reset. */
+            FLUSH_TEXT();
+            uv_tty_reset(handle, error);
+            ansi_parser_state = ANSI_NORMAL;
+            continue;
+
+          case '7':
+            /* Save the cursor position and text attributes. */
+            FLUSH_TEXT();
+            uv_tty_save_state(handle, 1, error);
+            ansi_parser_state = ANSI_NORMAL;
+            continue;
+
+          case '8':
+            /* Restore the cursor position and text attributes */
+            FLUSH_TEXT();
+            uv_tty_restore_state(handle, 1, error);
+            ansi_parser_state = ANSI_NORMAL;
+            continue;
+
+          default:
+            if (utf8_codepoint >= '@' && utf8_codepoint <= '_') {
+              /* Single-char control. */
+              ansi_parser_state = ANSI_NORMAL;
+              continue;
+            } else {
+              /* Invalid - proceed as normal, */
+              ansi_parser_state = ANSI_NORMAL;
+            }
+        }
+
+      } else if (ansi_parser_state == ANSI_IGNORE) {
+        /* We're ignoring this command. Stop only on command character. */
+        if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
+          ansi_parser_state = ANSI_NORMAL;
+        }
+        continue;
+
+      } else if (ansi_parser_state == ANSI_DECSCUSR) {
+        /* So far we've the sequence `ESC [ arg space`, and we're waiting for
+         * the final command byte. */
+        if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
+          /* Command byte */
+          if (utf8_codepoint == 'q') {
+            /* Change the cursor shape */
+            int style = handle->tty.wr.ansi_csi_argc
+              ? handle->tty.wr.ansi_csi_argv[0] : 1;
+            if (style >= 0 && style <= 6) {
+              FLUSH_TEXT();
+              uv_tty_set_cursor_shape(handle, style, error);
+            }
+          }
+
+          /* Sequence ended - go back to normal state. */
+          ansi_parser_state = ANSI_NORMAL;
+          continue;
+        }
+        /* Unexpected character, but sequence hasn't ended yet. Ignore the rest
+         * of the sequence. */
+        ansi_parser_state = ANSI_IGNORE;
+
+      } else if (ansi_parser_state & ANSI_CSI) {
+        /* So far we've seen `ESC [`, and we may or may not have already parsed
+         * some of the arguments that follow. */
+
+        if (utf8_codepoint >= '0' && utf8_codepoint <= '9') {
+          /* Parse a numerical argument. */
+          if (!(ansi_parser_state & ANSI_IN_ARG)) {
+            /* We were not currently parsing a number, add a new one. */
+            /* Check for that there are too many arguments. */
+            if (handle->tty.wr.ansi_csi_argc >=
+                ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
+              ansi_parser_state = ANSI_IGNORE;
+              continue;
+            }
+            ansi_parser_state |= ANSI_IN_ARG;
+            handle->tty.wr.ansi_csi_argc++;
+            handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
+                (unsigned short) utf8_codepoint - '0';
+            continue;
+
+          } else {
+            /* We were already parsing a number. Parse next digit. */
+            uint32_t value = 10 *
+                handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1];
+
+            /* Check for overflow. */
+            if (value > UINT16_MAX) {
+              ansi_parser_state = ANSI_IGNORE;
+              continue;
+            }
+
+            handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
+                (unsigned short) value + (utf8_codepoint - '0');
+            continue;
+          }
+
+        } else if (utf8_codepoint == ';') {
+          /* Denotes the end of an argument. */
+          if (ansi_parser_state & ANSI_IN_ARG) {
+            ansi_parser_state &= ~ANSI_IN_ARG;
+            continue;
+
+          } else {
+            /* If ANSI_IN_ARG is not set, add another argument and default
+             * it to 0. */
+
+            /* Check for too many arguments */
+            if (handle->tty.wr.ansi_csi_argc >=
+
+                ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
+              ansi_parser_state = ANSI_IGNORE;
+              continue;
+            }
+
+            handle->tty.wr.ansi_csi_argc++;
+            handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0;
+            continue;
+          }
+
+        } else if (utf8_codepoint == '?' &&
+                   !(ansi_parser_state & ANSI_IN_ARG) &&
+                   !(ansi_parser_state & ANSI_EXTENSION) &&
+                   handle->tty.wr.ansi_csi_argc == 0) {
+          /* Pass through '?' if it is the first character after CSI */
+          /* This is an extension character from the VT100 codeset */
+          /* that is supported and used by most ANSI terminals today. */
+          ansi_parser_state |= ANSI_EXTENSION;
+          continue;
+
+        } else if (utf8_codepoint == ' ' &&
+                   !(ansi_parser_state & ANSI_EXTENSION)) {
+          /* We expect a command byte to follow after this space. The only
+           * command that we current support is 'set cursor style'. */
+          ansi_parser_state = ANSI_DECSCUSR;
+          continue;
+
+        } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
+          /* Command byte */
+          if (ansi_parser_state & ANSI_EXTENSION) {
+            /* Sequence is `ESC [ ? args command`. */
+            switch (utf8_codepoint) {
+              case 'l':
+                /* Hide the cursor */
+                if (handle->tty.wr.ansi_csi_argc == 1 &&
+                    handle->tty.wr.ansi_csi_argv[0] == 25) {
+                  FLUSH_TEXT();
+                  uv_tty_set_cursor_visibility(handle, 0, error);
+                }
+                break;
+
+              case 'h':
+                /* Show the cursor */
+                if (handle->tty.wr.ansi_csi_argc == 1 &&
+                    handle->tty.wr.ansi_csi_argv[0] == 25) {
+                  FLUSH_TEXT();
+                  uv_tty_set_cursor_visibility(handle, 1, error);
+                }
+                break;
+            }
+
+          } else {
+            /* Sequence is `ESC [ args command`. */
+            int x, y, d;
+            switch (utf8_codepoint) {
+              case 'A':
+                /* cursor up */
+                FLUSH_TEXT();
+                y = -(handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1);
+                uv_tty_move_caret(handle, 0, 1, y, 1, error);
+                break;
+
+              case 'B':
+                /* cursor down */
+                FLUSH_TEXT();
+                y = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1;
+                uv_tty_move_caret(handle, 0, 1, y, 1, error);
+                break;
+
+              case 'C':
+                /* cursor forward */
+                FLUSH_TEXT();
+                x = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1;
+                uv_tty_move_caret(handle, x, 1, 0, 1, error);
+                break;
+
+              case 'D':
+                /* cursor back */
+                FLUSH_TEXT();
+                x = -(handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1);
+                uv_tty_move_caret(handle, x, 1, 0, 1, error);
+                break;
+
+              case 'E':
+                /* cursor next line */
+                FLUSH_TEXT();
+                y = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1;
+                uv_tty_move_caret(handle, 0, 0, y, 1, error);
+                break;
+
+              case 'F':
+                /* cursor previous line */
+                FLUSH_TEXT();
+                y = -(handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 1);
+                uv_tty_move_caret(handle, 0, 0, y, 1, error);
+                break;
+
+              case 'G':
+                /* cursor horizontal move absolute */
+                FLUSH_TEXT();
+                x = (handle->tty.wr.ansi_csi_argc >= 1 &&
+                     handle->tty.wr.ansi_csi_argv[0])
+                  ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
+                uv_tty_move_caret(handle, x, 0, 0, 1, error);
+                break;
+
+              case 'H':
+              case 'f':
+                /* cursor move absolute */
+                FLUSH_TEXT();
+                y = (handle->tty.wr.ansi_csi_argc >= 1 &&
+                     handle->tty.wr.ansi_csi_argv[0])
+                  ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
+                x = (handle->tty.wr.ansi_csi_argc >= 2 &&
+                     handle->tty.wr.ansi_csi_argv[1])
+                  ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0;
+                uv_tty_move_caret(handle, x, 0, y, 0, error);
+                break;
+
+              case 'J':
+                /* Erase screen */
+                FLUSH_TEXT();
+                d = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 0;
+                if (d >= 0 && d <= 2) {
+                  uv_tty_clear(handle, d, 1, error);
+                }
+                break;
+
+              case 'K':
+                /* Erase line */
+                FLUSH_TEXT();
+                d = handle->tty.wr.ansi_csi_argc
+                  ? handle->tty.wr.ansi_csi_argv[0] : 0;
+                if (d >= 0 && d <= 2) {
+                  uv_tty_clear(handle, d, 0, error);
+                }
+                break;
+
+              case 'm':
+                /* Set style */
+                FLUSH_TEXT();
+                uv_tty_set_style(handle, error);
+                break;
+
+              case 's':
+                /* Save the cursor position. */
+                FLUSH_TEXT();
+                uv_tty_save_state(handle, 0, error);
+                break;
+
+              case 'u':
+                /* Restore the cursor position */
+                FLUSH_TEXT();
+                uv_tty_restore_state(handle, 0, error);
+                break;
+            }
+          }
+
+          /* Sequence ended - go back to normal state. */
+          ansi_parser_state = ANSI_NORMAL;
+          continue;
+
+        } else {
+          /* We don't support commands that use private mode characters or
+           * intermediaries. Ignore the rest of the sequence. */
+          ansi_parser_state = ANSI_IGNORE;
+          continue;
+        }
+
+      } else if (ansi_parser_state & ANSI_ST_CONTROL) {
+        /* Unsupported control code.
+         * Ignore everything until we see `BEL` or `ESC \`. */
+        if (ansi_parser_state & ANSI_IN_STRING) {
+          if (!(ansi_parser_state & ANSI_BACKSLASH_SEEN)) {
+            if (utf8_codepoint == '"') {
+              ansi_parser_state &= ~ANSI_IN_STRING;
+            } else if (utf8_codepoint == '\\') {
+              ansi_parser_state |= ANSI_BACKSLASH_SEEN;
+            }
+          } else {
+            ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
+          }
+        } else {
+          if (utf8_codepoint == '\007' || (utf8_codepoint == '\\' &&
+              (ansi_parser_state & ANSI_ESCAPE_SEEN))) {
+            /* End of sequence */
+            ansi_parser_state = ANSI_NORMAL;
+          } else if (utf8_codepoint == '\033') {
+            /* Escape character */
+            ansi_parser_state |= ANSI_ESCAPE_SEEN;
+          } else if (utf8_codepoint == '"') {
+             /* String starting */
+            ansi_parser_state |= ANSI_IN_STRING;
+            ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
+            ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
+          } else {
+            ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
+          }
+        }
+        continue;
+      } else {
+        /* Inconsistent state */
+        abort();
+      }
+
+      /* We wouldn't mind emitting utf-16 surrogate pairs. Too bad, the windows
+       * console doesn't really support UTF-16, so just emit the replacement
+       * character. */
+      if (utf8_codepoint > 0xffff) {
+        utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
+      }
+
+      if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) {
+        /* EOL conversion - emit \r\n when we see \n. */
+
+        if (utf8_codepoint == 0x0a && previous_eol != 0x0d) {
+          /* \n was not preceded by \r; print \r\n. */
+          ENSURE_BUFFER_SPACE(2);
+          utf16_buf[utf16_buf_used++] = L'\r';
+          utf16_buf[utf16_buf_used++] = L'\n';
+        } else if (utf8_codepoint == 0x0d && previous_eol == 0x0a) {
+          /* \n was followed by \r; do not print the \r, since the source was
+           * either \r\n\r (so the second \r is redundant) or was \n\r (so the
+           * \n was processed by the last case and an \r automatically
+           * inserted). */
+        } else {
+          /* \r without \n; print \r as-is. */
+          ENSURE_BUFFER_SPACE(1);
+          utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
+        }
+
+        previous_eol = (char) utf8_codepoint;
+
+      } else if (utf8_codepoint <= 0xffff) {
+        /* Encode character into utf-16 buffer. */
+        ENSURE_BUFFER_SPACE(1);
+        utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
+        previous_eol = 0;
+      }
+    }
+  }
+
+  /* Flush remaining characters */
+  FLUSH_TEXT();
+
+  /* Copy cached values back to struct. */
+  handle->tty.wr.utf8_bytes_left = utf8_bytes_left;
+  handle->tty.wr.utf8_codepoint = utf8_codepoint;
+  handle->tty.wr.previous_eol = previous_eol;
+  handle->tty.wr.ansi_parser_state = ansi_parser_state;
+
+  uv_sem_post(&uv_tty_output_lock);
+
+  if (*error == STATUS_SUCCESS) {
+    return 0;
+  } else {
+    return -1;
+  }
+
+#undef FLUSH_TEXT
+}
+
+
+int uv_tty_write(uv_loop_t* loop,
+                 uv_write_t* req,
+                 uv_tty_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 uv_write_cb cb) {
+  DWORD error;
+
+  UV_REQ_INIT(req, UV_WRITE);
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+
+  handle->reqs_pending++;
+  handle->stream.conn.write_reqs_pending++;
+  REGISTER_HANDLE_REQ(loop, handle, req);
+
+  req->u.io.queued_bytes = 0;
+
+  if (!uv_tty_write_bufs(handle, bufs, nbufs, &error)) {
+    SET_REQ_SUCCESS(req);
+  } else {
+    SET_REQ_ERROR(req, error);
+  }
+
+  uv_insert_pending_req(loop, (uv_req_t*) req);
+
+  return 0;
+}
+
+
+int uv__tty_try_write(uv_tty_t* handle,
+                      const uv_buf_t bufs[],
+                      unsigned int nbufs) {
+  DWORD error;
+
+  if (handle->stream.conn.write_reqs_pending > 0)
+    return UV_EAGAIN;
+
+  if (uv_tty_write_bufs(handle, bufs, nbufs, &error))
+    return uv_translate_sys_error(error);
+
+  return uv__count_bufs(bufs, nbufs);
+}
+
+
+void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
+  uv_write_t* req) {
+  int err;
+
+  handle->write_queue_size -= req->u.io.queued_bytes;
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (req->cb) {
+    err = GET_REQ_ERROR(req);
+    req->cb(req, uv_translate_sys_error(err));
+  }
+
+  handle->stream.conn.write_reqs_pending--;
+  if (handle->stream.conn.shutdown_req != NULL &&
+      handle->stream.conn.write_reqs_pending == 0) {
+    uv_want_endgame(loop, (uv_handle_t*)handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_tty_close(uv_tty_t* handle) {
+  assert(handle->u.fd == -1 || handle->u.fd > 2);
+  if (handle->flags & UV_HANDLE_READING)
+    uv_tty_read_stop(handle);
+
+  if (handle->u.fd == -1)
+    CloseHandle(handle->handle);
+  else
+    close(handle->u.fd);
+
+  handle->u.fd = -1;
+  handle->handle = INVALID_HANDLE_VALUE;
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  uv__handle_closing(handle);
+
+  if (handle->reqs_pending == 0) {
+    uv_want_endgame(handle->loop, (uv_handle_t*) handle);
+  }
+}
+
+
+void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
+  if (!(handle->flags & UV_HANDLE_TTY_READABLE) &&
+      handle->stream.conn.shutdown_req != NULL &&
+      handle->stream.conn.write_reqs_pending == 0) {
+    UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req);
+
+    /* TTY shutdown is really just a no-op */
+    if (handle->stream.conn.shutdown_req->cb) {
+      if (handle->flags & UV_HANDLE_CLOSING) {
+        handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED);
+      } else {
+        handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0);
+      }
+    }
+
+    handle->stream.conn.shutdown_req = NULL;
+
+    DECREASE_PENDING_REQ_COUNT(handle);
+    return;
+  }
+
+  if (handle->flags & UV_HANDLE_CLOSING &&
+      handle->reqs_pending == 0) {
+    /* The wait handle used for raw reading should be unregistered when the
+     * wait callback runs. */
+    assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
+           handle->tty.rd.read_raw_wait == NULL);
+
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    uv__handle_close(handle);
+  }
+}
+
+
+/*
+ * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* raw_req) {
+  abort();
+}
+
+
+/*
+ * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * TODO: find a way to remove it
+ */
+void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_connect_t* req) {
+  abort();
+}
+
+
+int uv_tty_reset_mode(void) {
+  /* Not necessary to do anything. */
+  return 0;
+}
+
+/* Determine whether or not this version of windows supports
+ * proper ANSI color codes. Should be supported as of windows
+ * 10 version 1511, build number 10.0.10586.
+ */
+static void uv__determine_vterm_state(HANDLE handle) {
+  DWORD dwMode = 0;
+
+  uv__need_check_vterm_state = FALSE;
+  if (!GetConsoleMode(handle, &dwMode)) {
+    return;
+  }
+
+  dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+  if (!SetConsoleMode(handle, dwMode)) {
+    return;
+  }
+
+  uv__vterm_state = UV_TTY_SUPPORTED;
+}
+
+static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
+  NTSTATUS status;
+  ULONG_PTR conhost_pid;
+  MSG msg;
+
+  if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL)
+    return 0;
+
+  status = pNtQueryInformationProcess(GetCurrentProcess(),
+                                      ProcessConsoleHostProcess,
+                                      &conhost_pid,
+                                      sizeof(conhost_pid),
+                                      NULL);
+
+  if (!NT_SUCCESS(status)) {
+    /* We couldn't retrieve our console host process, probably because this
+     * is a 32-bit process running on 64-bit Windows. Fall back to receiving
+     * console events from the input stream only. */
+    return 0;
+  }
+
+  /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */
+  conhost_pid &= ~(ULONG_PTR)0x3;
+
+  uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL);
+  if (uv__tty_console_resized == NULL)
+    return 0;
+  if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread,
+                        NULL,
+                        WT_EXECUTELONGFUNCTION) == 0)
+    return 0;
+
+  if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT,
+                        EVENT_CONSOLE_LAYOUT,
+                        NULL,
+                        uv__tty_console_resize_event,
+                        (DWORD)conhost_pid,
+                        0,
+                        WINEVENT_OUTOFCONTEXT))
+    return 0;
+
+  while (GetMessage(&msg, NULL, 0, 0)) {
+    TranslateMessage(&msg);
+    DispatchMessage(&msg);
+  }
+  return 0;
+}
+
+static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
+                                                  DWORD event,
+                                                  HWND hwnd,
+                                                  LONG idObject,
+                                                  LONG idChild,
+                                                  DWORD dwEventThread,
+                                                  DWORD dwmsEventTime) {
+  SetEvent(uv__tty_console_resized);
+}
+
+static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
+  for (;;) {
+    /* Make sure to not overwhelm the system with resize events */
+    Sleep(33);
+    WaitForSingleObject(uv__tty_console_resized, INFINITE);
+    uv__tty_console_signal_resize();
+    ResetEvent(uv__tty_console_resized);
+  }
+  return 0;
+}
+
+static void uv__tty_console_signal_resize(void) {
+  CONSOLE_SCREEN_BUFFER_INFO sb_info;
+  int width, height;
+
+  if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
+    return;
+
+  width = sb_info.dwSize.X;
+  height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
+
+  uv_mutex_lock(&uv__tty_console_resize_mutex);
+  assert(uv__tty_console_width != -1 && uv__tty_console_height != -1);
+  if (width != uv__tty_console_width || height != uv__tty_console_height) {
+    uv__tty_console_width = width;
+    uv__tty_console_height = height;
+    uv_mutex_unlock(&uv__tty_console_resize_mutex);
+    uv__signal_dispatch(SIGWINCH);
+  } else {
+    uv_mutex_unlock(&uv__tty_console_resize_mutex);
+  }
+}
+
+void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) {
+  uv_sem_wait(&uv_tty_output_lock);
+  uv__need_check_vterm_state = FALSE;
+  uv__vterm_state = state;
+  uv_sem_post(&uv_tty_output_lock);
+}
+
+int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) {
+  uv_sem_wait(&uv_tty_output_lock);
+  *state = uv__vterm_state;
+  uv_sem_post(&uv_tty_output_lock);
+  return 0;
+}
diff --git a/third_party/libuv/src/win/udp.c b/third_party/libuv/src/win/udp.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/udp.c
@@ -0,0 +1,1181 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "stream-inl.h"
+#include "req-inl.h"
+
+
+/*
+ * Threshold of active udp streams for which to preallocate udp read buffers.
+ */
+const unsigned int uv_active_udp_streams_threshold = 0;
+
+/* A zero-size buffer for use by uv_udp_read */
+static char uv_zero_[] = "";
+int uv_udp_getpeername(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             0);
+}
+
+
+int uv_udp_getsockname(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             0);
+}
+
+
+static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket,
+    int family) {
+  DWORD yes = 1;
+  WSAPROTOCOL_INFOW info;
+  int opt_len;
+
+  if (handle->socket != INVALID_SOCKET)
+    return UV_EBUSY;
+
+  /* Set the socket to nonblocking mode */
+  if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  /* Make the socket non-inheritable */
+  if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) {
+    return GetLastError();
+  }
+
+  /* Associate it with the I/O completion port. Use uv_handle_t pointer as
+   * completion key. */
+  if (CreateIoCompletionPort((HANDLE)socket,
+                             loop->iocp,
+                             (ULONG_PTR)socket,
+                             0) == NULL) {
+    return GetLastError();
+  }
+
+  /* All known Windows that support SetFileCompletionNotificationModes have a
+   * bug that makes it impossible to use this function in conjunction with
+   * datagram sockets. We can work around that but only if the user is using
+   * the default UDP driver (AFD) and has no other. LSPs stacked on top. Here
+   * we check whether that is the case. */
+  opt_len = (int) sizeof info;
+  if (getsockopt(
+          socket, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &info, &opt_len) ==
+      SOCKET_ERROR) {
+    return GetLastError();
+  }
+
+  if (info.ProtocolChain.ChainLen == 1) {
+    if (SetFileCompletionNotificationModes(
+            (HANDLE) socket,
+            FILE_SKIP_SET_EVENT_ON_HANDLE |
+                FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
+      handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
+      handle->func_wsarecv = uv_wsarecv_workaround;
+      handle->func_wsarecvfrom = uv_wsarecvfrom_workaround;
+    } else if (GetLastError() != ERROR_INVALID_FUNCTION) {
+      return GetLastError();
+    }
+  }
+
+  handle->socket = socket;
+
+  if (family == AF_INET6) {
+    handle->flags |= UV_HANDLE_IPV6;
+  } else {
+    assert(!(handle->flags & UV_HANDLE_IPV6));
+  }
+
+  return 0;
+}
+
+
+int uv__udp_init_ex(uv_loop_t* loop,
+                    uv_udp_t* handle,
+                    unsigned flags,
+                    int domain) {
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_UDP);
+  handle->socket = INVALID_SOCKET;
+  handle->reqs_pending = 0;
+  handle->activecnt = 0;
+  handle->func_wsarecv = WSARecv;
+  handle->func_wsarecvfrom = WSARecvFrom;
+  handle->send_queue_size = 0;
+  handle->send_queue_count = 0;
+  UV_REQ_INIT(&handle->recv_req, UV_UDP_RECV);
+  handle->recv_req.data = handle;
+
+  /* If anything fails beyond this point we need to remove the handle from
+   * the handle queue, since it was added by uv__handle_init.
+   */
+
+  if (domain != AF_UNSPEC) {
+    SOCKET sock;
+    DWORD err;
+
+    sock = socket(domain, SOCK_DGRAM, 0);
+    if (sock == INVALID_SOCKET) {
+      err = WSAGetLastError();
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+
+    err = uv_udp_set_socket(handle->loop, handle, sock, domain);
+    if (err) {
+      closesocket(sock);
+      QUEUE_REMOVE(&handle->handle_queue);
+      return uv_translate_sys_error(err);
+    }
+  }
+
+  return 0;
+}
+
+
+void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) {
+  uv_udp_recv_stop(handle);
+  closesocket(handle->socket);
+  handle->socket = INVALID_SOCKET;
+
+  uv__handle_closing(handle);
+
+  if (handle->reqs_pending == 0) {
+    uv_want_endgame(loop, (uv_handle_t*) handle);
+  }
+}
+
+
+void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
+  if (handle->flags & UV_HANDLE_CLOSING &&
+      handle->reqs_pending == 0) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    uv__handle_close(handle);
+  }
+}
+
+
+int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
+  return 0;
+}
+
+
+static int uv_udp_maybe_bind(uv_udp_t* handle,
+                             const struct sockaddr* addr,
+                             unsigned int addrlen,
+                             unsigned int flags) {
+  int r;
+  int err;
+  DWORD no = 0;
+
+  if (handle->flags & UV_HANDLE_BOUND)
+    return 0;
+
+  if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
+    /* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
+    return ERROR_INVALID_PARAMETER;
+  }
+
+  if (handle->socket == INVALID_SOCKET) {
+    SOCKET sock = socket(addr->sa_family, SOCK_DGRAM, 0);
+    if (sock == INVALID_SOCKET) {
+      return WSAGetLastError();
+    }
+
+    err = uv_udp_set_socket(handle->loop, handle, sock, addr->sa_family);
+    if (err) {
+      closesocket(sock);
+      return err;
+    }
+  }
+
+  if (flags & UV_UDP_REUSEADDR) {
+    DWORD yes = 1;
+    /* Set SO_REUSEADDR on the socket. */
+    if (setsockopt(handle->socket,
+                   SOL_SOCKET,
+                   SO_REUSEADDR,
+                   (char*) &yes,
+                   sizeof yes) == SOCKET_ERROR) {
+      err = WSAGetLastError();
+      return err;
+    }
+  }
+
+  if (addr->sa_family == AF_INET6)
+    handle->flags |= UV_HANDLE_IPV6;
+
+  if (addr->sa_family == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) {
+    /* On windows IPV6ONLY is on by default. If the user doesn't specify it
+     * libuv turns it off. */
+
+    /* TODO: how to handle errors? This may fail if there is no ipv4 stack
+     * available, or when run on XP/2003 which have no support for dualstack
+     * sockets. For now we're silently ignoring the error. */
+    setsockopt(handle->socket,
+               IPPROTO_IPV6,
+               IPV6_V6ONLY,
+               (char*) &no,
+               sizeof no);
+  }
+
+  r = bind(handle->socket, addr, addrlen);
+  if (r == SOCKET_ERROR) {
+    return WSAGetLastError();
+  }
+
+  handle->flags |= UV_HANDLE_BOUND;
+
+  return 0;
+}
+
+
+static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
+  uv_req_t* req;
+  uv_buf_t buf;
+  DWORD bytes, flags;
+  int result;
+
+  assert(handle->flags & UV_HANDLE_READING);
+  assert(!(handle->flags & UV_HANDLE_READ_PENDING));
+
+  req = &handle->recv_req;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  /*
+   * Preallocate a read buffer if the number of active streams is below
+   * the threshold.
+  */
+  if (loop->active_udp_streams < uv_active_udp_streams_threshold) {
+    handle->flags &= ~UV_HANDLE_ZERO_READ;
+
+    handle->recv_buffer = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->recv_buffer);
+    if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) {
+      handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0);
+      return;
+    }
+    assert(handle->recv_buffer.base != NULL);
+
+    buf = handle->recv_buffer;
+    memset(&handle->recv_from, 0, sizeof handle->recv_from);
+    handle->recv_from_len = sizeof handle->recv_from;
+    flags = 0;
+
+    result = handle->func_wsarecvfrom(handle->socket,
+                                      (WSABUF*) &buf,
+                                      1,
+                                      &bytes,
+                                      &flags,
+                                      (struct sockaddr*) &handle->recv_from,
+                                      &handle->recv_from_len,
+                                      &req->u.io.overlapped,
+                                      NULL);
+
+    if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+      /* Process the req without IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      req->u.io.overlapped.InternalHigh = bytes;
+      handle->reqs_pending++;
+      uv_insert_pending_req(loop, req);
+    } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+      /* The req will be processed with IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      handle->reqs_pending++;
+    } else {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, WSAGetLastError());
+      uv_insert_pending_req(loop, req);
+      handle->reqs_pending++;
+    }
+
+  } else {
+    handle->flags |= UV_HANDLE_ZERO_READ;
+
+    buf.base = (char*) uv_zero_;
+    buf.len = 0;
+    flags = MSG_PEEK;
+
+    result = handle->func_wsarecv(handle->socket,
+                                  (WSABUF*) &buf,
+                                  1,
+                                  &bytes,
+                                  &flags,
+                                  &req->u.io.overlapped,
+                                  NULL);
+
+    if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+      /* Process the req without IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      req->u.io.overlapped.InternalHigh = bytes;
+      handle->reqs_pending++;
+      uv_insert_pending_req(loop, req);
+    } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+      /* The req will be processed with IOCP. */
+      handle->flags |= UV_HANDLE_READ_PENDING;
+      handle->reqs_pending++;
+    } else {
+      /* Make this req pending reporting an error. */
+      SET_REQ_ERROR(req, WSAGetLastError());
+      uv_insert_pending_req(loop, req);
+      handle->reqs_pending++;
+    }
+  }
+}
+
+
+int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
+    uv_udp_recv_cb recv_cb) {
+  uv_loop_t* loop = handle->loop;
+  int err;
+
+  if (handle->flags & UV_HANDLE_READING) {
+    return UV_EALREADY;
+  }
+
+  err = uv_udp_maybe_bind(handle,
+                          (const struct sockaddr*) &uv_addr_ip4_any_,
+                          sizeof(uv_addr_ip4_any_),
+                          0);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  handle->flags |= UV_HANDLE_READING;
+  INCREASE_ACTIVE_COUNT(loop, handle);
+  loop->active_udp_streams++;
+
+  handle->recv_cb = recv_cb;
+  handle->alloc_cb = alloc_cb;
+
+  /* If reading was stopped and then started again, there could still be a recv
+   * request pending. */
+  if (!(handle->flags & UV_HANDLE_READ_PENDING))
+    uv_udp_queue_recv(loop, handle);
+
+  return 0;
+}
+
+
+int uv__udp_recv_stop(uv_udp_t* handle) {
+  if (handle->flags & UV_HANDLE_READING) {
+    handle->flags &= ~UV_HANDLE_READING;
+    handle->loop->active_udp_streams--;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  return 0;
+}
+
+
+static int uv__send(uv_udp_send_t* req,
+                    uv_udp_t* handle,
+                    const uv_buf_t bufs[],
+                    unsigned int nbufs,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen,
+                    uv_udp_send_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  DWORD result, bytes;
+
+  UV_REQ_INIT(req, UV_UDP_SEND);
+  req->handle = handle;
+  req->cb = cb;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  result = WSASendTo(handle->socket,
+                     (WSABUF*)bufs,
+                     nbufs,
+                     &bytes,
+                     0,
+                     addr,
+                     addrlen,
+                     &req->u.io.overlapped,
+                     NULL);
+
+  if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
+    /* Request completed immediately. */
+    req->u.io.queued_bytes = 0;
+    handle->reqs_pending++;
+    handle->send_queue_size += req->u.io.queued_bytes;
+    handle->send_queue_count++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    uv_insert_pending_req(loop, (uv_req_t*)req);
+  } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
+    /* Request queued by the kernel. */
+    req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
+    handle->reqs_pending++;
+    handle->send_queue_size += req->u.io.queued_bytes;
+    handle->send_queue_count++;
+    REGISTER_HANDLE_REQ(loop, handle, req);
+  } else {
+    /* Send failed due to an error. */
+    return WSAGetLastError();
+  }
+
+  return 0;
+}
+
+
+void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
+    uv_req_t* req) {
+  uv_buf_t buf;
+  int partial;
+
+  assert(handle->type == UV_UDP);
+
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+
+  if (!REQ_SUCCESS(req)) {
+    DWORD err = GET_REQ_SOCK_ERROR(req);
+    if (err == WSAEMSGSIZE) {
+      /* Not a real error, it just indicates that the received packet was
+       * bigger than the receive buffer. */
+    } else if (err == WSAECONNRESET || err == WSAENETRESET) {
+      /* A previous sendto operation failed; ignore this error. If zero-reading
+       * we need to call WSARecv/WSARecvFrom _without_ the. MSG_PEEK flag to
+       * clear out the error queue. For nonzero reads, immediately queue a new
+       * receive. */
+      if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
+        goto done;
+      }
+    } else {
+      /* A real error occurred. Report the error to the user only if we're
+       * currently reading. */
+      if (handle->flags & UV_HANDLE_READING) {
+        uv_udp_recv_stop(handle);
+        buf = (handle->flags & UV_HANDLE_ZERO_READ) ?
+              uv_buf_init(NULL, 0) : handle->recv_buffer;
+        handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
+      }
+      goto done;
+    }
+  }
+
+  if (!(handle->flags & UV_HANDLE_ZERO_READ)) {
+    /* Successful read */
+    partial = !REQ_SUCCESS(req);
+    handle->recv_cb(handle,
+                    req->u.io.overlapped.InternalHigh,
+                    &handle->recv_buffer,
+                    (const struct sockaddr*) &handle->recv_from,
+                    partial ? UV_UDP_PARTIAL : 0);
+  } else if (handle->flags & UV_HANDLE_READING) {
+    DWORD bytes, err, flags;
+    struct sockaddr_storage from;
+    int from_len;
+
+    /* Do a nonblocking receive.
+     * TODO: try to read multiple datagrams at once. FIONREAD maybe? */
+    buf = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, 65536, &buf);
+    if (buf.base == NULL || buf.len == 0) {
+      handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
+      goto done;
+    }
+    assert(buf.base != NULL);
+
+    memset(&from, 0, sizeof from);
+    from_len = sizeof from;
+
+    flags = 0;
+
+    if (WSARecvFrom(handle->socket,
+                    (WSABUF*)&buf,
+                    1,
+                    &bytes,
+                    &flags,
+                    (struct sockaddr*) &from,
+                    &from_len,
+                    NULL,
+                    NULL) != SOCKET_ERROR) {
+
+      /* Message received */
+      handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0);
+    } else {
+      err = WSAGetLastError();
+      if (err == WSAEMSGSIZE) {
+        /* Message truncated */
+        handle->recv_cb(handle,
+                        bytes,
+                        &buf,
+                        (const struct sockaddr*) &from,
+                        UV_UDP_PARTIAL);
+      } else if (err == WSAEWOULDBLOCK) {
+        /* Kernel buffer empty */
+        handle->recv_cb(handle, 0, &buf, NULL, 0);
+      } else if (err == WSAECONNRESET || err == WSAENETRESET) {
+        /* WSAECONNRESET/WSANETRESET is ignored because this just indicates
+         * that a previous sendto operation failed.
+         */
+        handle->recv_cb(handle, 0, &buf, NULL, 0);
+      } else {
+        /* Any other error that we want to report back to the user. */
+        uv_udp_recv_stop(handle);
+        handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0);
+      }
+    }
+  }
+
+done:
+  /* Post another read if still reading and not closing. */
+  if ((handle->flags & UV_HANDLE_READING) &&
+      !(handle->flags & UV_HANDLE_READ_PENDING)) {
+    uv_udp_queue_recv(loop, handle);
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
+    uv_udp_send_t* req) {
+  int err;
+
+  assert(handle->type == UV_UDP);
+
+  assert(handle->send_queue_size >= req->u.io.queued_bytes);
+  assert(handle->send_queue_count >= 1);
+  handle->send_queue_size -= req->u.io.queued_bytes;
+  handle->send_queue_count--;
+
+  UNREGISTER_HANDLE_REQ(loop, handle, req);
+
+  if (req->cb) {
+    err = 0;
+    if (!REQ_SUCCESS(req)) {
+      err = GET_REQ_SOCK_ERROR(req);
+    }
+    req->cb(req, uv_translate_sys_error(err));
+  }
+
+  DECREASE_PENDING_REQ_COUNT(handle);
+}
+
+
+static int uv__udp_set_membership4(uv_udp_t* handle,
+                                   const struct sockaddr_in* multicast_addr,
+                                   const char* interface_addr,
+                                   uv_membership membership) {
+  int err;
+  int optname;
+  struct ip_mreq mreq;
+
+  if (handle->flags & UV_HANDLE_IPV6)
+    return UV_EINVAL;
+
+  /* If the socket is unbound, bind to inaddr_any. */
+  err = uv_udp_maybe_bind(handle,
+                          (const struct sockaddr*) &uv_addr_ip4_any_,
+                          sizeof(uv_addr_ip4_any_),
+                          UV_UDP_REUSEADDR);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof mreq);
+
+  if (interface_addr) {
+    err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
+    if (err)
+      return err;
+  } else {
+    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  }
+
+  mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
+
+  switch (membership) {
+    case UV_JOIN_GROUP:
+      optname = IP_ADD_MEMBERSHIP;
+      break;
+    case UV_LEAVE_GROUP:
+      optname = IP_DROP_MEMBERSHIP;
+      break;
+    default:
+      return UV_EINVAL;
+  }
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IP,
+                 optname,
+                 (char*) &mreq,
+                 sizeof mreq) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv__udp_set_membership6(uv_udp_t* handle,
+                            const struct sockaddr_in6* multicast_addr,
+                            const char* interface_addr,
+                            uv_membership membership) {
+  int optname;
+  int err;
+  struct ipv6_mreq mreq;
+  struct sockaddr_in6 addr6;
+
+  if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
+    return UV_EINVAL;
+
+  err = uv_udp_maybe_bind(handle,
+                          (const struct sockaddr*) &uv_addr_ip6_any_,
+                          sizeof(uv_addr_ip6_any_),
+                          UV_UDP_REUSEADDR);
+
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr) {
+    if (uv_ip6_addr(interface_addr, 0, &addr6))
+      return UV_EINVAL;
+    mreq.ipv6mr_interface = addr6.sin6_scope_id;
+  } else {
+    mreq.ipv6mr_interface = 0;
+  }
+
+  mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr;
+
+  switch (membership) {
+  case UV_JOIN_GROUP:
+    optname = IPV6_ADD_MEMBERSHIP;
+    break;
+  case UV_LEAVE_GROUP:
+    optname = IPV6_DROP_MEMBERSHIP;
+    break;
+  default:
+    return UV_EINVAL;
+  }
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IPV6,
+                 optname,
+                 (char*) &mreq,
+                 sizeof mreq) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+static int uv__udp_set_source_membership4(uv_udp_t* handle,
+                                          const struct sockaddr_in* multicast_addr,
+                                          const char* interface_addr,
+                                          const struct sockaddr_in* source_addr,
+                                          uv_membership membership) {
+  struct ip_mreq_source mreq;
+  int optname;
+  int err;
+
+  if (handle->flags & UV_HANDLE_IPV6)
+    return UV_EINVAL;
+
+  /* If the socket is unbound, bind to inaddr_any. */
+  err = uv_udp_maybe_bind(handle,
+                          (const struct sockaddr*) &uv_addr_ip4_any_,
+                          sizeof(uv_addr_ip4_any_),
+                          UV_UDP_REUSEADDR);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr != NULL) {
+    err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
+    if (err)
+      return err;
+  } else {
+    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  }
+
+  mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
+  mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr;
+
+  if (membership == UV_JOIN_GROUP)
+    optname = IP_ADD_SOURCE_MEMBERSHIP;
+  else if (membership == UV_LEAVE_GROUP)
+    optname = IP_DROP_SOURCE_MEMBERSHIP;
+  else
+    return UV_EINVAL;
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IP,
+                 optname,
+                 (char*) &mreq,
+                 sizeof(mreq)) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv__udp_set_source_membership6(uv_udp_t* handle,
+                                   const struct sockaddr_in6* multicast_addr,
+                                   const char* interface_addr,
+                                   const struct sockaddr_in6* source_addr,
+                                   uv_membership membership) {
+  struct group_source_req mreq;
+  struct sockaddr_in6 addr6;
+  int optname;
+  int err;
+
+  STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr));
+  STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr));
+
+  if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
+    return UV_EINVAL;
+
+  err = uv_udp_maybe_bind(handle,
+                          (const struct sockaddr*) &uv_addr_ip6_any_,
+                          sizeof(uv_addr_ip6_any_),
+                          UV_UDP_REUSEADDR);
+
+  if (err)
+    return uv_translate_sys_error(err);
+
+  memset(&mreq, 0, sizeof(mreq));
+
+  if (interface_addr != NULL) {
+    err = uv_ip6_addr(interface_addr, 0, &addr6);
+    if (err)
+      return err;
+    mreq.gsr_interface = addr6.sin6_scope_id;
+  } else {
+    mreq.gsr_interface = 0;
+  }
+
+  memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr));
+  memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr));
+
+  if (membership == UV_JOIN_GROUP)
+    optname = MCAST_JOIN_SOURCE_GROUP;
+  else if (membership == UV_LEAVE_GROUP)
+    optname = MCAST_LEAVE_SOURCE_GROUP;
+  else
+    return UV_EINVAL;
+
+  if (setsockopt(handle->socket,
+                 IPPROTO_IPV6,
+                 optname,
+                 (char*) &mreq,
+                 sizeof(mreq)) == SOCKET_ERROR) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv_udp_set_membership(uv_udp_t* handle,
+                          const char* multicast_addr,
+                          const char* interface_addr,
+                          uv_membership membership) {
+  struct sockaddr_in addr4;
+  struct sockaddr_in6 addr6;
+
+  if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0)
+    return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
+  else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0)
+    return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
+  else
+    return UV_EINVAL;
+}
+
+
+int uv_udp_set_source_membership(uv_udp_t* handle,
+                                 const char* multicast_addr,
+                                 const char* interface_addr,
+                                 const char* source_addr,
+                                 uv_membership membership) {
+  int err;
+  struct sockaddr_storage mcast_addr;
+  struct sockaddr_in* mcast_addr4;
+  struct sockaddr_in6* mcast_addr6;
+  struct sockaddr_storage src_addr;
+  struct sockaddr_in* src_addr4;
+  struct sockaddr_in6* src_addr6;
+
+  mcast_addr4 = (struct sockaddr_in*)&mcast_addr;
+  mcast_addr6 = (struct sockaddr_in6*)&mcast_addr;
+  src_addr4 = (struct sockaddr_in*)&src_addr;
+  src_addr6 = (struct sockaddr_in6*)&src_addr;
+
+  err = uv_ip4_addr(multicast_addr, 0, mcast_addr4);
+  if (err) {
+    err = uv_ip6_addr(multicast_addr, 0, mcast_addr6);
+    if (err)
+      return err;
+    err = uv_ip6_addr(source_addr, 0, src_addr6);
+    if (err)
+      return err;
+    return uv__udp_set_source_membership6(handle,
+                                          mcast_addr6,
+                                          interface_addr,
+                                          src_addr6,
+                                          membership);
+  }
+  
+  err = uv_ip4_addr(source_addr, 0, src_addr4);
+  if (err)
+    return err;
+  return uv__udp_set_source_membership4(handle,
+                                        mcast_addr4,
+                                        interface_addr,
+                                        src_addr4,
+                                        membership);
+}
+
+
+int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
+  struct sockaddr_storage addr_st;
+  struct sockaddr_in* addr4;
+  struct sockaddr_in6* addr6;
+
+  addr4 = (struct sockaddr_in*) &addr_st;
+  addr6 = (struct sockaddr_in6*) &addr_st;
+
+  if (!interface_addr) {
+    memset(&addr_st, 0, sizeof addr_st);
+    if (handle->flags & UV_HANDLE_IPV6) {
+      addr_st.ss_family = AF_INET6;
+      addr6->sin6_scope_id = 0;
+    } else {
+      addr_st.ss_family = AF_INET;
+      addr4->sin_addr.s_addr = htonl(INADDR_ANY);
+    }
+  } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) {
+    /* nothing, address was parsed */
+  } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) {
+    /* nothing, address was parsed */
+  } else {
+    return UV_EINVAL;
+  }
+
+  if (handle->socket == INVALID_SOCKET)
+    return UV_EBADF;
+
+  if (addr_st.ss_family == AF_INET) {
+    if (setsockopt(handle->socket,
+                   IPPROTO_IP,
+                   IP_MULTICAST_IF,
+                   (char*) &addr4->sin_addr,
+                   sizeof(addr4->sin_addr)) == SOCKET_ERROR) {
+      return uv_translate_sys_error(WSAGetLastError());
+    }
+  } else if (addr_st.ss_family == AF_INET6) {
+    if (setsockopt(handle->socket,
+                   IPPROTO_IPV6,
+                   IPV6_MULTICAST_IF,
+                   (char*) &addr6->sin6_scope_id,
+                   sizeof(addr6->sin6_scope_id)) == SOCKET_ERROR) {
+      return uv_translate_sys_error(WSAGetLastError());
+    }
+  } else {
+    assert(0 && "unexpected address family");
+    abort();
+  }
+
+  return 0;
+}
+
+
+int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
+  BOOL optval = (BOOL) value;
+
+  if (handle->socket == INVALID_SOCKET)
+    return UV_EBADF;
+
+  if (setsockopt(handle->socket,
+                 SOL_SOCKET,
+                 SO_BROADCAST,
+                 (char*) &optval,
+                 sizeof optval)) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv__udp_is_bound(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getsockname(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
+int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
+  WSAPROTOCOL_INFOW protocol_info;
+  int opt_len;
+  int err;
+
+  /* Detect the address family of the socket. */
+  opt_len = (int) sizeof protocol_info;
+  if (getsockopt(sock,
+                 SOL_SOCKET,
+                 SO_PROTOCOL_INFOW,
+                 (char*) &protocol_info,
+                 &opt_len) == SOCKET_ERROR) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  err = uv_udp_set_socket(handle->loop,
+                          handle,
+                          sock,
+                          protocol_info.iAddressFamily);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  if (uv__udp_is_bound(handle))
+    handle->flags |= UV_HANDLE_BOUND;
+
+  if (uv__udp_is_connected(handle))
+    handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+#define SOCKOPT_SETTER(name, option4, option6, validate)                      \
+  int uv_udp_set_##name(uv_udp_t* handle, int value) {                        \
+    DWORD optval = (DWORD) value;                                             \
+                                                                              \
+    if (!(validate(value))) {                                                 \
+      return UV_EINVAL;                                                       \
+    }                                                                         \
+                                                                              \
+    if (handle->socket == INVALID_SOCKET)                                     \
+      return UV_EBADF;                                                        \
+                                                                              \
+    if (!(handle->flags & UV_HANDLE_IPV6)) {                                  \
+      /* Set IPv4 socket option */                                            \
+      if (setsockopt(handle->socket,                                          \
+                     IPPROTO_IP,                                              \
+                     option4,                                                 \
+                     (char*) &optval,                                         \
+                     sizeof optval)) {                                        \
+        return uv_translate_sys_error(WSAGetLastError());                     \
+      }                                                                       \
+    } else {                                                                  \
+      /* Set IPv6 socket option */                                            \
+      if (setsockopt(handle->socket,                                          \
+                     IPPROTO_IPV6,                                            \
+                     option6,                                                 \
+                     (char*) &optval,                                         \
+                     sizeof optval)) {                                        \
+        return uv_translate_sys_error(WSAGetLastError());                     \
+      }                                                                       \
+    }                                                                         \
+    return 0;                                                                 \
+  }
+
+#define VALIDATE_TTL(value) ((value) >= 1 && (value) <= 255)
+#define VALIDATE_MULTICAST_TTL(value) ((value) >= -1 && (value) <= 255)
+#define VALIDATE_MULTICAST_LOOP(value) (1)
+
+SOCKOPT_SETTER(ttl,
+               IP_TTL,
+               IPV6_HOPLIMIT,
+               VALIDATE_TTL)
+SOCKOPT_SETTER(multicast_ttl,
+               IP_MULTICAST_TTL,
+               IPV6_MULTICAST_HOPS,
+               VALIDATE_MULTICAST_TTL)
+SOCKOPT_SETTER(multicast_loop,
+               IP_MULTICAST_LOOP,
+               IPV6_MULTICAST_LOOP,
+               VALIDATE_MULTICAST_LOOP)
+
+#undef SOCKOPT_SETTER
+#undef VALIDATE_TTL
+#undef VALIDATE_MULTICAST_TTL
+#undef VALIDATE_MULTICAST_LOOP
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__udp_bind(uv_udp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+
+  err = uv_udp_maybe_bind(handle, addr, addrlen, flags);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
+
+
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen) {
+  const struct sockaddr* bind_addr;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+
+    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = connect(handle->socket, addr, addrlen);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+int uv__udp_disconnect(uv_udp_t* handle) {
+    int err;
+    struct sockaddr addr;
+
+    memset(&addr, 0, sizeof(addr));
+
+    err = connect(handle->socket, &addr, sizeof(addr));
+    if (err)
+      return uv_translate_sys_error(err);
+
+    handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
+    return 0;
+}
+
+
+/* This function is an egress point, i.e. it returns libuv errors rather than
+ * system errors.
+ */
+int uv__udp_send(uv_udp_send_t* req,
+                 uv_udp_t* handle,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 uv_udp_send_cb send_cb) {
+  const struct sockaddr* bind_addr;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+
+    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = uv__send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  return 0;
+}
+
+
+int uv__udp_try_send(uv_udp_t* handle,
+                     const uv_buf_t bufs[],
+                     unsigned int nbufs,
+                     const struct sockaddr* addr,
+                     unsigned int addrlen) {
+  DWORD bytes;
+  const struct sockaddr* bind_addr;
+  struct sockaddr_storage converted;
+  int err;
+
+  assert(nbufs > 0);
+
+  if (addr != NULL) {
+    err = uv__convert_to_localhost_if_unspecified(addr, &converted);
+    if (err)
+      return err;
+  }
+
+  /* Already sending a message.*/
+  if (handle->send_queue_count != 0)
+    return UV_EAGAIN;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = WSASendTo(handle->socket,
+                  (WSABUF*)bufs,
+                  nbufs,
+                  &bytes,
+                  0,
+                  (const struct sockaddr*) &converted,
+                  addrlen,
+                  NULL,
+                  NULL);
+
+  if (err)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return bytes;
+}
diff --git a/third_party/libuv/src/win/util.c b/third_party/libuv/src/win/util.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/util.c
@@ -0,0 +1,1977 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <direct.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+
+#include "uv.h"
+#include "internal.h"
+
+/* clang-format off */
+#include <winsock2.h>
+#include <winperf.h>
+#include <iphlpapi.h>
+#include <psapi.h>
+#include <tlhelp32.h>
+#include <windows.h>
+/* clang-format on */
+#include <userenv.h>
+#include <math.h>
+
+/*
+ * Max title length; the only thing MSDN tells us about the maximum length
+ * of the console title is that it is smaller than 64K. However in practice
+ * it is much smaller, and there is no way to figure out what the exact length
+ * of the title is or can be, at least not on XP. To make it even more
+ * annoying, GetConsoleTitle fails when the buffer to be read into is bigger
+ * than the actual maximum length. So we make a conservative guess here;
+ * just don't put the novel you're writing in the title, unless the plot
+ * survives truncation.
+ */
+#define MAX_TITLE_LENGTH 8192
+
+/* The number of nanoseconds in one second. */
+#define UV__NANOSEC 1000000000
+
+/* Max user name length, from iphlpapi.h */
+#ifndef UNLEN
+# define UNLEN 256
+#endif
+
+
+/* A RtlGenRandom() by any other name... */
+extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength);
+
+/* Cached copy of the process title, plus a mutex guarding it. */
+static char *process_title;
+static CRITICAL_SECTION process_title_lock;
+
+/* Frequency of the high-resolution clock. */
+static uint64_t hrtime_frequency_ = 0;
+
+
+/*
+ * One-time initialization code for functionality defined in util.c.
+ */
+void uv__util_init(void) {
+  LARGE_INTEGER perf_frequency;
+
+  /* Initialize process title access mutex. */
+  InitializeCriticalSection(&process_title_lock);
+
+  /* Retrieve high-resolution timer frequency
+   * and precompute its reciprocal.
+   */
+  if (QueryPerformanceFrequency(&perf_frequency)) {
+    hrtime_frequency_ = perf_frequency.QuadPart;
+  } else {
+    uv_fatal_error(GetLastError(), "QueryPerformanceFrequency");
+  }
+}
+
+
+int uv_exepath(char* buffer, size_t* size_ptr) {
+  int utf8_len, utf16_buffer_len, utf16_len;
+  WCHAR* utf16_buffer;
+  int err;
+
+  if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) {
+    return UV_EINVAL;
+  }
+
+  if (*size_ptr > 32768) {
+    /* Windows paths can never be longer than this. */
+    utf16_buffer_len = 32768;
+  } else {
+    utf16_buffer_len = (int) *size_ptr;
+  }
+
+  utf16_buffer = (WCHAR*) uv__malloc(sizeof(WCHAR) * utf16_buffer_len);
+  if (!utf16_buffer) {
+    return UV_ENOMEM;
+  }
+
+  /* Get the path as UTF-16. */
+  utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len);
+  if (utf16_len <= 0) {
+    err = GetLastError();
+    goto error;
+  }
+
+  /* utf16_len contains the length, *not* including the terminating null. */
+  utf16_buffer[utf16_len] = L'\0';
+
+  /* Convert to UTF-8 */
+  utf8_len = WideCharToMultiByte(CP_UTF8,
+                                 0,
+                                 utf16_buffer,
+                                 -1,
+                                 buffer,
+                                 (int) *size_ptr,
+                                 NULL,
+                                 NULL);
+  if (utf8_len == 0) {
+    err = GetLastError();
+    goto error;
+  }
+
+  uv__free(utf16_buffer);
+
+  /* utf8_len *does* include the terminating null at this point, but the
+   * returned size shouldn't. */
+  *size_ptr = utf8_len - 1;
+  return 0;
+
+ error:
+  uv__free(utf16_buffer);
+  return uv_translate_sys_error(err);
+}
+
+
+int uv_cwd(char* buffer, size_t* size) {
+  DWORD utf16_len;
+  WCHAR *utf16_buffer;
+  int r;
+
+  if (buffer == NULL || size == NULL) {
+    return UV_EINVAL;
+  }
+
+  utf16_len = GetCurrentDirectoryW(0, NULL);
+  if (utf16_len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+  utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
+  if (utf16_buffer == NULL) {
+    return UV_ENOMEM;
+  }
+
+  utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
+  if (utf16_len == 0) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* utf16_len contains the length, *not* including the terminating null. */
+  utf16_buffer[utf16_len] = L'\0';
+
+  /* The returned directory should not have a trailing slash, unless it points
+   * at a drive root, like c:\. Remove it if needed. */
+  if (utf16_buffer[utf16_len - 1] == L'\\' &&
+      !(utf16_len == 3 && utf16_buffer[1] == L':')) {
+    utf16_len--;
+    utf16_buffer[utf16_len] = L'\0';
+  }
+
+  /* Check how much space we need */
+  r = WideCharToMultiByte(CP_UTF8,
+                          0,
+                          utf16_buffer,
+                          -1,
+                          NULL,
+                          0,
+                          NULL,
+                          NULL);
+  if (r == 0) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  } else if (r > (int) *size) {
+    uv__free(utf16_buffer);
+    *size = r;
+    return UV_ENOBUFS;
+  }
+
+  /* Convert to UTF-8 */
+  r = WideCharToMultiByte(CP_UTF8,
+                          0,
+                          utf16_buffer,
+                          -1,
+                          buffer,
+                          *size > INT_MAX ? INT_MAX : (int) *size,
+                          NULL,
+                          NULL);
+  uv__free(utf16_buffer);
+
+  if (r == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  *size = r - 1;
+  return 0;
+}
+
+
+int uv_chdir(const char* dir) {
+  WCHAR *utf16_buffer;
+  size_t utf16_len, new_utf16_len;
+  WCHAR drive_letter, env_var[4];
+
+  if (dir == NULL) {
+    return UV_EINVAL;
+  }
+
+  utf16_len = MultiByteToWideChar(CP_UTF8,
+                                  0,
+                                  dir,
+                                  -1,
+                                  NULL,
+                                  0);
+  if (utf16_len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+  utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
+  if (utf16_buffer == NULL) {
+    return UV_ENOMEM;
+  }
+
+  if (MultiByteToWideChar(CP_UTF8,
+                          0,
+                          dir,
+                          -1,
+                          utf16_buffer,
+                          utf16_len) == 0) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  if (!SetCurrentDirectoryW(utf16_buffer)) {
+    uv__free(utf16_buffer);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* Windows stores the drive-local path in an "hidden" environment variable,
+   * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update
+   * this, so we'll have to do it. */
+  new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
+  if (new_utf16_len > utf16_len ) {
+    uv__free(utf16_buffer);
+    utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR));
+    if (utf16_buffer == NULL) {
+      /* When updating the environment variable fails, return UV_OK anyway.
+       * We did successfully change current working directory, only updating
+       * hidden env variable failed. */
+      return 0;
+    }
+    new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer);
+  }
+  if (utf16_len == 0) {
+    uv__free(utf16_buffer);
+    return 0;
+  }
+
+  /* The returned directory should not have a trailing slash, unless it points
+   * at a drive root, like c:\. Remove it if needed. */
+  if (utf16_buffer[utf16_len - 1] == L'\\' &&
+      !(utf16_len == 3 && utf16_buffer[1] == L':')) {
+    utf16_len--;
+    utf16_buffer[utf16_len] = L'\0';
+  }
+
+  if (utf16_len < 2 || utf16_buffer[1] != L':') {
+    /* Doesn't look like a drive letter could be there - probably an UNC path.
+     * TODO: Need to handle win32 namespaces like \\?\C:\ ? */
+    drive_letter = 0;
+  } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') {
+    drive_letter = utf16_buffer[0];
+  } else if (utf16_buffer[0] >= L'a' && utf16_buffer[0] <= L'z') {
+    /* Convert to uppercase. */
+    drive_letter = utf16_buffer[0] - L'a' + L'A';
+  } else {
+    /* Not valid. */
+    drive_letter = 0;
+  }
+
+  if (drive_letter != 0) {
+    /* Construct the environment variable name and set it. */
+    env_var[0] = L'=';
+    env_var[1] = drive_letter;
+    env_var[2] = L':';
+    env_var[3] = L'\0';
+
+    SetEnvironmentVariableW(env_var, utf16_buffer);
+  }
+
+  uv__free(utf16_buffer);
+  return 0;
+}
+
+
+void uv_loadavg(double avg[3]) {
+  /* Can't be implemented */
+  avg[0] = avg[1] = avg[2] = 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  MEMORYSTATUSEX memory_status;
+  memory_status.dwLength = sizeof(memory_status);
+
+  if (!GlobalMemoryStatusEx(&memory_status)) {
+     return -1;
+  }
+
+  return (uint64_t)memory_status.ullAvailPhys;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  MEMORYSTATUSEX memory_status;
+  memory_status.dwLength = sizeof(memory_status);
+
+  if (!GlobalMemoryStatusEx(&memory_status)) {
+    return -1;
+  }
+
+  return (uint64_t)memory_status.ullTotalPhys;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+uv_pid_t uv_os_getpid(void) {
+  return GetCurrentProcessId();
+}
+
+
+uv_pid_t uv_os_getppid(void) {
+  int parent_pid = -1;
+  HANDLE handle;
+  PROCESSENTRY32 pe;
+  DWORD current_pid = GetCurrentProcessId();
+
+  pe.dwSize = sizeof(PROCESSENTRY32);
+  handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+  if (Process32First(handle, &pe)) {
+    do {
+      if (pe.th32ProcessID == current_pid) {
+        parent_pid = pe.th32ParentProcessID;
+        break;
+      }
+    } while( Process32Next(handle, &pe));
+  }
+
+  CloseHandle(handle);
+  return parent_pid;
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  return argv;
+}
+
+
+void uv__process_title_cleanup(void) {
+}
+
+
+int uv_set_process_title(const char* title) {
+  int err;
+  int length;
+  WCHAR* title_w = NULL;
+
+  uv__once_init();
+
+  /* Find out how big the buffer for the wide-char title must be */
+  length = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0);
+  if (!length) {
+    err = GetLastError();
+    goto done;
+  }
+
+  /* Convert to wide-char string */
+  title_w = (WCHAR*)uv__malloc(sizeof(WCHAR) * length);
+  if (!title_w) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  }
+
+  length = MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, length);
+  if (!length) {
+    err = GetLastError();
+    goto done;
+  }
+
+  /* If the title must be truncated insert a \0 terminator there */
+  if (length > MAX_TITLE_LENGTH) {
+    title_w[MAX_TITLE_LENGTH - 1] = L'\0';
+  }
+
+  if (!SetConsoleTitleW(title_w)) {
+    err = GetLastError();
+    goto done;
+  }
+
+  EnterCriticalSection(&process_title_lock);
+  uv__free(process_title);
+  process_title = uv__strdup(title);
+  LeaveCriticalSection(&process_title_lock);
+
+  err = 0;
+
+done:
+  uv__free(title_w);
+  return uv_translate_sys_error(err);
+}
+
+
+static int uv__get_process_title(void) {
+  WCHAR title_w[MAX_TITLE_LENGTH];
+
+  if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) {
+    return -1;
+  }
+
+  if (uv__convert_utf16_to_utf8(title_w, -1, &process_title) != 0)
+    return -1;
+
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  size_t len;
+
+  if (buffer == NULL || size == 0)
+    return UV_EINVAL;
+
+  uv__once_init();
+
+  EnterCriticalSection(&process_title_lock);
+  /*
+   * If the process_title was never read before nor explicitly set,
+   * we must query it with getConsoleTitleW
+   */
+  if (!process_title && uv__get_process_title() == -1) {
+    LeaveCriticalSection(&process_title_lock);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  assert(process_title);
+  len = strlen(process_title) + 1;
+
+  if (size < len) {
+    LeaveCriticalSection(&process_title_lock);
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, process_title, len);
+  LeaveCriticalSection(&process_title_lock);
+
+  return 0;
+}
+
+
+uint64_t uv_hrtime(void) {
+  uv__once_init();
+  return uv__hrtime(UV__NANOSEC);
+}
+
+uint64_t uv__hrtime(unsigned int scale) {
+  LARGE_INTEGER counter;
+  double scaled_freq;
+  double result;
+
+  assert(hrtime_frequency_ != 0);
+  assert(scale != 0);
+  if (!QueryPerformanceCounter(&counter)) {
+    uv_fatal_error(GetLastError(), "QueryPerformanceCounter");
+  }
+  assert(counter.QuadPart != 0);
+
+  /* Because we have no guarantee about the order of magnitude of the
+   * performance counter interval, integer math could cause this computation
+   * to overflow. Therefore we resort to floating point math.
+   */
+  scaled_freq = (double) hrtime_frequency_ / scale;
+  result = (double) counter.QuadPart / scaled_freq;
+  return (uint64_t) result;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  HANDLE current_process;
+  PROCESS_MEMORY_COUNTERS pmc;
+
+  current_process = GetCurrentProcess();
+
+  if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  *rss = pmc.WorkingSetSize;
+
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  BYTE stack_buffer[4096];
+  BYTE* malloced_buffer = NULL;
+  BYTE* buffer = (BYTE*) stack_buffer;
+  size_t buffer_size = sizeof(stack_buffer);
+  DWORD data_size;
+
+  PERF_DATA_BLOCK* data_block;
+  PERF_OBJECT_TYPE* object_type;
+  PERF_COUNTER_DEFINITION* counter_definition;
+
+  DWORD i;
+
+  for (;;) {
+    LONG result;
+
+    data_size = (DWORD) buffer_size;
+    result = RegQueryValueExW(HKEY_PERFORMANCE_DATA,
+                              L"2",
+                              NULL,
+                              NULL,
+                              buffer,
+                              &data_size);
+    if (result == ERROR_SUCCESS) {
+      break;
+    } else if (result != ERROR_MORE_DATA) {
+      *uptime = 0;
+      return uv_translate_sys_error(result);
+    }
+
+    buffer_size *= 2;
+    /* Don't let the buffer grow infinitely. */
+    if (buffer_size > 1 << 20) {
+      goto internalError;
+    }
+
+    uv__free(malloced_buffer);
+
+    buffer = malloced_buffer = (BYTE*) uv__malloc(buffer_size);
+    if (malloced_buffer == NULL) {
+      *uptime = 0;
+      return UV_ENOMEM;
+    }
+  }
+
+  if (data_size < sizeof(*data_block))
+    goto internalError;
+
+  data_block = (PERF_DATA_BLOCK*) buffer;
+
+  if (wmemcmp(data_block->Signature, L"PERF", 4) != 0)
+    goto internalError;
+
+  if (data_size < data_block->HeaderLength + sizeof(*object_type))
+    goto internalError;
+
+  object_type = (PERF_OBJECT_TYPE*) (buffer + data_block->HeaderLength);
+
+  if (object_type->NumInstances != PERF_NO_INSTANCES)
+    goto internalError;
+
+  counter_definition = (PERF_COUNTER_DEFINITION*) (buffer +
+      data_block->HeaderLength + object_type->HeaderLength);
+  for (i = 0; i < object_type->NumCounters; i++) {
+    if ((BYTE*) counter_definition + sizeof(*counter_definition) >
+        buffer + data_size) {
+      break;
+    }
+
+    if (counter_definition->CounterNameTitleIndex == 674 &&
+        counter_definition->CounterSize == sizeof(uint64_t)) {
+      if (counter_definition->CounterOffset + sizeof(uint64_t) > data_size ||
+          !(counter_definition->CounterType & PERF_OBJECT_TIMER)) {
+        goto internalError;
+      } else {
+        BYTE* address = (BYTE*) object_type + object_type->DefinitionLength +
+                        counter_definition->CounterOffset;
+        uint64_t value = *((uint64_t*) address);
+        *uptime = floor((double) (object_type->PerfTime.QuadPart - value) /
+                        (double) object_type->PerfFreq.QuadPart);
+        uv__free(malloced_buffer);
+        return 0;
+      }
+    }
+
+    counter_definition = (PERF_COUNTER_DEFINITION*)
+        ((BYTE*) counter_definition + counter_definition->ByteLength);
+  }
+
+  /* If we get here, the uptime value was not found. */
+  uv__free(malloced_buffer);
+  *uptime = 0;
+  return UV_ENOSYS;
+
+ internalError:
+  uv__free(malloced_buffer);
+  *uptime = 0;
+  return UV_EIO;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
+  uv_cpu_info_t* cpu_infos;
+  SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi;
+  DWORD sppi_size;
+  SYSTEM_INFO system_info;
+  DWORD cpu_count, i;
+  NTSTATUS status;
+  ULONG result_size;
+  int err;
+  uv_cpu_info_t* cpu_info;
+
+  cpu_infos = NULL;
+  cpu_count = 0;
+  sppi = NULL;
+
+  uv__once_init();
+
+  GetSystemInfo(&system_info);
+  cpu_count = system_info.dwNumberOfProcessors;
+
+  cpu_infos = uv__calloc(cpu_count, sizeof *cpu_infos);
+  if (cpu_infos == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  sppi_size = cpu_count * sizeof(*sppi);
+  sppi = uv__malloc(sppi_size);
+  if (sppi == NULL) {
+    err = ERROR_OUTOFMEMORY;
+    goto error;
+  }
+
+  status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation,
+                                     sppi,
+                                     sppi_size,
+                                     &result_size);
+  if (!NT_SUCCESS(status)) {
+    err = pRtlNtStatusToDosError(status);
+    goto error;
+  }
+
+  assert(result_size == sppi_size);
+
+  for (i = 0; i < cpu_count; i++) {
+    WCHAR key_name[128];
+    HKEY processor_key;
+    DWORD cpu_speed;
+    DWORD cpu_speed_size = sizeof(cpu_speed);
+    WCHAR cpu_brand[256];
+    DWORD cpu_brand_size = sizeof(cpu_brand);
+    size_t len;
+
+    len = _snwprintf(key_name,
+                     ARRAY_SIZE(key_name),
+                     L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d",
+                     i);
+
+    assert(len > 0 && len < ARRAY_SIZE(key_name));
+
+    err = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                        key_name,
+                        0,
+                        KEY_QUERY_VALUE,
+                        &processor_key);
+    if (err != ERROR_SUCCESS) {
+      goto error;
+    }
+
+    err = RegQueryValueExW(processor_key,
+                           L"~MHz",
+                           NULL,
+                           NULL,
+                           (BYTE*)&cpu_speed,
+                           &cpu_speed_size);
+    if (err != ERROR_SUCCESS) {
+      RegCloseKey(processor_key);
+      goto error;
+    }
+
+    err = RegQueryValueExW(processor_key,
+                           L"ProcessorNameString",
+                           NULL,
+                           NULL,
+                           (BYTE*)&cpu_brand,
+                           &cpu_brand_size);
+    RegCloseKey(processor_key);
+    if (err != ERROR_SUCCESS)
+      goto error;
+
+    cpu_info = &cpu_infos[i];
+    cpu_info->speed = cpu_speed;
+    cpu_info->cpu_times.user = sppi[i].UserTime.QuadPart / 10000;
+    cpu_info->cpu_times.sys = (sppi[i].KernelTime.QuadPart -
+        sppi[i].IdleTime.QuadPart) / 10000;
+    cpu_info->cpu_times.idle = sppi[i].IdleTime.QuadPart / 10000;
+    cpu_info->cpu_times.irq = sppi[i].InterruptTime.QuadPart / 10000;
+    cpu_info->cpu_times.nice = 0;
+
+    uv__convert_utf16_to_utf8(cpu_brand,
+                              cpu_brand_size / sizeof(WCHAR),
+                              &(cpu_info->model));
+  }
+
+  uv__free(sppi);
+
+  *cpu_count_ptr = cpu_count;
+  *cpu_infos_ptr = cpu_infos;
+
+  return 0;
+
+ error:
+  if (cpu_infos != NULL) {
+    /* This is safe because the cpu_infos array is zeroed on allocation. */
+    for (i = 0; i < cpu_count; i++)
+      uv__free(cpu_infos[i].model);
+  }
+
+  uv__free(cpu_infos);
+  uv__free(sppi);
+
+  return uv_translate_sys_error(err);
+}
+
+
+static int is_windows_version_or_greater(DWORD os_major,
+                                         DWORD os_minor,
+                                         WORD service_pack_major,
+                                         WORD service_pack_minor) {
+  OSVERSIONINFOEX osvi;
+  DWORDLONG condition_mask = 0;
+  int op = VER_GREATER_EQUAL;
+
+  /* Initialize the OSVERSIONINFOEX structure. */
+  ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+  osvi.dwMajorVersion = os_major;
+  osvi.dwMinorVersion = os_minor;
+  osvi.wServicePackMajor = service_pack_major;
+  osvi.wServicePackMinor = service_pack_minor;
+
+  /* Initialize the condition mask. */
+  VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op);
+  VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op);
+  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op);
+  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op);
+
+  /* Perform the test. */
+  return (int) VerifyVersionInfo(
+    &osvi,
+    VER_MAJORVERSION | VER_MINORVERSION |
+    VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
+    condition_mask);
+}
+
+
+static int address_prefix_match(int family,
+                                struct sockaddr* address,
+                                struct sockaddr* prefix_address,
+                                int prefix_len) {
+  uint8_t* address_data;
+  uint8_t* prefix_address_data;
+  int i;
+
+  assert(address->sa_family == family);
+  assert(prefix_address->sa_family == family);
+
+  if (family == AF_INET6) {
+    address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr);
+    prefix_address_data =
+      (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr);
+  } else {
+    address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr);
+    prefix_address_data =
+      (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr);
+  }
+
+  for (i = 0; i < prefix_len >> 3; i++) {
+    if (address_data[i] != prefix_address_data[i])
+      return 0;
+  }
+
+  if (prefix_len % 8)
+    return prefix_address_data[i] ==
+      (address_data[i] & (0xff << (8 - prefix_len % 8)));
+
+  return 1;
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
+    int* count_ptr) {
+  IP_ADAPTER_ADDRESSES* win_address_buf;
+  ULONG win_address_buf_size;
+  IP_ADAPTER_ADDRESSES* adapter;
+
+  uv_interface_address_t* uv_address_buf;
+  char* name_buf;
+  size_t uv_address_buf_size;
+  uv_interface_address_t* uv_address;
+
+  int count;
+
+  int is_vista_or_greater;
+  ULONG flags;
+
+  *addresses_ptr = NULL;
+  *count_ptr = 0;
+
+  is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0);
+  if (is_vista_or_greater) {
+    flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+      GAA_FLAG_SKIP_DNS_SERVER;
+  } else {
+    /* We need at least XP SP1. */
+    if (!is_windows_version_or_greater(5, 1, 1, 0))
+      return UV_ENOTSUP;
+
+    flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
+      GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX;
+  }
+
+
+  /* Fetch the size of the adapters reported by windows, and then get the list
+   * itself. */
+  win_address_buf_size = 0;
+  win_address_buf = NULL;
+
+  for (;;) {
+    ULONG r;
+
+    /* If win_address_buf is 0, then GetAdaptersAddresses will fail with.
+     * ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in
+     * win_address_buf_size. */
+    r = GetAdaptersAddresses(AF_UNSPEC,
+                             flags,
+                             NULL,
+                             win_address_buf,
+                             &win_address_buf_size);
+
+    if (r == ERROR_SUCCESS)
+      break;
+
+    uv__free(win_address_buf);
+
+    switch (r) {
+      case ERROR_BUFFER_OVERFLOW:
+        /* This happens when win_address_buf is NULL or too small to hold all
+         * adapters. */
+        win_address_buf = uv__malloc(win_address_buf_size);
+        if (win_address_buf == NULL)
+          return UV_ENOMEM;
+
+        continue;
+
+      case ERROR_NO_DATA: {
+        /* No adapters were found. */
+        uv_address_buf = uv__malloc(1);
+        if (uv_address_buf == NULL)
+          return UV_ENOMEM;
+
+        *count_ptr = 0;
+        *addresses_ptr = uv_address_buf;
+
+        return 0;
+      }
+
+      case ERROR_ADDRESS_NOT_ASSOCIATED:
+        return UV_EAGAIN;
+
+      case ERROR_INVALID_PARAMETER:
+        /* MSDN says:
+         *   "This error is returned for any of the following conditions: the
+         *   SizePointer parameter is NULL, the Address parameter is not
+         *   AF_INET, AF_INET6, or AF_UNSPEC, or the address information for
+         *   the parameters requested is greater than ULONG_MAX."
+         * Since the first two conditions are not met, it must be that the
+         * adapter data is too big.
+         */
+        return UV_ENOBUFS;
+
+      default:
+        /* Other (unspecified) errors can happen, but we don't have any special
+         * meaning for them. */
+        assert(r != ERROR_SUCCESS);
+        return uv_translate_sys_error(r);
+    }
+  }
+
+  /* Count the number of enabled interfaces and compute how much space is
+   * needed to store their info. */
+  count = 0;
+  uv_address_buf_size = 0;
+
+  for (adapter = win_address_buf;
+       adapter != NULL;
+       adapter = adapter->Next) {
+    IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
+    int name_size;
+
+    /* Interfaces that are not 'up' should not be reported. Also skip
+     * interfaces that have no associated unicast address, as to avoid
+     * allocating space for the name for this interface. */
+    if (adapter->OperStatus != IfOperStatusUp ||
+        adapter->FirstUnicastAddress == NULL)
+      continue;
+
+    /* Compute the size of the interface name. */
+    name_size = WideCharToMultiByte(CP_UTF8,
+                                    0,
+                                    adapter->FriendlyName,
+                                    -1,
+                                    NULL,
+                                    0,
+                                    NULL,
+                                    FALSE);
+    if (name_size <= 0) {
+      uv__free(win_address_buf);
+      return uv_translate_sys_error(GetLastError());
+    }
+    uv_address_buf_size += name_size;
+
+    /* Count the number of addresses associated with this interface, and
+     * compute the size. */
+    for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
+                           adapter->FirstUnicastAddress;
+         unicast_address != NULL;
+         unicast_address = unicast_address->Next) {
+      count++;
+      uv_address_buf_size += sizeof(uv_interface_address_t);
+    }
+  }
+
+  /* Allocate space to store interface data plus adapter names. */
+  uv_address_buf = uv__malloc(uv_address_buf_size);
+  if (uv_address_buf == NULL) {
+    uv__free(win_address_buf);
+    return UV_ENOMEM;
+  }
+
+  /* Compute the start of the uv_interface_address_t array, and the place in
+   * the buffer where the interface names will be stored. */
+  uv_address = uv_address_buf;
+  name_buf = (char*) (uv_address_buf + count);
+
+  /* Fill out the output buffer. */
+  for (adapter = win_address_buf;
+       adapter != NULL;
+       adapter = adapter->Next) {
+    IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
+    int name_size;
+    size_t max_name_size;
+
+    if (adapter->OperStatus != IfOperStatusUp ||
+        adapter->FirstUnicastAddress == NULL)
+      continue;
+
+    /* Convert the interface name to UTF8. */
+    max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf;
+    if (max_name_size > (size_t) INT_MAX)
+      max_name_size = INT_MAX;
+    name_size = WideCharToMultiByte(CP_UTF8,
+                                    0,
+                                    adapter->FriendlyName,
+                                    -1,
+                                    name_buf,
+                                    (int) max_name_size,
+                                    NULL,
+                                    FALSE);
+    if (name_size <= 0) {
+      uv__free(win_address_buf);
+      uv__free(uv_address_buf);
+      return uv_translate_sys_error(GetLastError());
+    }
+
+    /* Add an uv_interface_address_t element for every unicast address. */
+    for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
+                           adapter->FirstUnicastAddress;
+         unicast_address != NULL;
+         unicast_address = unicast_address->Next) {
+      struct sockaddr* sa;
+      ULONG prefix_len;
+
+      sa = unicast_address->Address.lpSockaddr;
+
+      /* XP has no OnLinkPrefixLength field. */
+      if (is_vista_or_greater) {
+        prefix_len =
+          ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
+      } else {
+        /* Prior to Windows Vista the FirstPrefix pointed to the list with
+         * single prefix for each IP address assigned to the adapter.
+         * Order of FirstPrefix does not match order of FirstUnicastAddress,
+         * so we need to find corresponding prefix.
+         */
+        IP_ADAPTER_PREFIX* prefix;
+        prefix_len = 0;
+
+        for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) {
+          /* We want the longest matching prefix. */
+          if (prefix->Address.lpSockaddr->sa_family != sa->sa_family ||
+              prefix->PrefixLength <= prefix_len)
+            continue;
+
+          if (address_prefix_match(sa->sa_family, sa,
+              prefix->Address.lpSockaddr, prefix->PrefixLength)) {
+            prefix_len = prefix->PrefixLength;
+          }
+        }
+
+        /* If there is no matching prefix information, return a single-host
+         * subnet mask (e.g. 255.255.255.255 for IPv4).
+         */
+        if (!prefix_len)
+          prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32;
+      }
+
+      memset(uv_address, 0, sizeof *uv_address);
+
+      uv_address->name = name_buf;
+
+      if (adapter->PhysicalAddressLength == sizeof(uv_address->phys_addr)) {
+        memcpy(uv_address->phys_addr,
+               adapter->PhysicalAddress,
+               sizeof(uv_address->phys_addr));
+      }
+
+      uv_address->is_internal =
+          (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
+
+      if (sa->sa_family == AF_INET6) {
+        uv_address->address.address6 = *((struct sockaddr_in6 *) sa);
+
+        uv_address->netmask.netmask6.sin6_family = AF_INET6;
+        memset(uv_address->netmask.netmask6.sin6_addr.s6_addr, 0xff, prefix_len >> 3);
+        /* This check ensures that we don't write past the size of the data. */
+        if (prefix_len % 8) {
+          uv_address->netmask.netmask6.sin6_addr.s6_addr[prefix_len >> 3] =
+              0xff << (8 - prefix_len % 8);
+        }
+
+      } else {
+        uv_address->address.address4 = *((struct sockaddr_in *) sa);
+
+        uv_address->netmask.netmask4.sin_family = AF_INET;
+        uv_address->netmask.netmask4.sin_addr.s_addr = (prefix_len > 0) ?
+            htonl(0xffffffff << (32 - prefix_len)) : 0;
+      }
+
+      uv_address++;
+    }
+
+    name_buf += name_size;
+  }
+
+  uv__free(win_address_buf);
+
+  *addresses_ptr = uv_address_buf;
+  *count_ptr = count;
+
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+    int count) {
+  uv__free(addresses);
+}
+
+
+int uv_getrusage(uv_rusage_t *uv_rusage) {
+  FILETIME createTime, exitTime, kernelTime, userTime;
+  SYSTEMTIME kernelSystemTime, userSystemTime;
+  PROCESS_MEMORY_COUNTERS memCounters;
+  IO_COUNTERS ioCounters;
+  int ret;
+
+  ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = FileTimeToSystemTime(&userTime, &userSystemTime);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = GetProcessMemoryInfo(GetCurrentProcess(),
+                             &memCounters,
+                             sizeof(memCounters));
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
+  if (ret == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  memset(uv_rusage, 0, sizeof(*uv_rusage));
+
+  uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
+                               userSystemTime.wMinute * 60 +
+                               userSystemTime.wSecond;
+  uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
+
+  uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
+                               kernelSystemTime.wMinute * 60 +
+                               kernelSystemTime.wSecond;
+  uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
+
+  uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount;
+  uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024;
+
+  uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount;
+  uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount;
+
+  return 0;
+}
+
+
+int uv_os_homedir(char* buffer, size_t* size) {
+  uv_passwd_t pwd;
+  size_t len;
+  int r;
+
+  /* Check if the USERPROFILE environment variable is set first. The task of
+     performing input validation on buffer and size is taken care of by
+     uv_os_getenv(). */
+  r = uv_os_getenv("USERPROFILE", buffer, size);
+
+  /* Don't return an error if USERPROFILE was not found. */
+  if (r != UV_ENOENT)
+    return r;
+
+  /* USERPROFILE is not set, so call uv__getpwuid_r() */
+  r = uv__getpwuid_r(&pwd);
+
+  if (r != 0) {
+    return r;
+  }
+
+  len = strlen(pwd.homedir);
+
+  if (len >= *size) {
+    *size = len + 1;
+    uv_os_free_passwd(&pwd);
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, pwd.homedir, len + 1);
+  *size = len;
+  uv_os_free_passwd(&pwd);
+
+  return 0;
+}
+
+
+int uv_os_tmpdir(char* buffer, size_t* size) {
+  wchar_t *path;
+  DWORD bufsize;
+  size_t len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  len = 0;
+  len = GetTempPathW(0, NULL);
+  if (len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+  /* Include space for terminating null char. */
+  len += 1;
+  path = uv__malloc(len * sizeof(wchar_t));
+  if (path == NULL) {
+    return UV_ENOMEM;
+  }
+  len  = GetTempPathW(len, path);
+
+  if (len == 0) {
+    uv__free(path);
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  /* The returned directory should not have a trailing slash, unless it points
+   * at a drive root, like c:\. Remove it if needed. */
+  if (path[len - 1] == L'\\' &&
+      !(len == 3 && path[1] == L':')) {
+    len--;
+    path[len] = L'\0';
+  }
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL);
+
+  if (bufsize == 0) {
+    uv__free(path);
+    return uv_translate_sys_error(GetLastError());
+  } else if (bufsize > *size) {
+    uv__free(path);
+    *size = bufsize;
+    return UV_ENOBUFS;
+  }
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                path,
+                                -1,
+                                buffer,
+                                *size,
+                                NULL,
+                                NULL);
+  uv__free(path);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  *size = bufsize - 1;
+  return 0;
+}
+
+
+void uv_os_free_passwd(uv_passwd_t* pwd) {
+  if (pwd == NULL)
+    return;
+
+  uv__free(pwd->username);
+  uv__free(pwd->homedir);
+  pwd->username = NULL;
+  pwd->homedir = NULL;
+}
+
+
+/*
+ * Converts a UTF-16 string into a UTF-8 one. The resulting string is
+ * null-terminated.
+ *
+ * If utf16 is null terminated, utf16len can be set to -1, otherwise it must
+ * be specified.
+ */
+int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) {
+  DWORD bufsize;
+
+  if (utf16 == NULL)
+    return UV_EINVAL;
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                utf16,
+                                utf16len,
+                                NULL,
+                                0,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  /* Allocate the destination buffer adding an extra byte for the terminating
+   * NULL. If utf16len is not -1 WideCharToMultiByte will not add it, so
+   * we do it ourselves always, just in case. */
+  *utf8 = uv__malloc(bufsize + 1);
+
+  if (*utf8 == NULL)
+    return UV_ENOMEM;
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                utf16,
+                                utf16len,
+                                *utf8,
+                                bufsize,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0) {
+    uv__free(*utf8);
+    *utf8 = NULL;
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  (*utf8)[bufsize] = '\0';
+  return 0;
+}
+
+
+/*
+ * Converts a UTF-8 string into a UTF-16 one. The resulting string is
+ * null-terminated.
+ *
+ * If utf8 is null terminated, utf8len can be set to -1, otherwise it must
+ * be specified.
+ */
+int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) {
+  int bufsize;
+
+  if (utf8 == NULL)
+    return UV_EINVAL;
+
+  /* Check how much space we need */
+  bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, NULL, 0);
+
+  if (bufsize == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  /* Allocate the destination buffer adding an extra byte for the terminating
+   * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so
+   * we do it ourselves always, just in case. */
+  *utf16 = uv__malloc(sizeof(WCHAR) * (bufsize + 1));
+
+  if (*utf16 == NULL)
+    return UV_ENOMEM;
+
+  /* Convert to UTF-16 */
+  bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, *utf16, bufsize);
+
+  if (bufsize == 0) {
+    uv__free(*utf16);
+    *utf16 = NULL;
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  (*utf16)[bufsize] = L'\0';
+  return 0;
+}
+
+
+int uv__getpwuid_r(uv_passwd_t* pwd) {
+  HANDLE token;
+  wchar_t username[UNLEN + 1];
+  wchar_t *path;
+  DWORD bufsize;
+  int r;
+
+  if (pwd == NULL)
+    return UV_EINVAL;
+
+  /* Get the home directory using GetUserProfileDirectoryW() */
+  if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  bufsize = 0;
+  GetUserProfileDirectoryW(token, NULL, &bufsize);
+  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+    r = GetLastError();
+    CloseHandle(token);
+    return uv_translate_sys_error(r);
+  }
+
+  path = uv__malloc(bufsize * sizeof(wchar_t));
+  if (path == NULL) {
+    CloseHandle(token);
+    return UV_ENOMEM;
+  }
+
+  if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
+    r = GetLastError();
+    CloseHandle(token);
+    uv__free(path);
+    return uv_translate_sys_error(r);
+  }
+
+  CloseHandle(token);
+
+  /* Get the username using GetUserNameW() */
+  bufsize = ARRAY_SIZE(username);
+  if (!GetUserNameW(username, &bufsize)) {
+    r = GetLastError();
+    uv__free(path);
+
+    /* This should not be possible */
+    if (r == ERROR_INSUFFICIENT_BUFFER)
+      return UV_ENOMEM;
+
+    return uv_translate_sys_error(r);
+  }
+
+  pwd->homedir = NULL;
+  r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir);
+  uv__free(path);
+
+  if (r != 0)
+    return r;
+
+  pwd->username = NULL;
+  r = uv__convert_utf16_to_utf8(username, -1, &pwd->username);
+
+  if (r != 0) {
+    uv__free(pwd->homedir);
+    return r;
+  }
+
+  pwd->shell = NULL;
+  pwd->uid = -1;
+  pwd->gid = -1;
+
+  return 0;
+}
+
+
+int uv_os_get_passwd(uv_passwd_t* pwd) {
+  return uv__getpwuid_r(pwd);
+}
+
+
+int uv_os_environ(uv_env_item_t** envitems, int* count) {
+  wchar_t* env;
+  wchar_t* penv;
+  int i, cnt;
+  uv_env_item_t* envitem;
+
+  *envitems = NULL;
+  *count = 0;
+
+  env = GetEnvironmentStringsW();
+  if (env == NULL)
+    return 0;
+
+  for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++);
+
+  *envitems = uv__calloc(i, sizeof(**envitems));
+  if (*envitems == NULL) {
+    FreeEnvironmentStringsW(env);
+    return UV_ENOMEM;
+  }
+
+  penv = env;
+  cnt = 0;
+
+  while (*penv != L'\0' && cnt < i) {
+    char* buf;
+    char* ptr;
+
+    if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0)
+      goto fail;
+
+    /* Using buf + 1 here because we know that `buf` has length at least 1,
+     * and some special environment variables on Windows start with a = sign. */
+    ptr = strchr(buf + 1, '=');
+    if (ptr == NULL) {
+      uv__free(buf);
+      goto do_continue;
+    }
+
+    *ptr = '\0';
+
+    envitem = &(*envitems)[cnt];
+    envitem->name = buf;
+    envitem->value = ptr + 1;
+
+    cnt++;
+
+  do_continue:
+    penv += wcslen(penv) + 1;
+  }
+
+  FreeEnvironmentStringsW(env);
+
+  *count = cnt;
+  return 0;
+
+fail:
+  FreeEnvironmentStringsW(env);
+
+  for (i = 0; i < cnt; i++) {
+    envitem = &(*envitems)[cnt];
+    uv__free(envitem->name);
+  }
+  uv__free(*envitems);
+
+  *envitems = NULL;
+  *count = 0;
+  return UV_ENOMEM;
+}
+
+
+int uv_os_getenv(const char* name, char* buffer, size_t* size) {
+  wchar_t fastvar[512];
+  wchar_t* var;
+  DWORD varlen;
+  wchar_t* name_w;
+  DWORD bufsize;
+  size_t len;
+  int r;
+
+  if (name == NULL || buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  r = uv__convert_utf8_to_utf16(name, -1, &name_w);
+
+  if (r != 0)
+    return r;
+
+  var = fastvar;
+  varlen = ARRAY_SIZE(fastvar);
+
+  for (;;) {
+    SetLastError(ERROR_SUCCESS);
+    len = GetEnvironmentVariableW(name_w, var, varlen);
+
+    if (len < varlen)
+      break;
+
+    /* Try repeatedly because we might have been preempted by another thread
+     * modifying the environment variable just as we're trying to read it.
+     */
+    if (var != fastvar)
+      uv__free(var);
+
+    varlen = 1 + len;
+    var = uv__malloc(varlen * sizeof(*var));
+
+    if (var == NULL) {
+      r = UV_ENOMEM;
+      goto fail;
+    }
+  }
+
+  uv__free(name_w);
+  name_w = NULL;
+
+  if (len == 0) {
+    r = GetLastError();
+    if (r != ERROR_SUCCESS) {
+      r = uv_translate_sys_error(r);
+      goto fail;
+    }
+  }
+
+  /* Check how much space we need */
+  bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL);
+
+  if (bufsize == 0) {
+    r = uv_translate_sys_error(GetLastError());
+    goto fail;
+  } else if (bufsize > *size) {
+    *size = bufsize;
+    r = UV_ENOBUFS;
+    goto fail;
+  }
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                var,
+                                -1,
+                                buffer,
+                                *size,
+                                NULL,
+                                NULL);
+
+  if (bufsize == 0) {
+    r = uv_translate_sys_error(GetLastError());
+    goto fail;
+  }
+
+  *size = bufsize - 1;
+  r = 0;
+
+fail:
+
+  if (name_w != NULL)
+    uv__free(name_w);
+
+  if (var != fastvar)
+    uv__free(var);
+
+  return r;
+}
+
+
+int uv_os_setenv(const char* name, const char* value) {
+  wchar_t* name_w;
+  wchar_t* value_w;
+  int r;
+
+  if (name == NULL || value == NULL)
+    return UV_EINVAL;
+
+  r = uv__convert_utf8_to_utf16(name, -1, &name_w);
+
+  if (r != 0)
+    return r;
+
+  r = uv__convert_utf8_to_utf16(value, -1, &value_w);
+
+  if (r != 0) {
+    uv__free(name_w);
+    return r;
+  }
+
+  r = SetEnvironmentVariableW(name_w, value_w);
+  uv__free(name_w);
+  uv__free(value_w);
+
+  if (r == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  return 0;
+}
+
+
+int uv_os_unsetenv(const char* name) {
+  wchar_t* name_w;
+  int r;
+
+  if (name == NULL)
+    return UV_EINVAL;
+
+  r = uv__convert_utf8_to_utf16(name, -1, &name_w);
+
+  if (r != 0)
+    return r;
+
+  r = SetEnvironmentVariableW(name_w, NULL);
+  uv__free(name_w);
+
+  if (r == 0)
+    return uv_translate_sys_error(GetLastError());
+
+  return 0;
+}
+
+
+int uv_os_gethostname(char* buffer, size_t* size) {
+  char buf[UV_MAXHOSTNAMESIZE];
+  size_t len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  uv__once_init(); /* Initialize winsock */
+
+  if (gethostname(buf, sizeof(buf)) != 0)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */
+  len = strlen(buf);
+
+  if (len >= *size) {
+    *size = len + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, buf, len + 1);
+  *size = len;
+  return 0;
+}
+
+
+static int uv__get_handle(uv_pid_t pid, int access, HANDLE* handle) {
+  int r;
+
+  if (pid == 0)
+    *handle = GetCurrentProcess();
+  else
+    *handle = OpenProcess(access, FALSE, pid);
+
+  if (*handle == NULL) {
+    r = GetLastError();
+
+    if (r == ERROR_INVALID_PARAMETER)
+      return UV_ESRCH;
+    else
+      return uv_translate_sys_error(r);
+  }
+
+  return 0;
+}
+
+
+int uv_os_getpriority(uv_pid_t pid, int* priority) {
+  HANDLE handle;
+  int r;
+
+  if (priority == NULL)
+    return UV_EINVAL;
+
+  r = uv__get_handle(pid, PROCESS_QUERY_LIMITED_INFORMATION, &handle);
+
+  if (r != 0)
+    return r;
+
+  r = GetPriorityClass(handle);
+
+  if (r == 0) {
+    r = uv_translate_sys_error(GetLastError());
+  } else {
+    /* Map Windows priority classes to Unix nice values. */
+    if (r == REALTIME_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_HIGHEST;
+    else if (r == HIGH_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_HIGH;
+    else if (r == ABOVE_NORMAL_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_ABOVE_NORMAL;
+    else if (r == NORMAL_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_NORMAL;
+    else if (r == BELOW_NORMAL_PRIORITY_CLASS)
+      *priority = UV_PRIORITY_BELOW_NORMAL;
+    else  /* IDLE_PRIORITY_CLASS */
+      *priority = UV_PRIORITY_LOW;
+
+    r = 0;
+  }
+
+  CloseHandle(handle);
+  return r;
+}
+
+
+int uv_os_setpriority(uv_pid_t pid, int priority) {
+  HANDLE handle;
+  int priority_class;
+  int r;
+
+  /* Map Unix nice values to Windows priority classes. */
+  if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW)
+    return UV_EINVAL;
+  else if (priority < UV_PRIORITY_HIGH)
+    priority_class = REALTIME_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_ABOVE_NORMAL)
+    priority_class = HIGH_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_NORMAL)
+    priority_class = ABOVE_NORMAL_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_BELOW_NORMAL)
+    priority_class = NORMAL_PRIORITY_CLASS;
+  else if (priority < UV_PRIORITY_LOW)
+    priority_class = BELOW_NORMAL_PRIORITY_CLASS;
+  else
+    priority_class = IDLE_PRIORITY_CLASS;
+
+  r = uv__get_handle(pid, PROCESS_SET_INFORMATION, &handle);
+
+  if (r != 0)
+    return r;
+
+  if (SetPriorityClass(handle, priority_class) == 0)
+    r = uv_translate_sys_error(GetLastError());
+
+  CloseHandle(handle);
+  return r;
+}
+
+
+int uv_os_uname(uv_utsname_t* buffer) {
+  /* Implementation loosely based on
+     https://github.com/gagern/gnulib/blob/master/lib/uname.c */
+  OSVERSIONINFOW os_info;
+  SYSTEM_INFO system_info;
+  HKEY registry_key;
+  WCHAR product_name_w[256];
+  DWORD product_name_w_size;
+  int version_size;
+  int processor_level;
+  int r;
+
+  if (buffer == NULL)
+    return UV_EINVAL;
+
+  uv__once_init();
+  os_info.dwOSVersionInfoSize = sizeof(os_info);
+  os_info.szCSDVersion[0] = L'\0';
+
+  /* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx()
+     if RtlGetVersion() is not available. */
+  if (pRtlGetVersion) {
+    pRtlGetVersion(&os_info);
+  } else {
+    /* Silence GetVersionEx() deprecation warning. */
+    #ifdef _MSC_VER
+    #pragma warning(suppress : 4996)
+    #endif
+    if (GetVersionExW(&os_info) == 0) {
+      r = uv_translate_sys_error(GetLastError());
+      goto error;
+    }
+  }
+
+  /* Populate the version field. */
+  version_size = 0;
+  r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+                    0,
+                    KEY_QUERY_VALUE,
+                    &registry_key);
+
+  if (r == ERROR_SUCCESS) {
+    product_name_w_size = sizeof(product_name_w);
+    r = RegGetValueW(registry_key,
+                     NULL,
+                     L"ProductName",
+                     RRF_RT_REG_SZ,
+                     NULL,
+                     (PVOID) product_name_w,
+                     &product_name_w_size);
+    RegCloseKey(registry_key);
+
+    if (r == ERROR_SUCCESS) {
+      version_size = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         product_name_w,
+                                         -1,
+                                         buffer->version,
+                                         sizeof(buffer->version),
+                                         NULL,
+                                         NULL);
+      if (version_size == 0) {
+        r = uv_translate_sys_error(GetLastError());
+        goto error;
+      }
+    }
+  }
+
+  /* Append service pack information to the version if present. */
+  if (os_info.szCSDVersion[0] != L'\0') {
+    if (version_size > 0)
+      buffer->version[version_size - 1] = ' ';
+
+    if (WideCharToMultiByte(CP_UTF8,
+                            0,
+                            os_info.szCSDVersion,
+                            -1,
+                            buffer->version + version_size,
+                            sizeof(buffer->version) - version_size,
+                            NULL,
+                            NULL) == 0) {
+      r = uv_translate_sys_error(GetLastError());
+      goto error;
+    }
+  }
+
+  /* Populate the sysname field. */
+#ifdef __MINGW32__
+  r = snprintf(buffer->sysname,
+               sizeof(buffer->sysname),
+               "MINGW32_NT-%u.%u",
+               (unsigned int) os_info.dwMajorVersion,
+               (unsigned int) os_info.dwMinorVersion);
+  assert((size_t)r < sizeof(buffer->sysname));
+#else
+  uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname));
+#endif
+
+  /* Populate the release field. */
+  r = snprintf(buffer->release,
+               sizeof(buffer->release),
+               "%d.%d.%d",
+               (unsigned int) os_info.dwMajorVersion,
+               (unsigned int) os_info.dwMinorVersion,
+               (unsigned int) os_info.dwBuildNumber);
+  assert((size_t)r < sizeof(buffer->release));
+
+  /* Populate the machine field. */
+  GetSystemInfo(&system_info);
+
+  switch (system_info.wProcessorArchitecture) {
+    case PROCESSOR_ARCHITECTURE_AMD64:
+      uv__strscpy(buffer->machine, "x86_64", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_IA64:
+      uv__strscpy(buffer->machine, "ia64", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_INTEL:
+      uv__strscpy(buffer->machine, "i386", sizeof(buffer->machine));
+
+      if (system_info.wProcessorLevel > 3) {
+        processor_level = system_info.wProcessorLevel < 6 ?
+                          system_info.wProcessorLevel : 6;
+        buffer->machine[1] = '0' + processor_level;
+      }
+
+      break;
+    case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
+      uv__strscpy(buffer->machine, "i686", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_MIPS:
+      uv__strscpy(buffer->machine, "mips", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_ALPHA:
+    case PROCESSOR_ARCHITECTURE_ALPHA64:
+      uv__strscpy(buffer->machine, "alpha", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_PPC:
+      uv__strscpy(buffer->machine, "powerpc", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_SHX:
+      uv__strscpy(buffer->machine, "sh", sizeof(buffer->machine));
+      break;
+    case PROCESSOR_ARCHITECTURE_ARM:
+      uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine));
+      break;
+    default:
+      uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine));
+      break;
+  }
+
+  return 0;
+
+error:
+  buffer->sysname[0] = '\0';
+  buffer->release[0] = '\0';
+  buffer->version[0] = '\0';
+  buffer->machine[0] = '\0';
+  return r;
+}
+
+int uv_gettimeofday(uv_timeval64_t* tv) {
+  /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */
+  const uint64_t epoch = (uint64_t) 116444736000000000ULL;
+  FILETIME file_time;
+  ULARGE_INTEGER ularge;
+
+  if (tv == NULL)
+    return UV_EINVAL;
+
+  GetSystemTimeAsFileTime(&file_time);
+  ularge.LowPart = file_time.dwLowDateTime;
+  ularge.HighPart = file_time.dwHighDateTime;
+  tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L);
+  tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10);
+  return 0;
+}
+
+int uv__random_rtlgenrandom(void* buf, size_t buflen) {
+  if (buflen == 0)
+    return 0;
+
+  if (SystemFunction036(buf, buflen) == FALSE)
+    return UV_EIO;
+
+  return 0;
+}
+
+void uv_sleep(unsigned int msec) {
+  Sleep(msec);
+}
diff --git a/third_party/libuv/src/win/winapi.c b/third_party/libuv/src/win/winapi.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/winapi.c
@@ -0,0 +1,137 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+/* Ntdll function pointers */
+sRtlGetVersion pRtlGetVersion;
+sRtlNtStatusToDosError pRtlNtStatusToDosError;
+sNtDeviceIoControlFile pNtDeviceIoControlFile;
+sNtQueryInformationFile pNtQueryInformationFile;
+sNtSetInformationFile pNtSetInformationFile;
+sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
+sNtQueryDirectoryFile pNtQueryDirectoryFile;
+sNtQuerySystemInformation pNtQuerySystemInformation;
+sNtQueryInformationProcess pNtQueryInformationProcess;
+
+/* Kernel32 function pointers */
+sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
+
+/* Powrprof.dll function pointer */
+sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
+
+/* User32.dll function pointer */
+sSetWinEventHook pSetWinEventHook;
+
+
+void uv_winapi_init(void) {
+  HMODULE ntdll_module;
+  HMODULE powrprof_module;
+  HMODULE user32_module;
+  HMODULE kernel32_module;
+
+  ntdll_module = GetModuleHandleA("ntdll.dll");
+  if (ntdll_module == NULL) {
+    uv_fatal_error(GetLastError(), "GetModuleHandleA");
+  }
+
+  pRtlGetVersion = (sRtlGetVersion) GetProcAddress(ntdll_module,
+                                                   "RtlGetVersion");
+
+  pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress(
+      ntdll_module,
+      "RtlNtStatusToDosError");
+  if (pRtlNtStatusToDosError == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtDeviceIoControlFile = (sNtDeviceIoControlFile) GetProcAddress(
+      ntdll_module,
+      "NtDeviceIoControlFile");
+  if (pNtDeviceIoControlFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryInformationFile = (sNtQueryInformationFile) GetProcAddress(
+      ntdll_module,
+      "NtQueryInformationFile");
+  if (pNtQueryInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtSetInformationFile = (sNtSetInformationFile) GetProcAddress(
+      ntdll_module,
+      "NtSetInformationFile");
+  if (pNtSetInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryVolumeInformationFile = (sNtQueryVolumeInformationFile)
+      GetProcAddress(ntdll_module, "NtQueryVolumeInformationFile");
+  if (pNtQueryVolumeInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
+      GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
+  if (pNtQueryVolumeInformationFile == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQuerySystemInformation = (sNtQuerySystemInformation) GetProcAddress(
+      ntdll_module,
+      "NtQuerySystemInformation");
+  if (pNtQuerySystemInformation == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  pNtQueryInformationProcess = (sNtQueryInformationProcess) GetProcAddress(
+      ntdll_module,
+      "NtQueryInformationProcess");
+  if (pNtQueryInformationProcess == NULL) {
+    uv_fatal_error(GetLastError(), "GetProcAddress");
+  }
+
+  kernel32_module = GetModuleHandleA("kernel32.dll");
+  if (kernel32_module == NULL) {
+    uv_fatal_error(GetLastError(), "GetModuleHandleA");
+  }
+
+  pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress(
+      kernel32_module,
+      "GetQueuedCompletionStatusEx");
+
+  powrprof_module = LoadLibraryA("powrprof.dll");
+  if (powrprof_module != NULL) {
+    pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
+      GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification");
+  }
+
+  user32_module = LoadLibraryA("user32.dll");
+  if (user32_module != NULL) {
+    pSetWinEventHook = (sSetWinEventHook)
+      GetProcAddress(user32_module, "SetWinEventHook");
+  }
+}
diff --git a/third_party/libuv/src/win/winapi.h b/third_party/libuv/src/win/winapi.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/winapi.h
@@ -0,0 +1,4762 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_WINAPI_H_
+#define UV_WIN_WINAPI_H_
+
+#include <windows.h>
+
+
+/*
+ * Ntdll headers
+ */
+#ifndef STATUS_SEVERITY_SUCCESS
+# define STATUS_SEVERITY_SUCCESS 0x0
+#endif
+
+#ifndef STATUS_SEVERITY_INFORMATIONAL
+# define STATUS_SEVERITY_INFORMATIONAL 0x1
+#endif
+
+#ifndef STATUS_SEVERITY_WARNING
+# define STATUS_SEVERITY_WARNING 0x2
+#endif
+
+#ifndef STATUS_SEVERITY_ERROR
+# define STATUS_SEVERITY_ERROR 0x3
+#endif
+
+#ifndef FACILITY_NTWIN32
+# define FACILITY_NTWIN32 0x7
+#endif
+
+#ifndef NT_SUCCESS
+# define NT_SUCCESS(status) (((NTSTATUS) (status)) >= 0)
+#endif
+
+#ifndef NT_INFORMATION
+# define NT_INFORMATION(status) ((((ULONG) (status)) >> 30) == 1)
+#endif
+
+#ifndef NT_WARNING
+# define NT_WARNING(status) ((((ULONG) (status)) >> 30) == 2)
+#endif
+
+#ifndef NT_ERROR
+# define NT_ERROR(status) ((((ULONG) (status)) >> 30) == 3)
+#endif
+
+#ifndef STATUS_SUCCESS
+# define STATUS_SUCCESS ((NTSTATUS) 0x00000000L)
+#endif
+
+#ifndef STATUS_WAIT_0
+# define STATUS_WAIT_0 ((NTSTATUS) 0x00000000L)
+#endif
+
+#ifndef STATUS_WAIT_1
+# define STATUS_WAIT_1 ((NTSTATUS) 0x00000001L)
+#endif
+
+#ifndef STATUS_WAIT_2
+# define STATUS_WAIT_2 ((NTSTATUS) 0x00000002L)
+#endif
+
+#ifndef STATUS_WAIT_3
+# define STATUS_WAIT_3 ((NTSTATUS) 0x00000003L)
+#endif
+
+#ifndef STATUS_WAIT_63
+# define STATUS_WAIT_63 ((NTSTATUS) 0x0000003FL)
+#endif
+
+#ifndef STATUS_ABANDONED
+# define STATUS_ABANDONED ((NTSTATUS) 0x00000080L)
+#endif
+
+#ifndef STATUS_ABANDONED_WAIT_0
+# define STATUS_ABANDONED_WAIT_0 ((NTSTATUS) 0x00000080L)
+#endif
+
+#ifndef STATUS_ABANDONED_WAIT_63
+# define STATUS_ABANDONED_WAIT_63 ((NTSTATUS) 0x000000BFL)
+#endif
+
+#ifndef STATUS_USER_APC
+# define STATUS_USER_APC ((NTSTATUS) 0x000000C0L)
+#endif
+
+#ifndef STATUS_KERNEL_APC
+# define STATUS_KERNEL_APC ((NTSTATUS) 0x00000100L)
+#endif
+
+#ifndef STATUS_ALERTED
+# define STATUS_ALERTED ((NTSTATUS) 0x00000101L)
+#endif
+
+#ifndef STATUS_TIMEOUT
+# define STATUS_TIMEOUT ((NTSTATUS) 0x00000102L)
+#endif
+
+#ifndef STATUS_PENDING
+# define STATUS_PENDING ((NTSTATUS) 0x00000103L)
+#endif
+
+#ifndef STATUS_REPARSE
+# define STATUS_REPARSE ((NTSTATUS) 0x00000104L)
+#endif
+
+#ifndef STATUS_MORE_ENTRIES
+# define STATUS_MORE_ENTRIES ((NTSTATUS) 0x00000105L)
+#endif
+
+#ifndef STATUS_NOT_ALL_ASSIGNED
+# define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS) 0x00000106L)
+#endif
+
+#ifndef STATUS_SOME_NOT_MAPPED
+# define STATUS_SOME_NOT_MAPPED ((NTSTATUS) 0x00000107L)
+#endif
+
+#ifndef STATUS_OPLOCK_BREAK_IN_PROGRESS
+# define STATUS_OPLOCK_BREAK_IN_PROGRESS ((NTSTATUS) 0x00000108L)
+#endif
+
+#ifndef STATUS_VOLUME_MOUNTED
+# define STATUS_VOLUME_MOUNTED ((NTSTATUS) 0x00000109L)
+#endif
+
+#ifndef STATUS_RXACT_COMMITTED
+# define STATUS_RXACT_COMMITTED ((NTSTATUS) 0x0000010AL)
+#endif
+
+#ifndef STATUS_NOTIFY_CLEANUP
+# define STATUS_NOTIFY_CLEANUP ((NTSTATUS) 0x0000010BL)
+#endif
+
+#ifndef STATUS_NOTIFY_ENUM_DIR
+# define STATUS_NOTIFY_ENUM_DIR ((NTSTATUS) 0x0000010CL)
+#endif
+
+#ifndef STATUS_NO_QUOTAS_FOR_ACCOUNT
+# define STATUS_NO_QUOTAS_FOR_ACCOUNT ((NTSTATUS) 0x0000010DL)
+#endif
+
+#ifndef STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED
+# define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED ((NTSTATUS) 0x0000010EL)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_TRANSITION
+# define STATUS_PAGE_FAULT_TRANSITION ((NTSTATUS) 0x00000110L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_DEMAND_ZERO
+# define STATUS_PAGE_FAULT_DEMAND_ZERO ((NTSTATUS) 0x00000111L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_COPY_ON_WRITE
+# define STATUS_PAGE_FAULT_COPY_ON_WRITE ((NTSTATUS) 0x00000112L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_GUARD_PAGE
+# define STATUS_PAGE_FAULT_GUARD_PAGE ((NTSTATUS) 0x00000113L)
+#endif
+
+#ifndef STATUS_PAGE_FAULT_PAGING_FILE
+# define STATUS_PAGE_FAULT_PAGING_FILE ((NTSTATUS) 0x00000114L)
+#endif
+
+#ifndef STATUS_CACHE_PAGE_LOCKED
+# define STATUS_CACHE_PAGE_LOCKED ((NTSTATUS) 0x00000115L)
+#endif
+
+#ifndef STATUS_CRASH_DUMP
+# define STATUS_CRASH_DUMP ((NTSTATUS) 0x00000116L)
+#endif
+
+#ifndef STATUS_BUFFER_ALL_ZEROS
+# define STATUS_BUFFER_ALL_ZEROS ((NTSTATUS) 0x00000117L)
+#endif
+
+#ifndef STATUS_REPARSE_OBJECT
+# define STATUS_REPARSE_OBJECT ((NTSTATUS) 0x00000118L)
+#endif
+
+#ifndef STATUS_RESOURCE_REQUIREMENTS_CHANGED
+# define STATUS_RESOURCE_REQUIREMENTS_CHANGED ((NTSTATUS) 0x00000119L)
+#endif
+
+#ifndef STATUS_TRANSLATION_COMPLETE
+# define STATUS_TRANSLATION_COMPLETE ((NTSTATUS) 0x00000120L)
+#endif
+
+#ifndef STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY
+# define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY ((NTSTATUS) 0x00000121L)
+#endif
+
+#ifndef STATUS_NOTHING_TO_TERMINATE
+# define STATUS_NOTHING_TO_TERMINATE ((NTSTATUS) 0x00000122L)
+#endif
+
+#ifndef STATUS_PROCESS_NOT_IN_JOB
+# define STATUS_PROCESS_NOT_IN_JOB ((NTSTATUS) 0x00000123L)
+#endif
+
+#ifndef STATUS_PROCESS_IN_JOB
+# define STATUS_PROCESS_IN_JOB ((NTSTATUS) 0x00000124L)
+#endif
+
+#ifndef STATUS_VOLSNAP_HIBERNATE_READY
+# define STATUS_VOLSNAP_HIBERNATE_READY ((NTSTATUS) 0x00000125L)
+#endif
+
+#ifndef STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY
+# define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY ((NTSTATUS) 0x00000126L)
+#endif
+
+#ifndef STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED
+# define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED ((NTSTATUS) 0x00000127L)
+#endif
+
+#ifndef STATUS_INTERRUPT_STILL_CONNECTED
+# define STATUS_INTERRUPT_STILL_CONNECTED ((NTSTATUS) 0x00000128L)
+#endif
+
+#ifndef STATUS_PROCESS_CLONED
+# define STATUS_PROCESS_CLONED ((NTSTATUS) 0x00000129L)
+#endif
+
+#ifndef STATUS_FILE_LOCKED_WITH_ONLY_READERS
+# define STATUS_FILE_LOCKED_WITH_ONLY_READERS ((NTSTATUS) 0x0000012AL)
+#endif
+
+#ifndef STATUS_FILE_LOCKED_WITH_WRITERS
+# define STATUS_FILE_LOCKED_WITH_WRITERS ((NTSTATUS) 0x0000012BL)
+#endif
+
+#ifndef STATUS_RESOURCEMANAGER_READ_ONLY
+# define STATUS_RESOURCEMANAGER_READ_ONLY ((NTSTATUS) 0x00000202L)
+#endif
+
+#ifndef STATUS_RING_PREVIOUSLY_EMPTY
+# define STATUS_RING_PREVIOUSLY_EMPTY ((NTSTATUS) 0x00000210L)
+#endif
+
+#ifndef STATUS_RING_PREVIOUSLY_FULL
+# define STATUS_RING_PREVIOUSLY_FULL ((NTSTATUS) 0x00000211L)
+#endif
+
+#ifndef STATUS_RING_PREVIOUSLY_ABOVE_QUOTA
+# define STATUS_RING_PREVIOUSLY_ABOVE_QUOTA ((NTSTATUS) 0x00000212L)
+#endif
+
+#ifndef STATUS_RING_NEWLY_EMPTY
+# define STATUS_RING_NEWLY_EMPTY ((NTSTATUS) 0x00000213L)
+#endif
+
+#ifndef STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT
+# define STATUS_RING_SIGNAL_OPPOSITE_ENDPOINT ((NTSTATUS) 0x00000214L)
+#endif
+
+#ifndef STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE
+# define STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE ((NTSTATUS) 0x00000215L)
+#endif
+
+#ifndef STATUS_OPLOCK_HANDLE_CLOSED
+# define STATUS_OPLOCK_HANDLE_CLOSED ((NTSTATUS) 0x00000216L)
+#endif
+
+#ifndef STATUS_WAIT_FOR_OPLOCK
+# define STATUS_WAIT_FOR_OPLOCK ((NTSTATUS) 0x00000367L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_EXISTS
+# define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS) 0x40000000L)
+#endif
+
+#ifndef STATUS_THREAD_WAS_SUSPENDED
+# define STATUS_THREAD_WAS_SUSPENDED ((NTSTATUS) 0x40000001L)
+#endif
+
+#ifndef STATUS_WORKING_SET_LIMIT_RANGE
+# define STATUS_WORKING_SET_LIMIT_RANGE ((NTSTATUS) 0x40000002L)
+#endif
+
+#ifndef STATUS_IMAGE_NOT_AT_BASE
+# define STATUS_IMAGE_NOT_AT_BASE ((NTSTATUS) 0x40000003L)
+#endif
+
+#ifndef STATUS_RXACT_STATE_CREATED
+# define STATUS_RXACT_STATE_CREATED ((NTSTATUS) 0x40000004L)
+#endif
+
+#ifndef STATUS_SEGMENT_NOTIFICATION
+# define STATUS_SEGMENT_NOTIFICATION ((NTSTATUS) 0x40000005L)
+#endif
+
+#ifndef STATUS_LOCAL_USER_SESSION_KEY
+# define STATUS_LOCAL_USER_SESSION_KEY ((NTSTATUS) 0x40000006L)
+#endif
+
+#ifndef STATUS_BAD_CURRENT_DIRECTORY
+# define STATUS_BAD_CURRENT_DIRECTORY ((NTSTATUS) 0x40000007L)
+#endif
+
+#ifndef STATUS_SERIAL_MORE_WRITES
+# define STATUS_SERIAL_MORE_WRITES ((NTSTATUS) 0x40000008L)
+#endif
+
+#ifndef STATUS_REGISTRY_RECOVERED
+# define STATUS_REGISTRY_RECOVERED ((NTSTATUS) 0x40000009L)
+#endif
+
+#ifndef STATUS_FT_READ_RECOVERY_FROM_BACKUP
+# define STATUS_FT_READ_RECOVERY_FROM_BACKUP ((NTSTATUS) 0x4000000AL)
+#endif
+
+#ifndef STATUS_FT_WRITE_RECOVERY
+# define STATUS_FT_WRITE_RECOVERY ((NTSTATUS) 0x4000000BL)
+#endif
+
+#ifndef STATUS_SERIAL_COUNTER_TIMEOUT
+# define STATUS_SERIAL_COUNTER_TIMEOUT ((NTSTATUS) 0x4000000CL)
+#endif
+
+#ifndef STATUS_NULL_LM_PASSWORD
+# define STATUS_NULL_LM_PASSWORD ((NTSTATUS) 0x4000000DL)
+#endif
+
+#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH
+# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH ((NTSTATUS) 0x4000000EL)
+#endif
+
+#ifndef STATUS_RECEIVE_PARTIAL
+# define STATUS_RECEIVE_PARTIAL ((NTSTATUS) 0x4000000FL)
+#endif
+
+#ifndef STATUS_RECEIVE_EXPEDITED
+# define STATUS_RECEIVE_EXPEDITED ((NTSTATUS) 0x40000010L)
+#endif
+
+#ifndef STATUS_RECEIVE_PARTIAL_EXPEDITED
+# define STATUS_RECEIVE_PARTIAL_EXPEDITED ((NTSTATUS) 0x40000011L)
+#endif
+
+#ifndef STATUS_EVENT_DONE
+# define STATUS_EVENT_DONE ((NTSTATUS) 0x40000012L)
+#endif
+
+#ifndef STATUS_EVENT_PENDING
+# define STATUS_EVENT_PENDING ((NTSTATUS) 0x40000013L)
+#endif
+
+#ifndef STATUS_CHECKING_FILE_SYSTEM
+# define STATUS_CHECKING_FILE_SYSTEM ((NTSTATUS) 0x40000014L)
+#endif
+
+#ifndef STATUS_FATAL_APP_EXIT
+# define STATUS_FATAL_APP_EXIT ((NTSTATUS) 0x40000015L)
+#endif
+
+#ifndef STATUS_PREDEFINED_HANDLE
+# define STATUS_PREDEFINED_HANDLE ((NTSTATUS) 0x40000016L)
+#endif
+
+#ifndef STATUS_WAS_UNLOCKED
+# define STATUS_WAS_UNLOCKED ((NTSTATUS) 0x40000017L)
+#endif
+
+#ifndef STATUS_SERVICE_NOTIFICATION
+# define STATUS_SERVICE_NOTIFICATION ((NTSTATUS) 0x40000018L)
+#endif
+
+#ifndef STATUS_WAS_LOCKED
+# define STATUS_WAS_LOCKED ((NTSTATUS) 0x40000019L)
+#endif
+
+#ifndef STATUS_LOG_HARD_ERROR
+# define STATUS_LOG_HARD_ERROR ((NTSTATUS) 0x4000001AL)
+#endif
+
+#ifndef STATUS_ALREADY_WIN32
+# define STATUS_ALREADY_WIN32 ((NTSTATUS) 0x4000001BL)
+#endif
+
+#ifndef STATUS_WX86_UNSIMULATE
+# define STATUS_WX86_UNSIMULATE ((NTSTATUS) 0x4000001CL)
+#endif
+
+#ifndef STATUS_WX86_CONTINUE
+# define STATUS_WX86_CONTINUE ((NTSTATUS) 0x4000001DL)
+#endif
+
+#ifndef STATUS_WX86_SINGLE_STEP
+# define STATUS_WX86_SINGLE_STEP ((NTSTATUS) 0x4000001EL)
+#endif
+
+#ifndef STATUS_WX86_BREAKPOINT
+# define STATUS_WX86_BREAKPOINT ((NTSTATUS) 0x4000001FL)
+#endif
+
+#ifndef STATUS_WX86_EXCEPTION_CONTINUE
+# define STATUS_WX86_EXCEPTION_CONTINUE ((NTSTATUS) 0x40000020L)
+#endif
+
+#ifndef STATUS_WX86_EXCEPTION_LASTCHANCE
+# define STATUS_WX86_EXCEPTION_LASTCHANCE ((NTSTATUS) 0x40000021L)
+#endif
+
+#ifndef STATUS_WX86_EXCEPTION_CHAIN
+# define STATUS_WX86_EXCEPTION_CHAIN ((NTSTATUS) 0x40000022L)
+#endif
+
+#ifndef STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE
+# define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE ((NTSTATUS) 0x40000023L)
+#endif
+
+#ifndef STATUS_NO_YIELD_PERFORMED
+# define STATUS_NO_YIELD_PERFORMED ((NTSTATUS) 0x40000024L)
+#endif
+
+#ifndef STATUS_TIMER_RESUME_IGNORED
+# define STATUS_TIMER_RESUME_IGNORED ((NTSTATUS) 0x40000025L)
+#endif
+
+#ifndef STATUS_ARBITRATION_UNHANDLED
+# define STATUS_ARBITRATION_UNHANDLED ((NTSTATUS) 0x40000026L)
+#endif
+
+#ifndef STATUS_CARDBUS_NOT_SUPPORTED
+# define STATUS_CARDBUS_NOT_SUPPORTED ((NTSTATUS) 0x40000027L)
+#endif
+
+#ifndef STATUS_WX86_CREATEWX86TIB
+# define STATUS_WX86_CREATEWX86TIB ((NTSTATUS) 0x40000028L)
+#endif
+
+#ifndef STATUS_MP_PROCESSOR_MISMATCH
+# define STATUS_MP_PROCESSOR_MISMATCH ((NTSTATUS) 0x40000029L)
+#endif
+
+#ifndef STATUS_HIBERNATED
+# define STATUS_HIBERNATED ((NTSTATUS) 0x4000002AL)
+#endif
+
+#ifndef STATUS_RESUME_HIBERNATION
+# define STATUS_RESUME_HIBERNATION ((NTSTATUS) 0x4000002BL)
+#endif
+
+#ifndef STATUS_FIRMWARE_UPDATED
+# define STATUS_FIRMWARE_UPDATED ((NTSTATUS) 0x4000002CL)
+#endif
+
+#ifndef STATUS_DRIVERS_LEAKING_LOCKED_PAGES
+# define STATUS_DRIVERS_LEAKING_LOCKED_PAGES ((NTSTATUS) 0x4000002DL)
+#endif
+
+#ifndef STATUS_MESSAGE_RETRIEVED
+# define STATUS_MESSAGE_RETRIEVED ((NTSTATUS) 0x4000002EL)
+#endif
+
+#ifndef STATUS_SYSTEM_POWERSTATE_TRANSITION
+# define STATUS_SYSTEM_POWERSTATE_TRANSITION ((NTSTATUS) 0x4000002FL)
+#endif
+
+#ifndef STATUS_ALPC_CHECK_COMPLETION_LIST
+# define STATUS_ALPC_CHECK_COMPLETION_LIST ((NTSTATUS) 0x40000030L)
+#endif
+
+#ifndef STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION
+# define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION ((NTSTATUS) 0x40000031L)
+#endif
+
+#ifndef STATUS_ACCESS_AUDIT_BY_POLICY
+# define STATUS_ACCESS_AUDIT_BY_POLICY ((NTSTATUS) 0x40000032L)
+#endif
+
+#ifndef STATUS_ABANDON_HIBERFILE
+# define STATUS_ABANDON_HIBERFILE ((NTSTATUS) 0x40000033L)
+#endif
+
+#ifndef STATUS_BIZRULES_NOT_ENABLED
+# define STATUS_BIZRULES_NOT_ENABLED ((NTSTATUS) 0x40000034L)
+#endif
+
+#ifndef STATUS_GUARD_PAGE_VIOLATION
+# define STATUS_GUARD_PAGE_VIOLATION ((NTSTATUS) 0x80000001L)
+#endif
+
+#ifndef STATUS_DATATYPE_MISALIGNMENT
+# define STATUS_DATATYPE_MISALIGNMENT ((NTSTATUS) 0x80000002L)
+#endif
+
+#ifndef STATUS_BREAKPOINT
+# define STATUS_BREAKPOINT ((NTSTATUS) 0x80000003L)
+#endif
+
+#ifndef STATUS_SINGLE_STEP
+# define STATUS_SINGLE_STEP ((NTSTATUS) 0x80000004L)
+#endif
+
+#ifndef STATUS_BUFFER_OVERFLOW
+# define STATUS_BUFFER_OVERFLOW ((NTSTATUS) 0x80000005L)
+#endif
+
+#ifndef STATUS_NO_MORE_FILES
+# define STATUS_NO_MORE_FILES ((NTSTATUS) 0x80000006L)
+#endif
+
+#ifndef STATUS_WAKE_SYSTEM_DEBUGGER
+# define STATUS_WAKE_SYSTEM_DEBUGGER ((NTSTATUS) 0x80000007L)
+#endif
+
+#ifndef STATUS_HANDLES_CLOSED
+# define STATUS_HANDLES_CLOSED ((NTSTATUS) 0x8000000AL)
+#endif
+
+#ifndef STATUS_NO_INHERITANCE
+# define STATUS_NO_INHERITANCE ((NTSTATUS) 0x8000000BL)
+#endif
+
+#ifndef STATUS_GUID_SUBSTITUTION_MADE
+# define STATUS_GUID_SUBSTITUTION_MADE ((NTSTATUS) 0x8000000CL)
+#endif
+
+#ifndef STATUS_PARTIAL_COPY
+# define STATUS_PARTIAL_COPY ((NTSTATUS) 0x8000000DL)
+#endif
+
+#ifndef STATUS_DEVICE_PAPER_EMPTY
+# define STATUS_DEVICE_PAPER_EMPTY ((NTSTATUS) 0x8000000EL)
+#endif
+
+#ifndef STATUS_DEVICE_POWERED_OFF
+# define STATUS_DEVICE_POWERED_OFF ((NTSTATUS) 0x8000000FL)
+#endif
+
+#ifndef STATUS_DEVICE_OFF_LINE
+# define STATUS_DEVICE_OFF_LINE ((NTSTATUS) 0x80000010L)
+#endif
+
+#ifndef STATUS_DEVICE_BUSY
+# define STATUS_DEVICE_BUSY ((NTSTATUS) 0x80000011L)
+#endif
+
+#ifndef STATUS_NO_MORE_EAS
+# define STATUS_NO_MORE_EAS ((NTSTATUS) 0x80000012L)
+#endif
+
+#ifndef STATUS_INVALID_EA_NAME
+# define STATUS_INVALID_EA_NAME ((NTSTATUS) 0x80000013L)
+#endif
+
+#ifndef STATUS_EA_LIST_INCONSISTENT
+# define STATUS_EA_LIST_INCONSISTENT ((NTSTATUS) 0x80000014L)
+#endif
+
+#ifndef STATUS_INVALID_EA_FLAG
+# define STATUS_INVALID_EA_FLAG ((NTSTATUS) 0x80000015L)
+#endif
+
+#ifndef STATUS_VERIFY_REQUIRED
+# define STATUS_VERIFY_REQUIRED ((NTSTATUS) 0x80000016L)
+#endif
+
+#ifndef STATUS_EXTRANEOUS_INFORMATION
+# define STATUS_EXTRANEOUS_INFORMATION ((NTSTATUS) 0x80000017L)
+#endif
+
+#ifndef STATUS_RXACT_COMMIT_NECESSARY
+# define STATUS_RXACT_COMMIT_NECESSARY ((NTSTATUS) 0x80000018L)
+#endif
+
+#ifndef STATUS_NO_MORE_ENTRIES
+# define STATUS_NO_MORE_ENTRIES ((NTSTATUS) 0x8000001AL)
+#endif
+
+#ifndef STATUS_FILEMARK_DETECTED
+# define STATUS_FILEMARK_DETECTED ((NTSTATUS) 0x8000001BL)
+#endif
+
+#ifndef STATUS_MEDIA_CHANGED
+# define STATUS_MEDIA_CHANGED ((NTSTATUS) 0x8000001CL)
+#endif
+
+#ifndef STATUS_BUS_RESET
+# define STATUS_BUS_RESET ((NTSTATUS) 0x8000001DL)
+#endif
+
+#ifndef STATUS_END_OF_MEDIA
+# define STATUS_END_OF_MEDIA ((NTSTATUS) 0x8000001EL)
+#endif
+
+#ifndef STATUS_BEGINNING_OF_MEDIA
+# define STATUS_BEGINNING_OF_MEDIA ((NTSTATUS) 0x8000001FL)
+#endif
+
+#ifndef STATUS_MEDIA_CHECK
+# define STATUS_MEDIA_CHECK ((NTSTATUS) 0x80000020L)
+#endif
+
+#ifndef STATUS_SETMARK_DETECTED
+# define STATUS_SETMARK_DETECTED ((NTSTATUS) 0x80000021L)
+#endif
+
+#ifndef STATUS_NO_DATA_DETECTED
+# define STATUS_NO_DATA_DETECTED ((NTSTATUS) 0x80000022L)
+#endif
+
+#ifndef STATUS_REDIRECTOR_HAS_OPEN_HANDLES
+# define STATUS_REDIRECTOR_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000023L)
+#endif
+
+#ifndef STATUS_SERVER_HAS_OPEN_HANDLES
+# define STATUS_SERVER_HAS_OPEN_HANDLES ((NTSTATUS) 0x80000024L)
+#endif
+
+#ifndef STATUS_ALREADY_DISCONNECTED
+# define STATUS_ALREADY_DISCONNECTED ((NTSTATUS) 0x80000025L)
+#endif
+
+#ifndef STATUS_LONGJUMP
+# define STATUS_LONGJUMP ((NTSTATUS) 0x80000026L)
+#endif
+
+#ifndef STATUS_CLEANER_CARTRIDGE_INSTALLED
+# define STATUS_CLEANER_CARTRIDGE_INSTALLED ((NTSTATUS) 0x80000027L)
+#endif
+
+#ifndef STATUS_PLUGPLAY_QUERY_VETOED
+# define STATUS_PLUGPLAY_QUERY_VETOED ((NTSTATUS) 0x80000028L)
+#endif
+
+#ifndef STATUS_UNWIND_CONSOLIDATE
+# define STATUS_UNWIND_CONSOLIDATE ((NTSTATUS) 0x80000029L)
+#endif
+
+#ifndef STATUS_REGISTRY_HIVE_RECOVERED
+# define STATUS_REGISTRY_HIVE_RECOVERED ((NTSTATUS) 0x8000002AL)
+#endif
+
+#ifndef STATUS_DLL_MIGHT_BE_INSECURE
+# define STATUS_DLL_MIGHT_BE_INSECURE ((NTSTATUS) 0x8000002BL)
+#endif
+
+#ifndef STATUS_DLL_MIGHT_BE_INCOMPATIBLE
+# define STATUS_DLL_MIGHT_BE_INCOMPATIBLE ((NTSTATUS) 0x8000002CL)
+#endif
+
+#ifndef STATUS_STOPPED_ON_SYMLINK
+# define STATUS_STOPPED_ON_SYMLINK ((NTSTATUS) 0x8000002DL)
+#endif
+
+#ifndef STATUS_CANNOT_GRANT_REQUESTED_OPLOCK
+# define STATUS_CANNOT_GRANT_REQUESTED_OPLOCK ((NTSTATUS) 0x8000002EL)
+#endif
+
+#ifndef STATUS_NO_ACE_CONDITION
+# define STATUS_NO_ACE_CONDITION ((NTSTATUS) 0x8000002FL)
+#endif
+
+#ifndef STATUS_UNSUCCESSFUL
+# define STATUS_UNSUCCESSFUL ((NTSTATUS) 0xC0000001L)
+#endif
+
+#ifndef STATUS_NOT_IMPLEMENTED
+# define STATUS_NOT_IMPLEMENTED ((NTSTATUS) 0xC0000002L)
+#endif
+
+#ifndef STATUS_INVALID_INFO_CLASS
+# define STATUS_INVALID_INFO_CLASS ((NTSTATUS) 0xC0000003L)
+#endif
+
+#ifndef STATUS_INFO_LENGTH_MISMATCH
+# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xC0000004L)
+#endif
+
+#ifndef STATUS_ACCESS_VIOLATION
+# define STATUS_ACCESS_VIOLATION ((NTSTATUS) 0xC0000005L)
+#endif
+
+#ifndef STATUS_IN_PAGE_ERROR
+# define STATUS_IN_PAGE_ERROR ((NTSTATUS) 0xC0000006L)
+#endif
+
+#ifndef STATUS_PAGEFILE_QUOTA
+# define STATUS_PAGEFILE_QUOTA ((NTSTATUS) 0xC0000007L)
+#endif
+
+#ifndef STATUS_INVALID_HANDLE
+# define STATUS_INVALID_HANDLE ((NTSTATUS) 0xC0000008L)
+#endif
+
+#ifndef STATUS_BAD_INITIAL_STACK
+# define STATUS_BAD_INITIAL_STACK ((NTSTATUS) 0xC0000009L)
+#endif
+
+#ifndef STATUS_BAD_INITIAL_PC
+# define STATUS_BAD_INITIAL_PC ((NTSTATUS) 0xC000000AL)
+#endif
+
+#ifndef STATUS_INVALID_CID
+# define STATUS_INVALID_CID ((NTSTATUS) 0xC000000BL)
+#endif
+
+#ifndef STATUS_TIMER_NOT_CANCELED
+# define STATUS_TIMER_NOT_CANCELED ((NTSTATUS) 0xC000000CL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER
+# define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xC000000DL)
+#endif
+
+#ifndef STATUS_NO_SUCH_DEVICE
+# define STATUS_NO_SUCH_DEVICE ((NTSTATUS) 0xC000000EL)
+#endif
+
+#ifndef STATUS_NO_SUCH_FILE
+# define STATUS_NO_SUCH_FILE ((NTSTATUS) 0xC000000FL)
+#endif
+
+#ifndef STATUS_INVALID_DEVICE_REQUEST
+# define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS) 0xC0000010L)
+#endif
+
+#ifndef STATUS_END_OF_FILE
+# define STATUS_END_OF_FILE ((NTSTATUS) 0xC0000011L)
+#endif
+
+#ifndef STATUS_WRONG_VOLUME
+# define STATUS_WRONG_VOLUME ((NTSTATUS) 0xC0000012L)
+#endif
+
+#ifndef STATUS_NO_MEDIA_IN_DEVICE
+# define STATUS_NO_MEDIA_IN_DEVICE ((NTSTATUS) 0xC0000013L)
+#endif
+
+#ifndef STATUS_UNRECOGNIZED_MEDIA
+# define STATUS_UNRECOGNIZED_MEDIA ((NTSTATUS) 0xC0000014L)
+#endif
+
+#ifndef STATUS_NONEXISTENT_SECTOR
+# define STATUS_NONEXISTENT_SECTOR ((NTSTATUS) 0xC0000015L)
+#endif
+
+#ifndef STATUS_MORE_PROCESSING_REQUIRED
+# define STATUS_MORE_PROCESSING_REQUIRED ((NTSTATUS) 0xC0000016L)
+#endif
+
+#ifndef STATUS_NO_MEMORY
+# define STATUS_NO_MEMORY ((NTSTATUS) 0xC0000017L)
+#endif
+
+#ifndef STATUS_CONFLICTING_ADDRESSES
+# define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS) 0xC0000018L)
+#endif
+
+#ifndef STATUS_NOT_MAPPED_VIEW
+# define STATUS_NOT_MAPPED_VIEW ((NTSTATUS) 0xC0000019L)
+#endif
+
+#ifndef STATUS_UNABLE_TO_FREE_VM
+# define STATUS_UNABLE_TO_FREE_VM ((NTSTATUS) 0xC000001AL)
+#endif
+
+#ifndef STATUS_UNABLE_TO_DELETE_SECTION
+# define STATUS_UNABLE_TO_DELETE_SECTION ((NTSTATUS) 0xC000001BL)
+#endif
+
+#ifndef STATUS_INVALID_SYSTEM_SERVICE
+# define STATUS_INVALID_SYSTEM_SERVICE ((NTSTATUS) 0xC000001CL)
+#endif
+
+#ifndef STATUS_ILLEGAL_INSTRUCTION
+# define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS) 0xC000001DL)
+#endif
+
+#ifndef STATUS_INVALID_LOCK_SEQUENCE
+# define STATUS_INVALID_LOCK_SEQUENCE ((NTSTATUS) 0xC000001EL)
+#endif
+
+#ifndef STATUS_INVALID_VIEW_SIZE
+# define STATUS_INVALID_VIEW_SIZE ((NTSTATUS) 0xC000001FL)
+#endif
+
+#ifndef STATUS_INVALID_FILE_FOR_SECTION
+# define STATUS_INVALID_FILE_FOR_SECTION ((NTSTATUS) 0xC0000020L)
+#endif
+
+#ifndef STATUS_ALREADY_COMMITTED
+# define STATUS_ALREADY_COMMITTED ((NTSTATUS) 0xC0000021L)
+#endif
+
+#ifndef STATUS_ACCESS_DENIED
+# define STATUS_ACCESS_DENIED ((NTSTATUS) 0xC0000022L)
+#endif
+
+#ifndef STATUS_BUFFER_TOO_SMALL
+# define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xC0000023L)
+#endif
+
+#ifndef STATUS_OBJECT_TYPE_MISMATCH
+# define STATUS_OBJECT_TYPE_MISMATCH ((NTSTATUS) 0xC0000024L)
+#endif
+
+#ifndef STATUS_NONCONTINUABLE_EXCEPTION
+# define STATUS_NONCONTINUABLE_EXCEPTION ((NTSTATUS) 0xC0000025L)
+#endif
+
+#ifndef STATUS_INVALID_DISPOSITION
+# define STATUS_INVALID_DISPOSITION ((NTSTATUS) 0xC0000026L)
+#endif
+
+#ifndef STATUS_UNWIND
+# define STATUS_UNWIND ((NTSTATUS) 0xC0000027L)
+#endif
+
+#ifndef STATUS_BAD_STACK
+# define STATUS_BAD_STACK ((NTSTATUS) 0xC0000028L)
+#endif
+
+#ifndef STATUS_INVALID_UNWIND_TARGET
+# define STATUS_INVALID_UNWIND_TARGET ((NTSTATUS) 0xC0000029L)
+#endif
+
+#ifndef STATUS_NOT_LOCKED
+# define STATUS_NOT_LOCKED ((NTSTATUS) 0xC000002AL)
+#endif
+
+#ifndef STATUS_PARITY_ERROR
+# define STATUS_PARITY_ERROR ((NTSTATUS) 0xC000002BL)
+#endif
+
+#ifndef STATUS_UNABLE_TO_DECOMMIT_VM
+# define STATUS_UNABLE_TO_DECOMMIT_VM ((NTSTATUS) 0xC000002CL)
+#endif
+
+#ifndef STATUS_NOT_COMMITTED
+# define STATUS_NOT_COMMITTED ((NTSTATUS) 0xC000002DL)
+#endif
+
+#ifndef STATUS_INVALID_PORT_ATTRIBUTES
+# define STATUS_INVALID_PORT_ATTRIBUTES ((NTSTATUS) 0xC000002EL)
+#endif
+
+#ifndef STATUS_PORT_MESSAGE_TOO_LONG
+# define STATUS_PORT_MESSAGE_TOO_LONG ((NTSTATUS) 0xC000002FL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_MIX
+# define STATUS_INVALID_PARAMETER_MIX ((NTSTATUS) 0xC0000030L)
+#endif
+
+#ifndef STATUS_INVALID_QUOTA_LOWER
+# define STATUS_INVALID_QUOTA_LOWER ((NTSTATUS) 0xC0000031L)
+#endif
+
+#ifndef STATUS_DISK_CORRUPT_ERROR
+# define STATUS_DISK_CORRUPT_ERROR ((NTSTATUS) 0xC0000032L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_INVALID
+# define STATUS_OBJECT_NAME_INVALID ((NTSTATUS) 0xC0000033L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_NOT_FOUND
+# define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS) 0xC0000034L)
+#endif
+
+#ifndef STATUS_OBJECT_NAME_COLLISION
+# define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS) 0xC0000035L)
+#endif
+
+#ifndef STATUS_PORT_DISCONNECTED
+# define STATUS_PORT_DISCONNECTED ((NTSTATUS) 0xC0000037L)
+#endif
+
+#ifndef STATUS_DEVICE_ALREADY_ATTACHED
+# define STATUS_DEVICE_ALREADY_ATTACHED ((NTSTATUS) 0xC0000038L)
+#endif
+
+#ifndef STATUS_OBJECT_PATH_INVALID
+# define STATUS_OBJECT_PATH_INVALID ((NTSTATUS) 0xC0000039L)
+#endif
+
+#ifndef STATUS_OBJECT_PATH_NOT_FOUND
+# define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS) 0xC000003AL)
+#endif
+
+#ifndef STATUS_OBJECT_PATH_SYNTAX_BAD
+# define STATUS_OBJECT_PATH_SYNTAX_BAD ((NTSTATUS) 0xC000003BL)
+#endif
+
+#ifndef STATUS_DATA_OVERRUN
+# define STATUS_DATA_OVERRUN ((NTSTATUS) 0xC000003CL)
+#endif
+
+#ifndef STATUS_DATA_LATE_ERROR
+# define STATUS_DATA_LATE_ERROR ((NTSTATUS) 0xC000003DL)
+#endif
+
+#ifndef STATUS_DATA_ERROR
+# define STATUS_DATA_ERROR ((NTSTATUS) 0xC000003EL)
+#endif
+
+#ifndef STATUS_CRC_ERROR
+# define STATUS_CRC_ERROR ((NTSTATUS) 0xC000003FL)
+#endif
+
+#ifndef STATUS_SECTION_TOO_BIG
+# define STATUS_SECTION_TOO_BIG ((NTSTATUS) 0xC0000040L)
+#endif
+
+#ifndef STATUS_PORT_CONNECTION_REFUSED
+# define STATUS_PORT_CONNECTION_REFUSED ((NTSTATUS) 0xC0000041L)
+#endif
+
+#ifndef STATUS_INVALID_PORT_HANDLE
+# define STATUS_INVALID_PORT_HANDLE ((NTSTATUS) 0xC0000042L)
+#endif
+
+#ifndef STATUS_SHARING_VIOLATION
+# define STATUS_SHARING_VIOLATION ((NTSTATUS) 0xC0000043L)
+#endif
+
+#ifndef STATUS_QUOTA_EXCEEDED
+# define STATUS_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000044L)
+#endif
+
+#ifndef STATUS_INVALID_PAGE_PROTECTION
+# define STATUS_INVALID_PAGE_PROTECTION ((NTSTATUS) 0xC0000045L)
+#endif
+
+#ifndef STATUS_MUTANT_NOT_OWNED
+# define STATUS_MUTANT_NOT_OWNED ((NTSTATUS) 0xC0000046L)
+#endif
+
+#ifndef STATUS_SEMAPHORE_LIMIT_EXCEEDED
+# define STATUS_SEMAPHORE_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000047L)
+#endif
+
+#ifndef STATUS_PORT_ALREADY_SET
+# define STATUS_PORT_ALREADY_SET ((NTSTATUS) 0xC0000048L)
+#endif
+
+#ifndef STATUS_SECTION_NOT_IMAGE
+# define STATUS_SECTION_NOT_IMAGE ((NTSTATUS) 0xC0000049L)
+#endif
+
+#ifndef STATUS_SUSPEND_COUNT_EXCEEDED
+# define STATUS_SUSPEND_COUNT_EXCEEDED ((NTSTATUS) 0xC000004AL)
+#endif
+
+#ifndef STATUS_THREAD_IS_TERMINATING
+# define STATUS_THREAD_IS_TERMINATING ((NTSTATUS) 0xC000004BL)
+#endif
+
+#ifndef STATUS_BAD_WORKING_SET_LIMIT
+# define STATUS_BAD_WORKING_SET_LIMIT ((NTSTATUS) 0xC000004CL)
+#endif
+
+#ifndef STATUS_INCOMPATIBLE_FILE_MAP
+# define STATUS_INCOMPATIBLE_FILE_MAP ((NTSTATUS) 0xC000004DL)
+#endif
+
+#ifndef STATUS_SECTION_PROTECTION
+# define STATUS_SECTION_PROTECTION ((NTSTATUS) 0xC000004EL)
+#endif
+
+#ifndef STATUS_EAS_NOT_SUPPORTED
+# define STATUS_EAS_NOT_SUPPORTED ((NTSTATUS) 0xC000004FL)
+#endif
+
+#ifndef STATUS_EA_TOO_LARGE
+# define STATUS_EA_TOO_LARGE ((NTSTATUS) 0xC0000050L)
+#endif
+
+#ifndef STATUS_NONEXISTENT_EA_ENTRY
+# define STATUS_NONEXISTENT_EA_ENTRY ((NTSTATUS) 0xC0000051L)
+#endif
+
+#ifndef STATUS_NO_EAS_ON_FILE
+# define STATUS_NO_EAS_ON_FILE ((NTSTATUS) 0xC0000052L)
+#endif
+
+#ifndef STATUS_EA_CORRUPT_ERROR
+# define STATUS_EA_CORRUPT_ERROR ((NTSTATUS) 0xC0000053L)
+#endif
+
+#ifndef STATUS_FILE_LOCK_CONFLICT
+# define STATUS_FILE_LOCK_CONFLICT ((NTSTATUS) 0xC0000054L)
+#endif
+
+#ifndef STATUS_LOCK_NOT_GRANTED
+# define STATUS_LOCK_NOT_GRANTED ((NTSTATUS) 0xC0000055L)
+#endif
+
+#ifndef STATUS_DELETE_PENDING
+# define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056L)
+#endif
+
+#ifndef STATUS_CTL_FILE_NOT_SUPPORTED
+# define STATUS_CTL_FILE_NOT_SUPPORTED ((NTSTATUS) 0xC0000057L)
+#endif
+
+#ifndef STATUS_UNKNOWN_REVISION
+# define STATUS_UNKNOWN_REVISION ((NTSTATUS) 0xC0000058L)
+#endif
+
+#ifndef STATUS_REVISION_MISMATCH
+# define STATUS_REVISION_MISMATCH ((NTSTATUS) 0xC0000059L)
+#endif
+
+#ifndef STATUS_INVALID_OWNER
+# define STATUS_INVALID_OWNER ((NTSTATUS) 0xC000005AL)
+#endif
+
+#ifndef STATUS_INVALID_PRIMARY_GROUP
+# define STATUS_INVALID_PRIMARY_GROUP ((NTSTATUS) 0xC000005BL)
+#endif
+
+#ifndef STATUS_NO_IMPERSONATION_TOKEN
+# define STATUS_NO_IMPERSONATION_TOKEN ((NTSTATUS) 0xC000005CL)
+#endif
+
+#ifndef STATUS_CANT_DISABLE_MANDATORY
+# define STATUS_CANT_DISABLE_MANDATORY ((NTSTATUS) 0xC000005DL)
+#endif
+
+#ifndef STATUS_NO_LOGON_SERVERS
+# define STATUS_NO_LOGON_SERVERS ((NTSTATUS) 0xC000005EL)
+#endif
+
+#ifndef STATUS_NO_SUCH_LOGON_SESSION
+# define STATUS_NO_SUCH_LOGON_SESSION ((NTSTATUS) 0xC000005FL)
+#endif
+
+#ifndef STATUS_NO_SUCH_PRIVILEGE
+# define STATUS_NO_SUCH_PRIVILEGE ((NTSTATUS) 0xC0000060L)
+#endif
+
+#ifndef STATUS_PRIVILEGE_NOT_HELD
+# define STATUS_PRIVILEGE_NOT_HELD ((NTSTATUS) 0xC0000061L)
+#endif
+
+#ifndef STATUS_INVALID_ACCOUNT_NAME
+# define STATUS_INVALID_ACCOUNT_NAME ((NTSTATUS) 0xC0000062L)
+#endif
+
+#ifndef STATUS_USER_EXISTS
+# define STATUS_USER_EXISTS ((NTSTATUS) 0xC0000063L)
+#endif
+
+#ifndef STATUS_NO_SUCH_USER
+# define STATUS_NO_SUCH_USER ((NTSTATUS) 0xC0000064L)
+#endif
+
+#ifndef STATUS_GROUP_EXISTS
+# define STATUS_GROUP_EXISTS ((NTSTATUS) 0xC0000065L)
+#endif
+
+#ifndef STATUS_NO_SUCH_GROUP
+# define STATUS_NO_SUCH_GROUP ((NTSTATUS) 0xC0000066L)
+#endif
+
+#ifndef STATUS_MEMBER_IN_GROUP
+# define STATUS_MEMBER_IN_GROUP ((NTSTATUS) 0xC0000067L)
+#endif
+
+#ifndef STATUS_MEMBER_NOT_IN_GROUP
+# define STATUS_MEMBER_NOT_IN_GROUP ((NTSTATUS) 0xC0000068L)
+#endif
+
+#ifndef STATUS_LAST_ADMIN
+# define STATUS_LAST_ADMIN ((NTSTATUS) 0xC0000069L)
+#endif
+
+#ifndef STATUS_WRONG_PASSWORD
+# define STATUS_WRONG_PASSWORD ((NTSTATUS) 0xC000006AL)
+#endif
+
+#ifndef STATUS_ILL_FORMED_PASSWORD
+# define STATUS_ILL_FORMED_PASSWORD ((NTSTATUS) 0xC000006BL)
+#endif
+
+#ifndef STATUS_PASSWORD_RESTRICTION
+# define STATUS_PASSWORD_RESTRICTION ((NTSTATUS) 0xC000006CL)
+#endif
+
+#ifndef STATUS_LOGON_FAILURE
+# define STATUS_LOGON_FAILURE ((NTSTATUS) 0xC000006DL)
+#endif
+
+#ifndef STATUS_ACCOUNT_RESTRICTION
+# define STATUS_ACCOUNT_RESTRICTION ((NTSTATUS) 0xC000006EL)
+#endif
+
+#ifndef STATUS_INVALID_LOGON_HOURS
+# define STATUS_INVALID_LOGON_HOURS ((NTSTATUS) 0xC000006FL)
+#endif
+
+#ifndef STATUS_INVALID_WORKSTATION
+# define STATUS_INVALID_WORKSTATION ((NTSTATUS) 0xC0000070L)
+#endif
+
+#ifndef STATUS_PASSWORD_EXPIRED
+# define STATUS_PASSWORD_EXPIRED ((NTSTATUS) 0xC0000071L)
+#endif
+
+#ifndef STATUS_ACCOUNT_DISABLED
+# define STATUS_ACCOUNT_DISABLED ((NTSTATUS) 0xC0000072L)
+#endif
+
+#ifndef STATUS_NONE_MAPPED
+# define STATUS_NONE_MAPPED ((NTSTATUS) 0xC0000073L)
+#endif
+
+#ifndef STATUS_TOO_MANY_LUIDS_REQUESTED
+# define STATUS_TOO_MANY_LUIDS_REQUESTED ((NTSTATUS) 0xC0000074L)
+#endif
+
+#ifndef STATUS_LUIDS_EXHAUSTED
+# define STATUS_LUIDS_EXHAUSTED ((NTSTATUS) 0xC0000075L)
+#endif
+
+#ifndef STATUS_INVALID_SUB_AUTHORITY
+# define STATUS_INVALID_SUB_AUTHORITY ((NTSTATUS) 0xC0000076L)
+#endif
+
+#ifndef STATUS_INVALID_ACL
+# define STATUS_INVALID_ACL ((NTSTATUS) 0xC0000077L)
+#endif
+
+#ifndef STATUS_INVALID_SID
+# define STATUS_INVALID_SID ((NTSTATUS) 0xC0000078L)
+#endif
+
+#ifndef STATUS_INVALID_SECURITY_DESCR
+# define STATUS_INVALID_SECURITY_DESCR ((NTSTATUS) 0xC0000079L)
+#endif
+
+#ifndef STATUS_PROCEDURE_NOT_FOUND
+# define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS) 0xC000007AL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_FORMAT
+# define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS) 0xC000007BL)
+#endif
+
+#ifndef STATUS_NO_TOKEN
+# define STATUS_NO_TOKEN ((NTSTATUS) 0xC000007CL)
+#endif
+
+#ifndef STATUS_BAD_INHERITANCE_ACL
+# define STATUS_BAD_INHERITANCE_ACL ((NTSTATUS) 0xC000007DL)
+#endif
+
+#ifndef STATUS_RANGE_NOT_LOCKED
+# define STATUS_RANGE_NOT_LOCKED ((NTSTATUS) 0xC000007EL)
+#endif
+
+#ifndef STATUS_DISK_FULL
+# define STATUS_DISK_FULL ((NTSTATUS) 0xC000007FL)
+#endif
+
+#ifndef STATUS_SERVER_DISABLED
+# define STATUS_SERVER_DISABLED ((NTSTATUS) 0xC0000080L)
+#endif
+
+#ifndef STATUS_SERVER_NOT_DISABLED
+# define STATUS_SERVER_NOT_DISABLED ((NTSTATUS) 0xC0000081L)
+#endif
+
+#ifndef STATUS_TOO_MANY_GUIDS_REQUESTED
+# define STATUS_TOO_MANY_GUIDS_REQUESTED ((NTSTATUS) 0xC0000082L)
+#endif
+
+#ifndef STATUS_GUIDS_EXHAUSTED
+# define STATUS_GUIDS_EXHAUSTED ((NTSTATUS) 0xC0000083L)
+#endif
+
+#ifndef STATUS_INVALID_ID_AUTHORITY
+# define STATUS_INVALID_ID_AUTHORITY ((NTSTATUS) 0xC0000084L)
+#endif
+
+#ifndef STATUS_AGENTS_EXHAUSTED
+# define STATUS_AGENTS_EXHAUSTED ((NTSTATUS) 0xC0000085L)
+#endif
+
+#ifndef STATUS_INVALID_VOLUME_LABEL
+# define STATUS_INVALID_VOLUME_LABEL ((NTSTATUS) 0xC0000086L)
+#endif
+
+#ifndef STATUS_SECTION_NOT_EXTENDED
+# define STATUS_SECTION_NOT_EXTENDED ((NTSTATUS) 0xC0000087L)
+#endif
+
+#ifndef STATUS_NOT_MAPPED_DATA
+# define STATUS_NOT_MAPPED_DATA ((NTSTATUS) 0xC0000088L)
+#endif
+
+#ifndef STATUS_RESOURCE_DATA_NOT_FOUND
+# define STATUS_RESOURCE_DATA_NOT_FOUND ((NTSTATUS) 0xC0000089L)
+#endif
+
+#ifndef STATUS_RESOURCE_TYPE_NOT_FOUND
+# define STATUS_RESOURCE_TYPE_NOT_FOUND ((NTSTATUS) 0xC000008AL)
+#endif
+
+#ifndef STATUS_RESOURCE_NAME_NOT_FOUND
+# define STATUS_RESOURCE_NAME_NOT_FOUND ((NTSTATUS) 0xC000008BL)
+#endif
+
+#ifndef STATUS_ARRAY_BOUNDS_EXCEEDED
+# define STATUS_ARRAY_BOUNDS_EXCEEDED ((NTSTATUS) 0xC000008CL)
+#endif
+
+#ifndef STATUS_FLOAT_DENORMAL_OPERAND
+# define STATUS_FLOAT_DENORMAL_OPERAND ((NTSTATUS) 0xC000008DL)
+#endif
+
+#ifndef STATUS_FLOAT_DIVIDE_BY_ZERO
+# define STATUS_FLOAT_DIVIDE_BY_ZERO ((NTSTATUS) 0xC000008EL)
+#endif
+
+#ifndef STATUS_FLOAT_INEXACT_RESULT
+# define STATUS_FLOAT_INEXACT_RESULT ((NTSTATUS) 0xC000008FL)
+#endif
+
+#ifndef STATUS_FLOAT_INVALID_OPERATION
+# define STATUS_FLOAT_INVALID_OPERATION ((NTSTATUS) 0xC0000090L)
+#endif
+
+#ifndef STATUS_FLOAT_OVERFLOW
+# define STATUS_FLOAT_OVERFLOW ((NTSTATUS) 0xC0000091L)
+#endif
+
+#ifndef STATUS_FLOAT_STACK_CHECK
+# define STATUS_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000092L)
+#endif
+
+#ifndef STATUS_FLOAT_UNDERFLOW
+# define STATUS_FLOAT_UNDERFLOW ((NTSTATUS) 0xC0000093L)
+#endif
+
+#ifndef STATUS_INTEGER_DIVIDE_BY_ZERO
+# define STATUS_INTEGER_DIVIDE_BY_ZERO ((NTSTATUS) 0xC0000094L)
+#endif
+
+#ifndef STATUS_INTEGER_OVERFLOW
+# define STATUS_INTEGER_OVERFLOW ((NTSTATUS) 0xC0000095L)
+#endif
+
+#ifndef STATUS_PRIVILEGED_INSTRUCTION
+# define STATUS_PRIVILEGED_INSTRUCTION ((NTSTATUS) 0xC0000096L)
+#endif
+
+#ifndef STATUS_TOO_MANY_PAGING_FILES
+# define STATUS_TOO_MANY_PAGING_FILES ((NTSTATUS) 0xC0000097L)
+#endif
+
+#ifndef STATUS_FILE_INVALID
+# define STATUS_FILE_INVALID ((NTSTATUS) 0xC0000098L)
+#endif
+
+#ifndef STATUS_ALLOTTED_SPACE_EXCEEDED
+# define STATUS_ALLOTTED_SPACE_EXCEEDED ((NTSTATUS) 0xC0000099L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_RESOURCES
+# define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS) 0xC000009AL)
+#endif
+
+#ifndef STATUS_DFS_EXIT_PATH_FOUND
+# define STATUS_DFS_EXIT_PATH_FOUND ((NTSTATUS) 0xC000009BL)
+#endif
+
+#ifndef STATUS_DEVICE_DATA_ERROR
+# define STATUS_DEVICE_DATA_ERROR ((NTSTATUS) 0xC000009CL)
+#endif
+
+#ifndef STATUS_DEVICE_NOT_CONNECTED
+# define STATUS_DEVICE_NOT_CONNECTED ((NTSTATUS) 0xC000009DL)
+#endif
+
+#ifndef STATUS_DEVICE_POWER_FAILURE
+# define STATUS_DEVICE_POWER_FAILURE ((NTSTATUS) 0xC000009EL)
+#endif
+
+#ifndef STATUS_FREE_VM_NOT_AT_BASE
+# define STATUS_FREE_VM_NOT_AT_BASE ((NTSTATUS) 0xC000009FL)
+#endif
+
+#ifndef STATUS_MEMORY_NOT_ALLOCATED
+# define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS) 0xC00000A0L)
+#endif
+
+#ifndef STATUS_WORKING_SET_QUOTA
+# define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xC00000A1L)
+#endif
+
+#ifndef STATUS_MEDIA_WRITE_PROTECTED
+# define STATUS_MEDIA_WRITE_PROTECTED ((NTSTATUS) 0xC00000A2L)
+#endif
+
+#ifndef STATUS_DEVICE_NOT_READY
+# define STATUS_DEVICE_NOT_READY ((NTSTATUS) 0xC00000A3L)
+#endif
+
+#ifndef STATUS_INVALID_GROUP_ATTRIBUTES
+# define STATUS_INVALID_GROUP_ATTRIBUTES ((NTSTATUS) 0xC00000A4L)
+#endif
+
+#ifndef STATUS_BAD_IMPERSONATION_LEVEL
+# define STATUS_BAD_IMPERSONATION_LEVEL ((NTSTATUS) 0xC00000A5L)
+#endif
+
+#ifndef STATUS_CANT_OPEN_ANONYMOUS
+# define STATUS_CANT_OPEN_ANONYMOUS ((NTSTATUS) 0xC00000A6L)
+#endif
+
+#ifndef STATUS_BAD_VALIDATION_CLASS
+# define STATUS_BAD_VALIDATION_CLASS ((NTSTATUS) 0xC00000A7L)
+#endif
+
+#ifndef STATUS_BAD_TOKEN_TYPE
+# define STATUS_BAD_TOKEN_TYPE ((NTSTATUS) 0xC00000A8L)
+#endif
+
+#ifndef STATUS_BAD_MASTER_BOOT_RECORD
+# define STATUS_BAD_MASTER_BOOT_RECORD ((NTSTATUS) 0xC00000A9L)
+#endif
+
+#ifndef STATUS_INSTRUCTION_MISALIGNMENT
+# define STATUS_INSTRUCTION_MISALIGNMENT ((NTSTATUS) 0xC00000AAL)
+#endif
+
+#ifndef STATUS_INSTANCE_NOT_AVAILABLE
+# define STATUS_INSTANCE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ABL)
+#endif
+
+#ifndef STATUS_PIPE_NOT_AVAILABLE
+# define STATUS_PIPE_NOT_AVAILABLE ((NTSTATUS) 0xC00000ACL)
+#endif
+
+#ifndef STATUS_INVALID_PIPE_STATE
+# define STATUS_INVALID_PIPE_STATE ((NTSTATUS) 0xC00000ADL)
+#endif
+
+#ifndef STATUS_PIPE_BUSY
+# define STATUS_PIPE_BUSY ((NTSTATUS) 0xC00000AEL)
+#endif
+
+#ifndef STATUS_ILLEGAL_FUNCTION
+# define STATUS_ILLEGAL_FUNCTION ((NTSTATUS) 0xC00000AFL)
+#endif
+
+#ifndef STATUS_PIPE_DISCONNECTED
+# define STATUS_PIPE_DISCONNECTED ((NTSTATUS) 0xC00000B0L)
+#endif
+
+#ifndef STATUS_PIPE_CLOSING
+# define STATUS_PIPE_CLOSING ((NTSTATUS) 0xC00000B1L)
+#endif
+
+#ifndef STATUS_PIPE_CONNECTED
+# define STATUS_PIPE_CONNECTED ((NTSTATUS) 0xC00000B2L)
+#endif
+
+#ifndef STATUS_PIPE_LISTENING
+# define STATUS_PIPE_LISTENING ((NTSTATUS) 0xC00000B3L)
+#endif
+
+#ifndef STATUS_INVALID_READ_MODE
+# define STATUS_INVALID_READ_MODE ((NTSTATUS) 0xC00000B4L)
+#endif
+
+#ifndef STATUS_IO_TIMEOUT
+# define STATUS_IO_TIMEOUT ((NTSTATUS) 0xC00000B5L)
+#endif
+
+#ifndef STATUS_FILE_FORCED_CLOSED
+# define STATUS_FILE_FORCED_CLOSED ((NTSTATUS) 0xC00000B6L)
+#endif
+
+#ifndef STATUS_PROFILING_NOT_STARTED
+# define STATUS_PROFILING_NOT_STARTED ((NTSTATUS) 0xC00000B7L)
+#endif
+
+#ifndef STATUS_PROFILING_NOT_STOPPED
+# define STATUS_PROFILING_NOT_STOPPED ((NTSTATUS) 0xC00000B8L)
+#endif
+
+#ifndef STATUS_COULD_NOT_INTERPRET
+# define STATUS_COULD_NOT_INTERPRET ((NTSTATUS) 0xC00000B9L)
+#endif
+
+#ifndef STATUS_FILE_IS_A_DIRECTORY
+# define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS) 0xC00000BAL)
+#endif
+
+#ifndef STATUS_NOT_SUPPORTED
+# define STATUS_NOT_SUPPORTED ((NTSTATUS) 0xC00000BBL)
+#endif
+
+#ifndef STATUS_REMOTE_NOT_LISTENING
+# define STATUS_REMOTE_NOT_LISTENING ((NTSTATUS) 0xC00000BCL)
+#endif
+
+#ifndef STATUS_DUPLICATE_NAME
+# define STATUS_DUPLICATE_NAME ((NTSTATUS) 0xC00000BDL)
+#endif
+
+#ifndef STATUS_BAD_NETWORK_PATH
+# define STATUS_BAD_NETWORK_PATH ((NTSTATUS) 0xC00000BEL)
+#endif
+
+#ifndef STATUS_NETWORK_BUSY
+# define STATUS_NETWORK_BUSY ((NTSTATUS) 0xC00000BFL)
+#endif
+
+#ifndef STATUS_DEVICE_DOES_NOT_EXIST
+# define STATUS_DEVICE_DOES_NOT_EXIST ((NTSTATUS) 0xC00000C0L)
+#endif
+
+#ifndef STATUS_TOO_MANY_COMMANDS
+# define STATUS_TOO_MANY_COMMANDS ((NTSTATUS) 0xC00000C1L)
+#endif
+
+#ifndef STATUS_ADAPTER_HARDWARE_ERROR
+# define STATUS_ADAPTER_HARDWARE_ERROR ((NTSTATUS) 0xC00000C2L)
+#endif
+
+#ifndef STATUS_INVALID_NETWORK_RESPONSE
+# define STATUS_INVALID_NETWORK_RESPONSE ((NTSTATUS) 0xC00000C3L)
+#endif
+
+#ifndef STATUS_UNEXPECTED_NETWORK_ERROR
+# define STATUS_UNEXPECTED_NETWORK_ERROR ((NTSTATUS) 0xC00000C4L)
+#endif
+
+#ifndef STATUS_BAD_REMOTE_ADAPTER
+# define STATUS_BAD_REMOTE_ADAPTER ((NTSTATUS) 0xC00000C5L)
+#endif
+
+#ifndef STATUS_PRINT_QUEUE_FULL
+# define STATUS_PRINT_QUEUE_FULL ((NTSTATUS) 0xC00000C6L)
+#endif
+
+#ifndef STATUS_NO_SPOOL_SPACE
+# define STATUS_NO_SPOOL_SPACE ((NTSTATUS) 0xC00000C7L)
+#endif
+
+#ifndef STATUS_PRINT_CANCELLED
+# define STATUS_PRINT_CANCELLED ((NTSTATUS) 0xC00000C8L)
+#endif
+
+#ifndef STATUS_NETWORK_NAME_DELETED
+# define STATUS_NETWORK_NAME_DELETED ((NTSTATUS) 0xC00000C9L)
+#endif
+
+#ifndef STATUS_NETWORK_ACCESS_DENIED
+# define STATUS_NETWORK_ACCESS_DENIED ((NTSTATUS) 0xC00000CAL)
+#endif
+
+#ifndef STATUS_BAD_DEVICE_TYPE
+# define STATUS_BAD_DEVICE_TYPE ((NTSTATUS) 0xC00000CBL)
+#endif
+
+#ifndef STATUS_BAD_NETWORK_NAME
+# define STATUS_BAD_NETWORK_NAME ((NTSTATUS) 0xC00000CCL)
+#endif
+
+#ifndef STATUS_TOO_MANY_NAMES
+# define STATUS_TOO_MANY_NAMES ((NTSTATUS) 0xC00000CDL)
+#endif
+
+#ifndef STATUS_TOO_MANY_SESSIONS
+# define STATUS_TOO_MANY_SESSIONS ((NTSTATUS) 0xC00000CEL)
+#endif
+
+#ifndef STATUS_SHARING_PAUSED
+# define STATUS_SHARING_PAUSED ((NTSTATUS) 0xC00000CFL)
+#endif
+
+#ifndef STATUS_REQUEST_NOT_ACCEPTED
+# define STATUS_REQUEST_NOT_ACCEPTED ((NTSTATUS) 0xC00000D0L)
+#endif
+
+#ifndef STATUS_REDIRECTOR_PAUSED
+# define STATUS_REDIRECTOR_PAUSED ((NTSTATUS) 0xC00000D1L)
+#endif
+
+#ifndef STATUS_NET_WRITE_FAULT
+# define STATUS_NET_WRITE_FAULT ((NTSTATUS) 0xC00000D2L)
+#endif
+
+#ifndef STATUS_PROFILING_AT_LIMIT
+# define STATUS_PROFILING_AT_LIMIT ((NTSTATUS) 0xC00000D3L)
+#endif
+
+#ifndef STATUS_NOT_SAME_DEVICE
+# define STATUS_NOT_SAME_DEVICE ((NTSTATUS) 0xC00000D4L)
+#endif
+
+#ifndef STATUS_FILE_RENAMED
+# define STATUS_FILE_RENAMED ((NTSTATUS) 0xC00000D5L)
+#endif
+
+#ifndef STATUS_VIRTUAL_CIRCUIT_CLOSED
+# define STATUS_VIRTUAL_CIRCUIT_CLOSED ((NTSTATUS) 0xC00000D6L)
+#endif
+
+#ifndef STATUS_NO_SECURITY_ON_OBJECT
+# define STATUS_NO_SECURITY_ON_OBJECT ((NTSTATUS) 0xC00000D7L)
+#endif
+
+#ifndef STATUS_CANT_WAIT
+# define STATUS_CANT_WAIT ((NTSTATUS) 0xC00000D8L)
+#endif
+
+#ifndef STATUS_PIPE_EMPTY
+# define STATUS_PIPE_EMPTY ((NTSTATUS) 0xC00000D9L)
+#endif
+
+#ifndef STATUS_CANT_ACCESS_DOMAIN_INFO
+# define STATUS_CANT_ACCESS_DOMAIN_INFO ((NTSTATUS) 0xC00000DAL)
+#endif
+
+#ifndef STATUS_CANT_TERMINATE_SELF
+# define STATUS_CANT_TERMINATE_SELF ((NTSTATUS) 0xC00000DBL)
+#endif
+
+#ifndef STATUS_INVALID_SERVER_STATE
+# define STATUS_INVALID_SERVER_STATE ((NTSTATUS) 0xC00000DCL)
+#endif
+
+#ifndef STATUS_INVALID_DOMAIN_STATE
+# define STATUS_INVALID_DOMAIN_STATE ((NTSTATUS) 0xC00000DDL)
+#endif
+
+#ifndef STATUS_INVALID_DOMAIN_ROLE
+# define STATUS_INVALID_DOMAIN_ROLE ((NTSTATUS) 0xC00000DEL)
+#endif
+
+#ifndef STATUS_NO_SUCH_DOMAIN
+# define STATUS_NO_SUCH_DOMAIN ((NTSTATUS) 0xC00000DFL)
+#endif
+
+#ifndef STATUS_DOMAIN_EXISTS
+# define STATUS_DOMAIN_EXISTS ((NTSTATUS) 0xC00000E0L)
+#endif
+
+#ifndef STATUS_DOMAIN_LIMIT_EXCEEDED
+# define STATUS_DOMAIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00000E1L)
+#endif
+
+#ifndef STATUS_OPLOCK_NOT_GRANTED
+# define STATUS_OPLOCK_NOT_GRANTED ((NTSTATUS) 0xC00000E2L)
+#endif
+
+#ifndef STATUS_INVALID_OPLOCK_PROTOCOL
+# define STATUS_INVALID_OPLOCK_PROTOCOL ((NTSTATUS) 0xC00000E3L)
+#endif
+
+#ifndef STATUS_INTERNAL_DB_CORRUPTION
+# define STATUS_INTERNAL_DB_CORRUPTION ((NTSTATUS) 0xC00000E4L)
+#endif
+
+#ifndef STATUS_INTERNAL_ERROR
+# define STATUS_INTERNAL_ERROR ((NTSTATUS) 0xC00000E5L)
+#endif
+
+#ifndef STATUS_GENERIC_NOT_MAPPED
+# define STATUS_GENERIC_NOT_MAPPED ((NTSTATUS) 0xC00000E6L)
+#endif
+
+#ifndef STATUS_BAD_DESCRIPTOR_FORMAT
+# define STATUS_BAD_DESCRIPTOR_FORMAT ((NTSTATUS) 0xC00000E7L)
+#endif
+
+#ifndef STATUS_INVALID_USER_BUFFER
+# define STATUS_INVALID_USER_BUFFER ((NTSTATUS) 0xC00000E8L)
+#endif
+
+#ifndef STATUS_UNEXPECTED_IO_ERROR
+# define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS) 0xC00000E9L)
+#endif
+
+#ifndef STATUS_UNEXPECTED_MM_CREATE_ERR
+# define STATUS_UNEXPECTED_MM_CREATE_ERR ((NTSTATUS) 0xC00000EAL)
+#endif
+
+#ifndef STATUS_UNEXPECTED_MM_MAP_ERROR
+# define STATUS_UNEXPECTED_MM_MAP_ERROR ((NTSTATUS) 0xC00000EBL)
+#endif
+
+#ifndef STATUS_UNEXPECTED_MM_EXTEND_ERR
+# define STATUS_UNEXPECTED_MM_EXTEND_ERR ((NTSTATUS) 0xC00000ECL)
+#endif
+
+#ifndef STATUS_NOT_LOGON_PROCESS
+# define STATUS_NOT_LOGON_PROCESS ((NTSTATUS) 0xC00000EDL)
+#endif
+
+#ifndef STATUS_LOGON_SESSION_EXISTS
+# define STATUS_LOGON_SESSION_EXISTS ((NTSTATUS) 0xC00000EEL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_1
+# define STATUS_INVALID_PARAMETER_1 ((NTSTATUS) 0xC00000EFL)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_2
+# define STATUS_INVALID_PARAMETER_2 ((NTSTATUS) 0xC00000F0L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_3
+# define STATUS_INVALID_PARAMETER_3 ((NTSTATUS) 0xC00000F1L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_4
+# define STATUS_INVALID_PARAMETER_4 ((NTSTATUS) 0xC00000F2L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_5
+# define STATUS_INVALID_PARAMETER_5 ((NTSTATUS) 0xC00000F3L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_6
+# define STATUS_INVALID_PARAMETER_6 ((NTSTATUS) 0xC00000F4L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_7
+# define STATUS_INVALID_PARAMETER_7 ((NTSTATUS) 0xC00000F5L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_8
+# define STATUS_INVALID_PARAMETER_8 ((NTSTATUS) 0xC00000F6L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_9
+# define STATUS_INVALID_PARAMETER_9 ((NTSTATUS) 0xC00000F7L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_10
+# define STATUS_INVALID_PARAMETER_10 ((NTSTATUS) 0xC00000F8L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_11
+# define STATUS_INVALID_PARAMETER_11 ((NTSTATUS) 0xC00000F9L)
+#endif
+
+#ifndef STATUS_INVALID_PARAMETER_12
+# define STATUS_INVALID_PARAMETER_12 ((NTSTATUS) 0xC00000FAL)
+#endif
+
+#ifndef STATUS_REDIRECTOR_NOT_STARTED
+# define STATUS_REDIRECTOR_NOT_STARTED ((NTSTATUS) 0xC00000FBL)
+#endif
+
+#ifndef STATUS_REDIRECTOR_STARTED
+# define STATUS_REDIRECTOR_STARTED ((NTSTATUS) 0xC00000FCL)
+#endif
+
+#ifndef STATUS_STACK_OVERFLOW
+# define STATUS_STACK_OVERFLOW ((NTSTATUS) 0xC00000FDL)
+#endif
+
+#ifndef STATUS_NO_SUCH_PACKAGE
+# define STATUS_NO_SUCH_PACKAGE ((NTSTATUS) 0xC00000FEL)
+#endif
+
+#ifndef STATUS_BAD_FUNCTION_TABLE
+# define STATUS_BAD_FUNCTION_TABLE ((NTSTATUS) 0xC00000FFL)
+#endif
+
+#ifndef STATUS_VARIABLE_NOT_FOUND
+# define STATUS_VARIABLE_NOT_FOUND ((NTSTATUS) 0xC0000100L)
+#endif
+
+#ifndef STATUS_DIRECTORY_NOT_EMPTY
+# define STATUS_DIRECTORY_NOT_EMPTY ((NTSTATUS) 0xC0000101L)
+#endif
+
+#ifndef STATUS_FILE_CORRUPT_ERROR
+# define STATUS_FILE_CORRUPT_ERROR ((NTSTATUS) 0xC0000102L)
+#endif
+
+#ifndef STATUS_NOT_A_DIRECTORY
+# define STATUS_NOT_A_DIRECTORY ((NTSTATUS) 0xC0000103L)
+#endif
+
+#ifndef STATUS_BAD_LOGON_SESSION_STATE
+# define STATUS_BAD_LOGON_SESSION_STATE ((NTSTATUS) 0xC0000104L)
+#endif
+
+#ifndef STATUS_LOGON_SESSION_COLLISION
+# define STATUS_LOGON_SESSION_COLLISION ((NTSTATUS) 0xC0000105L)
+#endif
+
+#ifndef STATUS_NAME_TOO_LONG
+# define STATUS_NAME_TOO_LONG ((NTSTATUS) 0xC0000106L)
+#endif
+
+#ifndef STATUS_FILES_OPEN
+# define STATUS_FILES_OPEN ((NTSTATUS) 0xC0000107L)
+#endif
+
+#ifndef STATUS_CONNECTION_IN_USE
+# define STATUS_CONNECTION_IN_USE ((NTSTATUS) 0xC0000108L)
+#endif
+
+#ifndef STATUS_MESSAGE_NOT_FOUND
+# define STATUS_MESSAGE_NOT_FOUND ((NTSTATUS) 0xC0000109L)
+#endif
+
+#ifndef STATUS_PROCESS_IS_TERMINATING
+# define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS) 0xC000010AL)
+#endif
+
+#ifndef STATUS_INVALID_LOGON_TYPE
+# define STATUS_INVALID_LOGON_TYPE ((NTSTATUS) 0xC000010BL)
+#endif
+
+#ifndef STATUS_NO_GUID_TRANSLATION
+# define STATUS_NO_GUID_TRANSLATION ((NTSTATUS) 0xC000010CL)
+#endif
+
+#ifndef STATUS_CANNOT_IMPERSONATE
+# define STATUS_CANNOT_IMPERSONATE ((NTSTATUS) 0xC000010DL)
+#endif
+
+#ifndef STATUS_IMAGE_ALREADY_LOADED
+# define STATUS_IMAGE_ALREADY_LOADED ((NTSTATUS) 0xC000010EL)
+#endif
+
+#ifndef STATUS_ABIOS_NOT_PRESENT
+# define STATUS_ABIOS_NOT_PRESENT ((NTSTATUS) 0xC000010FL)
+#endif
+
+#ifndef STATUS_ABIOS_LID_NOT_EXIST
+# define STATUS_ABIOS_LID_NOT_EXIST ((NTSTATUS) 0xC0000110L)
+#endif
+
+#ifndef STATUS_ABIOS_LID_ALREADY_OWNED
+# define STATUS_ABIOS_LID_ALREADY_OWNED ((NTSTATUS) 0xC0000111L)
+#endif
+
+#ifndef STATUS_ABIOS_NOT_LID_OWNER
+# define STATUS_ABIOS_NOT_LID_OWNER ((NTSTATUS) 0xC0000112L)
+#endif
+
+#ifndef STATUS_ABIOS_INVALID_COMMAND
+# define STATUS_ABIOS_INVALID_COMMAND ((NTSTATUS) 0xC0000113L)
+#endif
+
+#ifndef STATUS_ABIOS_INVALID_LID
+# define STATUS_ABIOS_INVALID_LID ((NTSTATUS) 0xC0000114L)
+#endif
+
+#ifndef STATUS_ABIOS_SELECTOR_NOT_AVAILABLE
+# define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE ((NTSTATUS) 0xC0000115L)
+#endif
+
+#ifndef STATUS_ABIOS_INVALID_SELECTOR
+# define STATUS_ABIOS_INVALID_SELECTOR ((NTSTATUS) 0xC0000116L)
+#endif
+
+#ifndef STATUS_NO_LDT
+# define STATUS_NO_LDT ((NTSTATUS) 0xC0000117L)
+#endif
+
+#ifndef STATUS_INVALID_LDT_SIZE
+# define STATUS_INVALID_LDT_SIZE ((NTSTATUS) 0xC0000118L)
+#endif
+
+#ifndef STATUS_INVALID_LDT_OFFSET
+# define STATUS_INVALID_LDT_OFFSET ((NTSTATUS) 0xC0000119L)
+#endif
+
+#ifndef STATUS_INVALID_LDT_DESCRIPTOR
+# define STATUS_INVALID_LDT_DESCRIPTOR ((NTSTATUS) 0xC000011AL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_NE_FORMAT
+# define STATUS_INVALID_IMAGE_NE_FORMAT ((NTSTATUS) 0xC000011BL)
+#endif
+
+#ifndef STATUS_RXACT_INVALID_STATE
+# define STATUS_RXACT_INVALID_STATE ((NTSTATUS) 0xC000011CL)
+#endif
+
+#ifndef STATUS_RXACT_COMMIT_FAILURE
+# define STATUS_RXACT_COMMIT_FAILURE ((NTSTATUS) 0xC000011DL)
+#endif
+
+#ifndef STATUS_MAPPED_FILE_SIZE_ZERO
+# define STATUS_MAPPED_FILE_SIZE_ZERO ((NTSTATUS) 0xC000011EL)
+#endif
+
+#ifndef STATUS_TOO_MANY_OPENED_FILES
+# define STATUS_TOO_MANY_OPENED_FILES ((NTSTATUS) 0xC000011FL)
+#endif
+
+#ifndef STATUS_CANCELLED
+# define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L)
+#endif
+
+#ifndef STATUS_CANNOT_DELETE
+# define STATUS_CANNOT_DELETE ((NTSTATUS) 0xC0000121L)
+#endif
+
+#ifndef STATUS_INVALID_COMPUTER_NAME
+# define STATUS_INVALID_COMPUTER_NAME ((NTSTATUS) 0xC0000122L)
+#endif
+
+#ifndef STATUS_FILE_DELETED
+# define STATUS_FILE_DELETED ((NTSTATUS) 0xC0000123L)
+#endif
+
+#ifndef STATUS_SPECIAL_ACCOUNT
+# define STATUS_SPECIAL_ACCOUNT ((NTSTATUS) 0xC0000124L)
+#endif
+
+#ifndef STATUS_SPECIAL_GROUP
+# define STATUS_SPECIAL_GROUP ((NTSTATUS) 0xC0000125L)
+#endif
+
+#ifndef STATUS_SPECIAL_USER
+# define STATUS_SPECIAL_USER ((NTSTATUS) 0xC0000126L)
+#endif
+
+#ifndef STATUS_MEMBERS_PRIMARY_GROUP
+# define STATUS_MEMBERS_PRIMARY_GROUP ((NTSTATUS) 0xC0000127L)
+#endif
+
+#ifndef STATUS_FILE_CLOSED
+# define STATUS_FILE_CLOSED ((NTSTATUS) 0xC0000128L)
+#endif
+
+#ifndef STATUS_TOO_MANY_THREADS
+# define STATUS_TOO_MANY_THREADS ((NTSTATUS) 0xC0000129L)
+#endif
+
+#ifndef STATUS_THREAD_NOT_IN_PROCESS
+# define STATUS_THREAD_NOT_IN_PROCESS ((NTSTATUS) 0xC000012AL)
+#endif
+
+#ifndef STATUS_TOKEN_ALREADY_IN_USE
+# define STATUS_TOKEN_ALREADY_IN_USE ((NTSTATUS) 0xC000012BL)
+#endif
+
+#ifndef STATUS_PAGEFILE_QUOTA_EXCEEDED
+# define STATUS_PAGEFILE_QUOTA_EXCEEDED ((NTSTATUS) 0xC000012CL)
+#endif
+
+#ifndef STATUS_COMMITMENT_LIMIT
+# define STATUS_COMMITMENT_LIMIT ((NTSTATUS) 0xC000012DL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_LE_FORMAT
+# define STATUS_INVALID_IMAGE_LE_FORMAT ((NTSTATUS) 0xC000012EL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_NOT_MZ
+# define STATUS_INVALID_IMAGE_NOT_MZ ((NTSTATUS) 0xC000012FL)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_PROTECT
+# define STATUS_INVALID_IMAGE_PROTECT ((NTSTATUS) 0xC0000130L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_WIN_16
+# define STATUS_INVALID_IMAGE_WIN_16 ((NTSTATUS) 0xC0000131L)
+#endif
+
+#ifndef STATUS_LOGON_SERVER_CONFLICT
+# define STATUS_LOGON_SERVER_CONFLICT ((NTSTATUS) 0xC0000132L)
+#endif
+
+#ifndef STATUS_TIME_DIFFERENCE_AT_DC
+# define STATUS_TIME_DIFFERENCE_AT_DC ((NTSTATUS) 0xC0000133L)
+#endif
+
+#ifndef STATUS_SYNCHRONIZATION_REQUIRED
+# define STATUS_SYNCHRONIZATION_REQUIRED ((NTSTATUS) 0xC0000134L)
+#endif
+
+#ifndef STATUS_DLL_NOT_FOUND
+# define STATUS_DLL_NOT_FOUND ((NTSTATUS) 0xC0000135L)
+#endif
+
+#ifndef STATUS_OPEN_FAILED
+# define STATUS_OPEN_FAILED ((NTSTATUS) 0xC0000136L)
+#endif
+
+#ifndef STATUS_IO_PRIVILEGE_FAILED
+# define STATUS_IO_PRIVILEGE_FAILED ((NTSTATUS) 0xC0000137L)
+#endif
+
+#ifndef STATUS_ORDINAL_NOT_FOUND
+# define STATUS_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000138L)
+#endif
+
+#ifndef STATUS_ENTRYPOINT_NOT_FOUND
+# define STATUS_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000139L)
+#endif
+
+#ifndef STATUS_CONTROL_C_EXIT
+# define STATUS_CONTROL_C_EXIT ((NTSTATUS) 0xC000013AL)
+#endif
+
+#ifndef STATUS_LOCAL_DISCONNECT
+# define STATUS_LOCAL_DISCONNECT ((NTSTATUS) 0xC000013BL)
+#endif
+
+#ifndef STATUS_REMOTE_DISCONNECT
+# define STATUS_REMOTE_DISCONNECT ((NTSTATUS) 0xC000013CL)
+#endif
+
+#ifndef STATUS_REMOTE_RESOURCES
+# define STATUS_REMOTE_RESOURCES ((NTSTATUS) 0xC000013DL)
+#endif
+
+#ifndef STATUS_LINK_FAILED
+# define STATUS_LINK_FAILED ((NTSTATUS) 0xC000013EL)
+#endif
+
+#ifndef STATUS_LINK_TIMEOUT
+# define STATUS_LINK_TIMEOUT ((NTSTATUS) 0xC000013FL)
+#endif
+
+#ifndef STATUS_INVALID_CONNECTION
+# define STATUS_INVALID_CONNECTION ((NTSTATUS) 0xC0000140L)
+#endif
+
+#ifndef STATUS_INVALID_ADDRESS
+# define STATUS_INVALID_ADDRESS ((NTSTATUS) 0xC0000141L)
+#endif
+
+#ifndef STATUS_DLL_INIT_FAILED
+# define STATUS_DLL_INIT_FAILED ((NTSTATUS) 0xC0000142L)
+#endif
+
+#ifndef STATUS_MISSING_SYSTEMFILE
+# define STATUS_MISSING_SYSTEMFILE ((NTSTATUS) 0xC0000143L)
+#endif
+
+#ifndef STATUS_UNHANDLED_EXCEPTION
+# define STATUS_UNHANDLED_EXCEPTION ((NTSTATUS) 0xC0000144L)
+#endif
+
+#ifndef STATUS_APP_INIT_FAILURE
+# define STATUS_APP_INIT_FAILURE ((NTSTATUS) 0xC0000145L)
+#endif
+
+#ifndef STATUS_PAGEFILE_CREATE_FAILED
+# define STATUS_PAGEFILE_CREATE_FAILED ((NTSTATUS) 0xC0000146L)
+#endif
+
+#ifndef STATUS_NO_PAGEFILE
+# define STATUS_NO_PAGEFILE ((NTSTATUS) 0xC0000147L)
+#endif
+
+#ifndef STATUS_INVALID_LEVEL
+# define STATUS_INVALID_LEVEL ((NTSTATUS) 0xC0000148L)
+#endif
+
+#ifndef STATUS_WRONG_PASSWORD_CORE
+# define STATUS_WRONG_PASSWORD_CORE ((NTSTATUS) 0xC0000149L)
+#endif
+
+#ifndef STATUS_ILLEGAL_FLOAT_CONTEXT
+# define STATUS_ILLEGAL_FLOAT_CONTEXT ((NTSTATUS) 0xC000014AL)
+#endif
+
+#ifndef STATUS_PIPE_BROKEN
+# define STATUS_PIPE_BROKEN ((NTSTATUS) 0xC000014BL)
+#endif
+
+#ifndef STATUS_REGISTRY_CORRUPT
+# define STATUS_REGISTRY_CORRUPT ((NTSTATUS) 0xC000014CL)
+#endif
+
+#ifndef STATUS_REGISTRY_IO_FAILED
+# define STATUS_REGISTRY_IO_FAILED ((NTSTATUS) 0xC000014DL)
+#endif
+
+#ifndef STATUS_NO_EVENT_PAIR
+# define STATUS_NO_EVENT_PAIR ((NTSTATUS) 0xC000014EL)
+#endif
+
+#ifndef STATUS_UNRECOGNIZED_VOLUME
+# define STATUS_UNRECOGNIZED_VOLUME ((NTSTATUS) 0xC000014FL)
+#endif
+
+#ifndef STATUS_SERIAL_NO_DEVICE_INITED
+# define STATUS_SERIAL_NO_DEVICE_INITED ((NTSTATUS) 0xC0000150L)
+#endif
+
+#ifndef STATUS_NO_SUCH_ALIAS
+# define STATUS_NO_SUCH_ALIAS ((NTSTATUS) 0xC0000151L)
+#endif
+
+#ifndef STATUS_MEMBER_NOT_IN_ALIAS
+# define STATUS_MEMBER_NOT_IN_ALIAS ((NTSTATUS) 0xC0000152L)
+#endif
+
+#ifndef STATUS_MEMBER_IN_ALIAS
+# define STATUS_MEMBER_IN_ALIAS ((NTSTATUS) 0xC0000153L)
+#endif
+
+#ifndef STATUS_ALIAS_EXISTS
+# define STATUS_ALIAS_EXISTS ((NTSTATUS) 0xC0000154L)
+#endif
+
+#ifndef STATUS_LOGON_NOT_GRANTED
+# define STATUS_LOGON_NOT_GRANTED ((NTSTATUS) 0xC0000155L)
+#endif
+
+#ifndef STATUS_TOO_MANY_SECRETS
+# define STATUS_TOO_MANY_SECRETS ((NTSTATUS) 0xC0000156L)
+#endif
+
+#ifndef STATUS_SECRET_TOO_LONG
+# define STATUS_SECRET_TOO_LONG ((NTSTATUS) 0xC0000157L)
+#endif
+
+#ifndef STATUS_INTERNAL_DB_ERROR
+# define STATUS_INTERNAL_DB_ERROR ((NTSTATUS) 0xC0000158L)
+#endif
+
+#ifndef STATUS_FULLSCREEN_MODE
+# define STATUS_FULLSCREEN_MODE ((NTSTATUS) 0xC0000159L)
+#endif
+
+#ifndef STATUS_TOO_MANY_CONTEXT_IDS
+# define STATUS_TOO_MANY_CONTEXT_IDS ((NTSTATUS) 0xC000015AL)
+#endif
+
+#ifndef STATUS_LOGON_TYPE_NOT_GRANTED
+# define STATUS_LOGON_TYPE_NOT_GRANTED ((NTSTATUS) 0xC000015BL)
+#endif
+
+#ifndef STATUS_NOT_REGISTRY_FILE
+# define STATUS_NOT_REGISTRY_FILE ((NTSTATUS) 0xC000015CL)
+#endif
+
+#ifndef STATUS_NT_CROSS_ENCRYPTION_REQUIRED
+# define STATUS_NT_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000015DL)
+#endif
+
+#ifndef STATUS_DOMAIN_CTRLR_CONFIG_ERROR
+# define STATUS_DOMAIN_CTRLR_CONFIG_ERROR ((NTSTATUS) 0xC000015EL)
+#endif
+
+#ifndef STATUS_FT_MISSING_MEMBER
+# define STATUS_FT_MISSING_MEMBER ((NTSTATUS) 0xC000015FL)
+#endif
+
+#ifndef STATUS_ILL_FORMED_SERVICE_ENTRY
+# define STATUS_ILL_FORMED_SERVICE_ENTRY ((NTSTATUS) 0xC0000160L)
+#endif
+
+#ifndef STATUS_ILLEGAL_CHARACTER
+# define STATUS_ILLEGAL_CHARACTER ((NTSTATUS) 0xC0000161L)
+#endif
+
+#ifndef STATUS_UNMAPPABLE_CHARACTER
+# define STATUS_UNMAPPABLE_CHARACTER ((NTSTATUS) 0xC0000162L)
+#endif
+
+#ifndef STATUS_UNDEFINED_CHARACTER
+# define STATUS_UNDEFINED_CHARACTER ((NTSTATUS) 0xC0000163L)
+#endif
+
+#ifndef STATUS_FLOPPY_VOLUME
+# define STATUS_FLOPPY_VOLUME ((NTSTATUS) 0xC0000164L)
+#endif
+
+#ifndef STATUS_FLOPPY_ID_MARK_NOT_FOUND
+# define STATUS_FLOPPY_ID_MARK_NOT_FOUND ((NTSTATUS) 0xC0000165L)
+#endif
+
+#ifndef STATUS_FLOPPY_WRONG_CYLINDER
+# define STATUS_FLOPPY_WRONG_CYLINDER ((NTSTATUS) 0xC0000166L)
+#endif
+
+#ifndef STATUS_FLOPPY_UNKNOWN_ERROR
+# define STATUS_FLOPPY_UNKNOWN_ERROR ((NTSTATUS) 0xC0000167L)
+#endif
+
+#ifndef STATUS_FLOPPY_BAD_REGISTERS
+# define STATUS_FLOPPY_BAD_REGISTERS ((NTSTATUS) 0xC0000168L)
+#endif
+
+#ifndef STATUS_DISK_RECALIBRATE_FAILED
+# define STATUS_DISK_RECALIBRATE_FAILED ((NTSTATUS) 0xC0000169L)
+#endif
+
+#ifndef STATUS_DISK_OPERATION_FAILED
+# define STATUS_DISK_OPERATION_FAILED ((NTSTATUS) 0xC000016AL)
+#endif
+
+#ifndef STATUS_DISK_RESET_FAILED
+# define STATUS_DISK_RESET_FAILED ((NTSTATUS) 0xC000016BL)
+#endif
+
+#ifndef STATUS_SHARED_IRQ_BUSY
+# define STATUS_SHARED_IRQ_BUSY ((NTSTATUS) 0xC000016CL)
+#endif
+
+#ifndef STATUS_FT_ORPHANING
+# define STATUS_FT_ORPHANING ((NTSTATUS) 0xC000016DL)
+#endif
+
+#ifndef STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT
+# define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT ((NTSTATUS) 0xC000016EL)
+#endif
+
+#ifndef STATUS_PARTITION_FAILURE
+# define STATUS_PARTITION_FAILURE ((NTSTATUS) 0xC0000172L)
+#endif
+
+#ifndef STATUS_INVALID_BLOCK_LENGTH
+# define STATUS_INVALID_BLOCK_LENGTH ((NTSTATUS) 0xC0000173L)
+#endif
+
+#ifndef STATUS_DEVICE_NOT_PARTITIONED
+# define STATUS_DEVICE_NOT_PARTITIONED ((NTSTATUS) 0xC0000174L)
+#endif
+
+#ifndef STATUS_UNABLE_TO_LOCK_MEDIA
+# define STATUS_UNABLE_TO_LOCK_MEDIA ((NTSTATUS) 0xC0000175L)
+#endif
+
+#ifndef STATUS_UNABLE_TO_UNLOAD_MEDIA
+# define STATUS_UNABLE_TO_UNLOAD_MEDIA ((NTSTATUS) 0xC0000176L)
+#endif
+
+#ifndef STATUS_EOM_OVERFLOW
+# define STATUS_EOM_OVERFLOW ((NTSTATUS) 0xC0000177L)
+#endif
+
+#ifndef STATUS_NO_MEDIA
+# define STATUS_NO_MEDIA ((NTSTATUS) 0xC0000178L)
+#endif
+
+#ifndef STATUS_NO_SUCH_MEMBER
+# define STATUS_NO_SUCH_MEMBER ((NTSTATUS) 0xC000017AL)
+#endif
+
+#ifndef STATUS_INVALID_MEMBER
+# define STATUS_INVALID_MEMBER ((NTSTATUS) 0xC000017BL)
+#endif
+
+#ifndef STATUS_KEY_DELETED
+# define STATUS_KEY_DELETED ((NTSTATUS) 0xC000017CL)
+#endif
+
+#ifndef STATUS_NO_LOG_SPACE
+# define STATUS_NO_LOG_SPACE ((NTSTATUS) 0xC000017DL)
+#endif
+
+#ifndef STATUS_TOO_MANY_SIDS
+# define STATUS_TOO_MANY_SIDS ((NTSTATUS) 0xC000017EL)
+#endif
+
+#ifndef STATUS_LM_CROSS_ENCRYPTION_REQUIRED
+# define STATUS_LM_CROSS_ENCRYPTION_REQUIRED ((NTSTATUS) 0xC000017FL)
+#endif
+
+#ifndef STATUS_KEY_HAS_CHILDREN
+# define STATUS_KEY_HAS_CHILDREN ((NTSTATUS) 0xC0000180L)
+#endif
+
+#ifndef STATUS_CHILD_MUST_BE_VOLATILE
+# define STATUS_CHILD_MUST_BE_VOLATILE ((NTSTATUS) 0xC0000181L)
+#endif
+
+#ifndef STATUS_DEVICE_CONFIGURATION_ERROR
+# define STATUS_DEVICE_CONFIGURATION_ERROR ((NTSTATUS) 0xC0000182L)
+#endif
+
+#ifndef STATUS_DRIVER_INTERNAL_ERROR
+# define STATUS_DRIVER_INTERNAL_ERROR ((NTSTATUS) 0xC0000183L)
+#endif
+
+#ifndef STATUS_INVALID_DEVICE_STATE
+# define STATUS_INVALID_DEVICE_STATE ((NTSTATUS) 0xC0000184L)
+#endif
+
+#ifndef STATUS_IO_DEVICE_ERROR
+# define STATUS_IO_DEVICE_ERROR ((NTSTATUS) 0xC0000185L)
+#endif
+
+#ifndef STATUS_DEVICE_PROTOCOL_ERROR
+# define STATUS_DEVICE_PROTOCOL_ERROR ((NTSTATUS) 0xC0000186L)
+#endif
+
+#ifndef STATUS_BACKUP_CONTROLLER
+# define STATUS_BACKUP_CONTROLLER ((NTSTATUS) 0xC0000187L)
+#endif
+
+#ifndef STATUS_LOG_FILE_FULL
+# define STATUS_LOG_FILE_FULL ((NTSTATUS) 0xC0000188L)
+#endif
+
+#ifndef STATUS_TOO_LATE
+# define STATUS_TOO_LATE ((NTSTATUS) 0xC0000189L)
+#endif
+
+#ifndef STATUS_NO_TRUST_LSA_SECRET
+# define STATUS_NO_TRUST_LSA_SECRET ((NTSTATUS) 0xC000018AL)
+#endif
+
+#ifndef STATUS_NO_TRUST_SAM_ACCOUNT
+# define STATUS_NO_TRUST_SAM_ACCOUNT ((NTSTATUS) 0xC000018BL)
+#endif
+
+#ifndef STATUS_TRUSTED_DOMAIN_FAILURE
+# define STATUS_TRUSTED_DOMAIN_FAILURE ((NTSTATUS) 0xC000018CL)
+#endif
+
+#ifndef STATUS_TRUSTED_RELATIONSHIP_FAILURE
+# define STATUS_TRUSTED_RELATIONSHIP_FAILURE ((NTSTATUS) 0xC000018DL)
+#endif
+
+#ifndef STATUS_EVENTLOG_FILE_CORRUPT
+# define STATUS_EVENTLOG_FILE_CORRUPT ((NTSTATUS) 0xC000018EL)
+#endif
+
+#ifndef STATUS_EVENTLOG_CANT_START
+# define STATUS_EVENTLOG_CANT_START ((NTSTATUS) 0xC000018FL)
+#endif
+
+#ifndef STATUS_TRUST_FAILURE
+# define STATUS_TRUST_FAILURE ((NTSTATUS) 0xC0000190L)
+#endif
+
+#ifndef STATUS_MUTANT_LIMIT_EXCEEDED
+# define STATUS_MUTANT_LIMIT_EXCEEDED ((NTSTATUS) 0xC0000191L)
+#endif
+
+#ifndef STATUS_NETLOGON_NOT_STARTED
+# define STATUS_NETLOGON_NOT_STARTED ((NTSTATUS) 0xC0000192L)
+#endif
+
+#ifndef STATUS_ACCOUNT_EXPIRED
+# define STATUS_ACCOUNT_EXPIRED ((NTSTATUS) 0xC0000193L)
+#endif
+
+#ifndef STATUS_POSSIBLE_DEADLOCK
+# define STATUS_POSSIBLE_DEADLOCK ((NTSTATUS) 0xC0000194L)
+#endif
+
+#ifndef STATUS_NETWORK_CREDENTIAL_CONFLICT
+# define STATUS_NETWORK_CREDENTIAL_CONFLICT ((NTSTATUS) 0xC0000195L)
+#endif
+
+#ifndef STATUS_REMOTE_SESSION_LIMIT
+# define STATUS_REMOTE_SESSION_LIMIT ((NTSTATUS) 0xC0000196L)
+#endif
+
+#ifndef STATUS_EVENTLOG_FILE_CHANGED
+# define STATUS_EVENTLOG_FILE_CHANGED ((NTSTATUS) 0xC0000197L)
+#endif
+
+#ifndef STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT
+# define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT ((NTSTATUS) 0xC0000198L)
+#endif
+
+#ifndef STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
+# define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT ((NTSTATUS) 0xC0000199L)
+#endif
+
+#ifndef STATUS_NOLOGON_SERVER_TRUST_ACCOUNT
+# define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT ((NTSTATUS) 0xC000019AL)
+#endif
+
+#ifndef STATUS_DOMAIN_TRUST_INCONSISTENT
+# define STATUS_DOMAIN_TRUST_INCONSISTENT ((NTSTATUS) 0xC000019BL)
+#endif
+
+#ifndef STATUS_FS_DRIVER_REQUIRED
+# define STATUS_FS_DRIVER_REQUIRED ((NTSTATUS) 0xC000019CL)
+#endif
+
+#ifndef STATUS_IMAGE_ALREADY_LOADED_AS_DLL
+# define STATUS_IMAGE_ALREADY_LOADED_AS_DLL ((NTSTATUS) 0xC000019DL)
+#endif
+
+#ifndef STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING
+# define STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING ((NTSTATUS) 0xC000019EL)
+#endif
+
+#ifndef STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME
+# define STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME ((NTSTATUS) 0xC000019FL)
+#endif
+
+#ifndef STATUS_SECURITY_STREAM_IS_INCONSISTENT
+# define STATUS_SECURITY_STREAM_IS_INCONSISTENT ((NTSTATUS) 0xC00001A0L)
+#endif
+
+#ifndef STATUS_INVALID_LOCK_RANGE
+# define STATUS_INVALID_LOCK_RANGE ((NTSTATUS) 0xC00001A1L)
+#endif
+
+#ifndef STATUS_INVALID_ACE_CONDITION
+# define STATUS_INVALID_ACE_CONDITION ((NTSTATUS) 0xC00001A2L)
+#endif
+
+#ifndef STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT
+# define STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT ((NTSTATUS) 0xC00001A3L)
+#endif
+
+#ifndef STATUS_NOTIFICATION_GUID_ALREADY_DEFINED
+# define STATUS_NOTIFICATION_GUID_ALREADY_DEFINED ((NTSTATUS) 0xC00001A4L)
+#endif
+
+#ifndef STATUS_NETWORK_OPEN_RESTRICTION
+# define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS) 0xC0000201L)
+#endif
+
+#ifndef STATUS_NO_USER_SESSION_KEY
+# define STATUS_NO_USER_SESSION_KEY ((NTSTATUS) 0xC0000202L)
+#endif
+
+#ifndef STATUS_USER_SESSION_DELETED
+# define STATUS_USER_SESSION_DELETED ((NTSTATUS) 0xC0000203L)
+#endif
+
+#ifndef STATUS_RESOURCE_LANG_NOT_FOUND
+# define STATUS_RESOURCE_LANG_NOT_FOUND ((NTSTATUS) 0xC0000204L)
+#endif
+
+#ifndef STATUS_INSUFF_SERVER_RESOURCES
+# define STATUS_INSUFF_SERVER_RESOURCES ((NTSTATUS) 0xC0000205L)
+#endif
+
+#ifndef STATUS_INVALID_BUFFER_SIZE
+# define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS) 0xC0000206L)
+#endif
+
+#ifndef STATUS_INVALID_ADDRESS_COMPONENT
+# define STATUS_INVALID_ADDRESS_COMPONENT ((NTSTATUS) 0xC0000207L)
+#endif
+
+#ifndef STATUS_INVALID_ADDRESS_WILDCARD
+# define STATUS_INVALID_ADDRESS_WILDCARD ((NTSTATUS) 0xC0000208L)
+#endif
+
+#ifndef STATUS_TOO_MANY_ADDRESSES
+# define STATUS_TOO_MANY_ADDRESSES ((NTSTATUS) 0xC0000209L)
+#endif
+
+#ifndef STATUS_ADDRESS_ALREADY_EXISTS
+# define STATUS_ADDRESS_ALREADY_EXISTS ((NTSTATUS) 0xC000020AL)
+#endif
+
+#ifndef STATUS_ADDRESS_CLOSED
+# define STATUS_ADDRESS_CLOSED ((NTSTATUS) 0xC000020BL)
+#endif
+
+#ifndef STATUS_CONNECTION_DISCONNECTED
+# define STATUS_CONNECTION_DISCONNECTED ((NTSTATUS) 0xC000020CL)
+#endif
+
+#ifndef STATUS_CONNECTION_RESET
+# define STATUS_CONNECTION_RESET ((NTSTATUS) 0xC000020DL)
+#endif
+
+#ifndef STATUS_TOO_MANY_NODES
+# define STATUS_TOO_MANY_NODES ((NTSTATUS) 0xC000020EL)
+#endif
+
+#ifndef STATUS_TRANSACTION_ABORTED
+# define STATUS_TRANSACTION_ABORTED ((NTSTATUS) 0xC000020FL)
+#endif
+
+#ifndef STATUS_TRANSACTION_TIMED_OUT
+# define STATUS_TRANSACTION_TIMED_OUT ((NTSTATUS) 0xC0000210L)
+#endif
+
+#ifndef STATUS_TRANSACTION_NO_RELEASE
+# define STATUS_TRANSACTION_NO_RELEASE ((NTSTATUS) 0xC0000211L)
+#endif
+
+#ifndef STATUS_TRANSACTION_NO_MATCH
+# define STATUS_TRANSACTION_NO_MATCH ((NTSTATUS) 0xC0000212L)
+#endif
+
+#ifndef STATUS_TRANSACTION_RESPONDED
+# define STATUS_TRANSACTION_RESPONDED ((NTSTATUS) 0xC0000213L)
+#endif
+
+#ifndef STATUS_TRANSACTION_INVALID_ID
+# define STATUS_TRANSACTION_INVALID_ID ((NTSTATUS) 0xC0000214L)
+#endif
+
+#ifndef STATUS_TRANSACTION_INVALID_TYPE
+# define STATUS_TRANSACTION_INVALID_TYPE ((NTSTATUS) 0xC0000215L)
+#endif
+
+#ifndef STATUS_NOT_SERVER_SESSION
+# define STATUS_NOT_SERVER_SESSION ((NTSTATUS) 0xC0000216L)
+#endif
+
+#ifndef STATUS_NOT_CLIENT_SESSION
+# define STATUS_NOT_CLIENT_SESSION ((NTSTATUS) 0xC0000217L)
+#endif
+
+#ifndef STATUS_CANNOT_LOAD_REGISTRY_FILE
+# define STATUS_CANNOT_LOAD_REGISTRY_FILE ((NTSTATUS) 0xC0000218L)
+#endif
+
+#ifndef STATUS_DEBUG_ATTACH_FAILED
+# define STATUS_DEBUG_ATTACH_FAILED ((NTSTATUS) 0xC0000219L)
+#endif
+
+#ifndef STATUS_SYSTEM_PROCESS_TERMINATED
+# define STATUS_SYSTEM_PROCESS_TERMINATED ((NTSTATUS) 0xC000021AL)
+#endif
+
+#ifndef STATUS_DATA_NOT_ACCEPTED
+# define STATUS_DATA_NOT_ACCEPTED ((NTSTATUS) 0xC000021BL)
+#endif
+
+#ifndef STATUS_NO_BROWSER_SERVERS_FOUND
+# define STATUS_NO_BROWSER_SERVERS_FOUND ((NTSTATUS) 0xC000021CL)
+#endif
+
+#ifndef STATUS_VDM_HARD_ERROR
+# define STATUS_VDM_HARD_ERROR ((NTSTATUS) 0xC000021DL)
+#endif
+
+#ifndef STATUS_DRIVER_CANCEL_TIMEOUT
+# define STATUS_DRIVER_CANCEL_TIMEOUT ((NTSTATUS) 0xC000021EL)
+#endif
+
+#ifndef STATUS_REPLY_MESSAGE_MISMATCH
+# define STATUS_REPLY_MESSAGE_MISMATCH ((NTSTATUS) 0xC000021FL)
+#endif
+
+#ifndef STATUS_MAPPED_ALIGNMENT
+# define STATUS_MAPPED_ALIGNMENT ((NTSTATUS) 0xC0000220L)
+#endif
+
+#ifndef STATUS_IMAGE_CHECKSUM_MISMATCH
+# define STATUS_IMAGE_CHECKSUM_MISMATCH ((NTSTATUS) 0xC0000221L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA
+# define STATUS_LOST_WRITEBEHIND_DATA ((NTSTATUS) 0xC0000222L)
+#endif
+
+#ifndef STATUS_CLIENT_SERVER_PARAMETERS_INVALID
+# define STATUS_CLIENT_SERVER_PARAMETERS_INVALID ((NTSTATUS) 0xC0000223L)
+#endif
+
+#ifndef STATUS_PASSWORD_MUST_CHANGE
+# define STATUS_PASSWORD_MUST_CHANGE ((NTSTATUS) 0xC0000224L)
+#endif
+
+#ifndef STATUS_NOT_FOUND
+# define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L)
+#endif
+
+#ifndef STATUS_NOT_TINY_STREAM
+# define STATUS_NOT_TINY_STREAM ((NTSTATUS) 0xC0000226L)
+#endif
+
+#ifndef STATUS_RECOVERY_FAILURE
+# define STATUS_RECOVERY_FAILURE ((NTSTATUS) 0xC0000227L)
+#endif
+
+#ifndef STATUS_STACK_OVERFLOW_READ
+# define STATUS_STACK_OVERFLOW_READ ((NTSTATUS) 0xC0000228L)
+#endif
+
+#ifndef STATUS_FAIL_CHECK
+# define STATUS_FAIL_CHECK ((NTSTATUS) 0xC0000229L)
+#endif
+
+#ifndef STATUS_DUPLICATE_OBJECTID
+# define STATUS_DUPLICATE_OBJECTID ((NTSTATUS) 0xC000022AL)
+#endif
+
+#ifndef STATUS_OBJECTID_EXISTS
+# define STATUS_OBJECTID_EXISTS ((NTSTATUS) 0xC000022BL)
+#endif
+
+#ifndef STATUS_CONVERT_TO_LARGE
+# define STATUS_CONVERT_TO_LARGE ((NTSTATUS) 0xC000022CL)
+#endif
+
+#ifndef STATUS_RETRY
+# define STATUS_RETRY ((NTSTATUS) 0xC000022DL)
+#endif
+
+#ifndef STATUS_FOUND_OUT_OF_SCOPE
+# define STATUS_FOUND_OUT_OF_SCOPE ((NTSTATUS) 0xC000022EL)
+#endif
+
+#ifndef STATUS_ALLOCATE_BUCKET
+# define STATUS_ALLOCATE_BUCKET ((NTSTATUS) 0xC000022FL)
+#endif
+
+#ifndef STATUS_PROPSET_NOT_FOUND
+# define STATUS_PROPSET_NOT_FOUND ((NTSTATUS) 0xC0000230L)
+#endif
+
+#ifndef STATUS_MARSHALL_OVERFLOW
+# define STATUS_MARSHALL_OVERFLOW ((NTSTATUS) 0xC0000231L)
+#endif
+
+#ifndef STATUS_INVALID_VARIANT
+# define STATUS_INVALID_VARIANT ((NTSTATUS) 0xC0000232L)
+#endif
+
+#ifndef STATUS_DOMAIN_CONTROLLER_NOT_FOUND
+# define STATUS_DOMAIN_CONTROLLER_NOT_FOUND ((NTSTATUS) 0xC0000233L)
+#endif
+
+#ifndef STATUS_ACCOUNT_LOCKED_OUT
+# define STATUS_ACCOUNT_LOCKED_OUT ((NTSTATUS) 0xC0000234L)
+#endif
+
+#ifndef STATUS_HANDLE_NOT_CLOSABLE
+# define STATUS_HANDLE_NOT_CLOSABLE ((NTSTATUS) 0xC0000235L)
+#endif
+
+#ifndef STATUS_CONNECTION_REFUSED
+# define STATUS_CONNECTION_REFUSED ((NTSTATUS) 0xC0000236L)
+#endif
+
+#ifndef STATUS_GRACEFUL_DISCONNECT
+# define STATUS_GRACEFUL_DISCONNECT ((NTSTATUS) 0xC0000237L)
+#endif
+
+#ifndef STATUS_ADDRESS_ALREADY_ASSOCIATED
+# define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS) 0xC0000238L)
+#endif
+
+#ifndef STATUS_ADDRESS_NOT_ASSOCIATED
+# define STATUS_ADDRESS_NOT_ASSOCIATED ((NTSTATUS) 0xC0000239L)
+#endif
+
+#ifndef STATUS_CONNECTION_INVALID
+# define STATUS_CONNECTION_INVALID ((NTSTATUS) 0xC000023AL)
+#endif
+
+#ifndef STATUS_CONNECTION_ACTIVE
+# define STATUS_CONNECTION_ACTIVE ((NTSTATUS) 0xC000023BL)
+#endif
+
+#ifndef STATUS_NETWORK_UNREACHABLE
+# define STATUS_NETWORK_UNREACHABLE ((NTSTATUS) 0xC000023CL)
+#endif
+
+#ifndef STATUS_HOST_UNREACHABLE
+# define STATUS_HOST_UNREACHABLE ((NTSTATUS) 0xC000023DL)
+#endif
+
+#ifndef STATUS_PROTOCOL_UNREACHABLE
+# define STATUS_PROTOCOL_UNREACHABLE ((NTSTATUS) 0xC000023EL)
+#endif
+
+#ifndef STATUS_PORT_UNREACHABLE
+# define STATUS_PORT_UNREACHABLE ((NTSTATUS) 0xC000023FL)
+#endif
+
+#ifndef STATUS_REQUEST_ABORTED
+# define STATUS_REQUEST_ABORTED ((NTSTATUS) 0xC0000240L)
+#endif
+
+#ifndef STATUS_CONNECTION_ABORTED
+# define STATUS_CONNECTION_ABORTED ((NTSTATUS) 0xC0000241L)
+#endif
+
+#ifndef STATUS_BAD_COMPRESSION_BUFFER
+# define STATUS_BAD_COMPRESSION_BUFFER ((NTSTATUS) 0xC0000242L)
+#endif
+
+#ifndef STATUS_USER_MAPPED_FILE
+# define STATUS_USER_MAPPED_FILE ((NTSTATUS) 0xC0000243L)
+#endif
+
+#ifndef STATUS_AUDIT_FAILED
+# define STATUS_AUDIT_FAILED ((NTSTATUS) 0xC0000244L)
+#endif
+
+#ifndef STATUS_TIMER_RESOLUTION_NOT_SET
+# define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS) 0xC0000245L)
+#endif
+
+#ifndef STATUS_CONNECTION_COUNT_LIMIT
+# define STATUS_CONNECTION_COUNT_LIMIT ((NTSTATUS) 0xC0000246L)
+#endif
+
+#ifndef STATUS_LOGIN_TIME_RESTRICTION
+# define STATUS_LOGIN_TIME_RESTRICTION ((NTSTATUS) 0xC0000247L)
+#endif
+
+#ifndef STATUS_LOGIN_WKSTA_RESTRICTION
+# define STATUS_LOGIN_WKSTA_RESTRICTION ((NTSTATUS) 0xC0000248L)
+#endif
+
+#ifndef STATUS_IMAGE_MP_UP_MISMATCH
+# define STATUS_IMAGE_MP_UP_MISMATCH ((NTSTATUS) 0xC0000249L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_LOGON_INFO
+# define STATUS_INSUFFICIENT_LOGON_INFO ((NTSTATUS) 0xC0000250L)
+#endif
+
+#ifndef STATUS_BAD_DLL_ENTRYPOINT
+# define STATUS_BAD_DLL_ENTRYPOINT ((NTSTATUS) 0xC0000251L)
+#endif
+
+#ifndef STATUS_BAD_SERVICE_ENTRYPOINT
+# define STATUS_BAD_SERVICE_ENTRYPOINT ((NTSTATUS) 0xC0000252L)
+#endif
+
+#ifndef STATUS_LPC_REPLY_LOST
+# define STATUS_LPC_REPLY_LOST ((NTSTATUS) 0xC0000253L)
+#endif
+
+#ifndef STATUS_IP_ADDRESS_CONFLICT1
+# define STATUS_IP_ADDRESS_CONFLICT1 ((NTSTATUS) 0xC0000254L)
+#endif
+
+#ifndef STATUS_IP_ADDRESS_CONFLICT2
+# define STATUS_IP_ADDRESS_CONFLICT2 ((NTSTATUS) 0xC0000255L)
+#endif
+
+#ifndef STATUS_REGISTRY_QUOTA_LIMIT
+# define STATUS_REGISTRY_QUOTA_LIMIT ((NTSTATUS) 0xC0000256L)
+#endif
+
+#ifndef STATUS_PATH_NOT_COVERED
+# define STATUS_PATH_NOT_COVERED ((NTSTATUS) 0xC0000257L)
+#endif
+
+#ifndef STATUS_NO_CALLBACK_ACTIVE
+# define STATUS_NO_CALLBACK_ACTIVE ((NTSTATUS) 0xC0000258L)
+#endif
+
+#ifndef STATUS_LICENSE_QUOTA_EXCEEDED
+# define STATUS_LICENSE_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000259L)
+#endif
+
+#ifndef STATUS_PWD_TOO_SHORT
+# define STATUS_PWD_TOO_SHORT ((NTSTATUS) 0xC000025AL)
+#endif
+
+#ifndef STATUS_PWD_TOO_RECENT
+# define STATUS_PWD_TOO_RECENT ((NTSTATUS) 0xC000025BL)
+#endif
+
+#ifndef STATUS_PWD_HISTORY_CONFLICT
+# define STATUS_PWD_HISTORY_CONFLICT ((NTSTATUS) 0xC000025CL)
+#endif
+
+#ifndef STATUS_PLUGPLAY_NO_DEVICE
+# define STATUS_PLUGPLAY_NO_DEVICE ((NTSTATUS) 0xC000025EL)
+#endif
+
+#ifndef STATUS_UNSUPPORTED_COMPRESSION
+# define STATUS_UNSUPPORTED_COMPRESSION ((NTSTATUS) 0xC000025FL)
+#endif
+
+#ifndef STATUS_INVALID_HW_PROFILE
+# define STATUS_INVALID_HW_PROFILE ((NTSTATUS) 0xC0000260L)
+#endif
+
+#ifndef STATUS_INVALID_PLUGPLAY_DEVICE_PATH
+# define STATUS_INVALID_PLUGPLAY_DEVICE_PATH ((NTSTATUS) 0xC0000261L)
+#endif
+
+#ifndef STATUS_DRIVER_ORDINAL_NOT_FOUND
+# define STATUS_DRIVER_ORDINAL_NOT_FOUND ((NTSTATUS) 0xC0000262L)
+#endif
+
+#ifndef STATUS_DRIVER_ENTRYPOINT_NOT_FOUND
+# define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ((NTSTATUS) 0xC0000263L)
+#endif
+
+#ifndef STATUS_RESOURCE_NOT_OWNED
+# define STATUS_RESOURCE_NOT_OWNED ((NTSTATUS) 0xC0000264L)
+#endif
+
+#ifndef STATUS_TOO_MANY_LINKS
+# define STATUS_TOO_MANY_LINKS ((NTSTATUS) 0xC0000265L)
+#endif
+
+#ifndef STATUS_QUOTA_LIST_INCONSISTENT
+# define STATUS_QUOTA_LIST_INCONSISTENT ((NTSTATUS) 0xC0000266L)
+#endif
+
+#ifndef STATUS_FILE_IS_OFFLINE
+# define STATUS_FILE_IS_OFFLINE ((NTSTATUS) 0xC0000267L)
+#endif
+
+#ifndef STATUS_EVALUATION_EXPIRATION
+# define STATUS_EVALUATION_EXPIRATION ((NTSTATUS) 0xC0000268L)
+#endif
+
+#ifndef STATUS_ILLEGAL_DLL_RELOCATION
+# define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xC0000269L)
+#endif
+
+#ifndef STATUS_LICENSE_VIOLATION
+# define STATUS_LICENSE_VIOLATION ((NTSTATUS) 0xC000026AL)
+#endif
+
+#ifndef STATUS_DLL_INIT_FAILED_LOGOFF
+# define STATUS_DLL_INIT_FAILED_LOGOFF ((NTSTATUS) 0xC000026BL)
+#endif
+
+#ifndef STATUS_DRIVER_UNABLE_TO_LOAD
+# define STATUS_DRIVER_UNABLE_TO_LOAD ((NTSTATUS) 0xC000026CL)
+#endif
+
+#ifndef STATUS_DFS_UNAVAILABLE
+# define STATUS_DFS_UNAVAILABLE ((NTSTATUS) 0xC000026DL)
+#endif
+
+#ifndef STATUS_VOLUME_DISMOUNTED
+# define STATUS_VOLUME_DISMOUNTED ((NTSTATUS) 0xC000026EL)
+#endif
+
+#ifndef STATUS_WX86_INTERNAL_ERROR
+# define STATUS_WX86_INTERNAL_ERROR ((NTSTATUS) 0xC000026FL)
+#endif
+
+#ifndef STATUS_WX86_FLOAT_STACK_CHECK
+# define STATUS_WX86_FLOAT_STACK_CHECK ((NTSTATUS) 0xC0000270L)
+#endif
+
+#ifndef STATUS_VALIDATE_CONTINUE
+# define STATUS_VALIDATE_CONTINUE ((NTSTATUS) 0xC0000271L)
+#endif
+
+#ifndef STATUS_NO_MATCH
+# define STATUS_NO_MATCH ((NTSTATUS) 0xC0000272L)
+#endif
+
+#ifndef STATUS_NO_MORE_MATCHES
+# define STATUS_NO_MORE_MATCHES ((NTSTATUS) 0xC0000273L)
+#endif
+
+#ifndef STATUS_NOT_A_REPARSE_POINT
+# define STATUS_NOT_A_REPARSE_POINT ((NTSTATUS) 0xC0000275L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_TAG_INVALID
+# define STATUS_IO_REPARSE_TAG_INVALID ((NTSTATUS) 0xC0000276L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_TAG_MISMATCH
+# define STATUS_IO_REPARSE_TAG_MISMATCH ((NTSTATUS) 0xC0000277L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_DATA_INVALID
+# define STATUS_IO_REPARSE_DATA_INVALID ((NTSTATUS) 0xC0000278L)
+#endif
+
+#ifndef STATUS_IO_REPARSE_TAG_NOT_HANDLED
+# define STATUS_IO_REPARSE_TAG_NOT_HANDLED ((NTSTATUS) 0xC0000279L)
+#endif
+
+#ifndef STATUS_REPARSE_POINT_NOT_RESOLVED
+# define STATUS_REPARSE_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000280L)
+#endif
+
+#ifndef STATUS_DIRECTORY_IS_A_REPARSE_POINT
+# define STATUS_DIRECTORY_IS_A_REPARSE_POINT ((NTSTATUS) 0xC0000281L)
+#endif
+
+#ifndef STATUS_RANGE_LIST_CONFLICT
+# define STATUS_RANGE_LIST_CONFLICT ((NTSTATUS) 0xC0000282L)
+#endif
+
+#ifndef STATUS_SOURCE_ELEMENT_EMPTY
+# define STATUS_SOURCE_ELEMENT_EMPTY ((NTSTATUS) 0xC0000283L)
+#endif
+
+#ifndef STATUS_DESTINATION_ELEMENT_FULL
+# define STATUS_DESTINATION_ELEMENT_FULL ((NTSTATUS) 0xC0000284L)
+#endif
+
+#ifndef STATUS_ILLEGAL_ELEMENT_ADDRESS
+# define STATUS_ILLEGAL_ELEMENT_ADDRESS ((NTSTATUS) 0xC0000285L)
+#endif
+
+#ifndef STATUS_MAGAZINE_NOT_PRESENT
+# define STATUS_MAGAZINE_NOT_PRESENT ((NTSTATUS) 0xC0000286L)
+#endif
+
+#ifndef STATUS_REINITIALIZATION_NEEDED
+# define STATUS_REINITIALIZATION_NEEDED ((NTSTATUS) 0xC0000287L)
+#endif
+
+#ifndef STATUS_DEVICE_REQUIRES_CLEANING
+# define STATUS_DEVICE_REQUIRES_CLEANING ((NTSTATUS) 0x80000288L)
+#endif
+
+#ifndef STATUS_DEVICE_DOOR_OPEN
+# define STATUS_DEVICE_DOOR_OPEN ((NTSTATUS) 0x80000289L)
+#endif
+
+#ifndef STATUS_ENCRYPTION_FAILED
+# define STATUS_ENCRYPTION_FAILED ((NTSTATUS) 0xC000028AL)
+#endif
+
+#ifndef STATUS_DECRYPTION_FAILED
+# define STATUS_DECRYPTION_FAILED ((NTSTATUS) 0xC000028BL)
+#endif
+
+#ifndef STATUS_RANGE_NOT_FOUND
+# define STATUS_RANGE_NOT_FOUND ((NTSTATUS) 0xC000028CL)
+#endif
+
+#ifndef STATUS_NO_RECOVERY_POLICY
+# define STATUS_NO_RECOVERY_POLICY ((NTSTATUS) 0xC000028DL)
+#endif
+
+#ifndef STATUS_NO_EFS
+# define STATUS_NO_EFS ((NTSTATUS) 0xC000028EL)
+#endif
+
+#ifndef STATUS_WRONG_EFS
+# define STATUS_WRONG_EFS ((NTSTATUS) 0xC000028FL)
+#endif
+
+#ifndef STATUS_NO_USER_KEYS
+# define STATUS_NO_USER_KEYS ((NTSTATUS) 0xC0000290L)
+#endif
+
+#ifndef STATUS_FILE_NOT_ENCRYPTED
+# define STATUS_FILE_NOT_ENCRYPTED ((NTSTATUS) 0xC0000291L)
+#endif
+
+#ifndef STATUS_NOT_EXPORT_FORMAT
+# define STATUS_NOT_EXPORT_FORMAT ((NTSTATUS) 0xC0000292L)
+#endif
+
+#ifndef STATUS_FILE_ENCRYPTED
+# define STATUS_FILE_ENCRYPTED ((NTSTATUS) 0xC0000293L)
+#endif
+
+#ifndef STATUS_WAKE_SYSTEM
+# define STATUS_WAKE_SYSTEM ((NTSTATUS) 0x40000294L)
+#endif
+
+#ifndef STATUS_WMI_GUID_NOT_FOUND
+# define STATUS_WMI_GUID_NOT_FOUND ((NTSTATUS) 0xC0000295L)
+#endif
+
+#ifndef STATUS_WMI_INSTANCE_NOT_FOUND
+# define STATUS_WMI_INSTANCE_NOT_FOUND ((NTSTATUS) 0xC0000296L)
+#endif
+
+#ifndef STATUS_WMI_ITEMID_NOT_FOUND
+# define STATUS_WMI_ITEMID_NOT_FOUND ((NTSTATUS) 0xC0000297L)
+#endif
+
+#ifndef STATUS_WMI_TRY_AGAIN
+# define STATUS_WMI_TRY_AGAIN ((NTSTATUS) 0xC0000298L)
+#endif
+
+#ifndef STATUS_SHARED_POLICY
+# define STATUS_SHARED_POLICY ((NTSTATUS) 0xC0000299L)
+#endif
+
+#ifndef STATUS_POLICY_OBJECT_NOT_FOUND
+# define STATUS_POLICY_OBJECT_NOT_FOUND ((NTSTATUS) 0xC000029AL)
+#endif
+
+#ifndef STATUS_POLICY_ONLY_IN_DS
+# define STATUS_POLICY_ONLY_IN_DS ((NTSTATUS) 0xC000029BL)
+#endif
+
+#ifndef STATUS_VOLUME_NOT_UPGRADED
+# define STATUS_VOLUME_NOT_UPGRADED ((NTSTATUS) 0xC000029CL)
+#endif
+
+#ifndef STATUS_REMOTE_STORAGE_NOT_ACTIVE
+# define STATUS_REMOTE_STORAGE_NOT_ACTIVE ((NTSTATUS) 0xC000029DL)
+#endif
+
+#ifndef STATUS_REMOTE_STORAGE_MEDIA_ERROR
+# define STATUS_REMOTE_STORAGE_MEDIA_ERROR ((NTSTATUS) 0xC000029EL)
+#endif
+
+#ifndef STATUS_NO_TRACKING_SERVICE
+# define STATUS_NO_TRACKING_SERVICE ((NTSTATUS) 0xC000029FL)
+#endif
+
+#ifndef STATUS_SERVER_SID_MISMATCH
+# define STATUS_SERVER_SID_MISMATCH ((NTSTATUS) 0xC00002A0L)
+#endif
+
+#ifndef STATUS_DS_NO_ATTRIBUTE_OR_VALUE
+# define STATUS_DS_NO_ATTRIBUTE_OR_VALUE ((NTSTATUS) 0xC00002A1L)
+#endif
+
+#ifndef STATUS_DS_INVALID_ATTRIBUTE_SYNTAX
+# define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX ((NTSTATUS) 0xC00002A2L)
+#endif
+
+#ifndef STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED
+# define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED ((NTSTATUS) 0xC00002A3L)
+#endif
+
+#ifndef STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS
+# define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS ((NTSTATUS) 0xC00002A4L)
+#endif
+
+#ifndef STATUS_DS_BUSY
+# define STATUS_DS_BUSY ((NTSTATUS) 0xC00002A5L)
+#endif
+
+#ifndef STATUS_DS_UNAVAILABLE
+# define STATUS_DS_UNAVAILABLE ((NTSTATUS) 0xC00002A6L)
+#endif
+
+#ifndef STATUS_DS_NO_RIDS_ALLOCATED
+# define STATUS_DS_NO_RIDS_ALLOCATED ((NTSTATUS) 0xC00002A7L)
+#endif
+
+#ifndef STATUS_DS_NO_MORE_RIDS
+# define STATUS_DS_NO_MORE_RIDS ((NTSTATUS) 0xC00002A8L)
+#endif
+
+#ifndef STATUS_DS_INCORRECT_ROLE_OWNER
+# define STATUS_DS_INCORRECT_ROLE_OWNER ((NTSTATUS) 0xC00002A9L)
+#endif
+
+#ifndef STATUS_DS_RIDMGR_INIT_ERROR
+# define STATUS_DS_RIDMGR_INIT_ERROR ((NTSTATUS) 0xC00002AAL)
+#endif
+
+#ifndef STATUS_DS_OBJ_CLASS_VIOLATION
+# define STATUS_DS_OBJ_CLASS_VIOLATION ((NTSTATUS) 0xC00002ABL)
+#endif
+
+#ifndef STATUS_DS_CANT_ON_NON_LEAF
+# define STATUS_DS_CANT_ON_NON_LEAF ((NTSTATUS) 0xC00002ACL)
+#endif
+
+#ifndef STATUS_DS_CANT_ON_RDN
+# define STATUS_DS_CANT_ON_RDN ((NTSTATUS) 0xC00002ADL)
+#endif
+
+#ifndef STATUS_DS_CANT_MOD_OBJ_CLASS
+# define STATUS_DS_CANT_MOD_OBJ_CLASS ((NTSTATUS) 0xC00002AEL)
+#endif
+
+#ifndef STATUS_DS_CROSS_DOM_MOVE_FAILED
+# define STATUS_DS_CROSS_DOM_MOVE_FAILED ((NTSTATUS) 0xC00002AFL)
+#endif
+
+#ifndef STATUS_DS_GC_NOT_AVAILABLE
+# define STATUS_DS_GC_NOT_AVAILABLE ((NTSTATUS) 0xC00002B0L)
+#endif
+
+#ifndef STATUS_DIRECTORY_SERVICE_REQUIRED
+# define STATUS_DIRECTORY_SERVICE_REQUIRED ((NTSTATUS) 0xC00002B1L)
+#endif
+
+#ifndef STATUS_REPARSE_ATTRIBUTE_CONFLICT
+# define STATUS_REPARSE_ATTRIBUTE_CONFLICT ((NTSTATUS) 0xC00002B2L)
+#endif
+
+#ifndef STATUS_CANT_ENABLE_DENY_ONLY
+# define STATUS_CANT_ENABLE_DENY_ONLY ((NTSTATUS) 0xC00002B3L)
+#endif
+
+#ifndef STATUS_FLOAT_MULTIPLE_FAULTS
+# define STATUS_FLOAT_MULTIPLE_FAULTS ((NTSTATUS) 0xC00002B4L)
+#endif
+
+#ifndef STATUS_FLOAT_MULTIPLE_TRAPS
+# define STATUS_FLOAT_MULTIPLE_TRAPS ((NTSTATUS) 0xC00002B5L)
+#endif
+
+#ifndef STATUS_DEVICE_REMOVED
+# define STATUS_DEVICE_REMOVED ((NTSTATUS) 0xC00002B6L)
+#endif
+
+#ifndef STATUS_JOURNAL_DELETE_IN_PROGRESS
+# define STATUS_JOURNAL_DELETE_IN_PROGRESS ((NTSTATUS) 0xC00002B7L)
+#endif
+
+#ifndef STATUS_JOURNAL_NOT_ACTIVE
+# define STATUS_JOURNAL_NOT_ACTIVE ((NTSTATUS) 0xC00002B8L)
+#endif
+
+#ifndef STATUS_NOINTERFACE
+# define STATUS_NOINTERFACE ((NTSTATUS) 0xC00002B9L)
+#endif
+
+#ifndef STATUS_DS_ADMIN_LIMIT_EXCEEDED
+# define STATUS_DS_ADMIN_LIMIT_EXCEEDED ((NTSTATUS) 0xC00002C1L)
+#endif
+
+#ifndef STATUS_DRIVER_FAILED_SLEEP
+# define STATUS_DRIVER_FAILED_SLEEP ((NTSTATUS) 0xC00002C2L)
+#endif
+
+#ifndef STATUS_MUTUAL_AUTHENTICATION_FAILED
+# define STATUS_MUTUAL_AUTHENTICATION_FAILED ((NTSTATUS) 0xC00002C3L)
+#endif
+
+#ifndef STATUS_CORRUPT_SYSTEM_FILE
+# define STATUS_CORRUPT_SYSTEM_FILE ((NTSTATUS) 0xC00002C4L)
+#endif
+
+#ifndef STATUS_DATATYPE_MISALIGNMENT_ERROR
+# define STATUS_DATATYPE_MISALIGNMENT_ERROR ((NTSTATUS) 0xC00002C5L)
+#endif
+
+#ifndef STATUS_WMI_READ_ONLY
+# define STATUS_WMI_READ_ONLY ((NTSTATUS) 0xC00002C6L)
+#endif
+
+#ifndef STATUS_WMI_SET_FAILURE
+# define STATUS_WMI_SET_FAILURE ((NTSTATUS) 0xC00002C7L)
+#endif
+
+#ifndef STATUS_COMMITMENT_MINIMUM
+# define STATUS_COMMITMENT_MINIMUM ((NTSTATUS) 0xC00002C8L)
+#endif
+
+#ifndef STATUS_REG_NAT_CONSUMPTION
+# define STATUS_REG_NAT_CONSUMPTION ((NTSTATUS) 0xC00002C9L)
+#endif
+
+#ifndef STATUS_TRANSPORT_FULL
+# define STATUS_TRANSPORT_FULL ((NTSTATUS) 0xC00002CAL)
+#endif
+
+#ifndef STATUS_DS_SAM_INIT_FAILURE
+# define STATUS_DS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002CBL)
+#endif
+
+#ifndef STATUS_ONLY_IF_CONNECTED
+# define STATUS_ONLY_IF_CONNECTED ((NTSTATUS) 0xC00002CCL)
+#endif
+
+#ifndef STATUS_DS_SENSITIVE_GROUP_VIOLATION
+# define STATUS_DS_SENSITIVE_GROUP_VIOLATION ((NTSTATUS) 0xC00002CDL)
+#endif
+
+#ifndef STATUS_PNP_RESTART_ENUMERATION
+# define STATUS_PNP_RESTART_ENUMERATION ((NTSTATUS) 0xC00002CEL)
+#endif
+
+#ifndef STATUS_JOURNAL_ENTRY_DELETED
+# define STATUS_JOURNAL_ENTRY_DELETED ((NTSTATUS) 0xC00002CFL)
+#endif
+
+#ifndef STATUS_DS_CANT_MOD_PRIMARYGROUPID
+# define STATUS_DS_CANT_MOD_PRIMARYGROUPID ((NTSTATUS) 0xC00002D0L)
+#endif
+
+#ifndef STATUS_SYSTEM_IMAGE_BAD_SIGNATURE
+# define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE ((NTSTATUS) 0xC00002D1L)
+#endif
+
+#ifndef STATUS_PNP_REBOOT_REQUIRED
+# define STATUS_PNP_REBOOT_REQUIRED ((NTSTATUS) 0xC00002D2L)
+#endif
+
+#ifndef STATUS_POWER_STATE_INVALID
+# define STATUS_POWER_STATE_INVALID ((NTSTATUS) 0xC00002D3L)
+#endif
+
+#ifndef STATUS_DS_INVALID_GROUP_TYPE
+# define STATUS_DS_INVALID_GROUP_TYPE ((NTSTATUS) 0xC00002D4L)
+#endif
+
+#ifndef STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN
+# define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D5L)
+#endif
+
+#ifndef STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN
+# define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN ((NTSTATUS) 0xC00002D6L)
+#endif
+
+#ifndef STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER
+# define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D7L)
+#endif
+
+#ifndef STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER
+# define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC00002D8L)
+#endif
+
+#ifndef STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER
+# define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER ((NTSTATUS) 0xC00002D9L)
+#endif
+
+#ifndef STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER
+# define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER ((NTSTATUS) 0xC00002DAL)
+#endif
+
+#ifndef STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER
+# define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER ((NTSTATUS) 0xC00002DBL)
+#endif
+
+#ifndef STATUS_DS_HAVE_PRIMARY_MEMBERS
+# define STATUS_DS_HAVE_PRIMARY_MEMBERS ((NTSTATUS) 0xC00002DCL)
+#endif
+
+#ifndef STATUS_WMI_NOT_SUPPORTED
+# define STATUS_WMI_NOT_SUPPORTED ((NTSTATUS) 0xC00002DDL)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_POWER
+# define STATUS_INSUFFICIENT_POWER ((NTSTATUS) 0xC00002DEL)
+#endif
+
+#ifndef STATUS_SAM_NEED_BOOTKEY_PASSWORD
+# define STATUS_SAM_NEED_BOOTKEY_PASSWORD ((NTSTATUS) 0xC00002DFL)
+#endif
+
+#ifndef STATUS_SAM_NEED_BOOTKEY_FLOPPY
+# define STATUS_SAM_NEED_BOOTKEY_FLOPPY ((NTSTATUS) 0xC00002E0L)
+#endif
+
+#ifndef STATUS_DS_CANT_START
+# define STATUS_DS_CANT_START ((NTSTATUS) 0xC00002E1L)
+#endif
+
+#ifndef STATUS_DS_INIT_FAILURE
+# define STATUS_DS_INIT_FAILURE ((NTSTATUS) 0xC00002E2L)
+#endif
+
+#ifndef STATUS_SAM_INIT_FAILURE
+# define STATUS_SAM_INIT_FAILURE ((NTSTATUS) 0xC00002E3L)
+#endif
+
+#ifndef STATUS_DS_GC_REQUIRED
+# define STATUS_DS_GC_REQUIRED ((NTSTATUS) 0xC00002E4L)
+#endif
+
+#ifndef STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY
+# define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY ((NTSTATUS) 0xC00002E5L)
+#endif
+
+#ifndef STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS
+# define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS ((NTSTATUS) 0xC00002E6L)
+#endif
+
+#ifndef STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED
+# define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED ((NTSTATUS) 0xC00002E7L)
+#endif
+
+#ifndef STATUS_MULTIPLE_FAULT_VIOLATION
+# define STATUS_MULTIPLE_FAULT_VIOLATION ((NTSTATUS) 0xC00002E8L)
+#endif
+
+#ifndef STATUS_CURRENT_DOMAIN_NOT_ALLOWED
+# define STATUS_CURRENT_DOMAIN_NOT_ALLOWED ((NTSTATUS) 0xC00002E9L)
+#endif
+
+#ifndef STATUS_CANNOT_MAKE
+# define STATUS_CANNOT_MAKE ((NTSTATUS) 0xC00002EAL)
+#endif
+
+#ifndef STATUS_SYSTEM_SHUTDOWN
+# define STATUS_SYSTEM_SHUTDOWN ((NTSTATUS) 0xC00002EBL)
+#endif
+
+#ifndef STATUS_DS_INIT_FAILURE_CONSOLE
+# define STATUS_DS_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002ECL)
+#endif
+
+#ifndef STATUS_DS_SAM_INIT_FAILURE_CONSOLE
+# define STATUS_DS_SAM_INIT_FAILURE_CONSOLE ((NTSTATUS) 0xC00002EDL)
+#endif
+
+#ifndef STATUS_UNFINISHED_CONTEXT_DELETED
+# define STATUS_UNFINISHED_CONTEXT_DELETED ((NTSTATUS) 0xC00002EEL)
+#endif
+
+#ifndef STATUS_NO_TGT_REPLY
+# define STATUS_NO_TGT_REPLY ((NTSTATUS) 0xC00002EFL)
+#endif
+
+#ifndef STATUS_OBJECTID_NOT_FOUND
+# define STATUS_OBJECTID_NOT_FOUND ((NTSTATUS) 0xC00002F0L)
+#endif
+
+#ifndef STATUS_NO_IP_ADDRESSES
+# define STATUS_NO_IP_ADDRESSES ((NTSTATUS) 0xC00002F1L)
+#endif
+
+#ifndef STATUS_WRONG_CREDENTIAL_HANDLE
+# define STATUS_WRONG_CREDENTIAL_HANDLE ((NTSTATUS) 0xC00002F2L)
+#endif
+
+#ifndef STATUS_CRYPTO_SYSTEM_INVALID
+# define STATUS_CRYPTO_SYSTEM_INVALID ((NTSTATUS) 0xC00002F3L)
+#endif
+
+#ifndef STATUS_MAX_REFERRALS_EXCEEDED
+# define STATUS_MAX_REFERRALS_EXCEEDED ((NTSTATUS) 0xC00002F4L)
+#endif
+
+#ifndef STATUS_MUST_BE_KDC
+# define STATUS_MUST_BE_KDC ((NTSTATUS) 0xC00002F5L)
+#endif
+
+#ifndef STATUS_STRONG_CRYPTO_NOT_SUPPORTED
+# define STATUS_STRONG_CRYPTO_NOT_SUPPORTED ((NTSTATUS) 0xC00002F6L)
+#endif
+
+#ifndef STATUS_TOO_MANY_PRINCIPALS
+# define STATUS_TOO_MANY_PRINCIPALS ((NTSTATUS) 0xC00002F7L)
+#endif
+
+#ifndef STATUS_NO_PA_DATA
+# define STATUS_NO_PA_DATA ((NTSTATUS) 0xC00002F8L)
+#endif
+
+#ifndef STATUS_PKINIT_NAME_MISMATCH
+# define STATUS_PKINIT_NAME_MISMATCH ((NTSTATUS) 0xC00002F9L)
+#endif
+
+#ifndef STATUS_SMARTCARD_LOGON_REQUIRED
+# define STATUS_SMARTCARD_LOGON_REQUIRED ((NTSTATUS) 0xC00002FAL)
+#endif
+
+#ifndef STATUS_KDC_INVALID_REQUEST
+# define STATUS_KDC_INVALID_REQUEST ((NTSTATUS) 0xC00002FBL)
+#endif
+
+#ifndef STATUS_KDC_UNABLE_TO_REFER
+# define STATUS_KDC_UNABLE_TO_REFER ((NTSTATUS) 0xC00002FCL)
+#endif
+
+#ifndef STATUS_KDC_UNKNOWN_ETYPE
+# define STATUS_KDC_UNKNOWN_ETYPE ((NTSTATUS) 0xC00002FDL)
+#endif
+
+#ifndef STATUS_SHUTDOWN_IN_PROGRESS
+# define STATUS_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FEL)
+#endif
+
+#ifndef STATUS_SERVER_SHUTDOWN_IN_PROGRESS
+# define STATUS_SERVER_SHUTDOWN_IN_PROGRESS ((NTSTATUS) 0xC00002FFL)
+#endif
+
+#ifndef STATUS_NOT_SUPPORTED_ON_SBS
+# define STATUS_NOT_SUPPORTED_ON_SBS ((NTSTATUS) 0xC0000300L)
+#endif
+
+#ifndef STATUS_WMI_GUID_DISCONNECTED
+# define STATUS_WMI_GUID_DISCONNECTED ((NTSTATUS) 0xC0000301L)
+#endif
+
+#ifndef STATUS_WMI_ALREADY_DISABLED
+# define STATUS_WMI_ALREADY_DISABLED ((NTSTATUS) 0xC0000302L)
+#endif
+
+#ifndef STATUS_WMI_ALREADY_ENABLED
+# define STATUS_WMI_ALREADY_ENABLED ((NTSTATUS) 0xC0000303L)
+#endif
+
+#ifndef STATUS_MFT_TOO_FRAGMENTED
+# define STATUS_MFT_TOO_FRAGMENTED ((NTSTATUS) 0xC0000304L)
+#endif
+
+#ifndef STATUS_COPY_PROTECTION_FAILURE
+# define STATUS_COPY_PROTECTION_FAILURE ((NTSTATUS) 0xC0000305L)
+#endif
+
+#ifndef STATUS_CSS_AUTHENTICATION_FAILURE
+# define STATUS_CSS_AUTHENTICATION_FAILURE ((NTSTATUS) 0xC0000306L)
+#endif
+
+#ifndef STATUS_CSS_KEY_NOT_PRESENT
+# define STATUS_CSS_KEY_NOT_PRESENT ((NTSTATUS) 0xC0000307L)
+#endif
+
+#ifndef STATUS_CSS_KEY_NOT_ESTABLISHED
+# define STATUS_CSS_KEY_NOT_ESTABLISHED ((NTSTATUS) 0xC0000308L)
+#endif
+
+#ifndef STATUS_CSS_SCRAMBLED_SECTOR
+# define STATUS_CSS_SCRAMBLED_SECTOR ((NTSTATUS) 0xC0000309L)
+#endif
+
+#ifndef STATUS_CSS_REGION_MISMATCH
+# define STATUS_CSS_REGION_MISMATCH ((NTSTATUS) 0xC000030AL)
+#endif
+
+#ifndef STATUS_CSS_RESETS_EXHAUSTED
+# define STATUS_CSS_RESETS_EXHAUSTED ((NTSTATUS) 0xC000030BL)
+#endif
+
+#ifndef STATUS_PKINIT_FAILURE
+# define STATUS_PKINIT_FAILURE ((NTSTATUS) 0xC0000320L)
+#endif
+
+#ifndef STATUS_SMARTCARD_SUBSYSTEM_FAILURE
+# define STATUS_SMARTCARD_SUBSYSTEM_FAILURE ((NTSTATUS) 0xC0000321L)
+#endif
+
+#ifndef STATUS_NO_KERB_KEY
+# define STATUS_NO_KERB_KEY ((NTSTATUS) 0xC0000322L)
+#endif
+
+#ifndef STATUS_HOST_DOWN
+# define STATUS_HOST_DOWN ((NTSTATUS) 0xC0000350L)
+#endif
+
+#ifndef STATUS_UNSUPPORTED_PREAUTH
+# define STATUS_UNSUPPORTED_PREAUTH ((NTSTATUS) 0xC0000351L)
+#endif
+
+#ifndef STATUS_EFS_ALG_BLOB_TOO_BIG
+# define STATUS_EFS_ALG_BLOB_TOO_BIG ((NTSTATUS) 0xC0000352L)
+#endif
+
+#ifndef STATUS_PORT_NOT_SET
+# define STATUS_PORT_NOT_SET ((NTSTATUS) 0xC0000353L)
+#endif
+
+#ifndef STATUS_DEBUGGER_INACTIVE
+# define STATUS_DEBUGGER_INACTIVE ((NTSTATUS) 0xC0000354L)
+#endif
+
+#ifndef STATUS_DS_VERSION_CHECK_FAILURE
+# define STATUS_DS_VERSION_CHECK_FAILURE ((NTSTATUS) 0xC0000355L)
+#endif
+
+#ifndef STATUS_AUDITING_DISABLED
+# define STATUS_AUDITING_DISABLED ((NTSTATUS) 0xC0000356L)
+#endif
+
+#ifndef STATUS_PRENT4_MACHINE_ACCOUNT
+# define STATUS_PRENT4_MACHINE_ACCOUNT ((NTSTATUS) 0xC0000357L)
+#endif
+
+#ifndef STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER
+# define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER ((NTSTATUS) 0xC0000358L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_WIN_32
+# define STATUS_INVALID_IMAGE_WIN_32 ((NTSTATUS) 0xC0000359L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_WIN_64
+# define STATUS_INVALID_IMAGE_WIN_64 ((NTSTATUS) 0xC000035AL)
+#endif
+
+#ifndef STATUS_BAD_BINDINGS
+# define STATUS_BAD_BINDINGS ((NTSTATUS) 0xC000035BL)
+#endif
+
+#ifndef STATUS_NETWORK_SESSION_EXPIRED
+# define STATUS_NETWORK_SESSION_EXPIRED ((NTSTATUS) 0xC000035CL)
+#endif
+
+#ifndef STATUS_APPHELP_BLOCK
+# define STATUS_APPHELP_BLOCK ((NTSTATUS) 0xC000035DL)
+#endif
+
+#ifndef STATUS_ALL_SIDS_FILTERED
+# define STATUS_ALL_SIDS_FILTERED ((NTSTATUS) 0xC000035EL)
+#endif
+
+#ifndef STATUS_NOT_SAFE_MODE_DRIVER
+# define STATUS_NOT_SAFE_MODE_DRIVER ((NTSTATUS) 0xC000035FL)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT
+# define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT ((NTSTATUS) 0xC0000361L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PATH
+# define STATUS_ACCESS_DISABLED_BY_POLICY_PATH ((NTSTATUS) 0xC0000362L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER
+# define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER ((NTSTATUS) 0xC0000363L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_BY_POLICY_OTHER
+# define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER ((NTSTATUS) 0xC0000364L)
+#endif
+
+#ifndef STATUS_FAILED_DRIVER_ENTRY
+# define STATUS_FAILED_DRIVER_ENTRY ((NTSTATUS) 0xC0000365L)
+#endif
+
+#ifndef STATUS_DEVICE_ENUMERATION_ERROR
+# define STATUS_DEVICE_ENUMERATION_ERROR ((NTSTATUS) 0xC0000366L)
+#endif
+
+#ifndef STATUS_MOUNT_POINT_NOT_RESOLVED
+# define STATUS_MOUNT_POINT_NOT_RESOLVED ((NTSTATUS) 0xC0000368L)
+#endif
+
+#ifndef STATUS_INVALID_DEVICE_OBJECT_PARAMETER
+# define STATUS_INVALID_DEVICE_OBJECT_PARAMETER ((NTSTATUS) 0xC0000369L)
+#endif
+
+#ifndef STATUS_MCA_OCCURED
+# define STATUS_MCA_OCCURED ((NTSTATUS) 0xC000036AL)
+#endif
+
+#ifndef STATUS_DRIVER_BLOCKED_CRITICAL
+# define STATUS_DRIVER_BLOCKED_CRITICAL ((NTSTATUS) 0xC000036BL)
+#endif
+
+#ifndef STATUS_DRIVER_BLOCKED
+# define STATUS_DRIVER_BLOCKED ((NTSTATUS) 0xC000036CL)
+#endif
+
+#ifndef STATUS_DRIVER_DATABASE_ERROR
+# define STATUS_DRIVER_DATABASE_ERROR ((NTSTATUS) 0xC000036DL)
+#endif
+
+#ifndef STATUS_SYSTEM_HIVE_TOO_LARGE
+# define STATUS_SYSTEM_HIVE_TOO_LARGE ((NTSTATUS) 0xC000036EL)
+#endif
+
+#ifndef STATUS_INVALID_IMPORT_OF_NON_DLL
+# define STATUS_INVALID_IMPORT_OF_NON_DLL ((NTSTATUS) 0xC000036FL)
+#endif
+
+#ifndef STATUS_DS_SHUTTING_DOWN
+# define STATUS_DS_SHUTTING_DOWN ((NTSTATUS) 0x40000370L)
+#endif
+
+#ifndef STATUS_NO_SECRETS
+# define STATUS_NO_SECRETS ((NTSTATUS) 0xC0000371L)
+#endif
+
+#ifndef STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY
+# define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY ((NTSTATUS) 0xC0000372L)
+#endif
+
+#ifndef STATUS_FAILED_STACK_SWITCH
+# define STATUS_FAILED_STACK_SWITCH ((NTSTATUS) 0xC0000373L)
+#endif
+
+#ifndef STATUS_HEAP_CORRUPTION
+# define STATUS_HEAP_CORRUPTION ((NTSTATUS) 0xC0000374L)
+#endif
+
+#ifndef STATUS_SMARTCARD_WRONG_PIN
+# define STATUS_SMARTCARD_WRONG_PIN ((NTSTATUS) 0xC0000380L)
+#endif
+
+#ifndef STATUS_SMARTCARD_CARD_BLOCKED
+# define STATUS_SMARTCARD_CARD_BLOCKED ((NTSTATUS) 0xC0000381L)
+#endif
+
+#ifndef STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED
+# define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED ((NTSTATUS) 0xC0000382L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_CARD
+# define STATUS_SMARTCARD_NO_CARD ((NTSTATUS) 0xC0000383L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_KEY_CONTAINER
+# define STATUS_SMARTCARD_NO_KEY_CONTAINER ((NTSTATUS) 0xC0000384L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_CERTIFICATE
+# define STATUS_SMARTCARD_NO_CERTIFICATE ((NTSTATUS) 0xC0000385L)
+#endif
+
+#ifndef STATUS_SMARTCARD_NO_KEYSET
+# define STATUS_SMARTCARD_NO_KEYSET ((NTSTATUS) 0xC0000386L)
+#endif
+
+#ifndef STATUS_SMARTCARD_IO_ERROR
+# define STATUS_SMARTCARD_IO_ERROR ((NTSTATUS) 0xC0000387L)
+#endif
+
+#ifndef STATUS_DOWNGRADE_DETECTED
+# define STATUS_DOWNGRADE_DETECTED ((NTSTATUS) 0xC0000388L)
+#endif
+
+#ifndef STATUS_SMARTCARD_CERT_REVOKED
+# define STATUS_SMARTCARD_CERT_REVOKED ((NTSTATUS) 0xC0000389L)
+#endif
+
+#ifndef STATUS_ISSUING_CA_UNTRUSTED
+# define STATUS_ISSUING_CA_UNTRUSTED ((NTSTATUS) 0xC000038AL)
+#endif
+
+#ifndef STATUS_REVOCATION_OFFLINE_C
+# define STATUS_REVOCATION_OFFLINE_C ((NTSTATUS) 0xC000038BL)
+#endif
+
+#ifndef STATUS_PKINIT_CLIENT_FAILURE
+# define STATUS_PKINIT_CLIENT_FAILURE ((NTSTATUS) 0xC000038CL)
+#endif
+
+#ifndef STATUS_SMARTCARD_CERT_EXPIRED
+# define STATUS_SMARTCARD_CERT_EXPIRED ((NTSTATUS) 0xC000038DL)
+#endif
+
+#ifndef STATUS_DRIVER_FAILED_PRIOR_UNLOAD
+# define STATUS_DRIVER_FAILED_PRIOR_UNLOAD ((NTSTATUS) 0xC000038EL)
+#endif
+
+#ifndef STATUS_SMARTCARD_SILENT_CONTEXT
+# define STATUS_SMARTCARD_SILENT_CONTEXT ((NTSTATUS) 0xC000038FL)
+#endif
+
+#ifndef STATUS_PER_USER_TRUST_QUOTA_EXCEEDED
+# define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000401L)
+#endif
+
+#ifndef STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED
+# define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000402L)
+#endif
+
+#ifndef STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED
+# define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000403L)
+#endif
+
+#ifndef STATUS_DS_NAME_NOT_UNIQUE
+# define STATUS_DS_NAME_NOT_UNIQUE ((NTSTATUS) 0xC0000404L)
+#endif
+
+#ifndef STATUS_DS_DUPLICATE_ID_FOUND
+# define STATUS_DS_DUPLICATE_ID_FOUND ((NTSTATUS) 0xC0000405L)
+#endif
+
+#ifndef STATUS_DS_GROUP_CONVERSION_ERROR
+# define STATUS_DS_GROUP_CONVERSION_ERROR ((NTSTATUS) 0xC0000406L)
+#endif
+
+#ifndef STATUS_VOLSNAP_PREPARE_HIBERNATE
+# define STATUS_VOLSNAP_PREPARE_HIBERNATE ((NTSTATUS) 0xC0000407L)
+#endif
+
+#ifndef STATUS_USER2USER_REQUIRED
+# define STATUS_USER2USER_REQUIRED ((NTSTATUS) 0xC0000408L)
+#endif
+
+#ifndef STATUS_STACK_BUFFER_OVERRUN
+# define STATUS_STACK_BUFFER_OVERRUN ((NTSTATUS) 0xC0000409L)
+#endif
+
+#ifndef STATUS_NO_S4U_PROT_SUPPORT
+# define STATUS_NO_S4U_PROT_SUPPORT ((NTSTATUS) 0xC000040AL)
+#endif
+
+#ifndef STATUS_CROSSREALM_DELEGATION_FAILURE
+# define STATUS_CROSSREALM_DELEGATION_FAILURE ((NTSTATUS) 0xC000040BL)
+#endif
+
+#ifndef STATUS_REVOCATION_OFFLINE_KDC
+# define STATUS_REVOCATION_OFFLINE_KDC ((NTSTATUS) 0xC000040CL)
+#endif
+
+#ifndef STATUS_ISSUING_CA_UNTRUSTED_KDC
+# define STATUS_ISSUING_CA_UNTRUSTED_KDC ((NTSTATUS) 0xC000040DL)
+#endif
+
+#ifndef STATUS_KDC_CERT_EXPIRED
+# define STATUS_KDC_CERT_EXPIRED ((NTSTATUS) 0xC000040EL)
+#endif
+
+#ifndef STATUS_KDC_CERT_REVOKED
+# define STATUS_KDC_CERT_REVOKED ((NTSTATUS) 0xC000040FL)
+#endif
+
+#ifndef STATUS_PARAMETER_QUOTA_EXCEEDED
+# define STATUS_PARAMETER_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000410L)
+#endif
+
+#ifndef STATUS_HIBERNATION_FAILURE
+# define STATUS_HIBERNATION_FAILURE ((NTSTATUS) 0xC0000411L)
+#endif
+
+#ifndef STATUS_DELAY_LOAD_FAILED
+# define STATUS_DELAY_LOAD_FAILED ((NTSTATUS) 0xC0000412L)
+#endif
+
+#ifndef STATUS_AUTHENTICATION_FIREWALL_FAILED
+# define STATUS_AUTHENTICATION_FIREWALL_FAILED ((NTSTATUS) 0xC0000413L)
+#endif
+
+#ifndef STATUS_VDM_DISALLOWED
+# define STATUS_VDM_DISALLOWED ((NTSTATUS) 0xC0000414L)
+#endif
+
+#ifndef STATUS_HUNG_DISPLAY_DRIVER_THREAD
+# define STATUS_HUNG_DISPLAY_DRIVER_THREAD ((NTSTATUS) 0xC0000415L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE
+# define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE ((NTSTATUS) 0xC0000416L)
+#endif
+
+#ifndef STATUS_INVALID_CRUNTIME_PARAMETER
+# define STATUS_INVALID_CRUNTIME_PARAMETER ((NTSTATUS) 0xC0000417L)
+#endif
+
+#ifndef STATUS_NTLM_BLOCKED
+# define STATUS_NTLM_BLOCKED ((NTSTATUS) 0xC0000418L)
+#endif
+
+#ifndef STATUS_DS_SRC_SID_EXISTS_IN_FOREST
+# define STATUS_DS_SRC_SID_EXISTS_IN_FOREST ((NTSTATUS) 0xC0000419L)
+#endif
+
+#ifndef STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST
+# define STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041AL)
+#endif
+
+#ifndef STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST
+# define STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST ((NTSTATUS) 0xC000041BL)
+#endif
+
+#ifndef STATUS_INVALID_USER_PRINCIPAL_NAME
+# define STATUS_INVALID_USER_PRINCIPAL_NAME ((NTSTATUS) 0xC000041CL)
+#endif
+
+#ifndef STATUS_FATAL_USER_CALLBACK_EXCEPTION
+# define STATUS_FATAL_USER_CALLBACK_EXCEPTION ((NTSTATUS) 0xC000041DL)
+#endif
+
+#ifndef STATUS_ASSERTION_FAILURE
+# define STATUS_ASSERTION_FAILURE ((NTSTATUS) 0xC0000420L)
+#endif
+
+#ifndef STATUS_VERIFIER_STOP
+# define STATUS_VERIFIER_STOP ((NTSTATUS) 0xC0000421L)
+#endif
+
+#ifndef STATUS_CALLBACK_POP_STACK
+# define STATUS_CALLBACK_POP_STACK ((NTSTATUS) 0xC0000423L)
+#endif
+
+#ifndef STATUS_INCOMPATIBLE_DRIVER_BLOCKED
+# define STATUS_INCOMPATIBLE_DRIVER_BLOCKED ((NTSTATUS) 0xC0000424L)
+#endif
+
+#ifndef STATUS_HIVE_UNLOADED
+# define STATUS_HIVE_UNLOADED ((NTSTATUS) 0xC0000425L)
+#endif
+
+#ifndef STATUS_COMPRESSION_DISABLED
+# define STATUS_COMPRESSION_DISABLED ((NTSTATUS) 0xC0000426L)
+#endif
+
+#ifndef STATUS_FILE_SYSTEM_LIMITATION
+# define STATUS_FILE_SYSTEM_LIMITATION ((NTSTATUS) 0xC0000427L)
+#endif
+
+#ifndef STATUS_INVALID_IMAGE_HASH
+# define STATUS_INVALID_IMAGE_HASH ((NTSTATUS) 0xC0000428L)
+#endif
+
+#ifndef STATUS_NOT_CAPABLE
+# define STATUS_NOT_CAPABLE ((NTSTATUS) 0xC0000429L)
+#endif
+
+#ifndef STATUS_REQUEST_OUT_OF_SEQUENCE
+# define STATUS_REQUEST_OUT_OF_SEQUENCE ((NTSTATUS) 0xC000042AL)
+#endif
+
+#ifndef STATUS_IMPLEMENTATION_LIMIT
+# define STATUS_IMPLEMENTATION_LIMIT ((NTSTATUS) 0xC000042BL)
+#endif
+
+#ifndef STATUS_ELEVATION_REQUIRED
+# define STATUS_ELEVATION_REQUIRED ((NTSTATUS) 0xC000042CL)
+#endif
+
+#ifndef STATUS_NO_SECURITY_CONTEXT
+# define STATUS_NO_SECURITY_CONTEXT ((NTSTATUS) 0xC000042DL)
+#endif
+
+#ifndef STATUS_PKU2U_CERT_FAILURE
+# define STATUS_PKU2U_CERT_FAILURE ((NTSTATUS) 0xC000042FL)
+#endif
+
+#ifndef STATUS_BEYOND_VDL
+# define STATUS_BEYOND_VDL ((NTSTATUS) 0xC0000432L)
+#endif
+
+#ifndef STATUS_ENCOUNTERED_WRITE_IN_PROGRESS
+# define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS ((NTSTATUS) 0xC0000433L)
+#endif
+
+#ifndef STATUS_PTE_CHANGED
+# define STATUS_PTE_CHANGED ((NTSTATUS) 0xC0000434L)
+#endif
+
+#ifndef STATUS_PURGE_FAILED
+# define STATUS_PURGE_FAILED ((NTSTATUS) 0xC0000435L)
+#endif
+
+#ifndef STATUS_CRED_REQUIRES_CONFIRMATION
+# define STATUS_CRED_REQUIRES_CONFIRMATION ((NTSTATUS) 0xC0000440L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE
+# define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE ((NTSTATUS) 0xC0000441L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER
+# define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER ((NTSTATUS) 0xC0000442L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE
+# define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE ((NTSTATUS) 0xC0000443L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE
+# define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE ((NTSTATUS) 0xC0000444L)
+#endif
+
+#ifndef STATUS_CS_ENCRYPTION_FILE_NOT_CSE
+# define STATUS_CS_ENCRYPTION_FILE_NOT_CSE ((NTSTATUS) 0xC0000445L)
+#endif
+
+#ifndef STATUS_INVALID_LABEL
+# define STATUS_INVALID_LABEL ((NTSTATUS) 0xC0000446L)
+#endif
+
+#ifndef STATUS_DRIVER_PROCESS_TERMINATED
+# define STATUS_DRIVER_PROCESS_TERMINATED ((NTSTATUS) 0xC0000450L)
+#endif
+
+#ifndef STATUS_AMBIGUOUS_SYSTEM_DEVICE
+# define STATUS_AMBIGUOUS_SYSTEM_DEVICE ((NTSTATUS) 0xC0000451L)
+#endif
+
+#ifndef STATUS_SYSTEM_DEVICE_NOT_FOUND
+# define STATUS_SYSTEM_DEVICE_NOT_FOUND ((NTSTATUS) 0xC0000452L)
+#endif
+
+#ifndef STATUS_RESTART_BOOT_APPLICATION
+# define STATUS_RESTART_BOOT_APPLICATION ((NTSTATUS) 0xC0000453L)
+#endif
+
+#ifndef STATUS_INSUFFICIENT_NVRAM_RESOURCES
+# define STATUS_INSUFFICIENT_NVRAM_RESOURCES ((NTSTATUS) 0xC0000454L)
+#endif
+
+#ifndef STATUS_INVALID_TASK_NAME
+# define STATUS_INVALID_TASK_NAME ((NTSTATUS) 0xC0000500L)
+#endif
+
+#ifndef STATUS_INVALID_TASK_INDEX
+# define STATUS_INVALID_TASK_INDEX ((NTSTATUS) 0xC0000501L)
+#endif
+
+#ifndef STATUS_THREAD_ALREADY_IN_TASK
+# define STATUS_THREAD_ALREADY_IN_TASK ((NTSTATUS) 0xC0000502L)
+#endif
+
+#ifndef STATUS_CALLBACK_BYPASS
+# define STATUS_CALLBACK_BYPASS ((NTSTATUS) 0xC0000503L)
+#endif
+
+#ifndef STATUS_FAIL_FAST_EXCEPTION
+# define STATUS_FAIL_FAST_EXCEPTION ((NTSTATUS) 0xC0000602L)
+#endif
+
+#ifndef STATUS_IMAGE_CERT_REVOKED
+# define STATUS_IMAGE_CERT_REVOKED ((NTSTATUS) 0xC0000603L)
+#endif
+
+#ifndef STATUS_PORT_CLOSED
+# define STATUS_PORT_CLOSED ((NTSTATUS) 0xC0000700L)
+#endif
+
+#ifndef STATUS_MESSAGE_LOST
+# define STATUS_MESSAGE_LOST ((NTSTATUS) 0xC0000701L)
+#endif
+
+#ifndef STATUS_INVALID_MESSAGE
+# define STATUS_INVALID_MESSAGE ((NTSTATUS) 0xC0000702L)
+#endif
+
+#ifndef STATUS_REQUEST_CANCELED
+# define STATUS_REQUEST_CANCELED ((NTSTATUS) 0xC0000703L)
+#endif
+
+#ifndef STATUS_RECURSIVE_DISPATCH
+# define STATUS_RECURSIVE_DISPATCH ((NTSTATUS) 0xC0000704L)
+#endif
+
+#ifndef STATUS_LPC_RECEIVE_BUFFER_EXPECTED
+# define STATUS_LPC_RECEIVE_BUFFER_EXPECTED ((NTSTATUS) 0xC0000705L)
+#endif
+
+#ifndef STATUS_LPC_INVALID_CONNECTION_USAGE
+# define STATUS_LPC_INVALID_CONNECTION_USAGE ((NTSTATUS) 0xC0000706L)
+#endif
+
+#ifndef STATUS_LPC_REQUESTS_NOT_ALLOWED
+# define STATUS_LPC_REQUESTS_NOT_ALLOWED ((NTSTATUS) 0xC0000707L)
+#endif
+
+#ifndef STATUS_RESOURCE_IN_USE
+# define STATUS_RESOURCE_IN_USE ((NTSTATUS) 0xC0000708L)
+#endif
+
+#ifndef STATUS_HARDWARE_MEMORY_ERROR
+# define STATUS_HARDWARE_MEMORY_ERROR ((NTSTATUS) 0xC0000709L)
+#endif
+
+#ifndef STATUS_THREADPOOL_HANDLE_EXCEPTION
+# define STATUS_THREADPOOL_HANDLE_EXCEPTION ((NTSTATUS) 0xC000070AL)
+#endif
+
+#ifndef STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070BL)
+#endif
+
+#ifndef STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070CL)
+#endif
+
+#ifndef STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070DL)
+#endif
+
+#ifndef STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED
+# define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED ((NTSTATUS) 0xC000070EL)
+#endif
+
+#ifndef STATUS_THREADPOOL_RELEASED_DURING_OPERATION
+# define STATUS_THREADPOOL_RELEASED_DURING_OPERATION ((NTSTATUS) 0xC000070FL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING
+# define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000710L)
+#endif
+
+#ifndef STATUS_APC_RETURNED_WHILE_IMPERSONATING
+# define STATUS_APC_RETURNED_WHILE_IMPERSONATING ((NTSTATUS) 0xC0000711L)
+#endif
+
+#ifndef STATUS_PROCESS_IS_PROTECTED
+# define STATUS_PROCESS_IS_PROTECTED ((NTSTATUS) 0xC0000712L)
+#endif
+
+#ifndef STATUS_MCA_EXCEPTION
+# define STATUS_MCA_EXCEPTION ((NTSTATUS) 0xC0000713L)
+#endif
+
+#ifndef STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE
+# define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE ((NTSTATUS) 0xC0000714L)
+#endif
+
+#ifndef STATUS_SYMLINK_CLASS_DISABLED
+# define STATUS_SYMLINK_CLASS_DISABLED ((NTSTATUS) 0xC0000715L)
+#endif
+
+#ifndef STATUS_INVALID_IDN_NORMALIZATION
+# define STATUS_INVALID_IDN_NORMALIZATION ((NTSTATUS) 0xC0000716L)
+#endif
+
+#ifndef STATUS_NO_UNICODE_TRANSLATION
+# define STATUS_NO_UNICODE_TRANSLATION ((NTSTATUS) 0xC0000717L)
+#endif
+
+#ifndef STATUS_ALREADY_REGISTERED
+# define STATUS_ALREADY_REGISTERED ((NTSTATUS) 0xC0000718L)
+#endif
+
+#ifndef STATUS_CONTEXT_MISMATCH
+# define STATUS_CONTEXT_MISMATCH ((NTSTATUS) 0xC0000719L)
+#endif
+
+#ifndef STATUS_PORT_ALREADY_HAS_COMPLETION_LIST
+# define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST ((NTSTATUS) 0xC000071AL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_THREAD_PRIORITY
+# define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY ((NTSTATUS) 0xC000071BL)
+#endif
+
+#ifndef STATUS_INVALID_THREAD
+# define STATUS_INVALID_THREAD ((NTSTATUS) 0xC000071CL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_TRANSACTION
+# define STATUS_CALLBACK_RETURNED_TRANSACTION ((NTSTATUS) 0xC000071DL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_LDR_LOCK
+# define STATUS_CALLBACK_RETURNED_LDR_LOCK ((NTSTATUS) 0xC000071EL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_LANG
+# define STATUS_CALLBACK_RETURNED_LANG ((NTSTATUS) 0xC000071FL)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_PRI_BACK
+# define STATUS_CALLBACK_RETURNED_PRI_BACK ((NTSTATUS) 0xC0000720L)
+#endif
+
+#ifndef STATUS_CALLBACK_RETURNED_THREAD_AFFINITY
+# define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY ((NTSTATUS) 0xC0000721L)
+#endif
+
+#ifndef STATUS_DISK_REPAIR_DISABLED
+# define STATUS_DISK_REPAIR_DISABLED ((NTSTATUS) 0xC0000800L)
+#endif
+
+#ifndef STATUS_DS_DOMAIN_RENAME_IN_PROGRESS
+# define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS ((NTSTATUS) 0xC0000801L)
+#endif
+
+#ifndef STATUS_DISK_QUOTA_EXCEEDED
+# define STATUS_DISK_QUOTA_EXCEEDED ((NTSTATUS) 0xC0000802L)
+#endif
+
+#ifndef STATUS_DATA_LOST_REPAIR
+# define STATUS_DATA_LOST_REPAIR ((NTSTATUS) 0x80000803L)
+#endif
+
+#ifndef STATUS_CONTENT_BLOCKED
+# define STATUS_CONTENT_BLOCKED ((NTSTATUS) 0xC0000804L)
+#endif
+
+#ifndef STATUS_BAD_CLUSTERS
+# define STATUS_BAD_CLUSTERS ((NTSTATUS) 0xC0000805L)
+#endif
+
+#ifndef STATUS_VOLUME_DIRTY
+# define STATUS_VOLUME_DIRTY ((NTSTATUS) 0xC0000806L)
+#endif
+
+#ifndef STATUS_FILE_CHECKED_OUT
+# define STATUS_FILE_CHECKED_OUT ((NTSTATUS) 0xC0000901L)
+#endif
+
+#ifndef STATUS_CHECKOUT_REQUIRED
+# define STATUS_CHECKOUT_REQUIRED ((NTSTATUS) 0xC0000902L)
+#endif
+
+#ifndef STATUS_BAD_FILE_TYPE
+# define STATUS_BAD_FILE_TYPE ((NTSTATUS) 0xC0000903L)
+#endif
+
+#ifndef STATUS_FILE_TOO_LARGE
+# define STATUS_FILE_TOO_LARGE ((NTSTATUS) 0xC0000904L)
+#endif
+
+#ifndef STATUS_FORMS_AUTH_REQUIRED
+# define STATUS_FORMS_AUTH_REQUIRED ((NTSTATUS) 0xC0000905L)
+#endif
+
+#ifndef STATUS_VIRUS_INFECTED
+# define STATUS_VIRUS_INFECTED ((NTSTATUS) 0xC0000906L)
+#endif
+
+#ifndef STATUS_VIRUS_DELETED
+# define STATUS_VIRUS_DELETED ((NTSTATUS) 0xC0000907L)
+#endif
+
+#ifndef STATUS_BAD_MCFG_TABLE
+# define STATUS_BAD_MCFG_TABLE ((NTSTATUS) 0xC0000908L)
+#endif
+
+#ifndef STATUS_CANNOT_BREAK_OPLOCK
+# define STATUS_CANNOT_BREAK_OPLOCK ((NTSTATUS) 0xC0000909L)
+#endif
+
+#ifndef STATUS_WOW_ASSERTION
+# define STATUS_WOW_ASSERTION ((NTSTATUS) 0xC0009898L)
+#endif
+
+#ifndef STATUS_INVALID_SIGNATURE
+# define STATUS_INVALID_SIGNATURE ((NTSTATUS) 0xC000A000L)
+#endif
+
+#ifndef STATUS_HMAC_NOT_SUPPORTED
+# define STATUS_HMAC_NOT_SUPPORTED ((NTSTATUS) 0xC000A001L)
+#endif
+
+#ifndef STATUS_AUTH_TAG_MISMATCH
+# define STATUS_AUTH_TAG_MISMATCH ((NTSTATUS) 0xC000A002L)
+#endif
+
+#ifndef STATUS_IPSEC_QUEUE_OVERFLOW
+# define STATUS_IPSEC_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A010L)
+#endif
+
+#ifndef STATUS_ND_QUEUE_OVERFLOW
+# define STATUS_ND_QUEUE_OVERFLOW ((NTSTATUS) 0xC000A011L)
+#endif
+
+#ifndef STATUS_HOPLIMIT_EXCEEDED
+# define STATUS_HOPLIMIT_EXCEEDED ((NTSTATUS) 0xC000A012L)
+#endif
+
+#ifndef STATUS_PROTOCOL_NOT_SUPPORTED
+# define STATUS_PROTOCOL_NOT_SUPPORTED ((NTSTATUS) 0xC000A013L)
+#endif
+
+#ifndef STATUS_FASTPATH_REJECTED
+# define STATUS_FASTPATH_REJECTED ((NTSTATUS) 0xC000A014L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED
+# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED ((NTSTATUS) 0xC000A080L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR
+# define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR ((NTSTATUS) 0xC000A081L)
+#endif
+
+#ifndef STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR
+# define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR ((NTSTATUS) 0xC000A082L)
+#endif
+
+#ifndef STATUS_XML_PARSE_ERROR
+# define STATUS_XML_PARSE_ERROR ((NTSTATUS) 0xC000A083L)
+#endif
+
+#ifndef STATUS_XMLDSIG_ERROR
+# define STATUS_XMLDSIG_ERROR ((NTSTATUS) 0xC000A084L)
+#endif
+
+#ifndef STATUS_WRONG_COMPARTMENT
+# define STATUS_WRONG_COMPARTMENT ((NTSTATUS) 0xC000A085L)
+#endif
+
+#ifndef STATUS_AUTHIP_FAILURE
+# define STATUS_AUTHIP_FAILURE ((NTSTATUS) 0xC000A086L)
+#endif
+
+#ifndef STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS
+# define STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS ((NTSTATUS) 0xC000A087L)
+#endif
+
+#ifndef STATUS_DS_OID_NOT_FOUND
+# define STATUS_DS_OID_NOT_FOUND ((NTSTATUS) 0xC000A088L)
+#endif
+
+#ifndef STATUS_HASH_NOT_SUPPORTED
+# define STATUS_HASH_NOT_SUPPORTED ((NTSTATUS) 0xC000A100L)
+#endif
+
+#ifndef STATUS_HASH_NOT_PRESENT
+# define STATUS_HASH_NOT_PRESENT ((NTSTATUS) 0xC000A101L)
+#endif
+
+/* This is not the NTSTATUS_FROM_WIN32 that the DDK provides, because the DDK
+ * got it wrong! */
+#ifdef NTSTATUS_FROM_WIN32
+# undef NTSTATUS_FROM_WIN32
+#endif
+#define NTSTATUS_FROM_WIN32(error) ((NTSTATUS) (error) <= 0 ? \
+        ((NTSTATUS) (error)) : ((NTSTATUS) (((error) & 0x0000FFFF) | \
+        (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_WARNING)))
+
+#ifndef JOB_OBJECT_LIMIT_PROCESS_MEMORY
+# define JOB_OBJECT_LIMIT_PROCESS_MEMORY             0x00000100
+#endif
+#ifndef JOB_OBJECT_LIMIT_JOB_MEMORY
+# define JOB_OBJECT_LIMIT_JOB_MEMORY                 0x00000200
+#endif
+#ifndef JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
+# define JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION 0x00000400
+#endif
+#ifndef JOB_OBJECT_LIMIT_BREAKAWAY_OK
+# define JOB_OBJECT_LIMIT_BREAKAWAY_OK               0x00000800
+#endif
+#ifndef JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK
+# define JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK        0x00001000
+#endif
+#ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
+# define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE          0x00002000
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x00000002
+#endif
+
+/* from winternl.h */
+#if !defined(__UNICODE_STRING_DEFINED) && defined(__MINGW32__)
+#define __UNICODE_STRING_DEFINED
+#endif
+typedef struct _UNICODE_STRING {
+  USHORT Length;
+  USHORT MaximumLength;
+  PWSTR  Buffer;
+} UNICODE_STRING, *PUNICODE_STRING;
+
+typedef const UNICODE_STRING *PCUNICODE_STRING;
+
+/* from ntifs.h */
+#ifndef DEVICE_TYPE
+# define DEVICE_TYPE DWORD
+#endif
+
+/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does
+ * not.
+ */
+#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
+  typedef struct _REPARSE_DATA_BUFFER {
+    ULONG  ReparseTag;
+    USHORT ReparseDataLength;
+    USHORT Reserved;
+    union {
+      struct {
+        USHORT SubstituteNameOffset;
+        USHORT SubstituteNameLength;
+        USHORT PrintNameOffset;
+        USHORT PrintNameLength;
+        ULONG Flags;
+        WCHAR PathBuffer[1];
+      } SymbolicLinkReparseBuffer;
+      struct {
+        USHORT SubstituteNameOffset;
+        USHORT SubstituteNameLength;
+        USHORT PrintNameOffset;
+        USHORT PrintNameLength;
+        WCHAR PathBuffer[1];
+      } MountPointReparseBuffer;
+      struct {
+        UCHAR  DataBuffer[1];
+      } GenericReparseBuffer;
+      struct {
+        ULONG StringCount;
+        WCHAR StringList[1];
+      } AppExecLinkReparseBuffer;
+    };
+  } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+#endif
+
+typedef struct _IO_STATUS_BLOCK {
+  union {
+    NTSTATUS Status;
+    PVOID Pointer;
+  };
+  ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FILE_INFORMATION_CLASS {
+  FileDirectoryInformation = 1,
+  FileFullDirectoryInformation,
+  FileBothDirectoryInformation,
+  FileBasicInformation,
+  FileStandardInformation,
+  FileInternalInformation,
+  FileEaInformation,
+  FileAccessInformation,
+  FileNameInformation,
+  FileRenameInformation,
+  FileLinkInformation,
+  FileNamesInformation,
+  FileDispositionInformation,
+  FilePositionInformation,
+  FileFullEaInformation,
+  FileModeInformation,
+  FileAlignmentInformation,
+  FileAllInformation,
+  FileAllocationInformation,
+  FileEndOfFileInformation,
+  FileAlternateNameInformation,
+  FileStreamInformation,
+  FilePipeInformation,
+  FilePipeLocalInformation,
+  FilePipeRemoteInformation,
+  FileMailslotQueryInformation,
+  FileMailslotSetInformation,
+  FileCompressionInformation,
+  FileObjectIdInformation,
+  FileCompletionInformation,
+  FileMoveClusterInformation,
+  FileQuotaInformation,
+  FileReparsePointInformation,
+  FileNetworkOpenInformation,
+  FileAttributeTagInformation,
+  FileTrackingInformation,
+  FileIdBothDirectoryInformation,
+  FileIdFullDirectoryInformation,
+  FileValidDataLengthInformation,
+  FileShortNameInformation,
+  FileIoCompletionNotificationInformation,
+  FileIoStatusBlockRangeInformation,
+  FileIoPriorityHintInformation,
+  FileSfioReserveInformation,
+  FileSfioVolumeInformation,
+  FileHardLinkInformation,
+  FileProcessIdsUsingFileInformation,
+  FileNormalizedNameInformation,
+  FileNetworkPhysicalNameInformation,
+  FileIdGlobalTxDirectoryInformation,
+  FileIsRemoteDeviceInformation,
+  FileAttributeCacheInformation,
+  FileNumaNodeInformation,
+  FileStandardLinkInformation,
+  FileRemoteProtocolInformation,
+  FileMaximumInformation
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef struct _FILE_DIRECTORY_INFORMATION {
+  ULONG NextEntryOffset;
+  ULONG FileIndex;
+  LARGE_INTEGER CreationTime;
+  LARGE_INTEGER LastAccessTime;
+  LARGE_INTEGER LastWriteTime;
+  LARGE_INTEGER ChangeTime;
+  LARGE_INTEGER EndOfFile;
+  LARGE_INTEGER AllocationSize;
+  ULONG FileAttributes;
+  ULONG FileNameLength;
+  WCHAR FileName[1];
+} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
+
+typedef struct _FILE_BOTH_DIR_INFORMATION {
+  ULONG NextEntryOffset;
+  ULONG FileIndex;
+  LARGE_INTEGER CreationTime;
+  LARGE_INTEGER LastAccessTime;
+  LARGE_INTEGER LastWriteTime;
+  LARGE_INTEGER ChangeTime;
+  LARGE_INTEGER EndOfFile;
+  LARGE_INTEGER AllocationSize;
+  ULONG FileAttributes;
+  ULONG FileNameLength;
+  ULONG EaSize;
+  CCHAR ShortNameLength;
+  WCHAR ShortName[12];
+  WCHAR FileName[1];
+} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
+
+typedef struct _FILE_BASIC_INFORMATION {
+  LARGE_INTEGER CreationTime;
+  LARGE_INTEGER LastAccessTime;
+  LARGE_INTEGER LastWriteTime;
+  LARGE_INTEGER ChangeTime;
+  DWORD FileAttributes;
+} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
+typedef struct _FILE_STANDARD_INFORMATION {
+  LARGE_INTEGER AllocationSize;
+  LARGE_INTEGER EndOfFile;
+  ULONG         NumberOfLinks;
+  BOOLEAN       DeletePending;
+  BOOLEAN       Directory;
+} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
+
+typedef struct _FILE_INTERNAL_INFORMATION {
+  LARGE_INTEGER IndexNumber;
+} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION;
+
+typedef struct _FILE_EA_INFORMATION {
+  ULONG EaSize;
+} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION;
+
+typedef struct _FILE_ACCESS_INFORMATION {
+  ACCESS_MASK AccessFlags;
+} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
+
+typedef struct _FILE_POSITION_INFORMATION {
+  LARGE_INTEGER CurrentByteOffset;
+} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
+
+typedef struct _FILE_MODE_INFORMATION {
+  ULONG Mode;
+} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION;
+
+typedef struct _FILE_ALIGNMENT_INFORMATION {
+  ULONG AlignmentRequirement;
+} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION;
+
+typedef struct _FILE_NAME_INFORMATION {
+  ULONG FileNameLength;
+  WCHAR FileName[1];
+} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
+
+typedef struct _FILE_END_OF_FILE_INFORMATION {
+  LARGE_INTEGER  EndOfFile;
+} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION;
+
+typedef struct _FILE_ALL_INFORMATION {
+  FILE_BASIC_INFORMATION     BasicInformation;
+  FILE_STANDARD_INFORMATION  StandardInformation;
+  FILE_INTERNAL_INFORMATION  InternalInformation;
+  FILE_EA_INFORMATION        EaInformation;
+  FILE_ACCESS_INFORMATION    AccessInformation;
+  FILE_POSITION_INFORMATION  PositionInformation;
+  FILE_MODE_INFORMATION      ModeInformation;
+  FILE_ALIGNMENT_INFORMATION AlignmentInformation;
+  FILE_NAME_INFORMATION      NameInformation;
+} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION;
+
+typedef struct _FILE_DISPOSITION_INFORMATION {
+  BOOLEAN DeleteFile;
+} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
+
+typedef struct _FILE_PIPE_LOCAL_INFORMATION {
+  ULONG NamedPipeType;
+  ULONG NamedPipeConfiguration;
+  ULONG MaximumInstances;
+  ULONG CurrentInstances;
+  ULONG InboundQuota;
+  ULONG ReadDataAvailable;
+  ULONG OutboundQuota;
+  ULONG WriteQuotaAvailable;
+  ULONG NamedPipeState;
+  ULONG NamedPipeEnd;
+} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
+
+#define FILE_SYNCHRONOUS_IO_ALERT               0x00000010
+#define FILE_SYNCHRONOUS_IO_NONALERT            0x00000020
+
+typedef enum _FS_INFORMATION_CLASS {
+  FileFsVolumeInformation       = 1,
+  FileFsLabelInformation        = 2,
+  FileFsSizeInformation         = 3,
+  FileFsDeviceInformation       = 4,
+  FileFsAttributeInformation    = 5,
+  FileFsControlInformation      = 6,
+  FileFsFullSizeInformation     = 7,
+  FileFsObjectIdInformation     = 8,
+  FileFsDriverPathInformation   = 9,
+  FileFsVolumeFlagsInformation  = 10,
+  FileFsSectorSizeInformation   = 11
+} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
+
+typedef struct _FILE_FS_VOLUME_INFORMATION {
+  LARGE_INTEGER VolumeCreationTime;
+  ULONG         VolumeSerialNumber;
+  ULONG         VolumeLabelLength;
+  BOOLEAN       SupportsObjects;
+  WCHAR         VolumeLabel[1];
+} FILE_FS_VOLUME_INFORMATION, *PFILE_FS_VOLUME_INFORMATION;
+
+typedef struct _FILE_FS_LABEL_INFORMATION {
+  ULONG VolumeLabelLength;
+  WCHAR VolumeLabel[1];
+} FILE_FS_LABEL_INFORMATION, *PFILE_FS_LABEL_INFORMATION;
+
+typedef struct _FILE_FS_SIZE_INFORMATION {
+  LARGE_INTEGER TotalAllocationUnits;
+  LARGE_INTEGER AvailableAllocationUnits;
+  ULONG         SectorsPerAllocationUnit;
+  ULONG         BytesPerSector;
+} FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION;
+
+typedef struct _FILE_FS_DEVICE_INFORMATION {
+  DEVICE_TYPE DeviceType;
+  ULONG       Characteristics;
+} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION;
+
+typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
+  ULONG FileSystemAttributes;
+  LONG  MaximumComponentNameLength;
+  ULONG FileSystemNameLength;
+  WCHAR FileSystemName[1];
+} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
+
+typedef struct _FILE_FS_CONTROL_INFORMATION {
+  LARGE_INTEGER FreeSpaceStartFiltering;
+  LARGE_INTEGER FreeSpaceThreshold;
+  LARGE_INTEGER FreeSpaceStopFiltering;
+  LARGE_INTEGER DefaultQuotaThreshold;
+  LARGE_INTEGER DefaultQuotaLimit;
+  ULONG         FileSystemControlFlags;
+} FILE_FS_CONTROL_INFORMATION, *PFILE_FS_CONTROL_INFORMATION;
+
+typedef struct _FILE_FS_FULL_SIZE_INFORMATION {
+  LARGE_INTEGER TotalAllocationUnits;
+  LARGE_INTEGER CallerAvailableAllocationUnits;
+  LARGE_INTEGER ActualAvailableAllocationUnits;
+  ULONG         SectorsPerAllocationUnit;
+  ULONG         BytesPerSector;
+} FILE_FS_FULL_SIZE_INFORMATION, *PFILE_FS_FULL_SIZE_INFORMATION;
+
+typedef struct _FILE_FS_OBJECTID_INFORMATION {
+  UCHAR ObjectId[16];
+  UCHAR ExtendedInfo[48];
+} FILE_FS_OBJECTID_INFORMATION, *PFILE_FS_OBJECTID_INFORMATION;
+
+typedef struct _FILE_FS_DRIVER_PATH_INFORMATION {
+  BOOLEAN DriverInPath;
+  ULONG   DriverNameLength;
+  WCHAR   DriverName[1];
+} FILE_FS_DRIVER_PATH_INFORMATION, *PFILE_FS_DRIVER_PATH_INFORMATION;
+
+typedef struct _FILE_FS_VOLUME_FLAGS_INFORMATION {
+  ULONG Flags;
+} FILE_FS_VOLUME_FLAGS_INFORMATION, *PFILE_FS_VOLUME_FLAGS_INFORMATION;
+
+typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION {
+  ULONG LogicalBytesPerSector;
+  ULONG PhysicalBytesPerSectorForAtomicity;
+  ULONG PhysicalBytesPerSectorForPerformance;
+  ULONG FileSystemEffectivePhysicalBytesPerSectorForAtomicity;
+  ULONG Flags;
+  ULONG ByteOffsetForSectorAlignment;
+  ULONG ByteOffsetForPartitionAlignment;
+} FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION;
+
+typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
+    LARGE_INTEGER IdleTime;
+    LARGE_INTEGER KernelTime;
+    LARGE_INTEGER UserTime;
+    LARGE_INTEGER DpcTime;
+    LARGE_INTEGER InterruptTime;
+    ULONG InterruptCount;
+} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
+
+#ifndef SystemProcessorPerformanceInformation
+# define SystemProcessorPerformanceInformation 8
+#endif
+
+#ifndef ProcessConsoleHostProcess
+# define ProcessConsoleHostProcess 49
+#endif
+
+#ifndef FILE_DEVICE_FILE_SYSTEM
+# define FILE_DEVICE_FILE_SYSTEM 0x00000009
+#endif
+
+#ifndef FILE_DEVICE_NETWORK
+# define FILE_DEVICE_NETWORK 0x00000012
+#endif
+
+#ifndef METHOD_BUFFERED
+# define METHOD_BUFFERED 0
+#endif
+
+#ifndef METHOD_IN_DIRECT
+# define METHOD_IN_DIRECT 1
+#endif
+
+#ifndef METHOD_OUT_DIRECT
+# define METHOD_OUT_DIRECT 2
+#endif
+
+#ifndef METHOD_NEITHER
+#define METHOD_NEITHER 3
+#endif
+
+#ifndef METHOD_DIRECT_TO_HARDWARE
+# define METHOD_DIRECT_TO_HARDWARE METHOD_IN_DIRECT
+#endif
+
+#ifndef METHOD_DIRECT_FROM_HARDWARE
+# define METHOD_DIRECT_FROM_HARDWARE METHOD_OUT_DIRECT
+#endif
+
+#ifndef FILE_ANY_ACCESS
+# define FILE_ANY_ACCESS 0
+#endif
+
+#ifndef FILE_SPECIAL_ACCESS
+# define FILE_SPECIAL_ACCESS (FILE_ANY_ACCESS)
+#endif
+
+#ifndef FILE_READ_ACCESS
+# define FILE_READ_ACCESS 0x0001
+#endif
+
+#ifndef FILE_WRITE_ACCESS
+# define FILE_WRITE_ACCESS 0x0002
+#endif
+
+#ifndef CTL_CODE
+# define CTL_CODE(device_type, function, method, access)                      \
+    (((device_type) << 16) | ((access) << 14) | ((function) << 2) | (method))
+#endif
+
+#ifndef FSCTL_SET_REPARSE_POINT
+# define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM,            \
+                                          41,                                 \
+                                          METHOD_BUFFERED,                    \
+                                          FILE_SPECIAL_ACCESS)
+#endif
+
+#ifndef FSCTL_GET_REPARSE_POINT
+# define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM,            \
+                                          42,                                 \
+                                          METHOD_BUFFERED,                    \
+                                          FILE_ANY_ACCESS)
+#endif
+
+#ifndef FSCTL_DELETE_REPARSE_POINT
+# define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM,         \
+                                             43,                              \
+                                             METHOD_BUFFERED,                 \
+                                             FILE_SPECIAL_ACCESS)
+#endif
+
+#ifndef IO_REPARSE_TAG_SYMLINK
+# define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#endif
+#ifndef IO_REPARSE_TAG_APPEXECLINK
+# define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL)
+#endif
+
+typedef VOID (NTAPI *PIO_APC_ROUTINE)
+             (PVOID ApcContext,
+              PIO_STATUS_BLOCK IoStatusBlock,
+              ULONG Reserved);
+
+typedef NTSTATUS (NTAPI *sRtlGetVersion)
+                 (PRTL_OSVERSIONINFOW lpVersionInformation);
+
+typedef ULONG (NTAPI *sRtlNtStatusToDosError)
+              (NTSTATUS Status);
+
+typedef NTSTATUS (NTAPI *sNtDeviceIoControlFile)
+                 (HANDLE FileHandle,
+                  HANDLE Event,
+                  PIO_APC_ROUTINE ApcRoutine,
+                  PVOID ApcContext,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  ULONG IoControlCode,
+                  PVOID InputBuffer,
+                  ULONG InputBufferLength,
+                  PVOID OutputBuffer,
+                  ULONG OutputBufferLength);
+
+typedef NTSTATUS (NTAPI *sNtQueryInformationFile)
+                 (HANDLE FileHandle,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FileInformation,
+                  ULONG Length,
+                  FILE_INFORMATION_CLASS FileInformationClass);
+
+typedef NTSTATUS (NTAPI *sNtSetInformationFile)
+                 (HANDLE FileHandle,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FileInformation,
+                  ULONG Length,
+                  FILE_INFORMATION_CLASS FileInformationClass);
+
+typedef NTSTATUS (NTAPI *sNtQueryVolumeInformationFile)
+                 (HANDLE FileHandle,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FsInformation,
+                  ULONG Length,
+                  FS_INFORMATION_CLASS FsInformationClass);
+
+typedef NTSTATUS (NTAPI *sNtQuerySystemInformation)
+                 (UINT SystemInformationClass,
+                  PVOID SystemInformation,
+                  ULONG SystemInformationLength,
+                  PULONG ReturnLength);
+
+typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile)
+                 (HANDLE FileHandle,
+                  HANDLE Event,
+                  PIO_APC_ROUTINE ApcRoutine,
+                  PVOID ApcContext,
+                  PIO_STATUS_BLOCK IoStatusBlock,
+                  PVOID FileInformation,
+                  ULONG Length,
+                  FILE_INFORMATION_CLASS FileInformationClass,
+                  BOOLEAN ReturnSingleEntry,
+                  PUNICODE_STRING FileName,
+                  BOOLEAN RestartScan
+                );
+
+typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
+                 (HANDLE ProcessHandle,
+                  UINT ProcessInformationClass,
+                  PVOID ProcessInformation,
+                  ULONG Length,
+                  PULONG ReturnLength);
+
+/*
+ * Kernel32 headers
+ */
+#ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
+# define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1
+#endif
+
+#ifndef FILE_SKIP_SET_EVENT_ON_HANDLE
+# define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
+# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
+#endif
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+  typedef struct _OVERLAPPED_ENTRY {
+      ULONG_PTR lpCompletionKey;
+      LPOVERLAPPED lpOverlapped;
+      ULONG_PTR Internal;
+      DWORD dwNumberOfBytesTransferred;
+  } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;
+#endif
+
+/* from wincon.h */
+#ifndef ENABLE_INSERT_MODE
+# define ENABLE_INSERT_MODE 0x20
+#endif
+
+#ifndef ENABLE_QUICK_EDIT_MODE
+# define ENABLE_QUICK_EDIT_MODE 0x40
+#endif
+
+#ifndef ENABLE_EXTENDED_FLAGS
+# define ENABLE_EXTENDED_FLAGS 0x80
+#endif
+
+/* from winerror.h */
+#ifndef ERROR_ELEVATION_REQUIRED
+# define ERROR_ELEVATION_REQUIRED 740
+#endif
+
+#ifndef ERROR_SYMLINK_NOT_SUPPORTED
+# define ERROR_SYMLINK_NOT_SUPPORTED 1464
+#endif
+
+#ifndef ERROR_MUI_FILE_NOT_FOUND
+# define ERROR_MUI_FILE_NOT_FOUND 15100
+#endif
+
+#ifndef ERROR_MUI_INVALID_FILE
+# define ERROR_MUI_INVALID_FILE 15101
+#endif
+
+#ifndef ERROR_MUI_INVALID_RC_CONFIG
+# define ERROR_MUI_INVALID_RC_CONFIG 15102
+#endif
+
+#ifndef ERROR_MUI_INVALID_LOCALE_NAME
+# define ERROR_MUI_INVALID_LOCALE_NAME 15103
+#endif
+
+#ifndef ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME
+# define ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME 15104
+#endif
+
+#ifndef ERROR_MUI_FILE_NOT_LOADED
+# define ERROR_MUI_FILE_NOT_LOADED 15105
+#endif
+
+typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx)
+             (HANDLE CompletionPort,
+              LPOVERLAPPED_ENTRY lpCompletionPortEntries,
+              ULONG ulCount,
+              PULONG ulNumEntriesRemoved,
+              DWORD dwMilliseconds,
+              BOOL fAlertable);
+
+/* from powerbase.h */
+#ifndef DEVICE_NOTIFY_CALLBACK
+# define DEVICE_NOTIFY_CALLBACK 2
+#endif
+
+#ifndef PBT_APMRESUMEAUTOMATIC
+# define PBT_APMRESUMEAUTOMATIC 18
+#endif
+
+#ifndef PBT_APMRESUMESUSPEND
+# define PBT_APMRESUMESUSPEND 7
+#endif
+
+typedef ULONG CALLBACK _DEVICE_NOTIFY_CALLBACK_ROUTINE(
+  PVOID Context,
+  ULONG Type,
+  PVOID Setting
+);
+typedef _DEVICE_NOTIFY_CALLBACK_ROUTINE* _PDEVICE_NOTIFY_CALLBACK_ROUTINE;
+
+typedef struct _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
+  _PDEVICE_NOTIFY_CALLBACK_ROUTINE Callback;
+  PVOID Context;
+} _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS, *_PDEVICE_NOTIFY_SUBSCRIBE_PARAMETERS;
+
+typedef PVOID _HPOWERNOTIFY;
+typedef _HPOWERNOTIFY *_PHPOWERNOTIFY;
+
+typedef DWORD (WINAPI *sPowerRegisterSuspendResumeNotification)
+              (DWORD         Flags,
+               HANDLE        Recipient,
+               _PHPOWERNOTIFY RegistrationHandle);
+
+/* from Winuser.h */
+typedef VOID (CALLBACK* WINEVENTPROC)
+             (HWINEVENTHOOK hWinEventHook,
+              DWORD         event,
+              HWND          hwnd,
+              LONG          idObject,
+              LONG          idChild,
+              DWORD         idEventThread,
+              DWORD         dwmsEventTime);
+
+typedef HWINEVENTHOOK (WINAPI *sSetWinEventHook)
+                      (UINT         eventMin,
+                       UINT         eventMax,
+                       HMODULE      hmodWinEventProc,
+                       WINEVENTPROC lpfnWinEventProc,
+                       DWORD        idProcess,
+                       DWORD        idThread,
+                       UINT         dwflags);
+
+/* From mstcpip.h */
+typedef struct _TCP_INITIAL_RTO_PARAMETERS {
+  USHORT Rtt;
+  UCHAR  MaxSynRetransmissions;
+} TCP_INITIAL_RTO_PARAMETERS, *PTCP_INITIAL_RTO_PARAMETERS;
+
+#ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS
+# define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ((UCHAR) -2)
+#endif
+#ifndef SIO_TCP_INITIAL_RTO
+# define  SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17)
+#endif
+
+/* Ntdll function pointers */
+extern sRtlGetVersion pRtlGetVersion;
+extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
+extern sNtDeviceIoControlFile pNtDeviceIoControlFile;
+extern sNtQueryInformationFile pNtQueryInformationFile;
+extern sNtSetInformationFile pNtSetInformationFile;
+extern sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
+extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
+extern sNtQuerySystemInformation pNtQuerySystemInformation;
+extern sNtQueryInformationProcess pNtQueryInformationProcess;
+
+/* Kernel32 function pointers */
+extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
+
+/* Powrprof.dll function pointer */
+extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
+
+/* User32.dll function pointer */
+extern sSetWinEventHook pSetWinEventHook;
+
+#endif /* UV_WIN_WINAPI_H_ */
diff --git a/third_party/libuv/src/win/winsock.c b/third_party/libuv/src/win/winsock.c
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/winsock.c
@@ -0,0 +1,575 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+/* Whether there are any non-IFS LSPs stacked on TCP */
+int uv_tcp_non_ifs_lsp_ipv4;
+int uv_tcp_non_ifs_lsp_ipv6;
+
+/* Ip address used to bind to any port at any interface */
+struct sockaddr_in uv_addr_ip4_any_;
+struct sockaddr_in6 uv_addr_ip6_any_;
+
+
+/*
+ * Retrieves the pointer to a winsock extension function.
+ */
+static BOOL uv_get_extension_function(SOCKET socket, GUID guid,
+    void **target) {
+  int result;
+  DWORD bytes;
+
+  result = WSAIoctl(socket,
+                    SIO_GET_EXTENSION_FUNCTION_POINTER,
+                    &guid,
+                    sizeof(guid),
+                    (void*)target,
+                    sizeof(*target),
+                    &bytes,
+                    NULL,
+                    NULL);
+
+  if (result == SOCKET_ERROR) {
+    *target = NULL;
+    return FALSE;
+  } else {
+    return TRUE;
+  }
+}
+
+
+BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) {
+  const GUID wsaid_acceptex = WSAID_ACCEPTEX;
+  return uv_get_extension_function(socket, wsaid_acceptex, (void**)target);
+}
+
+
+BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) {
+  const GUID wsaid_connectex = WSAID_CONNECTEX;
+  return uv_get_extension_function(socket, wsaid_connectex, (void**)target);
+}
+
+
+
+void uv_winsock_init(void) {
+  WSADATA wsa_data;
+  int errorno;
+  SOCKET dummy;
+  WSAPROTOCOL_INFOW protocol_info;
+  int opt_len;
+
+  /* Set implicit binding address used by connectEx */
+  if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {
+    abort();
+  }
+
+  if (uv_ip6_addr("::", 0, &uv_addr_ip6_any_)) {
+    abort();
+  }
+
+  /* Skip initialization in safe mode without network support */
+  if (1 == GetSystemMetrics(SM_CLEANBOOT)) return;
+
+  /* Initialize winsock */
+  errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+  if (errorno != 0) {
+    uv_fatal_error(errorno, "WSAStartup");
+  }
+
+  /* Try to detect non-IFS LSPs */
+  uv_tcp_non_ifs_lsp_ipv4 = 1;
+  dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+  if (dummy != INVALID_SOCKET) {
+    opt_len = (int) sizeof protocol_info;
+    if (getsockopt(dummy,
+                   SOL_SOCKET,
+                   SO_PROTOCOL_INFOW,
+                   (char*) &protocol_info,
+                   &opt_len) == 0) {
+      if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)
+        uv_tcp_non_ifs_lsp_ipv4 = 0;
+    }
+    closesocket(dummy);
+  }
+
+  /* Try to detect IPV6 support and non-IFS LSPs */
+  uv_tcp_non_ifs_lsp_ipv6 = 1;
+  dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP);
+  if (dummy != INVALID_SOCKET) {
+    opt_len = (int) sizeof protocol_info;
+    if (getsockopt(dummy,
+                   SOL_SOCKET,
+                   SO_PROTOCOL_INFOW,
+                   (char*) &protocol_info,
+                   &opt_len) == 0) {
+      if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)
+        uv_tcp_non_ifs_lsp_ipv6 = 0;
+    }
+    closesocket(dummy);
+  }
+}
+
+
+int uv_ntstatus_to_winsock_error(NTSTATUS status) {
+  switch (status) {
+    case STATUS_SUCCESS:
+      return ERROR_SUCCESS;
+
+    case STATUS_PENDING:
+      return ERROR_IO_PENDING;
+
+    case STATUS_INVALID_HANDLE:
+    case STATUS_OBJECT_TYPE_MISMATCH:
+      return WSAENOTSOCK;
+
+    case STATUS_INSUFFICIENT_RESOURCES:
+    case STATUS_PAGEFILE_QUOTA:
+    case STATUS_COMMITMENT_LIMIT:
+    case STATUS_WORKING_SET_QUOTA:
+    case STATUS_NO_MEMORY:
+    case STATUS_QUOTA_EXCEEDED:
+    case STATUS_TOO_MANY_PAGING_FILES:
+    case STATUS_REMOTE_RESOURCES:
+      return WSAENOBUFS;
+
+    case STATUS_TOO_MANY_ADDRESSES:
+    case STATUS_SHARING_VIOLATION:
+    case STATUS_ADDRESS_ALREADY_EXISTS:
+      return WSAEADDRINUSE;
+
+    case STATUS_LINK_TIMEOUT:
+    case STATUS_IO_TIMEOUT:
+    case STATUS_TIMEOUT:
+      return WSAETIMEDOUT;
+
+    case STATUS_GRACEFUL_DISCONNECT:
+      return WSAEDISCON;
+
+    case STATUS_REMOTE_DISCONNECT:
+    case STATUS_CONNECTION_RESET:
+    case STATUS_LINK_FAILED:
+    case STATUS_CONNECTION_DISCONNECTED:
+    case STATUS_PORT_UNREACHABLE:
+    case STATUS_HOPLIMIT_EXCEEDED:
+      return WSAECONNRESET;
+
+    case STATUS_LOCAL_DISCONNECT:
+    case STATUS_TRANSACTION_ABORTED:
+    case STATUS_CONNECTION_ABORTED:
+      return WSAECONNABORTED;
+
+    case STATUS_BAD_NETWORK_PATH:
+    case STATUS_NETWORK_UNREACHABLE:
+    case STATUS_PROTOCOL_UNREACHABLE:
+      return WSAENETUNREACH;
+
+    case STATUS_HOST_UNREACHABLE:
+      return WSAEHOSTUNREACH;
+
+    case STATUS_CANCELLED:
+    case STATUS_REQUEST_ABORTED:
+      return WSAEINTR;
+
+    case STATUS_BUFFER_OVERFLOW:
+    case STATUS_INVALID_BUFFER_SIZE:
+      return WSAEMSGSIZE;
+
+    case STATUS_BUFFER_TOO_SMALL:
+    case STATUS_ACCESS_VIOLATION:
+      return WSAEFAULT;
+
+    case STATUS_DEVICE_NOT_READY:
+    case STATUS_REQUEST_NOT_ACCEPTED:
+      return WSAEWOULDBLOCK;
+
+    case STATUS_INVALID_NETWORK_RESPONSE:
+    case STATUS_NETWORK_BUSY:
+    case STATUS_NO_SUCH_DEVICE:
+    case STATUS_NO_SUCH_FILE:
+    case STATUS_OBJECT_PATH_NOT_FOUND:
+    case STATUS_OBJECT_NAME_NOT_FOUND:
+    case STATUS_UNEXPECTED_NETWORK_ERROR:
+      return WSAENETDOWN;
+
+    case STATUS_INVALID_CONNECTION:
+      return WSAENOTCONN;
+
+    case STATUS_REMOTE_NOT_LISTENING:
+    case STATUS_CONNECTION_REFUSED:
+      return WSAECONNREFUSED;
+
+    case STATUS_PIPE_DISCONNECTED:
+      return WSAESHUTDOWN;
+
+    case STATUS_CONFLICTING_ADDRESSES:
+    case STATUS_INVALID_ADDRESS:
+    case STATUS_INVALID_ADDRESS_COMPONENT:
+      return WSAEADDRNOTAVAIL;
+
+    case STATUS_NOT_SUPPORTED:
+    case STATUS_NOT_IMPLEMENTED:
+      return WSAEOPNOTSUPP;
+
+    case STATUS_ACCESS_DENIED:
+      return WSAEACCES;
+
+    default:
+      if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) &&
+          (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) {
+        /* It's a windows error that has been previously mapped to an ntstatus
+         * code. */
+        return (DWORD) (status & 0xffff);
+      } else {
+        /* The default fallback for unmappable ntstatus codes. */
+        return WSAEINVAL;
+      }
+  }
+}
+
+
+/*
+ * This function provides a workaround for a bug in the winsock implementation
+ * of WSARecv. The problem is that when SetFileCompletionNotificationModes is
+ * used to avoid IOCP notifications of completed reads, WSARecv does not
+ * reliably indicate whether we can expect a completion package to be posted
+ * when the receive buffer is smaller than the received datagram.
+ *
+ * However it is desirable to use SetFileCompletionNotificationModes because
+ * it yields a massive performance increase.
+ *
+ * This function provides a workaround for that bug, but it only works for the
+ * specific case that we need it for. E.g. it assumes that the "avoid iocp"
+ * bit has been set, and supports only overlapped operation. It also requires
+ * the user to use the default msafd driver, doesn't work when other LSPs are
+ * stacked on top of it.
+ */
+int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
+  NTSTATUS status;
+  void* apc_context;
+  IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
+  AFD_RECV_INFO info;
+  DWORD error;
+
+  if (overlapped == NULL || completion_routine != NULL) {
+    WSASetLastError(WSAEINVAL);
+    return SOCKET_ERROR;
+  }
+
+  info.BufferArray = buffers;
+  info.BufferCount = buffer_count;
+  info.AfdFlags = AFD_OVERLAPPED;
+  info.TdiFlags = TDI_RECEIVE_NORMAL;
+
+  if (*flags & MSG_PEEK) {
+    info.TdiFlags |= TDI_RECEIVE_PEEK;
+  }
+
+  if (*flags & MSG_PARTIAL) {
+    info.TdiFlags |= TDI_RECEIVE_PARTIAL;
+  }
+
+  if (!((intptr_t) overlapped->hEvent & 1)) {
+    apc_context = (void*) overlapped;
+  } else {
+    apc_context = NULL;
+  }
+
+  iosb->Status = STATUS_PENDING;
+  iosb->Pointer = 0;
+
+  status = pNtDeviceIoControlFile((HANDLE) socket,
+                                  overlapped->hEvent,
+                                  NULL,
+                                  apc_context,
+                                  iosb,
+                                  IOCTL_AFD_RECEIVE,
+                                  &info,
+                                  sizeof(info),
+                                  NULL,
+                                  0);
+
+  *flags = 0;
+  *bytes = (DWORD) iosb->Information;
+
+  switch (status) {
+    case STATUS_SUCCESS:
+      error = ERROR_SUCCESS;
+      break;
+
+    case STATUS_PENDING:
+      error = WSA_IO_PENDING;
+      break;
+
+    case STATUS_BUFFER_OVERFLOW:
+      error = WSAEMSGSIZE;
+      break;
+
+    case STATUS_RECEIVE_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL | MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL;
+      break;
+
+    default:
+      error = uv_ntstatus_to_winsock_error(status);
+      break;
+  }
+
+  WSASetLastError(error);
+
+  if (error == ERROR_SUCCESS) {
+    return 0;
+  } else {
+    return SOCKET_ERROR;
+  }
+}
+
+
+/* See description of uv_wsarecv_workaround. */
+int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
+    DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
+    int* addr_len, WSAOVERLAPPED *overlapped,
+    LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
+  NTSTATUS status;
+  void* apc_context;
+  IO_STATUS_BLOCK* iosb = (IO_STATUS_BLOCK*) &overlapped->Internal;
+  AFD_RECV_DATAGRAM_INFO info;
+  DWORD error;
+
+  if (overlapped == NULL || addr == NULL || addr_len == NULL ||
+      completion_routine != NULL) {
+    WSASetLastError(WSAEINVAL);
+    return SOCKET_ERROR;
+  }
+
+  info.BufferArray = buffers;
+  info.BufferCount = buffer_count;
+  info.AfdFlags = AFD_OVERLAPPED;
+  info.TdiFlags = TDI_RECEIVE_NORMAL;
+  info.Address = addr;
+  info.AddressLength = addr_len;
+
+  if (*flags & MSG_PEEK) {
+    info.TdiFlags |= TDI_RECEIVE_PEEK;
+  }
+
+  if (*flags & MSG_PARTIAL) {
+    info.TdiFlags |= TDI_RECEIVE_PARTIAL;
+  }
+
+  if (!((intptr_t) overlapped->hEvent & 1)) {
+    apc_context = (void*) overlapped;
+  } else {
+    apc_context = NULL;
+  }
+
+  iosb->Status = STATUS_PENDING;
+  iosb->Pointer = 0;
+
+  status = pNtDeviceIoControlFile((HANDLE) socket,
+                                  overlapped->hEvent,
+                                  NULL,
+                                  apc_context,
+                                  iosb,
+                                  IOCTL_AFD_RECEIVE_DATAGRAM,
+                                  &info,
+                                  sizeof(info),
+                                  NULL,
+                                  0);
+
+  *flags = 0;
+  *bytes = (DWORD) iosb->Information;
+
+  switch (status) {
+    case STATUS_SUCCESS:
+      error = ERROR_SUCCESS;
+      break;
+
+    case STATUS_PENDING:
+      error = WSA_IO_PENDING;
+      break;
+
+    case STATUS_BUFFER_OVERFLOW:
+      error = WSAEMSGSIZE;
+      break;
+
+    case STATUS_RECEIVE_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL_EXPEDITED:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL | MSG_OOB;
+      break;
+
+    case STATUS_RECEIVE_PARTIAL:
+      error = ERROR_SUCCESS;
+      *flags = MSG_PARTIAL;
+      break;
+
+    default:
+      error = uv_ntstatus_to_winsock_error(status);
+      break;
+  }
+
+  WSASetLastError(error);
+
+  if (error == ERROR_SUCCESS) {
+    return 0;
+  } else {
+    return SOCKET_ERROR;
+  }
+}
+
+
+int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
+    AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {
+  IO_STATUS_BLOCK iosb;
+  IO_STATUS_BLOCK* iosb_ptr;
+  HANDLE event = NULL;
+  void* apc_context;
+  NTSTATUS status;
+  DWORD error;
+
+  if (overlapped != NULL) {
+    /* Overlapped operation. */
+    iosb_ptr = (IO_STATUS_BLOCK*) &overlapped->Internal;
+    event = overlapped->hEvent;
+
+    /* Do not report iocp completion if hEvent is tagged. */
+    if ((uintptr_t) event & 1) {
+      event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1);
+      apc_context = NULL;
+    } else {
+      apc_context = overlapped;
+    }
+
+  } else {
+    /* Blocking operation. */
+    iosb_ptr = &iosb;
+    event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (event == NULL) {
+      return SOCKET_ERROR;
+    }
+    apc_context = NULL;
+  }
+
+  iosb_ptr->Status = STATUS_PENDING;
+  status = pNtDeviceIoControlFile((HANDLE) socket,
+                                  event,
+                                  NULL,
+                                  apc_context,
+                                  iosb_ptr,
+                                  IOCTL_AFD_POLL,
+                                  info_in,
+                                  sizeof *info_in,
+                                  info_out,
+                                  sizeof *info_out);
+
+  if (overlapped == NULL) {
+    /* If this is a blocking operation, wait for the event to become signaled,
+     * and then grab the real status from the io status block. */
+    if (status == STATUS_PENDING) {
+      DWORD r = WaitForSingleObject(event, INFINITE);
+
+      if (r == WAIT_FAILED) {
+        DWORD saved_error = GetLastError();
+        CloseHandle(event);
+        WSASetLastError(saved_error);
+        return SOCKET_ERROR;
+      }
+
+      status = iosb.Status;
+    }
+
+    CloseHandle(event);
+  }
+
+  switch (status) {
+    case STATUS_SUCCESS:
+      error = ERROR_SUCCESS;
+      break;
+
+    case STATUS_PENDING:
+      error = WSA_IO_PENDING;
+      break;
+
+    default:
+      error = uv_ntstatus_to_winsock_error(status);
+      break;
+  }
+
+  WSASetLastError(error);
+
+  if (error == ERROR_SUCCESS) {
+    return 0;
+  } else {
+    return SOCKET_ERROR;
+  }
+}
+
+int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
+                                            struct sockaddr_storage* storage) {
+  struct sockaddr_in* dest4;
+  struct sockaddr_in6* dest6;
+
+  if (addr == NULL)
+    return UV_EINVAL;
+
+  switch (addr->sa_family) {
+  case AF_INET:
+    dest4 = (struct sockaddr_in*) storage;
+    memcpy(dest4, addr, sizeof(*dest4));
+    if (dest4->sin_addr.s_addr == 0)
+      dest4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    return 0;
+  case AF_INET6:
+    dest6 = (struct sockaddr_in6*) storage;
+    memcpy(dest6, addr, sizeof(*dest6));
+    if (memcmp(&dest6->sin6_addr,
+               &uv_addr_ip6_any_.sin6_addr,
+               sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) {
+      struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT;
+      dest6->sin6_addr = init_sin6_addr;
+    }
+    return 0;
+  default:
+    return UV_EINVAL;
+  }
+}
diff --git a/third_party/libuv/src/win/winsock.h b/third_party/libuv/src/win/winsock.h
new file mode 100644
--- /dev/null
+++ b/third_party/libuv/src/win/winsock.h
@@ -0,0 +1,201 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_WIN_WINSOCK_H_
+#define UV_WIN_WINSOCK_H_
+
+#include <winsock2.h>
+#include <iptypes.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+
+#include "winapi.h"
+
+
+/*
+ * MinGW is missing these too
+ */
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+# define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+
+#ifndef TCP_KEEPALIVE
+# define TCP_KEEPALIVE 3
+#endif
+
+#ifndef IPV6_V6ONLY
+# define IPV6_V6ONLY 27
+#endif
+
+#ifndef IPV6_HOPLIMIT
+# define IPV6_HOPLIMIT 21
+#endif
+
+#ifndef SIO_BASE_HANDLE
+# define SIO_BASE_HANDLE 0x48000022
+#endif
+
+#ifndef MCAST_JOIN_SOURCE_GROUP
+# define MCAST_JOIN_SOURCE_GROUP 45
+#endif
+
+#ifndef MCAST_LEAVE_SOURCE_GROUP
+# define MCAST_LEAVE_SOURCE_GROUP 46
+#endif
+
+/*
+ * TDI defines that are only in the DDK.
+ * We only need receive flags so far.
+ */
+#ifndef TDI_RECEIVE_NORMAL
+  #define TDI_RECEIVE_BROADCAST           0x00000004
+  #define TDI_RECEIVE_MULTICAST           0x00000008
+  #define TDI_RECEIVE_PARTIAL             0x00000010
+  #define TDI_RECEIVE_NORMAL              0x00000020
+  #define TDI_RECEIVE_EXPEDITED           0x00000040
+  #define TDI_RECEIVE_PEEK                0x00000080
+  #define TDI_RECEIVE_NO_RESPONSE_EXP     0x00000100
+  #define TDI_RECEIVE_COPY_LOOKAHEAD      0x00000200
+  #define TDI_RECEIVE_ENTIRE_MESSAGE      0x00000400
+  #define TDI_RECEIVE_AT_DISPATCH_LEVEL   0x00000800
+  #define TDI_RECEIVE_CONTROL_INFO        0x00001000
+  #define TDI_RECEIVE_FORCE_INDICATION    0x00002000
+  #define TDI_RECEIVE_NO_PUSH             0x00004000
+#endif
+
+/*
+ * The "Auxiliary Function Driver" is the windows kernel-mode driver that does
+ * TCP, UDP etc. Winsock is just a layer that dispatches requests to it.
+ * Having these definitions allows us to bypass winsock and make an AFD kernel
+ * call directly, avoiding a bug in winsock's recvfrom implementation.
+ */
+
+#define AFD_NO_FAST_IO   0x00000001
+#define AFD_OVERLAPPED   0x00000002
+#define AFD_IMMEDIATE    0x00000004
+
+#define AFD_POLL_RECEIVE_BIT            0
+#define AFD_POLL_RECEIVE                (1 << AFD_POLL_RECEIVE_BIT)
+#define AFD_POLL_RECEIVE_EXPEDITED_BIT  1
+#define AFD_POLL_RECEIVE_EXPEDITED      (1 << AFD_POLL_RECEIVE_EXPEDITED_BIT)
+#define AFD_POLL_SEND_BIT               2
+#define AFD_POLL_SEND                   (1 << AFD_POLL_SEND_BIT)
+#define AFD_POLL_DISCONNECT_BIT         3
+#define AFD_POLL_DISCONNECT             (1 << AFD_POLL_DISCONNECT_BIT)
+#define AFD_POLL_ABORT_BIT              4
+#define AFD_POLL_ABORT                  (1 << AFD_POLL_ABORT_BIT)
+#define AFD_POLL_LOCAL_CLOSE_BIT        5
+#define AFD_POLL_LOCAL_CLOSE            (1 << AFD_POLL_LOCAL_CLOSE_BIT)
+#define AFD_POLL_CONNECT_BIT            6
+#define AFD_POLL_CONNECT                (1 << AFD_POLL_CONNECT_BIT)
+#define AFD_POLL_ACCEPT_BIT             7
+#define AFD_POLL_ACCEPT                 (1 << AFD_POLL_ACCEPT_BIT)
+#define AFD_POLL_CONNECT_FAIL_BIT       8
+#define AFD_POLL_CONNECT_FAIL           (1 << AFD_POLL_CONNECT_FAIL_BIT)
+#define AFD_POLL_QOS_BIT                9
+#define AFD_POLL_QOS                    (1 << AFD_POLL_QOS_BIT)
+#define AFD_POLL_GROUP_QOS_BIT          10
+#define AFD_POLL_GROUP_QOS              (1 << AFD_POLL_GROUP_QOS_BIT)
+
+#define AFD_NUM_POLL_EVENTS             11
+#define AFD_POLL_ALL                    ((1 << AFD_NUM_POLL_EVENTS) - 1)
+
+typedef struct _AFD_RECV_DATAGRAM_INFO {
+    LPWSABUF BufferArray;
+    ULONG BufferCount;
+    ULONG AfdFlags;
+    ULONG TdiFlags;
+    struct sockaddr* Address;
+    int* AddressLength;
+} AFD_RECV_DATAGRAM_INFO, *PAFD_RECV_DATAGRAM_INFO;
+
+typedef struct _AFD_RECV_INFO {
+    LPWSABUF BufferArray;
+    ULONG BufferCount;
+    ULONG AfdFlags;
+    ULONG TdiFlags;
+} AFD_RECV_INFO, *PAFD_RECV_INFO;
+
+
+#define _AFD_CONTROL_CODE(operation, method) \
+    ((FSCTL_AFD_BASE) << 12 | (operation << 2) | method)
+
+#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
+
+#define AFD_RECEIVE            5
+#define AFD_RECEIVE_DATAGRAM   6
+#define AFD_POLL               9
+
+#define IOCTL_AFD_RECEIVE \
+    _AFD_CONTROL_CODE(AFD_RECEIVE, METHOD_NEITHER)
+
+#define IOCTL_AFD_RECEIVE_DATAGRAM \
+    _AFD_CONTROL_CODE(AFD_RECEIVE_DATAGRAM, METHOD_NEITHER)
+
+#define IOCTL_AFD_POLL \
+    _AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED)
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP {
+  /* FIXME: __C89_NAMELESS was removed */
+  /* __C89_NAMELESS */ union {
+    ULONGLONG Alignment;
+    /* __C89_NAMELESS */ struct {
+      ULONG Length;
+      DWORD Flags;
+    };
+  };
+  struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next;
+  SOCKET_ADDRESS Address;
+  IP_PREFIX_ORIGIN PrefixOrigin;
+  IP_SUFFIX_ORIGIN SuffixOrigin;
+  IP_DAD_STATE DadState;
+  ULONG ValidLifetime;
+  ULONG PreferredLifetime;
+  ULONG LeaseLifetime;
+} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP;
+
+typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
+  union {
+    ULONGLONG Alignment;
+    struct {
+      ULONG Length;
+      DWORD Flags;
+    };
+  };
+  struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next;
+  SOCKET_ADDRESS Address;
+  IP_PREFIX_ORIGIN PrefixOrigin;
+  IP_SUFFIX_ORIGIN SuffixOrigin;
+  IP_DAD_STATE DadState;
+  ULONG ValidLifetime;
+  ULONG PreferredLifetime;
+  ULONG LeaseLifetime;
+  UINT8 OnLinkPrefixLength;
+} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH;
+
+#endif
+
+int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
+                                            struct sockaddr_storage* storage);
+
+#endif /* UV_WIN_WINSOCK_H_ */
