libtuv initial commit (2018.08.31) accepted/tizen/unified/20180907.211632 submit/tizen/20180906.083646
authorHaesik Jun <haesik.jun@samsung.com>
Thu, 6 Sep 2018 02:21:53 +0000 (11:21 +0900)
committerHaesik Jun <haesik.jun@samsung.com>
Thu, 6 Sep 2018 02:21:53 +0000 (11:21 +0900)
github commitid: d0a176f27a89f2f49f767645dcd36247ca0677b8

Change-Id: Ib65b6689f382982b8f7ce55ee02031e499f6ee9a
Signed-off-by: Haesik, Jun <haesik.jun@samsung.com>
218 files changed:
.gitignore [new file with mode: 0644]
.travis.yml [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.all [new file with mode: 0644]
README.md [new file with mode: 0644]
cmake/apiemultest.cmake [new file with mode: 0644]
cmake/config/config_arm-linux.cmake [new file with mode: 0644]
cmake/config/config_arm-mbed.cmake [new file with mode: 0644]
cmake/config/config_arm-nuttx.cmake [new file with mode: 0644]
cmake/config/config_arm-tizen.cmake [new file with mode: 0644]
cmake/config/config_arm-tizenrt.cmake [new file with mode: 0644]
cmake/config/config_armhf-linux.cmake [new file with mode: 0644]
cmake/config/config_i686-linux.cmake [new file with mode: 0644]
cmake/config/config_i686-windows.cmake [new file with mode: 0644]
cmake/config/config_mips-openwrt.cmake [new file with mode: 0644]
cmake/config/config_mipsel-linux.cmake [new file with mode: 0644]
cmake/config/config_noarch-linux.cmake [new file with mode: 0644]
cmake/config/config_noarch-tizen.cmake [new file with mode: 0644]
cmake/config/config_x86_64-darwin.cmake [new file with mode: 0644]
cmake/config/config_x86_64-linux.cmake [new file with mode: 0644]
cmake/libtuv.cmake [new file with mode: 0644]
cmake/option/option_arm-linux.cmake [new file with mode: 0644]
cmake/option/option_arm-mbed.cmake [new file with mode: 0644]
cmake/option/option_arm-nuttx.cmake [new file with mode: 0644]
cmake/option/option_arm-tizen.cmake [new file with mode: 0644]
cmake/option/option_arm-tizenrt.cmake [new file with mode: 0644]
cmake/option/option_armhf-linux.cmake [new file with mode: 0644]
cmake/option/option_i686-linux.cmake [new file with mode: 0644]
cmake/option/option_i686-windows.cmake [new file with mode: 0644]
cmake/option/option_linux_common.cmake [new file with mode: 0644]
cmake/option/option_mips-openwrt.cmake [new file with mode: 0644]
cmake/option/option_mipsel-linux.cmake [new file with mode: 0644]
cmake/option/option_noarch-linux.cmake [new file with mode: 0644]
cmake/option/option_noarch-tizen.cmake [new file with mode: 0644]
cmake/option/option_raw_common.cmake [new file with mode: 0644]
cmake/option/option_unix_common.cmake [new file with mode: 0644]
cmake/option/option_windows_common.cmake [new file with mode: 0644]
cmake/option/option_x86_64-darwin.cmake [new file with mode: 0644]
cmake/option/option_x86_64-linux.cmake [new file with mode: 0644]
cmake/options.cmake [new file with mode: 0644]
cmake/tuvtest.cmake [new file with mode: 0644]
config/tizen/.gbs.conf [new file with mode: 0644]
config/tizen/gbs.conf [new file with mode: 0644]
config/tizen/packaging/libtuv.manifest [new file with mode: 0644]
config/tizen/packaging/libtuv.pc.in [new file with mode: 0644]
config/tizen/packaging/libtuv.spec [new file with mode: 0644]
include/android-ifaddrs.h [new file with mode: 0644]
include/pthread-barrier.h [new file with mode: 0644]
include/stdint-msvc2008.h [new file with mode: 0644]
include/tree.h [new file with mode: 0644]
include/tuv__debuglog.h [new file with mode: 0644]
include/uv-aix.h [new file with mode: 0644]
include/uv-bsd.h [new file with mode: 0644]
include/uv-darwin.h [new file with mode: 0644]
include/uv-errno.h [new file with mode: 0644]
include/uv-linux.h [new file with mode: 0644]
include/uv-nuttx.h [new file with mode: 0644]
include/uv-os390.h [new file with mode: 0644]
include/uv-sunos.h [new file with mode: 0644]
include/uv-threadpool.h [new file with mode: 0644]
include/uv-tizenrt.h [new file with mode: 0644]
include/uv-unix.h [new file with mode: 0644]
include/uv-version.h [new file with mode: 0644]
include/uv-win.h [new file with mode: 0644]
include/uv.h [new file with mode: 0644]
packaging/libtuv.manifest [new file with mode: 0644]
packaging/libtuv.pc.in [new file with mode: 0644]
packaging/libtuv.spec [new file with mode: 0644]
src/fs-poll.c [new file with mode: 0644]
src/heap-inl.h [new file with mode: 0644]
src/inet.c [new file with mode: 0644]
src/queue.h [new file with mode: 0644]
src/threadpool.c [new file with mode: 0644]
src/tuv__config.h.in [new file with mode: 0644]
src/tuv_debuglog.c [new file with mode: 0644]
src/unix/aix.c [new file with mode: 0644]
src/unix/android-ifaddrs.c [new file with mode: 0644]
src/unix/async.c [new file with mode: 0644]
src/unix/atomic-ops.h [new file with mode: 0644]
src/unix/core.c [new file with mode: 0644]
src/unix/darwin-proctitle.c [new file with mode: 0644]
src/unix/darwin.c [new file with mode: 0644]
src/unix/dl.c [new file with mode: 0644]
src/unix/freebsd.c [new file with mode: 0644]
src/unix/fs.c [new file with mode: 0644]
src/unix/fsevents.c [new file with mode: 0644]
src/unix/getaddrinfo.c [new file with mode: 0644]
src/unix/getnameinfo.c [new file with mode: 0644]
src/unix/internal.h [new file with mode: 0644]
src/unix/kqueue.c [new file with mode: 0644]
src/unix/linux-core.c [new file with mode: 0644]
src/unix/linux-inotify.c [new file with mode: 0644]
src/unix/linux-syscalls.c [new file with mode: 0644]
src/unix/linux-syscalls.h [new file with mode: 0644]
src/unix/loop-watcher.c [new file with mode: 0644]
src/unix/loop.c [new file with mode: 0644]
src/unix/netbsd.c [new file with mode: 0644]
src/unix/nuttx.c [new file with mode: 0644]
src/unix/openbsd.c [new file with mode: 0644]
src/unix/os390.c [new file with mode: 0644]
src/unix/pipe.c [new file with mode: 0644]
src/unix/poll.c [new file with mode: 0644]
src/unix/process.c [new file with mode: 0644]
src/unix/proctitle.c [new file with mode: 0644]
src/unix/pthread-barrier.c [new file with mode: 0644]
src/unix/pthread-fixes.c [new file with mode: 0644]
src/unix/signal.c [new file with mode: 0644]
src/unix/spinlock.h [new file with mode: 0644]
src/unix/stream.c [new file with mode: 0644]
src/unix/sunos.c [new file with mode: 0644]
src/unix/tcp.c [new file with mode: 0644]
src/unix/thread.c [new file with mode: 0644]
src/unix/timer.c [new file with mode: 0644]
src/unix/tizenrt.c [new file with mode: 0644]
src/unix/tty.c [new file with mode: 0644]
src/unix/udp.c [new file with mode: 0644]
src/uv-common.c [new file with mode: 0644]
src/uv-common.h [new file with mode: 0644]
src/version.c [new file with mode: 0644]
src/win/async.c [new file with mode: 0644]
src/win/atomicops-inl.h [new file with mode: 0644]
src/win/core.c [new file with mode: 0644]
src/win/detect-wakeup.c [new file with mode: 0644]
src/win/dl.c [new file with mode: 0644]
src/win/error.c [new file with mode: 0644]
src/win/fs-event.c [new file with mode: 0644]
src/win/fs.c [new file with mode: 0644]
src/win/getaddrinfo.c [new file with mode: 0644]
src/win/getnameinfo.c [new file with mode: 0644]
src/win/handle-inl.h [new file with mode: 0644]
src/win/handle.c [new file with mode: 0644]
src/win/internal.h [new file with mode: 0644]
src/win/loop-watcher.c [new file with mode: 0644]
src/win/pipe.c [new file with mode: 0644]
src/win/poll.c [new file with mode: 0644]
src/win/process-stdio.c [new file with mode: 0644]
src/win/process.c [new file with mode: 0644]
src/win/req-inl.h [new file with mode: 0644]
src/win/req.c [new file with mode: 0644]
src/win/signal.c [new file with mode: 0644]
src/win/snprintf.c [new file with mode: 0644]
src/win/stream-inl.h [new file with mode: 0644]
src/win/stream.c [new file with mode: 0644]
src/win/tcp.c [new file with mode: 0644]
src/win/thread.c [new file with mode: 0644]
src/win/timer.c [new file with mode: 0644]
src/win/tty.c [new file with mode: 0644]
src/win/udp.c [new file with mode: 0644]
src/win/util.c [new file with mode: 0644]
src/win/winapi.c [new file with mode: 0644]
src/win/winapi.h [new file with mode: 0644]
src/win/winsock.c [new file with mode: 0644]
src/win/winsock.h [new file with mode: 0644]
test/echo_server.c [new file with mode: 0644]
test/raw/apiemul.h [new file with mode: 0644]
test/raw/apiemul_main.c [new file with mode: 0644]
test/raw/apiemul_socket.c [new file with mode: 0644]
test/raw/echo_server_raw.c [new file with mode: 0644]
test/raw/raw_main.c [new file with mode: 0644]
test/raw/raw_main.h [new file with mode: 0644]
test/raw/runner_main_raw.c [new file with mode: 0644]
test/raw/test_active_raw.c [new file with mode: 0644]
test/raw/test_async_raw.c [new file with mode: 0644]
test/raw/test_condvar_raw.c [new file with mode: 0644]
test/raw/test_idle_raw.c [new file with mode: 0644]
test/raw/test_shutdown_eof_raw.c [new file with mode: 0644]
test/raw/test_tcp_open_raw.c [new file with mode: 0644]
test/raw/test_threadpool_raw_queue_work_simple.c [new file with mode: 0644]
test/raw/test_timer_raw_again.c [new file with mode: 0644]
test/raw/test_timer_raw_huge_repeat.c [new file with mode: 0644]
test/raw/test_timer_raw_huge_timeout.c [new file with mode: 0644]
test/raw/test_timer_raw_init.c [new file with mode: 0644]
test/raw/test_timer_raw_norm.c [new file with mode: 0644]
test/raw/test_timer_raw_order.c [new file with mode: 0644]
test/raw/test_timer_raw_run_null_callback.c [new file with mode: 0644]
test/raw/test_timer_raw_run_once.c [new file with mode: 0644]
test/raw/test_timer_raw_start_twice.c [new file with mode: 0644]
test/raw/test_walk_handles_raw.c [new file with mode: 0644]
test/resources/empty_file [new file with mode: 0644]
test/runner.h [new file with mode: 0644]
test/runner_linux.c [new file with mode: 0644]
test/runner_linux_raw.c [new file with mode: 0644]
test/runner_list.h [new file with mode: 0644]
test/runner_main.c [new file with mode: 0644]
test/runner_nuttx.c [new file with mode: 0644]
test/runner_tizenrt.c [new file with mode: 0644]
test/test_active.c [new file with mode: 0644]
test/test_async.c [new file with mode: 0644]
test/test_condvar.c [new file with mode: 0644]
test/test_cwd.c [new file with mode: 0644]
test/test_error.c [new file with mode: 0644]
test/test_fs.c [new file with mode: 0644]
test/test_getaddrinfo.c [new file with mode: 0644]
test/test_idle.c [new file with mode: 0644]
test/test_ipc.c [new file with mode: 0644]
test/test_pipe_bind_error.c [new file with mode: 0644]
test/test_pipe_close_stdout_read_stdin.c [new file with mode: 0644]
test/test_pipe_connect_error.c [new file with mode: 0644]
test/test_pipe_connect_multiple.c [new file with mode: 0644]
test/test_pipe_connect_prepare.c [new file with mode: 0644]
test/test_pipe_getsockname.c [new file with mode: 0644]
test/test_pipe_pending_instances.c [new file with mode: 0644]
test/test_pipe_sendmsg.c [new file with mode: 0644]
test/test_pipe_server_close.c [new file with mode: 0644]
test/test_pipe_set_non_blocking.c [new file with mode: 0644]
test/test_shutdown_eof.c [new file with mode: 0644]
test/test_signal.c [new file with mode: 0644]
test/test_spawn.c [new file with mode: 0644]
test/test_tcp_open.c [new file with mode: 0644]
test/test_threadpool.c [new file with mode: 0644]
test/test_timer.c [new file with mode: 0644]
test/test_timer_again.c [new file with mode: 0644]
test/test_walk_handles.c [new file with mode: 0644]
tools/apt-get-install-arm.sh [new file with mode: 0755]
tools/apt-get-install-deps.sh [new file with mode: 0755]
tools/check-signed-off.sh [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5926595
--- /dev/null
@@ -0,0 +1,3 @@
+build/
+tuv__config.h
+tuv_host_ipaddress.h
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..2b0244c
--- /dev/null
@@ -0,0 +1,19 @@
+language: c
+
+os: linux
+dist: trusty
+sudo: required
+
+before_install:
+  - tools/check-signed-off.sh --travis
+  - tools/apt-get-install-deps.sh
+  - if [[ "$INSTALL_ARM_DEPS" == "yes" ]]; then tools/apt-get-install-arm.sh; fi
+
+install:
+
+script: "make run_test $OPT"
+
+env:
+  - OPT="TUV_BUILD_TYPE=debug"
+  - OPT="TUV_BUILD_TYPE=release"
+  - OPT="TUV_PLATFORM=arm-linux TUV_BOARD=rpi2" INSTALL_ARM_DEPS=yes
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..101fa40
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 2.8.12)
+project(libtuv C)
+
+set(LIBTUV_VERSION_MAJOR 0)
+set(LIBTUV_VERSION_MINOR 1)
+
+#
+# + unix_common: unix like, posix, multi-tasking
+#     + linux_common
+#         + arm-linux
+#         + i686-linux
+#         + x86_64-linux
+#     + arm-nuttx
+# + (mtsk_common: multi-tasking
+#     + (TBD)
+# + raw_common: single task, big-while-loop, event driven
+#     + arm-mbed
+#
+include(cmake/options.cmake)
+include(cmake/libtuv.cmake)
+if (DEFINED BUILDTESTER AND "${BUILDTESTER}" STREQUAL "yes")
+  include(cmake/tuvtest.cmake)
+elseif (DEFINED BUILDAPIEMULTESTER AND "${BUILDAPIEMULTESTER}" STREQUAL "yes")
+  include(cmake/apiemultest.cmake)
+endif()
+
+unset(BUILDTESTER CACHE)
+unset(BUILDAPIEMULTESTER CACHE)
+unset(BUILD_TEST_LIB CACHE)
+unset(BUILD_HOST_HELPER CACHE)
+unset(CREATE_SHARED_LIB CACHE)
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..dc1ef2a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,275 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=============================================================================
+
+
+libuv is licensed for use as follows:
+
+====
+Copyright (c) 2015-present libuv project contributors.
+
+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 license applies to parts of libuv originating from the
+https://github.com/joyent/libuv repository:
+
+====
+
+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 license applies to all parts of libuv that are not externally
+maintained libraries.
+
+The externally maintained libraries used by libuv are:
+
+  - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license.
+
+  - inet_pton and inet_ntop implementations, contained in src/inet.c, are
+    copyright the Internet Systems Consortium, Inc., and licensed under the ISC
+    license.
+
+  - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three
+    clause BSD license.
+
+  - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile
+    Communications AB. Three clause BSD license.
+
+  - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design
+    Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement
+    n° 289016). Three clause BSD license.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c3e4d1d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,89 @@
+# Copyright 2015-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+UNAME_M := $(shell uname -m)
+UNAME_S := $(shell uname -s)
+ifeq ($(UNAME_S),Linux)
+       UNAME_S := linux
+endif
+ifeq ($(UNAME_S),Darwin)
+       UNAME_S := darwin
+endif
+
+
+TUV_PLATFORM    ?= $(UNAME_M)-$(UNAME_S)
+TUV_BUILD_TYPE  ?= debug
+TUV_BOARD       ?= unknown
+TUV_SYSTEMROOT  ?= default
+TUV_BUILDTESTER ?= yes
+TUV_BUILDAPIEMULTESTER ?= no
+TUV_BUILDHOSTHELPER ?= no
+TUV_CREATE_SHARED_LIB ?= no
+
+OUTPUT_ROOT     := build
+BUILD_FOLDER    := ./$(OUTPUT_ROOT)/$(TUV_PLATFORM)/$(TUV_BUILD_TYPE)
+CMAKE_FOLDER    := $(BUILD_FOLDER)/cmake
+CMAKE_DEFINES   := \
+       -DCMAKE_TOOLCHAIN_FILE=./cmake/config/config_$(TUV_PLATFORM).cmake \
+       -DCMAKE_BUILD_TYPE=$(TUV_BUILD_TYPE) \
+       -DTARGET_PLATFORM=$(TUV_PLATFORM) \
+       -DBUILDTESTER=${TUV_BUILDTESTER} \
+       -DBUILD_HOST_HELPER=${TUV_BUILDHOSTHELPER} \
+       -DCREATE_SHARED_LIB=${TUV_CREATE_SHARED_LIB} \
+       -DTUV_FEATURE_PROCESS=ON
+
+ifneq ($(TUV_BOARD),unknown)
+       CMAKE_DEFINES += -DTARGET_BOARD=${TUV_BOARD}
+endif
+
+ifneq ($(TUV_SYSTEMROOT),default)
+       CMAKE_DEFINES += -DTARGET_SYSTEMROOT=${TUV_SYSTEMROOT}
+endif
+
+ifneq ($(TUV_BUILDAPIEMULTESTER),no)
+       CMAKE_DEFINES += -DBUILDAPIEMULTESTER=${TUV_BUILDAPIEMULTESTER}
+endif
+
+.phony: all
+
+all:
+       mkdir -p $(CMAKE_FOLDER)
+       cmake -B$(CMAKE_FOLDER) -H./ $(CMAKE_DEFINES)
+       make -C $(CMAKE_FOLDER)
+ifeq ($(TUV_PLATFORM), $(filter $(TUV_PLATFORM), i686-linux x86_64-linux x86_64-darwin))
+       @echo '=============================================================='
+       @echo 'to run test,'
+       @echo '$(BUILD_FOLDER)/bin/tuvtester'
+       @echo '=============================================================='
+endif
+ifeq ("$(TUV_BUILDHOSTHELPER)","yes")
+       @echo '=============================================================='
+       @echo 'to run helper in host,'
+       @echo '$(BUILD_FOLDER)/bin/tuvhelper'
+       @echo '=============================================================='
+endif
+
+run_test:
+       make
+ifeq ($(TUV_PLATFORM), $(filter $(TUV_PLATFORM), i686-linux x86_64-linux x86_64-darwin))
+       $(BUILD_FOLDER)/bin/tuvtester
+else
+       @echo 'Cannot run test for $(TUV_PLATFORM)'
+endif
+
+call:
+       make -C $(CMAKE_FOLDER)
+
+clean:
+       rm -rf $(OUTPUT_ROOT)
diff --git a/Makefile.all b/Makefile.all
new file mode 100644 (file)
index 0000000..eefce67
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+HARMONY ?= $(HOME)/harmony
+
+#
+# Use this makefile to check build all platforms.
+# You may have to set "HARMONY" to a valid folder for NuttX on
+# STM32F4 discovery board.
+#
+
+.phony: all
+
+all: clean linux rpi2 stm32f4
+
+
+clean:
+       make clean
+
+
+linux:
+       make
+
+
+rpi2:
+       TUV_PLATFORM=arm-linux TUV_BOARD=rpi2 make
+
+
+stm32f4:
+       TUV_PLATFORM=arm-nuttx TUV_BOARD=stm32f4dis \
+        TUV_SYSTEMROOT=$(HARMONY)/nuttx/nuttx make
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..be30689
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+You can find project details in [wiki](https://github.com/Samsung/libtuv/wiki)
diff --git a/cmake/apiemultest.cmake b/cmake/apiemultest.cmake
new file mode 100644 (file)
index 0000000..7f92324
--- /dev/null
@@ -0,0 +1,46 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 2.8.12)
+
+set(TUVTESTNAME "tuvtester")
+
+add_definitions("-D__TUV_APIEMUL__")
+
+if(DEFINED BUILD_TEST_LIB AND BUILD_TEST_LIB STREQUAL "yes")
+  add_library(${TUVTESTNAME} ${TEST_MAINFILE} ${TEST_APIEMULFILES})
+else()
+  #add_executable(${TUVTESTNAME} ${TEST_MAINFILE} ${APIEMUL_FILES})
+  message(FATAL_ERROR "BUILD_TEST_LIB must be 'yes'")
+endif()
+
+
+target_include_directories(${TUVTESTNAME} SYSTEM PRIVATE ${TARGET_INC})
+target_include_directories(${TUVTESTNAME} PUBLIC ${LIB_TUV_INCDIRS}
+                                                 ${TUV_TEST_INCDIRS})
+target_link_libraries(${TUVTESTNAME} LINK_PUBLIC ${TARGETLIBNAME}
+                      ${TUV_LINK_LIBS})
+set_target_properties(${TUVTESTNAME} PROPERTIES
+    ARCHIVE_OUTPUT_DIRECTORY "${LIB_OUT}"
+    LIBRARY_OUTPUT_DIRECTORY "${LIB_OUT}"
+    RUNTIME_OUTPUT_DIRECTORY "${BIN_OUT}")
+
+if(DEFINED BUILD_TEST_LIB AND BUILD_TEST_LIB STREQUAL "yes")
+  if(DEFINED COPY_TARGET_LIB)
+    add_custom_command(TARGET ${TUVTESTNAME} POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${TUVTESTNAME}>
+                                        "${COPY_TARGET_LIB}"
+        COMMENT "Copying lib${TUVTESTNAME} to ${COPY_TARGET_LIB}")
+  endif()
+endif()
diff --git a/cmake/config/config_arm-linux.cmake b/cmake/config/config_arm-linux.cmake
new file mode 100644 (file)
index 0000000..508d46d
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2015-2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR arm)
+
+set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
diff --git a/cmake/config/config_arm-mbed.cmake b/cmake/config/config_arm-mbed.cmake
new file mode 100644 (file)
index 0000000..5350340
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright 2015-2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME mbed)
+set(CMAKE_SYSTEM_PROCESSOR arm)
+
+set(CMAKE_C_COMPILER arm-none-eabi-g++)
+set(CMAKE_C_COMPILER_WORKS TRUE)
diff --git a/cmake/config/config_arm-nuttx.cmake b/cmake/config/config_arm-nuttx.cmake
new file mode 100644 (file)
index 0000000..5b78535
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright 2015-2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME nuttx)
+set(CMAKE_SYSTEM_PROCESSOR arm)
+
+set(CMAKE_C_COMPILER arm-none-eabi-gcc)
+set(CMAKE_C_COMPILER_WORKS TRUE)
diff --git a/cmake/config/config_arm-tizen.cmake b/cmake/config/config_arm-tizen.cmake
new file mode 100644 (file)
index 0000000..24951f1
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR arm)
+
+if(NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER arm-linux-gnueabi-gcc)
+endif()
diff --git a/cmake/config/config_arm-tizenrt.cmake b/cmake/config/config_arm-tizenrt.cmake
new file mode 100644 (file)
index 0000000..d296c28
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright 2017-present Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME tizenrt)
+set(CMAKE_SYSTEM_PROCESSOR arm)
+
+set(CMAKE_C_COMPILER arm-none-eabi-gcc)
+set(CMAKE_C_COMPILER_WORKS TRUE)
diff --git a/cmake/config/config_armhf-linux.cmake b/cmake/config/config_armhf-linux.cmake
new file mode 100644 (file)
index 0000000..160ba9f
--- /dev/null
@@ -0,0 +1,15 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
diff --git a/cmake/config/config_i686-linux.cmake b/cmake/config/config_i686-linux.cmake
new file mode 100644 (file)
index 0000000..decb1f2
--- /dev/null
@@ -0,0 +1,16 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR i686)
diff --git a/cmake/config/config_i686-windows.cmake b/cmake/config/config_i686-windows.cmake
new file mode 100644 (file)
index 0000000..71575e3
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright 2015-present Samsung Electronics Co., Ltd. and other contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Windows)
+set(CMAKE_SYSTEM_PROCESSOR i686)
+
+if(NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER CL.exe)
+endif()
diff --git a/cmake/config/config_mips-openwrt.cmake b/cmake/config/config_mips-openwrt.cmake
new file mode 100644 (file)
index 0000000..1a02128
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Openwrt)
+set(CMAKE_SYSTEM_PROCESSOR mips)
+
+set(CMAKE_C_COMPILER mips-openwrt-linux-gcc)
diff --git a/cmake/config/config_mipsel-linux.cmake b/cmake/config/config_mipsel-linux.cmake
new file mode 100644 (file)
index 0000000..07049ae
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2015-2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR mipsel)
+
+set(CMAKE_C_COMPILER mipsel-linux-gcc)
diff --git a/cmake/config/config_noarch-linux.cmake b/cmake/config/config_noarch-linux.cmake
new file mode 100644 (file)
index 0000000..e665b74
--- /dev/null
@@ -0,0 +1,21 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is used for compilation without any compiler flags or 
+# cross compilation options. Default make always checks for CPU
+# and operating system and tries to find config_cpu_os.cmake.
+#
+# This file you can include using flags:
+# $  TUV_PLATFORM=noarch-linux make
+
diff --git a/cmake/config/config_noarch-tizen.cmake b/cmake/config/config_noarch-tizen.cmake
new file mode 100644 (file)
index 0000000..787bf66
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright 2018 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include(CMakeForceCompiler)
+
+set(CMAKE_SYSTEM_NAME Linux)
diff --git a/cmake/config/config_x86_64-darwin.cmake b/cmake/config/config_x86_64-darwin.cmake
new file mode 100644 (file)
index 0000000..3bb3781
--- /dev/null
@@ -0,0 +1,16 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Darwin)
+set(CMAKE_SYSTEM_PROCESSOR x86_64)
diff --git a/cmake/config/config_x86_64-linux.cmake b/cmake/config/config_x86_64-linux.cmake
new file mode 100644 (file)
index 0000000..b66dc76
--- /dev/null
@@ -0,0 +1,16 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR x86_64)
diff --git a/cmake/libtuv.cmake b/cmake/libtuv.cmake
new file mode 100644 (file)
index 0000000..e844672
--- /dev/null
@@ -0,0 +1,78 @@
+# Copyright 2015-2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 2.8.12)
+
+#
+# { TUV_CHANGES@20161129: It corresponds to uv.gyp's `sources` }
+#
+set(COMMON_SRCFILES
+    ${INCLUDE_ROOT}/uv.h
+# { TUV_CHANGES@20161129:
+#   Makefile.am include tree.h only in WINNT
+#   But tree.h is used in unix's several files (i.e. loop.c)
+#   But we don't provide unix's the corresponding part.     }
+#   ${INCLUDE_ROOT}/tree.h
+    ${INCLUDE_ROOT}/uv-errno.h
+    ${INCLUDE_ROOT}/uv-threadpool.h
+    ${INCLUDE_ROOT}/uv-version.h
+    ${SOURCE_ROOT}/fs-poll.c
+    ${SOURCE_ROOT}/heap-inl.h
+    ${SOURCE_ROOT}/inet.c
+    ${SOURCE_ROOT}/queue.h
+    ${SOURCE_ROOT}/threadpool.c
+    ${SOURCE_ROOT}/uv-common.c
+    ${SOURCE_ROOT}/uv-common.h
+#   ${SOURCE_ROOT}/version.c
+    ${SOURCE_ROOT}/tuv_debuglog.c
+    )
+
+set(LIB_TUV_SRCFILES
+      ${COMMON_SRCFILES}
+      ${PLATFORM_SRCFILES}
+      )
+
+set(LIB_TUV_INCDIRS
+      ${INCLUDE_ROOT}
+      ${SOURCE_ROOT}
+      )
+
+# build tuv library
+set(TARGETLIBNAME tuv)
+add_library(${TARGETLIBNAME} ${LIB_TUV_SRCFILES})
+target_include_directories(${TARGETLIBNAME} SYSTEM PRIVATE ${TARGET_INC})
+target_include_directories(${TARGETLIBNAME} PUBLIC ${LIB_TUV_INCDIRS})
+set_target_properties(${TARGETLIBNAME} PROPERTIES
+    ARCHIVE_OUTPUT_DIRECTORY "${LIB_OUT}"
+    LIBRARY_OUTPUT_DIRECTORY "${LIB_OUT}"
+    RUNTIME_OUTPUT_DIRECTORY "${BIN_OUT}")
+
+# build tuv shared library
+if (DEFINED CREATE_SHARED_LIB AND CREATE_SHARED_LIB STREQUAL "yes")
+  set(TARGETSHAREDLIBNAME tuv_shared)
+  add_library(${TARGETSHAREDLIBNAME} SHARED ${LIB_TUV_SRCFILES})
+  target_include_directories(${TARGETSHAREDLIBNAME} SYSTEM PRIVATE ${TARGET_INC})
+  target_include_directories(${TARGETSHAREDLIBNAME} PUBLIC ${LIB_TUV_INCDIRS})
+  set_target_properties(${TARGETSHAREDLIBNAME} PROPERTIES
+      LIBRARY_OUTPUT_DIRECTORY "${LIB_OUT}"
+      COMPILE_FLAGS -fPIC
+      OUTPUT_NAME tuv)
+endif()
+
+if(DEFINED COPY_TARGET_LIB)
+  add_custom_command(TARGET ${TARGETLIBNAME} POST_BUILD
+      COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${TARGETLIBNAME}>
+                                  "${COPY_TARGET_LIB}"
+      COMMENT "Copying lib${TARGETLIBNAME} to ${COPY_TARGET_LIB}")
+endif()
diff --git a/cmake/option/option_arm-linux.cmake b/cmake/option/option_arm-linux.cmake
new file mode 100644 (file)
index 0000000..5f96230
--- /dev/null
@@ -0,0 +1,34 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
+
+# arm-linux specific
+if(DEFINED TARGET_BOARD)
+  if(${TARGET_BOARD} STREQUAL "rpi2")
+    # rpi2 specific
+    set(FLAGS_COMMON
+          ${FLAGS_COMMON}
+          "-mcpu=cortex-a7"
+          "-mfloat-abi=hard"
+          "-mfpu=neon-vfpv4"
+          )
+  else()
+    message(FATAL_ERROR "TARGET_BOARD=`${TARGET_BOARD}` is unknown to make")
+  endif()
+else()
+  message(FATAL_ERROR "TARGET_BOARD is undefined")
+endif()
diff --git a/cmake/option/option_arm-mbed.cmake b/cmake/option/option_arm-mbed.cmake
new file mode 100644 (file)
index 0000000..b3859b3
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include("cmake/option/option_raw_common.cmake")
+
+# mbed common source files
+set(MBED_PATH "${SOURCE_ROOT}/mbed")
+
+set(PLATFORM_SRCFILES
+      ${PLATFORM_SRCFILES}
+      "${MBED_PATH}/uv_mbed.c"
+      "${MBED_PATH}/uv_mbed_io.c"
+      "${MBED_PATH}/uv_mbed_stream.c"
+      "${MBED_PATH}/uv_mbed_tcp.c"
+      )
+
+#set(PLATFORM_TESTFILES "${TEST_ROOT}/runner_mbed.c")
+
+set(FLAGS_COMMON
+      ${FLAGS_COMMON}
+      "-D__TUV_MBED__"
+      )
+
+# use "mbed" lower case for platform path
+set(TUV_PLATFORM_PATH ${PLATFORM_NAME_L})
+
+if(DEFINED TARGET_BOARD)
+  if(${TARGET_BOARD} STREQUAL "mbedk64f")
+    set(FLAGS_COMMON
+          ${FLAGS_COMMON}
+          "-mcpu=cortex-m4"
+          "-mthumb"
+          "-mlittle-endian"
+          )
+    if(NOT DEFINED TARGET_SYSTEMROOT)
+      set(TARGET_SYSTEMROOT "${CMAKE_SOURCE_DIR}/targets/mbedk64f")
+    endif()
+  else()
+    message(FATAL_ERROR "TARGET_BOARD=`${TARGET_BOARD}` is unknown to make")
+  endif()
+else()
+  message(FATAL_ERROR "TARGET_BOARD is undefined")
+endif()
+
+# mbed port source/include
+set(TARGET_INC ${TARGET_INC} "${TARGET_SYSTEMROOT}/source")
+
+
+# build tester as library
+set(BUILD_TEST_LIB "yes")
+unset(BUILD_TEST_LIB CACHE)
+
+
+# set copy libs to ${TARGET_SYSTEMROOT}/libtiv
+file(MAKE_DIRECTORY "${TARGET_SYSTEMROOT}/libtuv")
+set(COPY_TARGET_LIB "${TARGET_SYSTEMROOT}/libtuv/.")
+
+# to help unittest to give host ip address
+if(EXISTS ${RAW_TEST}/tuv_host_ipaddress.h)
+  add_definitions("-D__TUV_HOST_IPEXIST__")
+endif()
diff --git a/cmake/option/option_arm-nuttx.cmake b/cmake/option/option_arm-nuttx.cmake
new file mode 100644 (file)
index 0000000..5157ff8
--- /dev/null
@@ -0,0 +1,65 @@
+# Copyright 2015-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include("cmake/option/option_unix_common.cmake")
+
+set(FLAGS_COMMON ${FLAGS_COMMON} "-D__NUTTX__")
+
+# override TUV_PLATFORM_PATH for NuttX
+# use "nuttx" for NuttX platform dependent source files
+# set(TUV_PLATFORM_PATH ${PLATFORM_NAME_L})
+
+# nuttx source files
+# set(NUTTX_PATH "${SOURCE_ROOT}/${TUV_PLATFORM_PATH}")
+
+set(PLATFORM_SRCFILES
+      ${PLATFORM_SRCFILES}
+      ${UNIX_PATH}/nuttx.c
+      )
+
+set(PLATFORM_TESTFILES "${TEST_ROOT}/runner_nuttx.c")
+
+if(DEFINED TARGET_BOARD)
+  if(${TARGET_BOARD} STREQUAL "stm32f4dis")
+    set(FLAGS_COMMON
+          ${FLAGS_COMMON}
+          "-mcpu=cortex-m4"
+          "-mthumb"
+          "-march=armv7e-m"
+          "-mfpu=fpv4-sp-d16"
+          "-mfloat-abi=hard"
+          )
+  else()
+    message(FATAL_ERROR "TARGET_BOARD=`${TARGET_BOARD}` is unknown to make")
+  endif()
+else()
+  message(FATAL_ERROR "TARGET_BOARD is undefined")
+endif()
+
+if(NOT DEFINED TARGET_SYSTEMROOT OR "${TARGET_SYSTEMROOT}" STREQUAL "default")
+  message(FATAL_ERROR "\nPlease set TARGET_SYSTEMROOT for NuttX include path\n")
+endif()
+
+# system include folder
+set(TARGET_INC
+      ${TARGET_INC}
+      "${TARGET_SYSTEMROOT}/include"
+      )
+
+# build tester as library
+set(BUILD_TEST_LIB "yes")
+unset(BUILD_TEST_LIB CACHE)
+
+# set copy libs to ${TARGET_SYSTEMROOT}/lib
+set(COPY_TARGET_LIB "${TARGET_SYSTEMROOT}/lib")
diff --git a/cmake/option/option_arm-tizen.cmake b/cmake/option/option_arm-tizen.cmake
new file mode 100644 (file)
index 0000000..1d50e5f
--- /dev/null
@@ -0,0 +1,33 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
+
+# arm-tizen specific
+if(DEFINED TARGET_BOARD)
+  if(${TARGET_BOARD} STREQUAL "artik10")
+    set(FLAGS_COMMON
+          ${FLAGS_COMMON}
+          "-mcpu=cortex-a7"
+          "-mfloat-abi=softfp"
+          "-mfpu=neon-vfpv4"
+          )
+  else()
+    message(FATAL_ERROR "TARGET_BOARD=`${TARGET_BOARD}` is unknown to make")
+  endif()
+else()
+  message(FATAL_ERROR "TARGET_BOARD is undefined")
+endif()
diff --git a/cmake/option/option_arm-tizenrt.cmake b/cmake/option/option_arm-tizenrt.cmake
new file mode 100644 (file)
index 0000000..c2c0b07
--- /dev/null
@@ -0,0 +1,54 @@
+# Copyright 2017-present Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include("cmake/option/option_unix_common.cmake")
+
+set(FLAGS_COMMON ${FLAGS_COMMON} "-D__TIZENRT__")
+
+
+set(PLATFORM_SRCFILES
+      ${PLATFORM_SRCFILES}
+      ${UNIX_PATH}/tizenrt.c
+      )
+
+set(PLATFORM_TESTFILES "${TEST_ROOT}/runner_tizenrt.c")
+
+if(DEFINED TARGET_BOARD)
+  if(${TARGET_BOARD} STREQUAL "artik05x")
+    set(FLAGS_COMMON
+          ${FLAGS_COMMON}
+          "-mcpu=cortex-r4"
+          "-mfpu=vfp3"
+          )
+  else()
+    message(FATAL_ERROR "TARGET_BOARD=`${TARGET_BOARD}` is unknown to make")
+  endif()
+else()
+  message(FATAL_ERROR "TARGET_BOARD is undefined")
+endif()
+
+if(NOT DEFINED TARGET_SYSTEMROOT OR "${TARGET_SYSTEMROOT}" STREQUAL "default")
+  message(FATAL_ERROR "\nPlease set TARGET_SYSTEMROOT for TizenRT include path\n")
+endif()
+
+# system include folder
+set(TARGET_INC
+      ${TARGET_INC}
+      "${TARGET_SYSTEMROOT}/include"
+      "${TARGET_SYSTEMROOT}/include/cxx"
+      )
+
+# build tester as library
+set(BUILD_TEST_LIB "yes")
+unset(BUILD_TEST_LIB CACHE)
diff --git a/cmake/option/option_armhf-linux.cmake b/cmake/option/option_armhf-linux.cmake
new file mode 100644 (file)
index 0000000..02aed1d
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
+
+set(FLAGS_COMMON ${FLAGS_COMMON} -lrt)
+
diff --git a/cmake/option/option_i686-linux.cmake b/cmake/option/option_i686-linux.cmake
new file mode 100644 (file)
index 0000000..2740918
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
+
+# i686 specific
+
+set(FLAGS_COMMON
+      ${FLAGS_COMMON}
+      "-march=i686"
+      "-m32"
+      )
diff --git a/cmake/option/option_i686-windows.cmake b/cmake/option/option_i686-windows.cmake
new file mode 100644 (file)
index 0000000..7eb0117
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_windows_common.cmake")
+
+# i686 specific
+
diff --git a/cmake/option/option_linux_common.cmake b/cmake/option/option_linux_common.cmake
new file mode 100644 (file)
index 0000000..8eca41e
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright 2015-2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# override TUV_PLATFORM_PATH for Linux
+# use "linux" for Linux platform dependent source files
+set(TUV_PLATFORM_PATH ${PLATFORM_NAME_L})
+
+#
+# { TUV_CHANGES@20161129:
+#   It corresponds to uv.gyp's `conditions` with OS == `linux` }
+#
+set(PLATFORM_SRCFILES
+      ${PLATFORM_SRCFILES}
+#     ${UNIX_PATH}/proctitle.c
+      ${UNIX_PATH}/linux-core.c
+#     ${UNIX_PATH}/linux-inotify.c
+      ${UNIX_PATH}/linux-syscalls.c
+      ${UNIX_PATH}/linux-syscalls.h
+      )
+
+set(PLATFORM_TESTFILES "${TEST_ROOT}/runner_linux.c")
+
+set(PLATFORM_HOSTHELPERFILES "${TEST_ROOT}/runner_linux_raw.c")
+
+set(TUV_LINK_LIBS "pthread")
+
+if(DEFINED BUILD_HOST_HELPER AND BUILD_HOST_HELPER STREQUAL "yes")
+  add_definitions("-D__HOST_HELPER__")
+endif()
diff --git a/cmake/option/option_mips-openwrt.cmake b/cmake/option/option_mips-openwrt.cmake
new file mode 100644 (file)
index 0000000..45d1baf
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright 2016 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
diff --git a/cmake/option/option_mipsel-linux.cmake b/cmake/option/option_mipsel-linux.cmake
new file mode 100644 (file)
index 0000000..f6e5455
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
diff --git a/cmake/option/option_noarch-linux.cmake b/cmake/option/option_noarch-linux.cmake
new file mode 100644 (file)
index 0000000..6fdecd6
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
+
diff --git a/cmake/option/option_noarch-tizen.cmake b/cmake/option/option_noarch-tizen.cmake
new file mode 100644 (file)
index 0000000..19ffdd4
--- /dev/null
@@ -0,0 +1,33 @@
+# Copyright 2018 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
+
+# gbs-tizen specific
+if(DEFINED TARGET_BOARD)
+  if(${TARGET_BOARD} STREQUAL "rpi3")
+    set(FLAGS_COMMON
+          ${FLAGS_COMMON}
+          "-mfloat-abi=softfp"
+          "-mfpu=neon-vfpv4"
+          )
+  elseif(${TARGET_BOARD} STREQUAL "None")
+  else()
+    message(FATAL_ERROR "TARGET_BOARD=`${TARGET_BOARD}` is unknown to make")
+  endif()
+else()
+  message(FATAL_ERROR "TARGET_BOARD is undefined")
+endif()
diff --git a/cmake/option/option_raw_common.cmake b/cmake/option/option_raw_common.cmake
new file mode 100644 (file)
index 0000000..3eba62f
--- /dev/null
@@ -0,0 +1,77 @@
+# Copyright 2015-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set(FLAGS_COMMON
+      "-fno-builtin"
+      "-D__TUV_RAW__"
+      )
+
+set(CMAKE_C_FLAGS_DEBUG     "-O0 -g -DDEBUG")
+set(CMAKE_C_FLAGS_RELEASE   "-O2 -DNDEBUG")
+
+# raw common source files
+set(RAW_PATH "${SOURCE_ROOT}/raw")
+set(RAW_TEST "${TEST_ROOT}/raw")
+
+# test include
+set(TUV_TEST_INCDIRS
+      "${TEST_ROOT}"
+      "${TEST_ROOT}/raw"
+      )
+
+set(PLATFORM_SRCFILES
+      "${RAW_PATH}/uv_raw.c"
+      "${RAW_PATH}/uv_raw_clock.c"
+      "${RAW_PATH}/uv_raw_io.c"
+      "${RAW_PATH}/uv_raw_loop.c"
+      "${RAW_PATH}/uv_raw_threadpool.c"
+      "${RAW_PATH}/uv_raw_thread.c"
+
+      "${RAW_PATH}/uv_raw_async.c"
+      "${RAW_PATH}/uv_raw_process.c"
+      )
+
+set(TEST_MAINFILE
+      "${RAW_TEST}/raw_main.c"
+      )
+
+set(TEST_UNITFILES
+      "${RAW_TEST}/runner_main_raw.c"
+      "${RAW_TEST}/test_idle_raw.c"
+      "${RAW_TEST}/test_active_raw.c"
+      "${RAW_TEST}/test_timer_raw_init.c"
+      "${RAW_TEST}/test_timer_raw_norm.c"
+      "${RAW_TEST}/test_timer_raw_start_twice.c"
+      "${RAW_TEST}/test_timer_raw_order.c"
+      "${RAW_TEST}/test_timer_raw_run_once.c"
+      "${RAW_TEST}/test_timer_raw_run_null_callback.c"
+      "${RAW_TEST}/test_timer_raw_again.c"
+      "${RAW_TEST}/test_timer_raw_huge_timeout.c"
+      "${RAW_TEST}/test_timer_raw_huge_repeat.c"
+      "${RAW_TEST}/test_condvar_raw.c"
+      "${RAW_TEST}/test_async_raw.c"
+      "${RAW_TEST}/test_threadpool_raw_queue_work_simple.c"
+      "${RAW_TEST}/test_walk_handles_raw.c"
+
+      "${RAW_TEST}/test_tcp_open_raw.c"
+      "${RAW_TEST}/test_shutdown_eof_raw.c"
+      )
+
+set(TEST_APIEMULFILES
+      "${RAW_TEST}/apiemul_main.c"
+      "${RAW_TEST}/apiemul_socket.c"
+      )
+
+# configuration values
+set(CONFIG_FILESYSTEM 0)
diff --git a/cmake/option/option_unix_common.cmake b/cmake/option/option_unix_common.cmake
new file mode 100644 (file)
index 0000000..6c295bd
--- /dev/null
@@ -0,0 +1,124 @@
+# Copyright 2015-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+set(FLAGS_COMMON
+      "-fno-builtin"
+      )
+
+set(CMAKE_C_FLAGS_DEBUG     "-O0 -g -DDEBUG")
+set(CMAKE_C_FLAGS_RELEASE   "-O2 -DNDEBUG")
+
+# unix common source files
+set(UNIX_PATH "${SOURCE_ROOT}/unix")
+
+# test include
+set(TUV_TEST_INCDIRS "${TEST_ROOT}")
+
+#
+# { TUV_CHANGES@20161129:
+#   It corresponds to uv.gyp's section "Not Windows i.e. POSIX" }
+#
+set(PLATFORM_SRCFILES
+      ${INCLUDE_ROOT}/uv-unix.h
+      ${INCLUDE_ROOT}/uv-linux.h
+#     ${INCLUDE_ROOT}/uv-sunos.h
+#     ${INCLUDE_ROOT}/uv-darwin.h
+#     ${INCLUDE_ROOT}/uv-bsd.h
+#     ${INCLUDE_ROOT}/uv-aix.h
+      ${UNIX_PATH}/async.c
+      ${UNIX_PATH}/atomic-ops.h
+      ${UNIX_PATH}/core.c
+#     ${UNIX_PATH}/dl.c
+      ${UNIX_PATH}/fs.c
+      ${UNIX_PATH}/getaddrinfo.c
+#     ${UNIX_PATH}/getnameinfo.c
+      ${UNIX_PATH}/internal.h
+      ${UNIX_PATH}/loop.c
+      ${UNIX_PATH}/loop-watcher.c
+      ${UNIX_PATH}/poll.c
+      ${UNIX_PATH}/process.c
+#     ${UNIX_PATH}/proctitle.c
+      ${UNIX_PATH}/spinlock.h
+      ${UNIX_PATH}/stream.c
+      ${UNIX_PATH}/tcp.c
+      ${UNIX_PATH}/thread.c
+      ${UNIX_PATH}/timer.c
+#     ${UNIX_PATH}/tty.c
+      ${UNIX_PATH}/udp.c
+    )
+
+set(TEST_MAINFILE "${TEST_ROOT}/runner_main.c")
+
+set(TEST_UNITFILES
+      "${TEST_ROOT}/test_idle.c"
+      "${TEST_ROOT}/test_timer.c"
+      "${TEST_ROOT}/test_timer_again.c"
+      "${TEST_ROOT}/test_fs.c"
+      "${TEST_ROOT}/test_cwd.c"
+      "${TEST_ROOT}/test_error.c"
+      "${TEST_ROOT}/test_tcp_open.c"
+      "${TEST_ROOT}/test_shutdown_eof.c"
+      "${TEST_ROOT}/echo_server.c"
+      "${TEST_ROOT}/test_getaddrinfo.c"
+      "${TEST_ROOT}/test_threadpool.c"
+      "${TEST_ROOT}/test_condvar.c"
+      "${TEST_ROOT}/test_active.c"
+      "${TEST_ROOT}/test_walk_handles.c"
+      "${TEST_ROOT}/test_async.c"
+    )
+
+# { TUV_CHANGES@20180803:
+#   Made signal build time configurable }
+if(TUV_FEATURE_PROCESS)
+      set(TUV_FEATURE_PIPE ON)
+      set(TUV_FEATURE_SIGNAL ON)
+      set(FLAGS_COMMON "${FLAGS_COMMON}" "-DTUV_FEATURE_PROCESS=1")
+      set(TEST_UNITFILES "${TEST_UNITFILES}"
+            "${TEST_ROOT}/test_ipc.c"
+            "${TEST_ROOT}/test_spawn.c")
+endif()
+
+
+# { TUV_CHANGES@20180724:
+#   Made pipe build time configurable }
+if(TUV_FEATURE_PIPE)
+      set(PLATFORM_SRCFILES "${PLATFORM_SRCFILES}"
+            ${UNIX_PATH}/pipe.c)
+      set(FLAGS_COMMON "${FLAGS_COMMON}" "-DTUV_FEATURE_PIPE=1")
+      set(TEST_UNITFILES "${TEST_UNITFILES}"
+            "${TEST_ROOT}/test_pipe_bind_error.c"
+            "${TEST_ROOT}/test_pipe_close_stdout_read_stdin.c"
+            "${TEST_ROOT}/test_pipe_connect_error.c"
+            "${TEST_ROOT}/test_pipe_connect_multiple.c"
+            "${TEST_ROOT}/test_pipe_connect_prepare.c"
+            "${TEST_ROOT}/test_pipe_getsockname.c"
+            "${TEST_ROOT}/test_pipe_pending_instances.c"
+            "${TEST_ROOT}/test_pipe_sendmsg.c"
+            "${TEST_ROOT}/test_pipe_server_close.c"
+            "${TEST_ROOT}/test_pipe_set_non_blocking.c"
+           )
+endif()
+
+# { TUV_CHANGES@20180723:
+#   Made signal build time configurable }
+if(TUV_FEATURE_SIGNAL)
+      set(PLATFORM_SRCFILES "${PLATFORM_SRCFILES}"
+            ${UNIX_PATH}/signal.c)
+      set(FLAGS_COMMON "${FLAGS_COMMON}" "-DTUV_FEATURE_SIGNAL=1")
+      set(TEST_UNITFILES "${TEST_UNITFILES}" "${TEST_ROOT}/test_signal.c")
+endif()
+
+# configuration values
+set(CONFIG_FILESYSTEM 1)
diff --git a/cmake/option/option_windows_common.cmake b/cmake/option/option_windows_common.cmake
new file mode 100644 (file)
index 0000000..d2f7060
--- /dev/null
@@ -0,0 +1,88 @@
+# Copyright 2015-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+#set(CMAKE_C_FLAGS_DEBUG     "-O0 -g -DDEBUG")
+#set(CMAKE_C_FLAGS_RELEASE   "-O2 -DNDEBUG")
+
+# unix common source files
+set(WIN_PATH "${SOURCE_ROOT}/win")
+
+# test include
+set(TUV_TEST_INCDIRS "${TEST_ROOT}")
+
+#
+# { TUV_CHANGES@20161129:
+#   It corresponds to uv.gyp's section "Windows" }
+#
+set(PLATFORM_SRCFILES
+      ${INCLUDE_ROOT}/uv-win.h
+      ${WIN_PATH}/async.c
+      ${WIN_PATH}/atomicops-inl.h
+      ${WIN_PATH}/core.c
+      ${WIN_PATH}/detect-wakeup.c
+      ${WIN_PATH}/dl.c
+      ${WIN_PATH}/error.c
+      ${WIN_PATH}/fs.c
+      ${WIN_PATH}/fs-event.c
+      ${WIN_PATH}/getaddrinfo.c
+      ${WIN_PATH}/getnameinfo.c
+      ${WIN_PATH}/handle.c
+      ${WIN_PATH}/handle-inl.h
+      ${WIN_PATH}/internal.h
+      ${WIN_PATH}/loop-watcher.c
+      ${WIN_PATH}/pipe.c
+      ${WIN_PATH}/thread.c
+      ${WIN_PATH}/poll.c
+      ${WIN_PATH}/process.c
+      ${WIN_PATH}/process-stdio.c
+      ${WIN_PATH}/req.c
+      ${WIN_PATH}/req-inl.h
+      ${WIN_PATH}/signal.c
+      ${WIN_PATH}/snprintf.c
+      ${WIN_PATH}/stream.c
+      ${WIN_PATH}/stream-inl.h
+      ${WIN_PATH}/tcp.c
+      ${WIN_PATH}/tty.c
+      ${WIN_PATH}/timer.c
+      ${WIN_PATH}/udp.c
+      ${WIN_PATH}/util.c
+      ${WIN_PATH}/winapi.c
+      ${WIN_PATH}/winapi.h
+      ${WIN_PATH}/winsock.c
+      ${WIN_PATH}/winsock.h
+    )
+
+set(TEST_MAINFILE "${TEST_ROOT}/runner_main.c")
+
+set(TEST_UNITFILES
+      ${TEST_ROOT}/test_idle.c
+      ${TEST_ROOT}/test_timer.c
+      ${TEST_ROOT}/test_timer_again.c
+      ${TEST_ROOT}/test_fs.c
+      ${TEST_ROOT}/test_cwd.c
+      ${TEST_ROOT}/test_error.c
+      ${TEST_ROOT}/test_tcp_open.c
+      ${TEST_ROOT}/test_shutdown_eof.c
+      ${TEST_ROOT}/echo_server.c
+      ${TEST_ROOT}/test_getaddrinfo.c
+      ${TEST_ROOT}/test_threadpool.c
+      ${TEST_ROOT}/test_condvar.c
+      ${TEST_ROOT}/test_active.c
+      ${TEST_ROOT}/test_walk_handles.c
+      ${TEST_ROOT}/test_async.c
+      )
+
+# configuration values
+set(CONFIG_FILESYSTEM 1)
diff --git a/cmake/option/option_x86_64-darwin.cmake b/cmake/option/option_x86_64-darwin.cmake
new file mode 100644 (file)
index 0000000..309dd01
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright 2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# unix common
+
+include("cmake/option/option_unix_common.cmake")
+
+# darwin specific
+
+set(PLATFORM_SRCFILES
+      ${PLATFORM_SRCFILES}
+      ${INCLUDE_ROOT}/uv-darwin.h
+      ${INCLUDE_ROOT}/pthread-barrier.h
+#     ${UNIX_PATH}/bsd-ifaddrs.c
+      ${UNIX_PATH}/darwin.c
+#     ${UNIX_PATH}/darwin-proctitle.c
+      ${UNIX_PATH}/fsevents.c
+      ${UNIX_PATH}/kqueue.c
+#     ${UNIX_PATH}/proctitle.c
+      ${UNIX_PATH}/pthread-barrier.c
+      )
+
+set(PLATFORM_TESTFILES "${TEST_ROOT}/runner_linux.c")
+
+set(PLATFORM_HOSTHELPERFILES "${TEST_ROOT}/runner_linux_raw.c")
+
+set(TUV_LINK_LIBS "pthread")
+
+if(DEFINED BUILD_HOST_HELPER AND BUILD_HOST_HELPER STREQUAL "yes")
+  add_definitions("-D__HOST_HELPER__")
+endif()
diff --git a/cmake/option/option_x86_64-linux.cmake b/cmake/option/option_x86_64-linux.cmake
new file mode 100644 (file)
index 0000000..4139a69
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# linux common
+include("cmake/option/option_unix_common.cmake")
+include("cmake/option/option_linux_common.cmake")
+
+# x86_64 specific
diff --git a/cmake/options.cmake b/cmake/options.cmake
new file mode 100644 (file)
index 0000000..04d017b
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright 2015-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 2.8.12)
+
+# platform name in lower case
+string(TOLOWER ${CMAKE_SYSTEM_NAME} PLATFORM_NAME_L)
+
+set(PATH_ROOT    ${CMAKE_SOURCE_DIR})
+set(SOURCE_ROOT  ${PATH_ROOT}/src)
+set(INCLUDE_ROOT ${PATH_ROOT}/include)
+set(TEST_ROOT    ${PATH_ROOT}/test)
+
+if (DEFINED LIBTUV_CUSTOM_LIB_OUT)
+  set(LIB_OUT ${LIBTUV_CUSTOM_LIB_OUT})
+else()
+  set(BIN_ROOT ${CMAKE_BINARY_DIR})
+  set(LIB_OUT "${BIN_ROOT}/../lib")
+  set(BIN_OUT "${BIN_ROOT}/../bin")
+endif()
+
+# path for platform depends files, use full name for default
+# (e.g, i686-linux)
+if (NOT DEFINED TARGET_PLATFORM)
+  message(FATAL_ERROR "TARGET_PLATFORM was not specified! (e.g., arm-linux)")
+endif()
+
+set(TUV_PLATFORM_PATH ${TARGET_PLATFORM})
+include("cmake/option/option_${TARGET_PLATFORM}.cmake")
+
+string(TOLOWER ${CMAKE_BUILD_TYPE} BUILD_TYPE_L)
+if (${BUILD_TYPE_L} STREQUAL "debug")
+  set(FLAGS_COMMON "${FLAGS_COMMON} -DENABLE_DEBUG_LOG")
+endif()
+
+foreach(FLAG ${FLAGS_COMMON})
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAG}")
+endforeach()
+
+# generate configure file
+configure_file("${SOURCE_ROOT}/tuv__config.h.in"
+               "${CMAKE_BINARY_DIR}/tuv__config.h")
+configure_file("${SOURCE_ROOT}/tuv__config.h.in"
+               "${INCLUDE_ROOT}/tuv__config.h")
+
+message(STATUS "Build Type: [${CMAKE_BUILD_TYPE}]")
diff --git a/cmake/tuvtest.cmake b/cmake/tuvtest.cmake
new file mode 100644 (file)
index 0000000..4f4e9e0
--- /dev/null
@@ -0,0 +1,66 @@
+# Copyright 2015 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 2.8.12)
+
+
+set(TEST_PLATFORMFILES )
+
+set(TUVTESTNAME "tuvtester")
+
+foreach(FILES ${PLATFORM_TESTFILES})
+  set(TEST_PLATFORMFILES ${TEST_PLATFORMFILES} ${FILES})
+endforeach()
+
+if(DEFINED BUILD_TEST_LIB AND BUILD_TEST_LIB STREQUAL "yes")
+  add_library(${TUVTESTNAME} ${TEST_MAINFILE} ${TEST_UNITFILES}
+              ${TEST_PLATFORMFILES})
+else()
+  add_executable(${TUVTESTNAME} ${TEST_MAINFILE} ${TEST_UNITFILES}
+                 ${TEST_PLATFORMFILES})
+endif()
+
+target_include_directories(${TUVTESTNAME} SYSTEM PRIVATE ${TARGET_INC})
+target_include_directories(${TUVTESTNAME} PUBLIC ${LIB_TUV_INCDIRS}
+                                                 ${TUV_TEST_INCDIRS})
+target_link_libraries(${TUVTESTNAME} LINK_PUBLIC ${TARGETLIBNAME}
+                      ${TUV_LINK_LIBS})
+set_target_properties(${TUVTESTNAME} PROPERTIES
+    ARCHIVE_OUTPUT_DIRECTORY "${LIB_OUT}"
+    LIBRARY_OUTPUT_DIRECTORY "${LIB_OUT}"
+    RUNTIME_OUTPUT_DIRECTORY "${BIN_OUT}")
+
+if(DEFINED BUILD_TEST_LIB AND BUILD_TEST_LIB STREQUAL "yes")
+  if(DEFINED COPY_TARGET_LIB)
+    add_custom_command(TARGET ${TUVTESTNAME} POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${TUVTESTNAME}>
+                                        "${COPY_TARGET_LIB}"
+        COMMENT "Copying lib${TUVTESTNAME} to ${COPY_TARGET_LIB}")
+  endif()
+endif()
+
+if(DEFINED BUILD_HOST_HELPER AND BUILD_HOST_HELPER STREQUAL "yes")
+  set(TUVHOSTHELPER "tuvhelper")
+  add_executable(${TUVHOSTHELPER} ${TEST_MAINFILE} ${TEST_UNITFILES}
+                 ${PLATFORM_HOSTHELPERFILES})
+
+  target_include_directories(${TUVHOSTHELPER} SYSTEM PRIVATE ${TARGET_INC})
+  target_include_directories(${TUVHOSTHELPER} PUBLIC ${LIB_TUV_INCDIRS})
+  target_link_libraries(${TUVHOSTHELPER} LINK_PUBLIC ${TARGETLIBNAME}
+                        ${TUV_LINK_LIBS})
+  set_target_properties(${TUVHOSTHELPER} PROPERTIES
+      ARCHIVE_OUTPUT_DIRECTORY "${LIB_OUT}"
+      LIBRARY_OUTPUT_DIRECTORY "${LIB_OUT}"
+      RUNTIME_OUTPUT_DIRECTORY "${BIN_OUT}")
+endif()
diff --git a/config/tizen/.gbs.conf b/config/tizen/.gbs.conf
new file mode 100644 (file)
index 0000000..27429e2
--- /dev/null
@@ -0,0 +1,4 @@
+[general]
+upstream_branch = ${upstreamversion}
+upstream_tag = ${upstreamversion}
+packaging_dir = config/tizen/packaging
diff --git a/config/tizen/gbs.conf b/config/tizen/gbs.conf
new file mode 100644 (file)
index 0000000..baff78a
--- /dev/null
@@ -0,0 +1,82 @@
+[general]
+#profile = profile.tizen_4.0_unified
+#profile = profile.tizen_4.0_iotpreview2
+#profile = profile.tizen_unified
+profile = profile.tizen_unified_m1
+upstream_branch = ${upstreamversion}
+upstream_tag = ${upstreamversion}
+packaging_dir = config/tizen/packaging
+
+
+[profile.tizen_unified_m1]
+obs = obs.spin
+repos = repo.tizen_base_m1, repo.tizen_unified_m1
+
+[repo.tizen_base_m1]
+url = http://download.tizen.org/releases/milestone/tizen/base/tizen-base_20180518.1/repos/standard/packages/
+user =
+passwdx =
+
+[repo.tizen_unified_m1]
+url = http://download.tizen.org/releases/milestone/tizen/unified/tizen-unified_20180528.1/repos/standard/packages/
+user =
+passwdx =
+
+
+[profile.tizen_4.0_iotpreview2]
+obs = obs.spin
+repos = repo.tizen_local, repo.tizen_4.0_base_arm_20171222.1, repo.tizen_4.0_unified_20180118.1
+
+[repo.tizen_4.0_base_arm_20171222.1]
+url = http://download.tizen.org/releases/previews/iot/preview2/tizen-4.0-base_20171222.1/repos/arm/packages/
+user =
+passwdx =
+
+[repo.tizen_4.0_unified_20180118.1]
+url = http://download.tizen.org/releases/previews/iot/preview2/tizen-4.0-unified_20180118.1/repos/standard/packages/
+user =
+passwdx =
+
+
+[profile.tizen_4.0_unified]
+obs = obs.spin
+repos = repo.public_4.0_base_arm, repo.tizen_4.0_unified
+
+[repo.public_4.0_base_arm]
+url = http://download.tizen.org/snapshots/tizen/4.0-base/latest/repos/arm/packages/
+user = 
+passwdx =
+
+[repo.tizen_4.0_unified]
+url = http://download.tizen.org/snapshots/tizen/4.0-unified/latest/repos/standard/packages/
+user =
+passwdx =
+
+
+[profile.tizen_unified]
+obs = obs.spin
+repos = repo.tizen_base, repo.tizen_unified
+
+[repo.tizen_base]
+url = http://download.tizen.org/snapshots/tizen/base/latest/repos/standard/packages/
+user =
+passwdx =
+
+[repo.tizen_unified]
+url = http://download.tizen.org/snapshots/tizen/unified/latest/repos/standard/packages/
+user =
+passwdx =
+
+
+
+[obs.spin]
+url = http://168.219.209.58:81
+
+[obs.tizen]
+url = https://api.tizen.org
+user = obs_viewer
+passwdx = QlpoOTFBWSZTWRP5nYMAAB6fgCAAeUA9mr+QBvzF4CAAVGAZDTRoDI0YBlCKeptQBoA0aGZIAottAkltEPOK7BAFXE9mTUzocPMzQRkPoPpNwEZx3rRQhxkXmGHS6wCjHskyVCP4u5IpwoSAn8zsGA==
+
+
+[repo.tizen_local_unified_m1]
+url = ~/GBS-ROOT/local/repos/tizen_unified_m1/
diff --git a/config/tizen/packaging/libtuv.manifest b/config/tizen/packaging/libtuv.manifest
new file mode 100644 (file)
index 0000000..f5a44ec
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+        <request>
+                <domain name="_"/>
+        </request>
+</manifest>
diff --git a/config/tizen/packaging/libtuv.pc.in b/config/tizen/packaging/libtuv.pc.in
new file mode 100644 (file)
index 0000000..d2e58e5
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libtuv
+Description: Asynchronous I/O for embedded system
+Version: 1.0.0
+Libs: -L${libdir} -lpthread
+Cflags: -I${includedir}/libtuv
diff --git a/config/tizen/packaging/libtuv.spec b/config/tizen/packaging/libtuv.spec
new file mode 100644 (file)
index 0000000..80675f8
--- /dev/null
@@ -0,0 +1,61 @@
+Name: libtuv
+Version: 1.0.0
+Release: 0
+Summary: Asynchronous I/O for embedded system
+Group: Development/System
+License: Apache-2.0
+Source:     %{name}-%{version}.tar.gz
+Source1:    %{name}.pc.in
+Source1001: %{name}.manifest
+ExclusiveArch: %arm %ix86 x86_64
+
+BuildRequires: cmake
+
+%description
+Asynchronous I/O for embedded system
+
+# Initialize the variables
+%{!?build_mode: %define build_mode release}
+%{!?board: %define board None}
+%{!?platform: %define platform noarch-tizen}
+
+%package devel
+Summary: Header files for %{name}
+Group: Development/System
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+Development libraries for %{name}
+
+%prep
+%setup -q -c
+cp %{SOURCE1001} .
+
+%build
+TUV_PLATFORM=%{platform} TUV_BOARD=%{board} TUV_BUILD_TYPE=%{build_mode} \
+TUV_BUILDTESTER=no TUV_CREATE_SHARED_LIB=yes make
+
+%install
+mkdir -p %{buildroot}%{_includedir}/libtuv
+mkdir -p %{buildroot}%{_libdir}/pkgconfig
+
+cp ./build/%{platform}/%{build_mode}/lib/* %{buildroot}%{_libdir}/
+
+cp ./include/*.h %{buildroot}%{_includedir}/libtuv
+cp ./config/tizen/packaging/%{name}.pc.in %{buildroot}/%{_libdir}/pkgconfig/%{name}.pc
+
+%post -p /sbin/ldconfig
+%postun -p /sbin/ldconfig
+
+%files
+%manifest config/tizen/packaging/%{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/libtuv.so
+%license LICENSE
+
+%files devel
+%manifest config/tizen/packaging/%{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/libtuv.*
+%{_libdir}/pkgconfig/%{name}.pc
+%{_includedir}/*
diff --git a/include/android-ifaddrs.h b/include/android-ifaddrs.h
new file mode 100644 (file)
index 0000000..9cd19fe
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1995, 1999
+ *     Berkeley Software Design, Inc.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``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 Berkeley Software Design, Inc. 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.
+ *
+ *     BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp
+ */
+
+#ifndef        _IFADDRS_H_
+#define        _IFADDRS_H_
+
+struct ifaddrs {
+       struct ifaddrs  *ifa_next;
+       char            *ifa_name;
+       unsigned int     ifa_flags;
+       struct sockaddr *ifa_addr;
+       struct sockaddr *ifa_netmask;
+       struct sockaddr *ifa_dstaddr;
+       void            *ifa_data;
+};
+
+/*
+ * This may have been defined in <net/if.h>.  Note that if <net/if.h> is
+ * to be included it must be included before this header file.
+ */
+#ifndef        ifa_broadaddr
+#define        ifa_broadaddr   ifa_dstaddr     /* broadcast address interface */
+#endif
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern int getifaddrs(struct ifaddrs **ifap);
+extern void freeifaddrs(struct ifaddrs *ifa);
+__END_DECLS
+
+#endif
diff --git a/include/pthread-barrier.h b/include/pthread-barrier.h
new file mode 100644 (file)
index 0000000..3e01705
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+Copyright (c) 2016, Kari Tristan Helgason <kthelgason@gmail.com>
+
+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_PTHREAD_BARRIER_
+#define _UV_PTHREAD_BARRIER_
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h> /* sem_t */
+
+#define PTHREAD_BARRIER_SERIAL_THREAD  0x12345
+
+/*
+ * To maintain ABI compatibility with
+ * libuv v1.x struct is padded according
+ * to target platform
+ */
+#if defined(__ANDROID__)
+# define UV_BARRIER_STRUCT_PADDING \
+  sizeof(pthread_mutex_t) + \
+  sizeof(pthread_cond_t) + \
+  sizeof(unsigned int) - \
+  sizeof(void *)
+#elif defined(__APPLE__)
+# define UV_BARRIER_STRUCT_PADDING \
+  sizeof(pthread_mutex_t) + \
+  2 * sizeof(sem_t) + \
+  2 * sizeof(unsigned int) - \
+  sizeof(void *)
+#else
+# define UV_BARRIER_STRUCT_PADDING 0
+#endif
+
+typedef struct {
+  pthread_mutex_t  mutex;
+  pthread_cond_t   cond;
+  unsigned         threshold;
+  unsigned         in;
+  unsigned         out;
+} _uv_barrier;
+
+typedef struct {
+  _uv_barrier* b;
+  char _pad[UV_BARRIER_STRUCT_PADDING];
+} pthread_barrier_t;
+
+int pthread_barrier_init(pthread_barrier_t* barrier,
+                         const void* barrier_attr,
+                         unsigned count);
+
+int pthread_barrier_wait(pthread_barrier_t* barrier);
+int pthread_barrier_destroy(pthread_barrier_t *barrier);
+
+#endif /* _UV_PTHREAD_BARRIER_ */
diff --git a/include/stdint-msvc2008.h b/include/stdint-msvc2008.h
new file mode 100644 (file)
index 0000000..d02608a
--- /dev/null
@@ -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/include/tree.h b/include/tree.h
new file mode 100644 (file)
index 0000000..f936416
--- /dev/null
@@ -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/include/tuv__debuglog.h b/include/tuv__debuglog.h
new file mode 100644 (file)
index 0000000..b67e1ca
--- /dev/null
@@ -0,0 +1,111 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __tuv_debuglog_header__
+#define __tuv_debuglog_header__
+
+
+#ifdef ENABLE_DEBUG_LOG
+
+#include <stdio.h>
+
+#if defined(DEBUG)
+#define ABORT()                  \
+  do {                           \
+    TDLOG("!!!!! ABORT !!!!!");  \
+    exit(-1);                    \
+  } while(0)
+#else
+#define ABORT() abort()
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern int tuv_debug_level;
+extern FILE *tuv_log_stream;
+extern const char* tuv_debug_prefix[4];
+
+#define TDBGLEV_ERR  1
+#define TDBGLEV_WARN 2
+#define TDBGLEV_INFO 3
+
+#define TUV_DLOG(lvl, ...) \
+  do { \
+    int errback = errno; \
+    if (0 <= lvl && lvl <= tuv_debug_level && tuv_log_stream) { \
+      fprintf(tuv_log_stream, "[%s] ", tuv_debug_prefix[lvl]); \
+      fprintf(tuv_log_stream, __VA_ARGS__); \
+      fprintf(tuv_log_stream, "\r\n"); \
+      fflush(tuv_log_stream); \
+    } \
+    errno = errback; \
+  } while (0)
+#define TDLOG(...)   TUV_DLOG(TDBGLEV_ERR, __VA_ARGS__)
+#define TDDLOG(...)  TUV_DLOG(TDBGLEV_WARN, __VA_ARGS__)
+#define TDDDLOG(...) TUV_DLOG(TDBGLEV_INFO, __VA_ARGS__)
+
+/*
+  Use DLOG for errors, default you will see them
+  Use DDLOG for warnings, set tuv_debug_level=2 to see them
+  USE DDDLOG for informations, set tuv_debug_level=3 to see them
+*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#else /* !ENABLE_DEBUG_LOG */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ABORT()
+#define TUV_DLOG(...)
+#define TDLOG(...)
+#define TDDLOG(...)
+#define TDDDLOG(...)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* ENABLE_DEBUG_LOG */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+void InitDebugSettings();
+void ReleaseDebugSettings();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __tuv_debuglog_header__ */
diff --git a/include/uv-aix.h b/include/uv-aix.h
new file mode 100644 (file)
index 0000000..7dc992f
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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_AIX_H
+#define UV_AIX_H
+
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  int fs_fd;                                                                  \
+
+#define UV_PLATFORM_FS_EVENT_FIELDS                                           \
+  uv__io_t event_watcher;                                                     \
+  char *dir_filename;                                                         \
+
+#endif /* UV_AIX_H */
diff --git a/include/uv-bsd.h b/include/uv-bsd.h
new file mode 100644 (file)
index 0000000..2d72b3d
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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_BSD_H
+#define UV_BSD_H
+
+#define UV_PLATFORM_FS_EVENT_FIELDS                                           \
+  uv__io_t event_watcher;                                                     \
+
+#define UV_IO_PRIVATE_PLATFORM_FIELDS                                         \
+  int rcount;                                                                 \
+  int wcount;                                                                 \
+
+#define UV_HAVE_KQUEUE 1
+
+#endif /* UV_BSD_H */
diff --git a/include/uv-darwin.h b/include/uv-darwin.h
new file mode 100644 (file)
index 0000000..d226415
--- /dev/null
@@ -0,0 +1,61 @@
+/* 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_DARWIN_H
+#define UV_DARWIN_H
+
+#if defined(__APPLE__) && defined(__MACH__)
+# include <mach/mach.h>
+# include <mach/task.h>
+# include <mach/semaphore.h>
+# include <TargetConditionals.h>
+# define UV_PLATFORM_SEM_T semaphore_t
+#endif
+
+#define UV_IO_PRIVATE_PLATFORM_FIELDS                                         \
+  int rcount;                                                                 \
+  int wcount;                                                                 \
+
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  uv_thread_t cf_thread;                                                      \
+  void* _cf_reserved;                                                         \
+  void* cf_state;                                                             \
+  uv_mutex_t cf_mutex;                                                        \
+  uv_sem_t cf_sem;                                                            \
+  void* cf_signals[2];                                                        \
+
+#define UV_PLATFORM_FS_EVENT_FIELDS                                           \
+  uv__io_t event_watcher;                                                     \
+  char* realpath;                                                             \
+  int realpath_len;                                                           \
+  int cf_flags;                                                               \
+  uv_async_t* cf_cb;                                                          \
+  void* cf_events[2];                                                         \
+  void* cf_member[2];                                                         \
+  int cf_error;                                                               \
+  uv_mutex_t cf_mutex;                                                        \
+
+#define UV_STREAM_PRIVATE_PLATFORM_FIELDS                                     \
+  void* select;                                                               \
+
+#define UV_HAVE_KQUEUE 1
+
+#endif /* UV_DARWIN_H */
diff --git a/include/uv-errno.h b/include/uv-errno.h
new file mode 100644 (file)
index 0000000..f137151
--- /dev/null
@@ -0,0 +1,419 @@
+/* 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>
+
+#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 (-E2BIG)
+#else
+# define UV__E2BIG (-4093)
+#endif
+
+#if defined(EACCES) && !defined(_WIN32)
+# define UV__EACCES (-EACCES)
+#else
+# define UV__EACCES (-4092)
+#endif
+
+#if defined(EADDRINUSE) && !defined(_WIN32)
+# define UV__EADDRINUSE (-EADDRINUSE)
+#else
+# define UV__EADDRINUSE (-4091)
+#endif
+
+#if defined(EADDRNOTAVAIL) && !defined(_WIN32)
+# define UV__EADDRNOTAVAIL (-EADDRNOTAVAIL)
+#else
+# define UV__EADDRNOTAVAIL (-4090)
+#endif
+
+#if defined(EAFNOSUPPORT) && !defined(_WIN32)
+# define UV__EAFNOSUPPORT (-EAFNOSUPPORT)
+#else
+# define UV__EAFNOSUPPORT (-4089)
+#endif
+
+#if defined(EAGAIN) && !defined(_WIN32)
+# define UV__EAGAIN (-EAGAIN)
+#else
+# define UV__EAGAIN (-4088)
+#endif
+
+#if defined(EALREADY) && !defined(_WIN32)
+# define UV__EALREADY (-EALREADY)
+#else
+# define UV__EALREADY (-4084)
+#endif
+
+#if defined(EBADF) && !defined(_WIN32)
+# define UV__EBADF (-EBADF)
+#else
+# define UV__EBADF (-4083)
+#endif
+
+#if defined(EBUSY) && !defined(_WIN32)
+# define UV__EBUSY (-EBUSY)
+#else
+# define UV__EBUSY (-4082)
+#endif
+
+#if defined(ECANCELED) && !defined(_WIN32)
+# define UV__ECANCELED (-ECANCELED)
+#else
+# define UV__ECANCELED (-4081)
+#endif
+
+#if defined(ECHARSET) && !defined(_WIN32)
+# define UV__ECHARSET (-ECHARSET)
+#else
+# define UV__ECHARSET (-4080)
+#endif
+
+#if defined(ECONNABORTED) && !defined(_WIN32)
+# define UV__ECONNABORTED (-ECONNABORTED)
+#else
+# define UV__ECONNABORTED (-4079)
+#endif
+
+#if defined(ECONNREFUSED) && !defined(_WIN32)
+# define UV__ECONNREFUSED (-ECONNREFUSED)
+#else
+# define UV__ECONNREFUSED (-4078)
+#endif
+
+#if defined(ECONNRESET) && !defined(_WIN32)
+# define UV__ECONNRESET (-ECONNRESET)
+#else
+# define UV__ECONNRESET (-4077)
+#endif
+
+#if defined(EDESTADDRREQ) && !defined(_WIN32)
+# define UV__EDESTADDRREQ (-EDESTADDRREQ)
+#else
+# define UV__EDESTADDRREQ (-4076)
+#endif
+
+#if defined(EEXIST) && !defined(_WIN32)
+# define UV__EEXIST (-EEXIST)
+#else
+# define UV__EEXIST (-4075)
+#endif
+
+#if defined(EFAULT) && !defined(_WIN32)
+# define UV__EFAULT (-EFAULT)
+#else
+# define UV__EFAULT (-4074)
+#endif
+
+#if defined(EHOSTUNREACH) && !defined(_WIN32)
+# define UV__EHOSTUNREACH (-EHOSTUNREACH)
+#else
+# define UV__EHOSTUNREACH (-4073)
+#endif
+
+#if defined(EINTR) && !defined(_WIN32)
+# define UV__EINTR (-EINTR)
+#else
+# define UV__EINTR (-4072)
+#endif
+
+#if defined(EINVAL) && !defined(_WIN32)
+# define UV__EINVAL (-EINVAL)
+#else
+# define UV__EINVAL (-4071)
+#endif
+
+#if defined(EIO) && !defined(_WIN32)
+# define UV__EIO (-EIO)
+#else
+# define UV__EIO (-4070)
+#endif
+
+#if defined(EISCONN) && !defined(_WIN32)
+# define UV__EISCONN (-EISCONN)
+#else
+# define UV__EISCONN (-4069)
+#endif
+
+#if defined(EISDIR) && !defined(_WIN32)
+# define UV__EISDIR (-EISDIR)
+#else
+# define UV__EISDIR (-4068)
+#endif
+
+#if defined(ELOOP) && !defined(_WIN32)
+# define UV__ELOOP (-ELOOP)
+#else
+# define UV__ELOOP (-4067)
+#endif
+
+#if defined(EMFILE) && !defined(_WIN32)
+# define UV__EMFILE (-EMFILE)
+#else
+# define UV__EMFILE (-4066)
+#endif
+
+#if defined(EMSGSIZE) && !defined(_WIN32)
+# define UV__EMSGSIZE (-EMSGSIZE)
+#else
+# define UV__EMSGSIZE (-4065)
+#endif
+
+#if defined(ENAMETOOLONG) && !defined(_WIN32)
+# define UV__ENAMETOOLONG (-ENAMETOOLONG)
+#else
+# define UV__ENAMETOOLONG (-4064)
+#endif
+
+#if defined(ENETDOWN) && !defined(_WIN32)
+# define UV__ENETDOWN (-ENETDOWN)
+#else
+# define UV__ENETDOWN (-4063)
+#endif
+
+#if defined(ENETUNREACH) && !defined(_WIN32)
+# define UV__ENETUNREACH (-ENETUNREACH)
+#else
+# define UV__ENETUNREACH (-4062)
+#endif
+
+#if defined(ENFILE) && !defined(_WIN32)
+# define UV__ENFILE (-ENFILE)
+#else
+# define UV__ENFILE (-4061)
+#endif
+
+#if defined(ENOBUFS) && !defined(_WIN32)
+# define UV__ENOBUFS (-ENOBUFS)
+#else
+# define UV__ENOBUFS (-4060)
+#endif
+
+#if defined(ENODEV) && !defined(_WIN32)
+# define UV__ENODEV (-ENODEV)
+#else
+# define UV__ENODEV (-4059)
+#endif
+
+#if defined(ENOENT) && !defined(_WIN32)
+# define UV__ENOENT (-ENOENT)
+#else
+# define UV__ENOENT (-4058)
+#endif
+
+#if defined(ENOMEM) && !defined(_WIN32)
+# define UV__ENOMEM (-ENOMEM)
+#else
+# define UV__ENOMEM (-4057)
+#endif
+
+#if defined(ENONET) && !defined(_WIN32)
+# define UV__ENONET (-ENONET)
+#else
+# define UV__ENONET (-4056)
+#endif
+
+#if defined(ENOSPC) && !defined(_WIN32)
+# define UV__ENOSPC (-ENOSPC)
+#else
+# define UV__ENOSPC (-4055)
+#endif
+
+#if defined(ENOSYS) && !defined(_WIN32)
+# define UV__ENOSYS (-ENOSYS)
+#else
+# define UV__ENOSYS (-4054)
+#endif
+
+#if defined(ENOTCONN) && !defined(_WIN32)
+# define UV__ENOTCONN (-ENOTCONN)
+#else
+# define UV__ENOTCONN (-4053)
+#endif
+
+#if defined(ENOTDIR) && !defined(_WIN32)
+# define UV__ENOTDIR (-ENOTDIR)
+#else
+# define UV__ENOTDIR (-4052)
+#endif
+
+#if defined(ENOTEMPTY) && !defined(_WIN32)
+# define UV__ENOTEMPTY (-ENOTEMPTY)
+#else
+# define UV__ENOTEMPTY (-4051)
+#endif
+
+#if defined(ENOTSOCK) && !defined(_WIN32)
+# define UV__ENOTSOCK (-ENOTSOCK)
+#else
+# define UV__ENOTSOCK (-4050)
+#endif
+
+#if defined(ENOTSUP) && !defined(_WIN32)
+# define UV__ENOTSUP (-ENOTSUP)
+#else
+# define UV__ENOTSUP (-4049)
+#endif
+
+#if defined(EPERM) && !defined(_WIN32)
+# define UV__EPERM (-EPERM)
+#else
+# define UV__EPERM (-4048)
+#endif
+
+#if defined(EPIPE) && !defined(_WIN32)
+# define UV__EPIPE (-EPIPE)
+#else
+# define UV__EPIPE (-4047)
+#endif
+
+#if defined(EPROTO) && !defined(_WIN32)
+# define UV__EPROTO (-EPROTO)
+#else
+# define UV__EPROTO (-4046)
+#endif
+
+#if defined(EPROTONOSUPPORT) && !defined(_WIN32)
+# define UV__EPROTONOSUPPORT (-EPROTONOSUPPORT)
+#else
+# define UV__EPROTONOSUPPORT (-4045)
+#endif
+
+#if defined(EPROTOTYPE) && !defined(_WIN32)
+# define UV__EPROTOTYPE (-EPROTOTYPE)
+#else
+# define UV__EPROTOTYPE (-4044)
+#endif
+
+#if defined(EROFS) && !defined(_WIN32)
+# define UV__EROFS (-EROFS)
+#else
+# define UV__EROFS (-4043)
+#endif
+
+#if defined(ESHUTDOWN) && !defined(_WIN32)
+# define UV__ESHUTDOWN (-ESHUTDOWN)
+#else
+# define UV__ESHUTDOWN (-4042)
+#endif
+
+#if defined(ESPIPE) && !defined(_WIN32)
+# define UV__ESPIPE (-ESPIPE)
+#else
+# define UV__ESPIPE (-4041)
+#endif
+
+#if defined(ESRCH) && !defined(_WIN32)
+# define UV__ESRCH (-ESRCH)
+#else
+# define UV__ESRCH (-4040)
+#endif
+
+#if defined(ETIMEDOUT) && !defined(_WIN32)
+# define UV__ETIMEDOUT (-ETIMEDOUT)
+#else
+# define UV__ETIMEDOUT (-4039)
+#endif
+
+#if defined(ETXTBSY) && !defined(_WIN32)
+# define UV__ETXTBSY (-ETXTBSY)
+#else
+# define UV__ETXTBSY (-4038)
+#endif
+
+#if defined(EXDEV) && !defined(_WIN32)
+# define UV__EXDEV (-EXDEV)
+#else
+# define UV__EXDEV (-4037)
+#endif
+
+#if defined(EFBIG) && !defined(_WIN32)
+# define UV__EFBIG (-EFBIG)
+#else
+# define UV__EFBIG (-4036)
+#endif
+
+#if defined(ENOPROTOOPT) && !defined(_WIN32)
+# define UV__ENOPROTOOPT (-ENOPROTOOPT)
+#else
+# define UV__ENOPROTOOPT (-4035)
+#endif
+
+#if defined(ERANGE) && !defined(_WIN32)
+# define UV__ERANGE (-ERANGE)
+#else
+# define UV__ERANGE (-4034)
+#endif
+
+#if defined(ENXIO) && !defined(_WIN32)
+# define UV__ENXIO (-ENXIO)
+#else
+# define UV__ENXIO (-4033)
+#endif
+
+#if defined(EMLINK) && !defined(_WIN32)
+# define UV__EMLINK (-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 (-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
+
+#endif /* UV_ERRNO_H_ */
diff --git a/include/uv-linux.h b/include/uv-linux.h
new file mode 100644 (file)
index 0000000..9b38405
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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_LINUX_H
+#define UV_LINUX_H
+
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  uv__io_t inotify_read_watcher;                                              \
+  void* inotify_watchers;                                                     \
+  int inotify_fd;                                                             \
+
+#define UV_PLATFORM_FS_EVENT_FIELDS                                           \
+  void* watchers[2];                                                          \
+  int wd;                                                                     \
+
+#endif /* UV_LINUX_H */
diff --git a/include/uv-nuttx.h b/include/uv-nuttx.h
new file mode 100644 (file)
index 0000000..7dac257
--- /dev/null
@@ -0,0 +1,112 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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_NUTTX_H
+#define UV_NUTTX_H
+
+#include <pthread.h>
+#include <poll.h>     // nuttx poll
+#include <unistd.h>
+#include <errno.h>
+
+#include <netdb.h>
+#include <sys/uio.h>
+
+#ifndef TUV_POLL_EVENTS_SIZE
+#define TUV_POLL_EVENTS_SIZE  32
+#endif
+
+// TUV_CHANGES@20161130: FIXME: What is the reasonable nubmer?
+#ifndef IOV_MAX
+#define IOV_MAX TUV_POLL_EVENTS_SIZE
+#endif
+
+//---------------------------------------------------------------------------
+// TUV_CHANGES@20161130: NuttX does not provide ENOTSUP.
+// TUV_CHANGES@20170407: NuttX started to provide ENOTSUP.
+#ifndef ENOTSUP
+#define ENOTSUP       EOPNOTSUPP
+#endif
+
+// TUV_CHANGES@20171130:
+// Not defined macros. Copied from x86-64 linux system header
+#define STDIN_FILENO  0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+#define SIGPROF       27
+#define TCP_NODELAY   1
+
+#ifndef _SC_CLK_TCK
+#define _SC_CLK_TCK 0x0006
+#endif
+
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC 1
+#endif
+
+//-----------------------------------------------------------------------------
+// date time extension
+// uint64_t uv__time_precise();
+
+
+//-----------------------------------------------------------------------------
+// thread and mutex
+#define UV_PLATFORM_RWLOCK_T pthread_mutex_t
+
+//-----------------------------------------------------------------------------
+// uio
+ssize_t readv(int __fd, const struct iovec* __iovec, int __count);
+ssize_t writev(int __fd, const struct iovec* __iovec, int __count);
+
+
+//-----------------------------------------------------------------------------
+// etc
+int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
+
+// Maximum queue length specifiable by listen
+#define SOMAXCONN 8
+
+//-----------------------------------------------------------------------------
+// structure extension for nuttx                                                          
+//                                                                                        
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  struct pollfd pollfds[TUV_POLL_EVENTS_SIZE];                                \
+  int npollfds;                                                               \
+
+
+
+#endif // UV_NUTTX_H
diff --git a/include/uv-os390.h b/include/uv-os390.h
new file mode 100644 (file)
index 0000000..b0b068f
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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_MVS_H
+#define UV_MVS_H
+
+#define UV_PLATFORM_SEM_T int
+
+#endif /* UV_MVS_H */
diff --git a/include/uv-sunos.h b/include/uv-sunos.h
new file mode 100644 (file)
index 0000000..0421664
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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_SUNOS_H
+#define UV_SUNOS_H
+
+#include <sys/port.h>
+#include <port.h>
+
+/* For the sake of convenience and reduced #ifdef-ery in src/unix/sunos.c,
+ * add the fs_event fields even when this version of SunOS doesn't support
+ * file watching.
+ */
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  uv__io_t fs_event_watcher;                                                  \
+  int fs_fd;                                                                  \
+
+#if defined(PORT_SOURCE_FILE)
+
+# define UV_PLATFORM_FS_EVENT_FIELDS                                          \
+  file_obj_t fo;                                                              \
+  int fd;                                                                     \
+
+#endif /* defined(PORT_SOURCE_FILE) */
+
+#endif /* UV_SUNOS_H */
diff --git a/include/uv-threadpool.h b/include/uv-threadpool.h
new file mode 100644 (file)
index 0000000..9708ebd
--- /dev/null
@@ -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/include/uv-tizenrt.h b/include/uv-tizenrt.h
new file mode 100644 (file)
index 0000000..8aaafd0
--- /dev/null
@@ -0,0 +1,106 @@
+/* Copyright 2017-present Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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_TIZENRT_H
+#define UV_TIZENRT_H
+
+#include <pthread.h>
+#include <poll.h>     // tizenrt poll
+#include <unistd.h>
+#include <errno.h>
+
+#include <netdb.h>
+#include <uio.h>
+
+#ifndef TUV_POLL_EVENTS_SIZE
+#define TUV_POLL_EVENTS_SIZE  32
+#endif
+
+// TUV_CHANGES@20161130: FIXME: What is the reasonable nubmer?
+#ifndef IOV_MAX
+#define IOV_MAX TUV_POLL_EVENTS_SIZE
+#endif
+
+//---------------------------------------------------------------------------
+// TUV_CHANGES@20161130: NuttX doesn't provide ENOTSUP.
+#define ENOTSUP       EOPNOTSUPP
+
+// TUV_CHANGES@20171130:
+// Not defined macros. Copied from x86-64 linux system header
+#define STDIN_FILENO  0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+#define SIGPROF       27
+
+//-----------------------------------------------------------------------------
+// TUV_CHANGES@20171130: Not used. I believe we can remove those.
+#define _SC_CLK_TCK           0x0006
+#define _SC_NPROCESSORS_ONLN  0x0061
+#define CLOCK_MONOTONIC       1
+
+//-----------------------------------------------------------------------------
+// date time extension
+// uint64_t uv__time_precise();
+
+
+//-----------------------------------------------------------------------------
+// thread and mutex
+#define UV_PLATFORM_RWLOCK_T pthread_mutex_t
+
+//-----------------------------------------------------------------------------
+// uio
+ssize_t readv(int __fd, const struct iovec* __iovec, int __count);
+ssize_t writev(int __fd, const struct iovec* __iovec, int __count);
+
+
+//-----------------------------------------------------------------------------
+// etc
+int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
+
+// Maximum queue length specifiable by listen
+#define SOMAXCONN 8
+
+//-----------------------------------------------------------------------------
+// structure extension for nuttx                                                          
+//                                                                                        
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  struct pollfd pollfds[TUV_POLL_EVENTS_SIZE];                                \
+  int npollfds;                                                               \
+
+
+
+#endif // UV_NUTTX_H
diff --git a/include/uv-unix.h b/include/uv-unix.h
new file mode 100644 (file)
index 0000000..d41bef6
--- /dev/null
@@ -0,0 +1,447 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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_UNIX_H
+#define UV_UNIX_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#if !defined(__NUTTX__) && \
+    !defined(__TIZENRT__) /* No netinet/tcp.h */
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <termios.h>
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+#include <pwd.h>
+#endif
+
+#include <semaphore.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "uv-threadpool.h"
+
+#if defined(__linux__)
+# include "uv-linux.h"
+#elif defined(_AIX)
+# include "uv-aix.h"
+#elif defined(__sun)
+# include "uv-sunos.h"
+#elif defined(__APPLE__)
+# include "uv-darwin.h"
+#elif defined(__DragonFly__)       || \
+      defined(__FreeBSD__)         || \
+      defined(__FreeBSD_kernel__)  || \
+      defined(__OpenBSD__)         || \
+      defined(__NetBSD__)
+# include "uv-bsd.h"
+#elif defined(__NUTTX__)
+# include "uv-nuttx.h"
+#elif defined(__TIZENRT__)
+# include "uv-tizenrt.h"
+#endif
+
+#ifndef PTHREAD_BARRIER_SERIAL_THREAD
+# include "pthread-barrier.h"
+#endif
+
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif
+
+#ifndef NI_MAXSERV
+# define NI_MAXSERV 32
+#endif
+
+#ifndef UV_IO_PRIVATE_PLATFORM_FIELDS
+# define UV_IO_PRIVATE_PLATFORM_FIELDS /* empty */
+#endif
+
+/*
+ * Enables memory optimizations for low memory systems.
+ * This might hurt the performance of the library.
+ */
+#define TUV_ENABLE_MEMORY_CONSTRAINTS /* empty */
+
+struct uv__io_s;
+struct uv__async;
+struct uv_loop_s;
+
+typedef void (*uv__io_cb)(struct uv_loop_s* loop,
+                          struct uv__io_s* w,
+                          unsigned int events);
+typedef struct uv__io_s uv__io_t;
+
+struct uv__io_s {
+  uv__io_cb cb;
+  void* pending_queue[2];
+  void* watcher_queue[2];
+  unsigned int pevents; /* Pending event mask i.e. mask at next tick. */
+  unsigned int events;  /* Current event mask. */
+  int fd;
+  UV_IO_PRIVATE_PLATFORM_FIELDS
+};
+
+typedef void (*uv__async_cb)(struct uv_loop_s* loop,
+                             struct uv__async* w,
+                             unsigned int nevents);
+
+struct uv__async {
+  uv__async_cb cb;
+  uv__io_t io_watcher;
+  int wfd;
+};
+
+#ifndef UV_PLATFORM_SEM_T
+# define UV_PLATFORM_SEM_T sem_t
+#endif
+
+/* TUV_CHANGES@20161128: for NuttX */
+#ifndef UV_PLATFORM_RWLOCK_T
+# define UV_PLATFORM_RWLOCK_T pthread_rwlock_t
+#endif
+
+#ifndef UV_PLATFORM_LOOP_FIELDS
+# define UV_PLATFORM_LOOP_FIELDS /* empty */
+#endif
+
+#ifndef UV_PLATFORM_FS_EVENT_FIELDS
+# define UV_PLATFORM_FS_EVENT_FIELDS /* empty */
+#endif
+
+#ifndef UV_STREAM_PRIVATE_PLATFORM_FIELDS
+# define UV_STREAM_PRIVATE_PLATFORM_FIELDS /* empty */
+#endif
+
+/* Note: May be cast to struct iovec. See writev(2). */
+typedef struct uv_buf_t {
+  char* base;
+  size_t len;
+} uv_buf_t;
+
+typedef int uv_file;
+typedef int uv_os_sock_t;
+typedef int uv_os_fd_t;
+
+#define UV_ONCE_INIT PTHREAD_ONCE_INIT
+
+typedef pthread_once_t uv_once_t;
+typedef pthread_t uv_thread_t;
+typedef pthread_mutex_t uv_mutex_t;
+typedef UV_PLATFORM_RWLOCK_T uv_rwlock_t;
+typedef UV_PLATFORM_SEM_T uv_sem_t;
+typedef pthread_cond_t uv_cond_t;
+typedef pthread_key_t uv_key_t;
+typedef pthread_barrier_t uv_barrier_t;
+
+
+/* Platform-specific definitions for uv_spawn support. */
+typedef gid_t uv_gid_t;
+typedef uid_t uv_uid_t;
+
+typedef struct dirent uv__dirent_t;
+
+#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
+
+/* Platform-specific definitions for uv_dlopen support. */
+#define UV_DYNAMIC /* empty */
+
+typedef struct {
+  void* handle;
+  char* errmsg;
+} uv_lib_t;
+
+#ifdef TUV_ENABLE_MEMORY_CONSTRAINTS
+# define UV_LOOP_PRIVATE_FIELDS                                               \
+  unsigned long flags;                                                        \
+  int backend_fd;                                                             \
+  void* pending_queue[2];                                                     \
+  void* watcher_queue[2];                                                     \
+  uv__io_t** watchers;                                                        \
+  unsigned int nwatchers;                                                     \
+  unsigned int nfds;                                                          \
+  void* wq[2];                                                                \
+  uv_mutex_t wq_mutex;                                                        \
+  uv_async_t wq_async;                                                        \
+  uv_rwlock_t cloexec_lock;                                                   \
+  uv_handle_t* closing_handles;                                               \
+  void* process_handles[2];                                                   \
+  void* prepare_handles[2];                                                   \
+  void* check_handles[2];                                                     \
+  void* idle_handles[2];                                                      \
+  void* async_handles[2];                                                     \
+  struct uv__async async_watcher;                                             \
+  struct {                                                                    \
+    void* min;                                                                \
+    unsigned int nelts;                                                       \
+  } timer_heap;                                                               \
+  uint32_t timer_counter;                                                     \
+  uint64_t time;                                                              \
+  int signal_pipefd[2];                                                       \
+  uv__io_t signal_io_watcher;                                                 \
+  uv_signal_t child_watcher;                                                  \
+  int emfile_fd;                                                              \
+  UV_PLATFORM_LOOP_FIELDS
+#else /* original libuv code */
+# define UV_LOOP_PRIVATE_FIELDS                                               \
+  unsigned long flags;                                                        \
+  int backend_fd;                                                             \
+  void* pending_queue[2];                                                     \
+  void* watcher_queue[2];                                                     \
+  uv__io_t** watchers;                                                        \
+  unsigned int nwatchers;                                                     \
+  unsigned int nfds;                                                          \
+  void* wq[2];                                                                \
+  uv_mutex_t wq_mutex;                                                        \
+  uv_async_t wq_async;                                                        \
+  uv_rwlock_t cloexec_lock;                                                   \
+  uv_handle_t* closing_handles;                                               \
+  void* process_handles[2];                                                   \
+  void* prepare_handles[2];                                                   \
+  void* check_handles[2];                                                     \
+  void* idle_handles[2];                                                      \
+  void* async_handles[2];                                                     \
+  struct uv__async async_watcher;                                             \
+  struct {                                                                    \
+    void* min;                                                                \
+    unsigned int nelts;                                                       \
+  } timer_heap;                                                               \
+  uint64_t timer_counter;                                                     \
+  uint64_t time;                                                              \
+  int signal_pipefd[2];                                                       \
+  uv__io_t signal_io_watcher;                                                 \
+  uv_signal_t child_watcher;                                                  \
+  int emfile_fd;                                                              \
+  UV_PLATFORM_LOOP_FIELDS
+#endif
+
+#define UV_REQ_TYPE_PRIVATE /* empty */
+
+#define UV_REQ_PRIVATE_FIELDS  /* empty */
+
+#define UV_PRIVATE_REQ_TYPES /* empty */
+
+#define UV_WRITE_PRIVATE_FIELDS                                               \
+  void* queue[2];                                                             \
+  unsigned int write_index;                                                   \
+  uv_buf_t* bufs;                                                             \
+  unsigned int nbufs;                                                         \
+  int error;                                                                  \
+  uv_buf_t bufsml[4];                                                         \
+
+#define UV_CONNECT_PRIVATE_FIELDS                                             \
+  void* queue[2];                                                             \
+
+#define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */
+
+#define UV_UDP_SEND_PRIVATE_FIELDS                                            \
+  void* queue[2];                                                             \
+  struct sockaddr_storage addr;                                               \
+  unsigned int nbufs;                                                         \
+  uv_buf_t* bufs;                                                             \
+  ssize_t status;                                                             \
+  uv_udp_send_cb send_cb;                                                     \
+  uv_buf_t bufsml[4];                                                         \
+
+#define UV_HANDLE_PRIVATE_FIELDS                                              \
+  uv_handle_t* next_closing;                                                  \
+  unsigned int flags;                                                         \
+
+#define UV_STREAM_PRIVATE_FIELDS                                              \
+  uv_connect_t *connect_req;                                                  \
+  uv_shutdown_t *shutdown_req;                                                \
+  uv__io_t io_watcher;                                                        \
+  void* write_queue[2];                                                       \
+  void* write_completed_queue[2];                                             \
+  uv_connection_cb connection_cb;                                             \
+  int delayed_error;                                                          \
+  int accepted_fd;                                                            \
+  void* queued_fds;                                                           \
+  UV_STREAM_PRIVATE_PLATFORM_FIELDS                                           \
+
+#define UV_TCP_PRIVATE_FIELDS /* empty */
+
+#define UV_UDP_PRIVATE_FIELDS                                                 \
+  uv_alloc_cb alloc_cb;                                                       \
+  uv_udp_recv_cb recv_cb;                                                     \
+  uv__io_t io_watcher;                                                        \
+  void* write_queue[2];                                                       \
+  void* write_completed_queue[2];                                             \
+
+#define UV_PIPE_PRIVATE_FIELDS                                                \
+  const char* pipe_fname; /* strdup'ed */
+
+#define UV_POLL_PRIVATE_FIELDS                                                \
+  uv__io_t io_watcher;
+
+#define UV_PREPARE_PRIVATE_FIELDS                                             \
+  uv_prepare_cb prepare_cb;                                                   \
+  void* queue[2];                                                             \
+
+#define UV_CHECK_PRIVATE_FIELDS                                               \
+  uv_check_cb check_cb;                                                       \
+  void* queue[2];                                                             \
+
+#define UV_IDLE_PRIVATE_FIELDS                                                \
+  uv_idle_cb idle_cb;                                                         \
+  void* queue[2];                                                             \
+
+#define UV_ASYNC_PRIVATE_FIELDS                                               \
+  uv_async_cb async_cb;                                                       \
+  void* queue[2];                                                             \
+  int pending;                                                                \
+
+#ifdef TUV_ENABLE_MEMORY_CONSTRAINTS
+# define UV_TIMER_PRIVATE_FIELDS                                              \
+  uv_timer_cb timer_cb;                                                       \
+  void* heap_node[1];                                                         \
+  uint64_t timeout;                                                           \
+  uint64_t repeat;
+#else /* original libuv code */
+# define UV_TIMER_PRIVATE_FIELDS                                              \
+  uv_timer_cb timer_cb;                                                       \
+  void* heap_node[3];                                                         \
+  uint64_t timeout;                                                           \
+  uint64_t repeat;                                                            \
+  uint64_t start_id;
+#endif
+
+#define UV_GETADDRINFO_PRIVATE_FIELDS                                         \
+  struct uv__work work_req;                                                   \
+  uv_getaddrinfo_cb cb;                                                       \
+  struct addrinfo* hints;                                                     \
+  char* hostname;                                                             \
+  char* service;                                                              \
+  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                                             \
+  void* queue[2];                                                             \
+  int status;                                                                 \
+
+#define UV_FS_PRIVATE_FIELDS                                                  \
+  const char *new_path;                                                       \
+  uv_file file;                                                               \
+  int flags;                                                                  \
+  mode_t mode;                                                                \
+  unsigned int nbufs;                                                         \
+  uv_buf_t* bufs;                                                             \
+  off_t off;                                                                  \
+  uv_uid_t uid;                                                               \
+  uv_gid_t gid;                                                               \
+  double atime;                                                               \
+  double mtime;                                                               \
+  struct uv__work work_req;                                                   \
+  uv_buf_t bufsml[4];                                                         \
+
+#define UV_WORK_PRIVATE_FIELDS                                                \
+  struct uv__work work_req;
+
+#define UV_TTY_PRIVATE_FIELDS                                                 \
+  struct termios orig_termios;                                                \
+  int mode;
+
+#define UV_SIGNAL_PRIVATE_FIELDS                                              \
+  /* RB_ENTRY(uv_signal_s) tree_entry; */                                     \
+  struct {                                                                    \
+    struct uv_signal_s* rbe_left;                                             \
+    struct uv_signal_s* rbe_right;                                            \
+    struct uv_signal_s* rbe_parent;                                           \
+    int rbe_color;                                                            \
+  } tree_entry;                                                               \
+  /* Use two counters here so we don have to fiddle with atomics. */          \
+  unsigned int caught_signals;                                                \
+  unsigned int dispatched_signals;
+
+#define UV_FS_EVENT_PRIVATE_FIELDS                                            \
+  uv_fs_event_cb cb;                                                          \
+  UV_PLATFORM_FS_EVENT_FIELDS                                                 \
+
+#endif /* UV_UNIX_H */
diff --git a/include/uv-version.h b/include/uv-version.h
new file mode 100644 (file)
index 0000000..63454a5
--- /dev/null
@@ -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
+ * and uv.gyp whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but
+ * not UV_VERSION_PATCH.)
+ */
+
+#define UV_VERSION_MAJOR 1
+#define UV_VERSION_MINOR 10
+#define UV_VERSION_PATCH 1
+#define UV_VERSION_IS_RELEASE 1
+#define UV_VERSION_SUFFIX ""
+
+#define UV_VERSION_HEX  ((UV_VERSION_MAJOR << 16) | \
+                         (UV_VERSION_MINOR <<  8) | \
+                         (UV_VERSION_PATCH))
+
+#endif /* UV_VERSION_H */
diff --git a/include/uv-win.h b/include/uv-win.h
new file mode 100644 (file)
index 0000000..e8b9b15
--- /dev/null
@@ -0,0 +1,649 @@
+/* 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   0x0502
+#endif
+
+#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
+typedef intptr_t ssize_t;
+# 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 <sys/stat.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+# include "stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#include "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
+
+/* 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 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;
+  } fallback;
+} 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 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;
+
+RB_HEAD(uv_timer_tree_s, uv_timer_s);
+
+#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;                                               \
+  /* The head of the timers tree */                                           \
+  struct uv_timer_tree_s timers;                                              \
+    /* 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 ipc_header;                                                             \
+  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 ipc_header_write_req;                                            \
+  int ipc_pid;                                                                \
+  uint64_t remaining_ipc_rawdata_bytes;                                       \
+  struct {                                                                    \
+    void* queue[2];                                                           \
+    int queue_len;                                                            \
+  } pending_ipc_info;                                                         \
+  uv_write_t* non_overlapped_writes_tail;                                     \
+  uv_mutex_t readfile_mutex;                                                  \
+  volatile HANDLE readfile_thread;
+
+#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 char 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                                               \
+  RB_ENTRY(uv_timer_s) tree_entry;                                            \
+  uint64_t due;                                                               \
+  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
diff --git a/include/uv.h b/include/uv.h
new file mode 100644 (file)
index 0000000..03209bb
--- /dev/null
@@ -0,0 +1,1530 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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
+
+#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 "stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#if defined(_WIN32)
+# include "uv-win.h"
+#else
+# include "uv-unix.h"
+#endif
+
+#include "tuv__debuglog.h"
+
+/* 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(EEXIST, "file already exists")                                           \
+  XX(EFAULT, "bad address in system call argument")                           \
+  XX(EHOSTUNREACH, "host is unreachable")                                     \
+  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(ENETUNREACH, "network is unreachable")                                   \
+  XX(ENOBUFS, "no buffer space available")                                    \
+  XX(ENOENT, "no such file or directory")                                     \
+  XX(ENOMEM, "not enough memory")                                             \
+  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(ESRCH, "no such process")                                                \
+  XX(EPROTONOSUPPORT, "protocol not supported")                               \
+  XX(EROFS, "read-only file system")                                          \
+  XX(ETIMEDOUT, "connection timed out")                                       \
+  XX(EXDEV, "cross-device link not permitted")                                \
+  XX(EOF, "end of file")                                                      \
+  XX(UNKNOWN, "unknown error")                                                \
+
+#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)                                                \
+
+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_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;
+
+/* None of the above. */
+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 enum {
+  UV_LOOP_BLOCK_SIGNAL
+} 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 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_has_active_reqs(const uv_loop_t* loop); /* test driver */
+
+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 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 const char* uv_err_name(int err);
+
+
+#ifdef TUV_ENABLE_MEMORY_CONSTRAINTS
+# define UV_REQ_FIELDS                                                        \
+  /* public */                                                                \
+  void* data;                                                                 \
+  /* read-only */                                                             \
+  uv_req_type type;                                                           \
+  /* private */                                                               \
+  void* active_queue[2];                                                      \
+  UV_REQ_PRIVATE_FIELDS
+#else /* original libuv code */
+# define UV_REQ_FIELDS                                                        \
+  /* public */                                                                \
+  void* data;                                                                 \
+  /* read-only */                                                             \
+  uv_req_type type;                                                           \
+  /* private */                                                               \
+  void* active_queue[2];                                                      \
+  void* reserved[4];                                                          \
+  UV_REQ_PRIVATE_FIELDS
+#endif
+
+/* 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
+};
+
+
+#ifdef TUV_ENABLE_MEMORY_CONSTRAINTS
+# 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;                                                                   \
+  } u;                                                                        \
+  UV_HANDLE_PRIVATE_FIELDS
+#else /* original libuv code */
+# 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
+#endif
+
+/* 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 size_t uv_req_size(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 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;
+  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_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
+};
+
+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_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_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_recv_stop(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;
+
+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);
+
+#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);
+
+
+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_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 tuv_async_deinit(uv_loop_t* loop,
+                               uv_async_t* handle);
+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 void tuv_timer_deinit(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
+} 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 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 = (1 << 4)
+};
+
+/*
+ * 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_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);
+#if defined(__NUTTX__) || defined(__TUV_RAW__) || defined(__TIZENRT__)
+// For embed systems that need cleanup before exit
+UV_EXTERN void tuv_cleanup(void);
+#endif
+
+struct uv_cpu_info_s {
+  char* model;
+  int speed;
+  struct uv_cpu_times_s {
+    uint64_t user;
+    uint64_t nice;
+    uint64_t sys;
+    uint64_t idle;
+    uint64_t irq;
+  } 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;
+};
+
+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 int uv_resident_set_memory(size_t* rss);
+UV_EXTERN int uv_uptime(double* uptime);
+
+typedef struct {
+  long tv_sec;
+  long tv_usec;
+} uv_timeval_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 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);
+
+
+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_type;
+
+/* 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 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);
+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_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_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_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);
+
+
+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_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);
+
+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_hrtime(void);
+
+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 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);
+
+typedef void (*uv_thread_cb)(void* arg);
+
+UV_EXTERN int uv_thread_create(uv_thread_t* tid, 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];
+  void* active_reqs[2];
+  /* Internal flag to signal loop stop. */
+  unsigned int stop_flag;
+  UV_LOOP_PRIVATE_FIELDS
+};
+
+/* Pause the calling thread for a number of milliseconds. */
+UV_EXTERN void uv_sleep(int msec);
+
+/* 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
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* UV_H */
diff --git a/packaging/libtuv.manifest b/packaging/libtuv.manifest
new file mode 100644 (file)
index 0000000..f5a44ec
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+        <request>
+                <domain name="_"/>
+        </request>
+</manifest>
diff --git a/packaging/libtuv.pc.in b/packaging/libtuv.pc.in
new file mode 100644 (file)
index 0000000..d2e58e5
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libtuv
+Description: Asynchronous I/O for embedded system
+Version: 1.0.0
+Libs: -L${libdir} -lpthread
+Cflags: -I${includedir}/libtuv
diff --git a/packaging/libtuv.spec b/packaging/libtuv.spec
new file mode 100644 (file)
index 0000000..80675f8
--- /dev/null
@@ -0,0 +1,61 @@
+Name: libtuv
+Version: 1.0.0
+Release: 0
+Summary: Asynchronous I/O for embedded system
+Group: Development/System
+License: Apache-2.0
+Source:     %{name}-%{version}.tar.gz
+Source1:    %{name}.pc.in
+Source1001: %{name}.manifest
+ExclusiveArch: %arm %ix86 x86_64
+
+BuildRequires: cmake
+
+%description
+Asynchronous I/O for embedded system
+
+# Initialize the variables
+%{!?build_mode: %define build_mode release}
+%{!?board: %define board None}
+%{!?platform: %define platform noarch-tizen}
+
+%package devel
+Summary: Header files for %{name}
+Group: Development/System
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+Development libraries for %{name}
+
+%prep
+%setup -q -c
+cp %{SOURCE1001} .
+
+%build
+TUV_PLATFORM=%{platform} TUV_BOARD=%{board} TUV_BUILD_TYPE=%{build_mode} \
+TUV_BUILDTESTER=no TUV_CREATE_SHARED_LIB=yes make
+
+%install
+mkdir -p %{buildroot}%{_includedir}/libtuv
+mkdir -p %{buildroot}%{_libdir}/pkgconfig
+
+cp ./build/%{platform}/%{build_mode}/lib/* %{buildroot}%{_libdir}/
+
+cp ./include/*.h %{buildroot}%{_includedir}/libtuv
+cp ./config/tizen/packaging/%{name}.pc.in %{buildroot}/%{_libdir}/pkgconfig/%{name}.pc
+
+%post -p /sbin/ldconfig
+%postun -p /sbin/ldconfig
+
+%files
+%manifest config/tizen/packaging/%{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/libtuv.so
+%license LICENSE
+
+%files devel
+%manifest config/tizen/packaging/%{name}.manifest
+%defattr(-,root,root,-)
+%{_libdir}/libtuv.*
+%{_libdir}/pkgconfig/%{name}.pc
+%{_includedir}/*
diff --git a/src/fs-poll.c b/src/fs-poll.c
new file mode 100644 (file)
index 0000000..ee73d5a
--- /dev/null
@@ -0,0 +1,256 @@
+/* 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 <stdlib.h>
+#include <string.h>
+
+struct poll_ctx {
+  uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */
+  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;
+  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);
+  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(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;
+
+  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(handle))
+    return 0;
+
+  ctx = handle->poll_ctx;
+  assert(ctx != NULL);
+  assert(ctx->parent_handle != NULL);
+  ctx->parent_handle = NULL;
+  handle->poll_ctx = NULL;
+
+  /* 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(&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(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);
+}
+
+
+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;
+
+  ctx = container_of(req, struct poll_ctx, fs_req);
+
+  if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */
+    uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
+    uv_fs_req_cleanup(req);
+    return;
+  }
+
+  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 (ctx->parent_handle == NULL) { /* handle has been stopped by callback */
+    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* handle) {
+  uv__free(container_of(handle, struct poll_ctx, timer_handle));
+}
+
+
+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/src/heap-inl.h b/src/heap-inl.h
new file mode 100644 (file)
index 0000000..12716cb
--- /dev/null
@@ -0,0 +1,343 @@
+/* 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
+
+#ifdef TUV_ENABLE_MEMORY_CONSTRAINTS
+struct heap_node {
+  struct heap_node* next;
+};
+
+struct heap {
+  struct heap_node* min; /* head */
+  unsigned int nelts; /* number of elements */
+};
+
+/* 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));
+
+/* 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;
+}
+
+HEAP_EXPORT(void heap_insert(struct heap* heap,
+                             struct heap_node* newnode,
+                             heap_compare_fn less_than)) {
+  struct heap_node* curr = heap->min;
+
+  if (curr == NULL || less_than(newnode, curr))
+  {
+    /* Update head */
+    newnode->next = curr;
+    heap->min = newnode;
+    heap->nelts++;
+    return;
+  }
+
+  while (curr->next != NULL)
+  {
+    if (less_than(newnode, curr->next))
+    {
+      newnode->next = curr->next;
+      curr->next = newnode;
+      heap->nelts++;
+      return;
+    }
+
+    curr = curr->next;
+  }
+
+  /* Insert to the end of the list */
+  curr->next = newnode;
+  newnode->next = NULL;
+  heap->nelts++;
+}
+
+HEAP_EXPORT(void heap_remove(struct heap* heap,
+                             struct heap_node* node)) {
+  struct heap_node* curr = heap->min;
+
+  if (curr == NULL)
+  {
+    return;
+  }
+
+  if (curr == node)
+  {
+    heap->min = heap->min->next;
+    return;
+  }
+
+  while (curr->next != NULL)
+  {
+    if (curr->next == node)
+    {
+      curr->next = curr->next->next;
+      break;
+    }
+
+    curr = curr->next;
+  }
+}
+
+#else /* original libuv code */
+
+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);
+}
+
+#endif
+
+#undef HEAP_EXPORT
+
+#endif  /* UV_SRC_HEAP_H_ */
diff --git a/src/inet.c b/src/inet.c
new file mode 100644 (file)
index 0000000..b68438c
--- /dev/null
@@ -0,0 +1,129 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * 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 "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_pton4(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));
+  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;
+  }
+  strncpy(dst, tmp, size);
+  dst[size - 1] = '\0';
+  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));
+  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;
+}
diff --git a/src/queue.h b/src/queue.h
new file mode 100644 (file)
index 0000000..ff3540a
--- /dev/null
@@ -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/src/threadpool.c b/src/threadpool.c
new file mode 100644 (file)
index 0000000..32b1001
--- /dev/null
@@ -0,0 +1,334 @@
+/* Copyright 2015-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+#else
+# include "win/req-inl.h"
+/* TODO(saghul): unify internal req functions */
+static void uv__req_init(uv_loop_t* loop,
+                         uv_req_t* req,
+                         uv_req_type type) {
+  uv_req_init(loop, req);
+  req->type = type;
+  uv__req_register(loop, req);
+}
+# define uv__req_init(loop, req, type) \
+    uv__req_init((loop), (uv_req_t*)(req), (type))
+#endif
+
+#include <stdlib.h>
+
+#define MAX_THREADPOOL_SIZE 128
+
+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 nthreads;
+static uv_thread_t* threads;
+static uv_thread_t default_threads[4];
+static QUEUE exit_message;
+static QUEUE wq;
+static volatile int initialized;
+
+
+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;
+
+  (void) arg;
+
+  for (;;) {
+    uv_mutex_lock(&mutex);
+
+    while (QUEUE_EMPTY(&wq)) {
+      idle_threads += 1;
+      uv_cond_wait(&cond, &mutex);
+      idle_threads -= 1;
+    }
+
+    q = QUEUE_HEAD(&wq);
+
+    if (q == &exit_message)
+      uv_cond_signal(&cond);
+    else {
+      QUEUE_REMOVE(q);
+      QUEUE_INIT(q);  /* Signal uv_cancel() that the work req is
+                             executing. */
+    }
+
+    uv_mutex_unlock(&mutex);
+
+    if (q == &exit_message)
+      break;
+
+    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);
+  }
+}
+
+
+static void post(QUEUE* q) {
+  uv_mutex_lock(&mutex);
+  QUEUE_INSERT_TAIL(&wq, q);
+  if (idle_threads > 0)
+    uv_cond_signal(&cond);
+  uv_mutex_unlock(&mutex);
+}
+
+
+#ifndef _WIN32
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+static void cleanup(void) {
+#else
+UV_DESTRUCTOR(static void cleanup(void)) {
+#endif
+  unsigned int i;
+
+  if (initialized == 0)
+    return;
+
+  post(&exit_message);
+
+  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;
+  initialized = 0;
+  // tuv
+  // It is very important to initialize once again.
+  // Without following once initialization,
+  // test/run_pass/test_fs_xxx blocks on second running.
+  uv_once_t once_again = UV_ONCE_INIT;
+  once = once_again;
+}
+#endif
+
+
+static void init_once(void) {
+  unsigned int i;
+  const char* val;
+
+  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);
+
+  for (i = 0; i < nthreads; i++)
+    if (uv_thread_create(threads + i, worker, NULL))
+      abort();
+
+  initialized = 1;
+}
+
+
+void uv__work_submit(uv_loop_t* loop,
+                     struct uv__work* w,
+                     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);
+}
+
+
+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__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_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);
+}
+
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+void tuv_cleanup(void) {
+  cleanup();
+}
+#endif
diff --git a/src/tuv__config.h.in b/src/tuv__config.h.in
new file mode 100644 (file)
index 0000000..f45510e
--- /dev/null
@@ -0,0 +1,24 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TUV_CONFIG_HEADER__
+#define __TUV_CONFIG_HEADER__
+
+
+#define TUV_CONFIG_PLATFORM     "@TARGET_PLATFORM@"
+#define TUV_CONFIG_FILESYSTEM   @CONFIG_FILESYSTEM@
+
+
+#endif /* __TUV_CONFIG_HEADER__ */
diff --git a/src/tuv_debuglog.c b/src/tuv_debuglog.c
new file mode 100644 (file)
index 0000000..14784ac
--- /dev/null
@@ -0,0 +1,71 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tuv__debuglog.h"
+
+#ifdef ENABLE_DEBUG_LOG
+
+#if defined(__NUTTX__) || defined(__TUV_MBED__) || defined(__TIZENRT__)
+int tuv_debug_level = TDBGLEV_INFO;
+#else
+int tuv_debug_level = TDBGLEV_ERR;
+#endif
+
+FILE *tuv_log_stream;
+const char* tuv_debug_prefix[4] = { "", "ERR", "WRN", "INF" };
+#endif // ENABLE_DEBUG_LOG
+
+
+void InitDebugSettings() {
+#ifdef ENABLE_DEBUG_LOG
+  const char* dbglevel = NULL;
+  const char* dbglogfile = NULL;
+
+  dbglevel = getenv("TUV_DEBUG_LEVEL");
+  dbglogfile = getenv("TUV_DEBUG_LOGFILE");
+
+  tuv_log_stream = stderr;
+
+  if (dbglevel) {
+    tuv_debug_level = atoi(dbglevel);
+    if (tuv_debug_level < 0) tuv_debug_level = 0;
+    if (tuv_debug_level > TDBGLEV_INFO) tuv_debug_level = TDBGLEV_INFO;
+  }
+  if (dbglogfile) {
+    FILE* logstream;
+    logstream  = fopen(dbglogfile, "w+");
+    if (logstream != NULL) {
+      tuv_log_stream = logstream;
+    }
+  }
+  //fprintf(stderr, "DBG LEV = %d\r\n", tuv_debug_level);
+  //fprintf(stderr, "DBG OUT = %s\r\n", (dbglogfile==NULL?"(stderr)":dbglogfile));
+#endif // ENABLE_DEBUG_LOG
+}
+
+
+void ReleaseDebugSettings() {
+#ifdef ENABLE_DEBUG_LOG
+  if (tuv_log_stream != stderr && tuv_log_stream != stdout) {
+    fclose(tuv_log_stream);
+  }
+  // some embed systems(ex, nuttx) may need this
+  tuv_log_stream = stderr;
+  tuv_debug_level = TDBGLEV_ERR;
+#endif // ENABLE_DEBUG_LOG
+}
diff --git a/src/unix/aix.c b/src/unix/aix.c
new file mode 100644 (file)
index 0000000..652cd98
--- /dev/null
@@ -0,0 +1,1154 @@
+/* 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"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <libgen.h>
+
+#include <sys/protosw.h>
+#include <libperfstat.h>
+#include <procinfo.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+
+#include <sys/poll.h>
+
+#include <sys/pollset.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_AHAFS_EVPRODS_H
+#include <sys/ahafs_evProds.h>
+#endif
+
+#include <sys/mntctl.h>
+#include <sys/vmount.h>
+#include <limits.h>
+#include <strings.h>
+#include <sys/vnode.h>
+
+#define RDWR_BUF_SIZE   4096
+#define EQ(a,b)         (strcmp(a,b) == 0)
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  loop->fs_fd = -1;
+
+  /* Passing maxfd of -1 should mean the limit is determined
+   * by the user's ulimit or the global limit as per the doc */
+  loop->backend_fd = pollset_create(-1);
+
+  if (loop->backend_fd == -1)
+    return -1;
+
+  return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  if (loop->fs_fd != -1) {
+    uv__close(loop->fs_fd);
+    loop->fs_fd = -1;
+  }
+
+  if (loop->backend_fd != -1) {
+    pollset_destroy(loop->backend_fd);
+    loop->backend_fd = -1;
+  }
+}
+
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct poll_ctl pc;
+
+  pc.events = POLLIN;
+  pc.cmd = PS_MOD;  /* Equivalent to PS_ADD if the fd is not in the pollset. */
+  pc.fd = fd;
+
+  if (pollset_ctl(loop->backend_fd, &pc, 1))
+    return -errno;
+
+  pc.cmd = PS_DELETE;
+  if (pollset_ctl(loop->backend_fd, &pc, 1))
+    abort();
+
+  return 0;
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  struct pollfd events[1024];
+  struct pollfd pqry;
+  struct pollfd* pe;
+  struct poll_ctl pc;
+  QUEUE* q;
+  uv__io_t* w;
+  uint64_t base;
+  uint64_t diff;
+  int have_signals;
+  int nevents;
+  int count;
+  int nfds;
+  int i;
+  int rc;
+  int add_failed;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int) loop->nwatchers);
+
+    pc.events = w->pevents;
+    pc.fd = w->fd;
+
+    add_failed = 0;
+    if (w->events == 0) {
+      pc.cmd = PS_ADD;
+      if (pollset_ctl(loop->backend_fd, &pc, 1)) {
+        if (errno != EINVAL) {
+          assert(0 && "Failed to add file descriptor (pc.fd) to pollset");
+          abort();
+        }
+        /* Check if the fd is already in the pollset */
+        pqry.fd = pc.fd;
+        rc = pollset_query(loop->backend_fd, &pqry);
+        switch (rc) {
+        case -1:
+          assert(0 && "Failed to query pollset for file descriptor");
+          abort();
+        case 0:
+          assert(0 && "Pollset does not contain file descriptor");
+          abort();
+        }
+        /* If we got here then the pollset already contained the file descriptor even though
+         * we didn't think it should. This probably shouldn't happen, but we can continue. */
+        add_failed = 1;
+      }
+    }
+    if (w->events != 0 || add_failed) {
+      /* Modify, potentially removing events -- need to delete then add.
+       * Could maybe mod if we knew for sure no events are removed, but
+       * content of w->events is handled above as not reliable (falls back)
+       * so may require a pollset_query() which would have to be pretty cheap
+       * compared to a PS_DELETE to be worth optimizing. Alternatively, could
+       * lazily remove events, squelching them in the mean time. */
+      pc.cmd = PS_DELETE;
+      if (pollset_ctl(loop->backend_fd, &pc, 1)) {
+        assert(0 && "Failed to delete file descriptor (pc.fd) from pollset");
+        abort();
+      }
+      pc.cmd = PS_ADD;
+      if (pollset_ctl(loop->backend_fd, &pc, 1)) {
+        assert(0 && "Failed to add file descriptor (pc.fd) to pollset");
+        abort();
+      }
+    }
+
+    w->events = w->pevents;
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 48; /* Benchmarks suggest this gives the best throughput. */
+
+  for (;;) {
+    nfds = pollset_poll(loop->backend_fd,
+                        events,
+                        ARRAY_SIZE(events),
+                        timeout);
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfds == 0) {
+      assert(timeout != -1);
+      return;
+    }
+
+    if (nfds == -1) {
+      if (errno != EINTR) {
+        abort();
+      }
+
+      if (timeout == -1)
+        continue;
+
+      if (timeout == 0)
+        return;
+
+      /* Interrupted by a signal. Update timeout and poll again. */
+      goto update_timeout;
+    }
+
+    have_signals = 0;
+    nevents = 0;
+
+    assert(loop->watchers != NULL);
+    loop->watchers[loop->nwatchers] = (void*) events;
+    loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
+
+    for (i = 0; i < nfds; i++) {
+      pe = events + i;
+      pc.cmd = PS_DELETE;
+      pc.fd = pe->fd;
+
+      /* Skip invalidated events, see uv__platform_invalidate_fd */
+      if (pc.fd == -1)
+        continue;
+
+      assert(pc.fd >= 0);
+      assert((unsigned) pc.fd < loop->nwatchers);
+
+      w = loop->watchers[pc.fd];
+
+      if (w == NULL) {
+        /* File descriptor that we've stopped watching, disarm it.
+         *
+         * Ignore all errors because we may be racing with another thread
+         * when the file descriptor is closed.
+         */
+        pollset_ctl(loop->backend_fd, &pc, 1);
+        continue;
+      }
+
+      /* Run signal watchers last.  This also affects child process watchers
+       * because those are implemented in terms of signal watchers.
+       */
+      if (w == &loop->signal_io_watcher)
+        have_signals = 1;
+      else
+        w->cb(loop, w, pe->revents);
+
+      nevents++;
+    }
+
+    if (have_signals != 0)
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+
+    loop->watchers[loop->nwatchers] = NULL;
+    loop->watchers[loop->nwatchers + 1] = NULL;
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0) {
+      if (nfds == ARRAY_SIZE(events) && --count != 0) {
+        /* Poll for more events but don't block this time. */
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+
+    if (timeout == 0)
+      return;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    diff = loop->time - base;
+    if (diff >= (uint64_t) timeout)
+      return;
+
+    timeout -= diff;
+  }
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  uint64_t G = 1000000000;
+  timebasestruct_t t;
+  read_wall_time(&t, TIMEBASE_SZ);
+  time_base_to_time(&t, TIMEBASE_SZ);
+  return (uint64_t) t.tb_high * G + t.tb_low;
+}
+
+
+/*
+ * We could use a static buffer for the path manipulations that we need outside
+ * of the function, but this function could be called by multiple consumers and
+ * we don't want to potentially create a race condition in the use of snprintf.
+ * There is no direct way of getting the exe path in AIX - either through /procfs
+ * or through some libc APIs. The below approach is to parse the argv[0]'s pattern
+ * and use it in conjunction with PATH environment variable to craft one.
+ */
+int uv_exepath(char* buffer, size_t* size) {
+  int res;
+  char args[PATH_MAX];
+  char abspath[PATH_MAX];
+  size_t abspath_size;
+  struct procsinfo pi;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  pi.pi_pid = getpid();
+  res = getargs(&pi, sizeof(pi), args, sizeof(args));
+  if (res < 0)
+    return -EINVAL;
+
+  /*
+   * Possibilities for args:
+   * i) an absolute path such as: /home/user/myprojects/nodejs/node
+   * ii) a relative path such as: ./node or ../myprojects/nodejs/node
+   * iii) a bare filename such as "node", after exporting PATH variable
+   *     to its location.
+   */
+
+  /* Case i) and ii) absolute or relative paths */
+  if (strchr(args, '/') != NULL) {
+    if (realpath(args, abspath) != abspath)
+      return -errno;
+
+    abspath_size = strlen(abspath);
+
+    *size -= 1;
+    if (*size > abspath_size)
+      *size = abspath_size;
+
+    memcpy(buffer, abspath, *size);
+    buffer[*size] = '\0';
+
+    return 0;
+  } else {
+  /* Case iii). Search PATH environment variable */
+    char trypath[PATH_MAX];
+    char *clonedpath = NULL;
+    char *token = NULL;
+    char *path = getenv("PATH");
+
+    if (path == NULL)
+      return -EINVAL;
+
+    clonedpath = uv__strdup(path);
+    if (clonedpath == NULL)
+      return -ENOMEM;
+
+    token = strtok(clonedpath, ":");
+    while (token != NULL) {
+      snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args);
+      if (realpath(trypath, abspath) == abspath) {
+        /* Check the match is executable */
+        if (access(abspath, X_OK) == 0) {
+          abspath_size = strlen(abspath);
+
+          *size -= 1;
+          if (*size > abspath_size)
+            *size = abspath_size;
+
+          memcpy(buffer, abspath, *size);
+          buffer[*size] = '\0';
+
+          uv__free(clonedpath);
+          return 0;
+        }
+      }
+      token = strtok(NULL, ":");
+    }
+    uv__free(clonedpath);
+
+    /* Out of tokens (path entries), and no match found */
+    return -EINVAL;
+  }
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  perfstat_memory_total_t mem_total;
+  int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1);
+  if (result == -1) {
+    return 0;
+  }
+  return mem_total.real_free * 4096;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  perfstat_memory_total_t mem_total;
+  int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1);
+  if (result == -1) {
+    return 0;
+  }
+  return mem_total.real_total * 4096;
+}
+
+
+void uv_loadavg(double avg[3]) {
+  perfstat_cpu_total_t ps_total;
+  int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1);
+  if (result == -1) {
+    avg[0] = 0.; avg[1] = 0.; avg[2] = 0.;
+    return;
+  }
+  avg[0] = ps_total.loadavg[0] / (double)(1 << SBITS);
+  avg[1] = ps_total.loadavg[1] / (double)(1 << SBITS);
+  avg[2] = ps_total.loadavg[2] / (double)(1 << SBITS);
+}
+
+
+#ifdef HAVE_SYS_AHAFS_EVPRODS_H
+static char *uv__rawname(char *cp) {
+  static char rawbuf[FILENAME_MAX+1];
+  char *dp = rindex(cp, '/');
+
+  if (dp == 0)
+    return 0;
+
+  *dp = 0;
+  strcpy(rawbuf, cp);
+  *dp = '/';
+  strcat(rawbuf, "/r");
+  strcat(rawbuf, dp+1);
+  return rawbuf;
+}
+
+
+/*
+ * Determine whether given pathname is a directory
+ * Returns 0 if the path is a directory, -1 if not
+ *
+ * Note: Opportunity here for more detailed error information but
+ *       that requires changing callers of this function as well
+ */
+static int uv__path_is_a_directory(char* filename) {
+  struct stat statbuf;
+
+  if (stat(filename, &statbuf) < 0)
+    return -1;  /* failed: not a directory, assume it is a file */
+
+  if (statbuf.st_type == VDIR)
+    return 0;
+
+  return -1;
+}
+
+
+/*
+ * Check whether AHAFS is mounted.
+ * Returns 0 if AHAFS is mounted, or an error code < 0 on failure
+ */
+static int uv__is_ahafs_mounted(void){
+  int rv, i = 2;
+  struct vmount *p;
+  int size_multiplier = 10;
+  size_t siz = sizeof(struct vmount)*size_multiplier;
+  struct vmount *vmt;
+  const char *dev = "/aha";
+  char *obj, *stub;
+
+  p = uv__malloc(siz);
+  if (p == NULL)
+    return -errno;
+
+  /* Retrieve all mounted filesystems */
+  rv = mntctl(MCTL_QUERY, siz, (char*)p);
+  if (rv < 0)
+    return -errno;
+  if (rv == 0) {
+    /* buffer was not large enough, reallocate to correct size */
+    siz = *(int*)p;
+    uv__free(p);
+    p = uv__malloc(siz);
+    if (p == NULL)
+      return -errno;
+    rv = mntctl(MCTL_QUERY, siz, (char*)p);
+    if (rv < 0)
+      return -errno;
+  }
+
+  /* Look for dev in filesystems mount info */
+  for(vmt = p, i = 0; i < rv; i++) {
+    obj = vmt2dataptr(vmt, VMT_OBJECT);     /* device */
+    stub = vmt2dataptr(vmt, VMT_STUB);      /* mount point */
+
+    if (EQ(obj, dev) || EQ(uv__rawname(obj), dev) || EQ(stub, dev)) {
+      uv__free(p);  /* Found a match */
+      return 0;
+    }
+    vmt = (struct vmount *) ((char *) vmt + vmt->vmt_length);
+  }
+
+  /* /aha is required for monitoring filesystem changes */
+  return -1;
+}
+
+/*
+ * Recursive call to mkdir() to create intermediate folders, if any
+ * Returns code from mkdir call
+ */
+static int uv__makedir_p(const char *dir) {
+  char tmp[256];
+  char *p = NULL;
+  size_t len;
+  int err;
+
+  snprintf(tmp, sizeof(tmp),"%s",dir);
+  len = strlen(tmp);
+  if (tmp[len - 1] == '/')
+    tmp[len - 1] = 0;
+  for (p = tmp + 1; *p; p++) {
+    if (*p == '/') {
+      *p = 0;
+      err = mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+      if (err != 0 && errno != EEXIST)
+        return err;
+      *p = '/';
+    }
+  }
+  return mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+}
+
+/*
+ * Creates necessary subdirectories in the AIX Event Infrastructure
+ * file system for monitoring the object specified.
+ * Returns code from mkdir call
+ */
+static int uv__make_subdirs_p(const char *filename) {
+  char cmd[2048];
+  char *p;
+  int rc = 0;
+
+  /* Strip off the monitor file name */
+  p = strrchr(filename, '/');
+
+  if (p == NULL)
+    return 0;
+
+  if (uv__path_is_a_directory((char*)filename) == 0) {
+    sprintf(cmd, "/aha/fs/modDir.monFactory");
+  } else {
+    sprintf(cmd, "/aha/fs/modFile.monFactory");
+  }
+
+  strncat(cmd, filename, (p - filename));
+  rc = uv__makedir_p(cmd);
+
+  if (rc == -1 && errno != EEXIST){
+    return -errno;
+  }
+
+  return rc;
+}
+
+
+/*
+ * Checks if /aha is mounted, then proceeds to set up the monitoring
+ * objects for the specified file.
+ * Returns 0 on success, or an error code < 0 on failure
+ */
+static int uv__setup_ahafs(const char* filename, int *fd) {
+  int rc = 0;
+  char mon_file_write_string[RDWR_BUF_SIZE];
+  char mon_file[PATH_MAX];
+  int file_is_directory = 0; /* -1 == NO, 0 == YES  */
+
+  /* Create monitor file name for object */
+  file_is_directory = uv__path_is_a_directory((char*)filename);
+
+  if (file_is_directory == 0)
+    sprintf(mon_file, "/aha/fs/modDir.monFactory");
+  else
+    sprintf(mon_file, "/aha/fs/modFile.monFactory");
+
+  if ((strlen(mon_file) + strlen(filename) + 5) > PATH_MAX)
+    return -ENAMETOOLONG;
+
+  /* Make the necessary subdirectories for the monitor file */
+  rc = uv__make_subdirs_p(filename);
+  if (rc == -1 && errno != EEXIST)
+    return rc;
+
+  strcat(mon_file, filename);
+  strcat(mon_file, ".mon");
+
+  *fd = 0; errno = 0;
+
+  /* Open the monitor file, creating it if necessary */
+  *fd = open(mon_file, O_CREAT|O_RDWR);
+  if (*fd < 0)
+    return -errno;
+
+  /* Write out the monitoring specifications.
+   * In this case, we are monitoring for a state change event type
+   *    CHANGED=YES
+   * We will be waiting in select call, rather than a read:
+   *    WAIT_TYPE=WAIT_IN_SELECT
+   * We only want minimal information for files:
+   *      INFO_LVL=1
+   * For directories, we want more information to track what file
+   * caused the change
+   *      INFO_LVL=2
+   */
+
+  if (file_is_directory == 0)
+    sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=2");
+  else
+    sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=1");
+
+  rc = write(*fd, mon_file_write_string, strlen(mon_file_write_string)+1);
+  if (rc < 0)
+    return -errno;
+
+  return 0;
+}
+
+/*
+ * Skips a specified number of lines in the buffer passed in.
+ * Walks the buffer pointed to by p and attempts to skip n lines.
+ * Returns the total number of lines skipped
+ */
+static int uv__skip_lines(char **p, int n) {
+  int lines = 0;
+
+  while(n > 0) {
+    *p = strchr(*p, '\n');
+    if (!p)
+      return lines;
+
+    (*p)++;
+    n--;
+    lines++;
+  }
+  return lines;
+}
+
+
+/*
+ * Parse the event occurrence data to figure out what event just occurred
+ * and take proper action.
+ *
+ * The buf is a pointer to the buffer containing the event occurrence data
+ * Returns 0 on success, -1 if unrecoverable error in parsing
+ *
+ */
+static int uv__parse_data(char *buf, int *events, uv_fs_event_t* handle) {
+  int    evp_rc, i;
+  char   *p;
+  char   filename[PATH_MAX]; /* To be used when handling directories */
+
+  p = buf;
+  *events = 0;
+
+  /* Clean the filename buffer*/
+  for(i = 0; i < PATH_MAX; i++) {
+    filename[i] = 0;
+  }
+  i = 0;
+
+  /* Check for BUF_WRAP */
+  if (strncmp(buf, "BUF_WRAP", strlen("BUF_WRAP")) == 0) {
+    assert(0 && "Buffer wrap detected, Some event occurrences lost!");
+    return 0;
+  }
+
+  /* Since we are using the default buffer size (4K), and have specified
+   * INFO_LVL=1, we won't see any EVENT_OVERFLOW conditions.  Applications
+   * should check for this keyword if they are using an INFO_LVL of 2 or
+   * higher, and have a buffer size of <= 4K
+   */
+
+  /* Skip to RC_FROM_EVPROD */
+  if (uv__skip_lines(&p, 9) != 9)
+    return -1;
+
+  if (sscanf(p, "RC_FROM_EVPROD=%d\nEND_EVENT_DATA", &evp_rc) == 1) {
+    if (uv__path_is_a_directory(handle->path) == 0) { /* Directory */
+      if (evp_rc == AHAFS_MODDIR_UNMOUNT || evp_rc == AHAFS_MODDIR_REMOVE_SELF) {
+        /* The directory is no longer available for monitoring */
+        *events = UV_RENAME;
+        handle->dir_filename = NULL;
+      } else {
+        /* A file was added/removed inside the directory */
+        *events = UV_CHANGE;
+
+        /* Get the EVPROD_INFO */
+        if (uv__skip_lines(&p, 1) != 1)
+          return -1;
+
+        /* Scan out the name of the file that triggered the event*/
+        if (sscanf(p, "BEGIN_EVPROD_INFO\n%sEND_EVPROD_INFO", filename) == 1) {
+          handle->dir_filename = uv__strdup((const char*)&filename);
+        } else
+          return -1;
+        }
+    } else { /* Regular File */
+      if (evp_rc == AHAFS_MODFILE_RENAME)
+        *events = UV_RENAME;
+      else
+        *events = UV_CHANGE;
+    }
+  }
+  else
+    return -1;
+
+  return 0;
+}
+
+
+/* This is the internal callback */
+static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) {
+  char   result_data[RDWR_BUF_SIZE];
+  int bytes, rc = 0;
+  uv_fs_event_t* handle;
+  int events = 0;
+  char fname[PATH_MAX];
+  char *p;
+
+  handle = container_of(event_watch, uv_fs_event_t, event_watcher);
+
+  /* At this point, we assume that polling has been done on the
+   * file descriptor, so we can just read the AHAFS event occurrence
+   * data and parse its results without having to block anything
+   */
+  bytes = pread(event_watch->fd, result_data, RDWR_BUF_SIZE, 0);
+
+  assert((bytes >= 0) && "uv__ahafs_event - Error reading monitor file");
+
+  /* Parse the data */
+  if(bytes > 0)
+    rc = uv__parse_data(result_data, &events, handle);
+
+  /* Unrecoverable error */
+  if (rc == -1)
+    return;
+
+  /* For directory changes, the name of the files that triggered the change
+   * are never absolute pathnames
+   */
+  if (uv__path_is_a_directory(handle->path) == 0) {
+    p = handle->dir_filename;
+  } else {
+    p = strrchr(handle->path, '/');
+    if (p == NULL)
+      p = handle->path;
+    else
+      p++;
+  }
+  strncpy(fname, p, sizeof(fname) - 1);
+  /* Just in case */
+  fname[sizeof(fname) - 1] = '\0';
+
+  handle->cb(handle, fname, events, 0);
+}
+#endif
+
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+#ifdef HAVE_SYS_AHAFS_EVPRODS_H
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
+  return 0;
+#else
+  return -ENOSYS;
+#endif
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* filename,
+                      unsigned int flags) {
+#ifdef HAVE_SYS_AHAFS_EVPRODS_H
+  int  fd, rc, str_offset = 0;
+  char cwd[PATH_MAX];
+  char absolute_path[PATH_MAX];
+  char readlink_cwd[PATH_MAX];
+
+
+  /* Figure out whether filename is absolute or not */
+  if (filename[0] == '/') {
+    /* We have absolute pathname */
+    snprintf(absolute_path, sizeof(absolute_path), "%s", filename);
+  } else {
+    /* We have a relative pathname, compose the absolute pathname */
+    snprintf(cwd, sizeof(cwd), "/proc/%lu/cwd", (unsigned long) getpid());
+    rc = readlink(cwd, readlink_cwd, sizeof(readlink_cwd) - 1);
+    if (rc < 0)
+      return rc;
+    /* readlink does not null terminate our string */
+    readlink_cwd[rc] = '\0';
+
+    if (filename[0] == '.' && filename[1] == '/')
+      str_offset = 2;
+
+    snprintf(absolute_path, sizeof(absolute_path), "%s%s", readlink_cwd,
+             filename + str_offset);
+  }
+
+  if (uv__is_ahafs_mounted() < 0)  /* /aha checks failed */
+    return UV_ENOSYS;
+
+  /* Setup ahafs */
+  rc = uv__setup_ahafs((const char *)absolute_path, &fd);
+  if (rc != 0)
+    return rc;
+
+  /* Setup/Initialize all the libuv routines */
+  uv__handle_start(handle);
+  uv__io_init(&handle->event_watcher, uv__ahafs_event, fd);
+  handle->path = uv__strdup(filename);
+  handle->cb = cb;
+
+  uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
+
+  return 0;
+#else
+  return -ENOSYS;
+#endif
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+#ifdef HAVE_SYS_AHAFS_EVPRODS_H
+  if (!uv__is_active(handle))
+    return 0;
+
+  uv__io_close(handle->loop, &handle->event_watcher);
+  uv__handle_stop(handle);
+
+  if (uv__path_is_a_directory(handle->path) == 0) {
+    uv__free(handle->dir_filename);
+    handle->dir_filename = NULL;
+  }
+
+  uv__free(handle->path);
+  handle->path = NULL;
+  uv__close(handle->event_watcher.fd);
+  handle->event_watcher.fd = -1;
+
+  return 0;
+#else
+  return -ENOSYS;
+#endif
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+#ifdef HAVE_SYS_AHAFS_EVPRODS_H
+  uv_fs_event_stop(handle);
+#else
+  UNREACHABLE();
+#endif
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  return argv;
+}
+
+
+int uv_set_process_title(const char* title) {
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  if (buffer == NULL || size == 0)
+    return -EINVAL;
+
+  buffer[0] = '\0';
+  return 0;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  char pp[64];
+  psinfo_t psinfo;
+  int err;
+  int fd;
+
+  snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid());
+
+  fd = open(pp, O_RDONLY);
+  if (fd == -1)
+    return -errno;
+
+  /* FIXME(bnoordhuis) Handle EINTR. */
+  err = -EINVAL;
+  if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) {
+    *rss = (size_t)psinfo.pr_rssize * 1024;
+    err = 0;
+  }
+  uv__close(fd);
+
+  return err;
+}
+
+
+int uv_uptime(double* uptime) {
+  struct utmp *utmp_buf;
+  size_t entries = 0;
+  time_t boot_time;
+
+  utmpname(UTMP_FILE);
+
+  setutent();
+
+  while ((utmp_buf = getutent()) != NULL) {
+    if (utmp_buf->ut_user[0] && utmp_buf->ut_type == USER_PROCESS)
+      ++entries;
+    if (utmp_buf->ut_type == BOOT_TIME)
+      boot_time = utmp_buf->ut_time;
+  }
+
+  endutent();
+
+  if (boot_time == 0)
+    return -ENOSYS;
+
+  *uptime = time(NULL) - boot_time;
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  uv_cpu_info_t* cpu_info;
+  perfstat_cpu_total_t ps_total;
+  perfstat_cpu_t* ps_cpus;
+  perfstat_id_t cpu_id;
+  int result, ncpus, idx = 0;
+
+  result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1);
+  if (result == -1) {
+    return -ENOSYS;
+  }
+
+  ncpus = result = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+  if (result == -1) {
+    return -ENOSYS;
+  }
+
+  ps_cpus = (perfstat_cpu_t*) uv__malloc(ncpus * sizeof(perfstat_cpu_t));
+  if (!ps_cpus) {
+    return -ENOMEM;
+  }
+
+  strcpy(cpu_id.name, FIRST_CPU);
+  result = perfstat_cpu(&cpu_id, ps_cpus, sizeof(perfstat_cpu_t), ncpus);
+  if (result == -1) {
+    uv__free(ps_cpus);
+    return -ENOSYS;
+  }
+
+  *cpu_infos = (uv_cpu_info_t*) uv__malloc(ncpus * sizeof(uv_cpu_info_t));
+  if (!*cpu_infos) {
+    uv__free(ps_cpus);
+    return -ENOMEM;
+  }
+
+  *count = ncpus;
+
+  cpu_info = *cpu_infos;
+  while (idx < ncpus) {
+    cpu_info->speed = (int)(ps_total.processorHZ / 1000000);
+    cpu_info->model = uv__strdup(ps_total.description);
+    cpu_info->cpu_times.user = ps_cpus[idx].user;
+    cpu_info->cpu_times.sys = ps_cpus[idx].sys;
+    cpu_info->cpu_times.idle = ps_cpus[idx].idle;
+    cpu_info->cpu_times.irq = ps_cpus[idx].wait;
+    cpu_info->cpu_times.nice = 0;
+    cpu_info++;
+    idx++;
+  }
+
+  uv__free(ps_cpus);
+  return 0;
+}
+
+
+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);
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses,
+  int* count) {
+  uv_interface_address_t* address;
+  int sockfd, size = 1;
+  struct ifconf ifc;
+  struct ifreq *ifr, *p, flg;
+
+  *count = 0;
+
+  if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) {
+    return -errno;
+  }
+
+  if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) {
+    uv__close(sockfd);
+    return -errno;
+  }
+
+  ifc.ifc_req = (struct ifreq*)uv__malloc(size);
+  ifc.ifc_len = size;
+  if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
+    uv__close(sockfd);
+    return -errno;
+  }
+
+#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p))
+
+  /* Count all up and running ipv4/ipv6 addresses */
+  ifr = ifc.ifc_req;
+  while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
+    p = ifr;
+    ifr = (struct ifreq*)
+      ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
+
+    if (!(p->ifr_addr.sa_family == AF_INET6 ||
+          p->ifr_addr.sa_family == AF_INET))
+      continue;
+
+    memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
+      uv__close(sockfd);
+      return -errno;
+    }
+
+    if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
+      continue;
+
+    (*count)++;
+  }
+
+  /* Alloc the return interface structs */
+  *addresses = (uv_interface_address_t*)
+    uv__malloc(*count * sizeof(uv_interface_address_t));
+  if (!(*addresses)) {
+    uv__close(sockfd);
+    return -ENOMEM;
+  }
+  address = *addresses;
+
+  ifr = ifc.ifc_req;
+  while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
+    p = ifr;
+    ifr = (struct ifreq*)
+      ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
+
+    if (!(p->ifr_addr.sa_family == AF_INET6 ||
+          p->ifr_addr.sa_family == AF_INET))
+      continue;
+
+    memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
+    if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
+      uv__close(sockfd);
+      return -ENOSYS;
+    }
+
+    if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
+      continue;
+
+    /* All conditions above must match count loop */
+
+    address->name = uv__strdup(p->ifr_name);
+
+    if (p->ifr_addr.sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr);
+    }
+
+    /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */
+
+    address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0;
+
+    address++;
+  }
+
+#undef ADDR_SIZE
+
+  uv__close(sockfd);
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+  int count) {
+  int i;
+
+  for (i = 0; i < count; ++i) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  struct pollfd* events;
+  uintptr_t i;
+  uintptr_t nfds;
+  struct poll_ctl pc;
+
+  assert(loop->watchers != NULL);
+
+  events = (struct pollfd*) loop->watchers[loop->nwatchers];
+  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
+
+  if (events != NULL)
+    /* Invalidate events with same file descriptor */
+    for (i = 0; i < nfds; i++)
+      if ((int) events[i].fd == fd)
+        events[i].fd = -1;
+
+  /* Remove the file descriptor from the poll set */
+  pc.events = 0;
+  pc.cmd = PS_DELETE;
+  pc.fd = fd;
+  if(loop->backend_fd >= 0)
+    pollset_ctl(loop->backend_fd, &pc, 1);
+}
diff --git a/src/unix/android-ifaddrs.c b/src/unix/android-ifaddrs.c
new file mode 100644 (file)
index 0000000..30f681b
--- /dev/null
@@ -0,0 +1,703 @@
+/*
+Copyright (c) 2013, Kenneth MacKay
+Copyright (c) 2014, Emergya (Cloud4all, FP7/2007-2013 grant agreement #289016)
+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.
+
+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 HOLDER 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 "android-ifaddrs.h"
+#include "uv-common.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+typedef struct NetlinkList
+{
+    struct NetlinkList *m_next;
+    struct nlmsghdr *m_data;
+    unsigned int m_size;
+} NetlinkList;
+
+static int netlink_socket(void)
+{
+    struct sockaddr_nl l_addr;
+
+    int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+    if(l_socket < 0)
+    {
+        return -1;
+    }
+
+    memset(&l_addr, 0, sizeof(l_addr));
+    l_addr.nl_family = AF_NETLINK;
+    if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
+    {
+        close(l_socket);
+        return -1;
+    }
+
+    return l_socket;
+}
+
+static int netlink_send(int p_socket, int p_request)
+{
+    char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
+
+    struct nlmsghdr *l_hdr;
+    struct rtgenmsg *l_msg;
+    struct sockaddr_nl l_addr;
+
+    memset(l_buffer, 0, sizeof(l_buffer));
+
+    l_hdr = (struct nlmsghdr *)l_buffer;
+    l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr);
+
+    l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg));
+    l_hdr->nlmsg_type = p_request;
+    l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+    l_hdr->nlmsg_pid = 0;
+    l_hdr->nlmsg_seq = p_socket;
+    l_msg->rtgen_family = AF_UNSPEC;
+
+    memset(&l_addr, 0, sizeof(l_addr));
+    l_addr.nl_family = AF_NETLINK;
+    return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
+}
+
+static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
+{
+    struct sockaddr_nl l_addr;
+    struct msghdr l_msg;
+
+    struct iovec l_iov;
+    l_iov.iov_base = p_buffer;
+    l_iov.iov_len = p_len;
+
+    for(;;)
+    {
+        int l_result;
+        l_msg.msg_name = (void *)&l_addr;
+        l_msg.msg_namelen = sizeof(l_addr);
+        l_msg.msg_iov = &l_iov;
+        l_msg.msg_iovlen = 1;
+        l_msg.msg_control = NULL;
+        l_msg.msg_controllen = 0;
+        l_msg.msg_flags = 0;
+        l_result = recvmsg(p_socket, &l_msg, 0);
+
+        if(l_result < 0)
+        {
+            if(errno == EINTR)
+            {
+                continue;
+            }
+            return -2;
+        }
+
+        /* Buffer was too small */
+        if(l_msg.msg_flags & MSG_TRUNC)
+        {
+            return -1;
+        }
+        return l_result;
+    }
+}
+
+static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done)
+{
+    size_t l_size = 4096;
+    void *l_buffer = NULL;
+
+    for(;;)
+    {
+        int l_read;
+
+        uv__free(l_buffer);
+        l_buffer = uv__malloc(l_size);
+        if (l_buffer == NULL)
+        {
+            return NULL;
+        }
+
+        l_read = netlink_recv(p_socket, l_buffer, l_size);
+        *p_size = l_read;
+        if(l_read == -2)
+        {
+            uv__free(l_buffer);
+            return NULL;
+        }
+        if(l_read >= 0)
+        {
+            pid_t l_pid = getpid();
+            struct nlmsghdr *l_hdr;
+            for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
+            {
+                if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
+                {
+                    continue;
+                }
+
+                if(l_hdr->nlmsg_type == NLMSG_DONE)
+                {
+                    *p_done = 1;
+                    break;
+                }
+
+                if(l_hdr->nlmsg_type == NLMSG_ERROR)
+                {
+                    uv__free(l_buffer);
+                    return NULL;
+                }
+            }
+            return l_buffer;
+        }
+
+        l_size *= 2;
+    }
+}
+
+static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
+{
+    NetlinkList *l_item = uv__malloc(sizeof(NetlinkList));
+    if (l_item == NULL)
+    {
+        return NULL;
+    }
+
+    l_item->m_next = NULL;
+    l_item->m_data = p_data;
+    l_item->m_size = p_size;
+    return l_item;
+}
+
+static void freeResultList(NetlinkList *p_list)
+{
+    NetlinkList *l_cur;
+    while(p_list)
+    {
+        l_cur = p_list;
+        p_list = p_list->m_next;
+        uv__free(l_cur->m_data);
+        uv__free(l_cur);
+    }
+}
+
+static NetlinkList *getResultList(int p_socket, int p_request)
+{
+    int l_size;
+    int l_done;
+    NetlinkList *l_list;
+    NetlinkList *l_end;
+
+    if(netlink_send(p_socket, p_request) < 0)
+    {
+        return NULL;
+    }
+
+    l_list = NULL;
+    l_end = NULL;
+
+    l_done = 0;
+    while(!l_done)
+    {
+        NetlinkList *l_item;
+
+        struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
+        /* Error */
+        if(!l_hdr)
+        {
+            freeResultList(l_list);
+            return NULL;
+        }
+
+        l_item = newListItem(l_hdr, l_size);
+        if (!l_item)
+        {
+            freeResultList(l_list);
+            return NULL;
+        }
+        if(!l_list)
+        {
+            l_list = l_item;
+        }
+        else
+        {
+            l_end->m_next = l_item;
+        }
+        l_end = l_item;
+    }
+    return l_list;
+}
+
+static size_t maxSize(size_t a, size_t b)
+{
+    return (a > b ? a : b);
+}
+
+static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
+{
+    switch(p_family)
+    {
+        case AF_INET:
+            return sizeof(struct sockaddr_in);
+        case AF_INET6:
+            return sizeof(struct sockaddr_in6);
+        case AF_PACKET:
+            return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
+        default:
+            return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
+    }
+}
+
+static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
+{
+    switch(p_family)
+    {
+        case AF_INET:
+            memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
+            break;
+        case AF_INET6:
+            memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
+            break;
+        case AF_PACKET:
+            memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
+            ((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
+            break;
+        default:
+            memcpy(p_dest->sa_data, p_data, p_size);
+            break;
+    }
+    p_dest->sa_family = p_family;
+}
+
+static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
+{
+    if(!*p_resultList)
+    {
+        *p_resultList = p_entry;
+    }
+    else
+    {
+        struct ifaddrs *l_cur = *p_resultList;
+        while(l_cur->ifa_next)
+        {
+            l_cur = l_cur->ifa_next;
+        }
+        l_cur->ifa_next = p_entry;
+    }
+}
+
+static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
+{
+    struct ifaddrs *l_entry;
+
+    char *l_index;
+    char *l_name;
+    char *l_addr;
+    char *l_data;
+
+    struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
+
+    size_t l_nameSize = 0;
+    size_t l_addrSize = 0;
+    size_t l_dataSize = 0;
+
+    size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
+    struct rtattr *l_rta;
+    for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
+    {
+        size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
+        switch(l_rta->rta_type)
+        {
+            case IFLA_ADDRESS:
+            case IFLA_BROADCAST:
+                l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
+                break;
+            case IFLA_IFNAME:
+                l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
+                break;
+            case IFLA_STATS:
+                l_dataSize += NLMSG_ALIGN(l_rtaSize);
+                break;
+            default:
+                break;
+        }
+    }
+
+    l_entry = uv__malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
+    if (l_entry == NULL)
+    {
+        return -1;
+    }
+    memset(l_entry, 0, sizeof(struct ifaddrs));
+    l_entry->ifa_name = "";
+
+    l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
+    l_name = l_index + sizeof(int);
+    l_addr = l_name + l_nameSize;
+    l_data = l_addr + l_addrSize;
+
+    /* Save the interface index so we can look it up when handling the
+     * addresses.
+     */
+    memcpy(l_index, &l_info->ifi_index, sizeof(int));
+
+    l_entry->ifa_flags = l_info->ifi_flags;
+
+    l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
+    for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
+    {
+        void *l_rtaData = RTA_DATA(l_rta);
+        size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
+        switch(l_rta->rta_type)
+        {
+            case IFLA_ADDRESS:
+            case IFLA_BROADCAST:
+            {
+                size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
+                makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
+                ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
+                ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
+                if(l_rta->rta_type == IFLA_ADDRESS)
+                {
+                    l_entry->ifa_addr = (struct sockaddr *)l_addr;
+                }
+                else
+                {
+                    l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
+                }
+                l_addr += NLMSG_ALIGN(l_addrLen);
+                break;
+            }
+            case IFLA_IFNAME:
+                strncpy(l_name, l_rtaData, l_rtaDataSize);
+                l_name[l_rtaDataSize] = '\0';
+                l_entry->ifa_name = l_name;
+                break;
+            case IFLA_STATS:
+                memcpy(l_data, l_rtaData, l_rtaDataSize);
+                l_entry->ifa_data = l_data;
+                break;
+            default:
+                break;
+        }
+    }
+
+    addToEnd(p_resultList, l_entry);
+    return 0;
+}
+
+static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
+{
+    int l_num = 0;
+    struct ifaddrs *l_cur = *p_links;
+    while(l_cur && l_num < p_numLinks)
+    {
+        char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
+        int l_index;
+        memcpy(&l_index, l_indexPtr, sizeof(int));
+        if(l_index == p_index)
+        {
+            return l_cur;
+        }
+
+        l_cur = l_cur->ifa_next;
+        ++l_num;
+    }
+    return NULL;
+}
+
+static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
+{
+    struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
+    struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
+
+    size_t l_nameSize = 0;
+    size_t l_addrSize = 0;
+
+    int l_addedNetmask = 0;
+
+    size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
+    struct rtattr *l_rta;
+    struct ifaddrs *l_entry;
+
+    char *l_name;
+    char *l_addr;
+
+    for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
+    {
+        size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
+        if(l_info->ifa_family == AF_PACKET)
+        {
+            continue;
+        }
+
+        switch(l_rta->rta_type)
+        {
+            case IFA_ADDRESS:
+            case IFA_LOCAL:
+                if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
+                {
+                    /* Make room for netmask */
+                    l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
+                    l_addedNetmask = 1;
+                }
+            case IFA_BROADCAST:
+                l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
+                break;
+            case IFA_LABEL:
+                l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
+                break;
+            default:
+                break;
+        }
+    }
+
+    l_entry = uv__malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
+    if (l_entry == NULL)
+    {
+        return -1;
+    }
+    memset(l_entry, 0, sizeof(struct ifaddrs));
+    l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
+
+    l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
+    l_addr = l_name + l_nameSize;
+
+    l_entry->ifa_flags = l_info->ifa_flags;
+    if(l_interface)
+    {
+        l_entry->ifa_flags |= l_interface->ifa_flags;
+    }
+
+    l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
+    for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
+    {
+        void *l_rtaData = RTA_DATA(l_rta);
+        size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
+        switch(l_rta->rta_type)
+        {
+            case IFA_ADDRESS:
+            case IFA_BROADCAST:
+            case IFA_LOCAL:
+            {
+                size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
+                makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
+                if(l_info->ifa_family == AF_INET6)
+                {
+                    if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
+                    {
+                        ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
+                    }
+                }
+
+                /* Apparently in a point-to-point network IFA_ADDRESS contains
+                 * the dest address and IFA_LOCAL contains the local address
+                 */
+                if(l_rta->rta_type == IFA_ADDRESS)
+                {
+                    if(l_entry->ifa_addr)
+                    {
+                        l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
+                    }
+                    else
+                    {
+                        l_entry->ifa_addr = (struct sockaddr *)l_addr;
+                    }
+                }
+                else if(l_rta->rta_type == IFA_LOCAL)
+                {
+                    if(l_entry->ifa_addr)
+                    {
+                        l_entry->ifa_dstaddr = l_entry->ifa_addr;
+                    }
+                    l_entry->ifa_addr = (struct sockaddr *)l_addr;
+                }
+                else
+                {
+                    l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
+                }
+                l_addr += NLMSG_ALIGN(l_addrLen);
+                break;
+            }
+            case IFA_LABEL:
+                strncpy(l_name, l_rtaData, l_rtaDataSize);
+                l_name[l_rtaDataSize] = '\0';
+                l_entry->ifa_name = l_name;
+                break;
+            default:
+                break;
+        }
+    }
+
+    if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6))
+    {
+        unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128);
+        unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen);
+        char l_mask[16] = {0};
+        unsigned i;
+        for(i=0; i<(l_prefix/8); ++i)
+        {
+            l_mask[i] = 0xff;
+        }
+        if(l_prefix % 8)
+        {
+            l_mask[i] = 0xff << (8 - (l_prefix % 8));
+        }
+
+        makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
+        l_entry->ifa_netmask = (struct sockaddr *)l_addr;
+    }
+
+    addToEnd(p_resultList, l_entry);
+    return 0;
+}
+
+static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList)
+{
+
+    int l_numLinks = 0;
+    pid_t l_pid = getpid();
+    for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
+    {
+        unsigned int l_nlsize = p_netlinkList->m_size;
+        struct nlmsghdr *l_hdr;
+        for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
+        {
+            if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
+            {
+                continue;
+            }
+
+            if(l_hdr->nlmsg_type == NLMSG_DONE)
+            {
+                break;
+            }
+
+            if(l_hdr->nlmsg_type == RTM_NEWLINK)
+            {
+                if(interpretLink(l_hdr, p_resultList) == -1)
+                {
+                    return -1;
+                }
+                ++l_numLinks;
+            }
+        }
+    }
+    return l_numLinks;
+}
+
+static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks)
+{
+    pid_t l_pid = getpid();
+    for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
+    {
+        unsigned int l_nlsize = p_netlinkList->m_size;
+        struct nlmsghdr *l_hdr;
+        for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
+        {
+            if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
+            {
+                continue;
+            }
+
+            if(l_hdr->nlmsg_type == NLMSG_DONE)
+            {
+                break;
+            }
+
+            if(l_hdr->nlmsg_type == RTM_NEWADDR)
+            {
+                if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
+                {
+                    return -1;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+int getifaddrs(struct ifaddrs **ifap)
+{
+    int l_socket;
+    int l_result;
+    int l_numLinks;
+    NetlinkList *l_linkResults;
+    NetlinkList *l_addrResults;
+
+    if(!ifap)
+    {
+        return -1;
+    }
+    *ifap = NULL;
+
+    l_socket = netlink_socket();
+    if(l_socket < 0)
+    {
+        return -1;
+    }
+
+    l_linkResults = getResultList(l_socket, RTM_GETLINK);
+    if(!l_linkResults)
+    {
+        close(l_socket);
+        return -1;
+    }
+
+    l_addrResults = getResultList(l_socket, RTM_GETADDR);
+    if(!l_addrResults)
+    {
+        close(l_socket);
+        freeResultList(l_linkResults);
+        return -1;
+    }
+
+    l_result = 0;
+    l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
+    if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
+    {
+        l_result = -1;
+    }
+
+    freeResultList(l_linkResults);
+    freeResultList(l_addrResults);
+    close(l_socket);
+    return l_result;
+}
+
+void freeifaddrs(struct ifaddrs *ifa)
+{
+    struct ifaddrs *l_cur;
+    while(ifa)
+    {
+        l_cur = ifa;
+        ifa = ifa->ifa_next;
+        uv__free(l_cur);
+    }
+}
diff --git a/src/unix/async.c b/src/unix/async.c
new file mode 100644 (file)
index 0000000..87beb1d
--- /dev/null
@@ -0,0 +1,314 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 contains both the uv__async internal infrastructure and the
+ * user-facing uv_async_t functions.
+ */
+
+#include "uv.h"
+#include "internal.h"
+#include "atomic-ops.h"
+
+#include <errno.h>
+#include <stdio.h>  /* snprintf() */
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void uv__async_event(uv_loop_t* loop,
+                            struct uv__async* w,
+                            unsigned int nevents);
+static int uv__async_eventfd(void);
+
+
+int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
+  int err;
+
+  err = uv__async_start(loop, &loop->async_watcher, uv__async_event);
+  if (err)
+    return err;
+
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC);
+  handle->async_cb = async_cb;
+  handle->pending = 0;
+
+  QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue);
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+int tuv_async_deinit(uv_loop_t* loop, uv_async_t* handle) {
+  uv__async_stop(loop, &loop->async_watcher);
+  uv__handle_stop(handle);
+  QUEUE_REMOVE(&handle->queue);
+  QUEUE_INIT(&handle->queue);
+  tuv__handle_deinit(handle);
+
+  return 0;
+}
+
+int uv_async_send(uv_async_t* handle) {
+  /* Do a cheap read first. */
+  if (ACCESS_ONCE(int, handle->pending) != 0)
+    return 0;
+
+  if (cmpxchgi(&handle->pending, 0, 1) == 0)
+    uv__async_send(&handle->loop->async_watcher);
+
+  return 0;
+}
+
+
+void uv__async_close(uv_async_t* handle) {
+  QUEUE_REMOVE(&handle->queue);
+  uv__handle_stop(handle);
+}
+
+
+static void uv__async_event(uv_loop_t* loop,
+                            struct uv__async* w,
+                            unsigned int nevents) {
+  QUEUE queue;
+  QUEUE* q;
+  uv_async_t* h;
+
+  QUEUE_MOVE(&loop->async_handles, &queue);
+  while (!QUEUE_EMPTY(&queue)) {
+    q = QUEUE_HEAD(&queue);
+    h = QUEUE_DATA(q, uv_async_t, queue);
+
+    QUEUE_REMOVE(q);
+    QUEUE_INSERT_TAIL(&loop->async_handles, q);
+
+    if (cmpxchgi(&h->pending, 1, 0) == 0)
+      continue;
+
+    if (h->async_cb == NULL)
+      continue;
+    h->async_cb(h);
+  }
+}
+
+
+static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  struct uv__async* wa;
+  char buf[1024];
+  unsigned n;
+  ssize_t r;
+
+  n = 0;
+  for (;;) {
+    r = read(w->fd, buf, sizeof(buf));
+
+    if (r > 0)
+      n += r;
+
+    if (r == sizeof(buf))
+      continue;
+
+    if (r != -1)
+      break;
+
+    if (errno == EAGAIN || errno == EWOULDBLOCK)
+      break;
+
+    if (errno == EINTR)
+      continue;
+
+    abort();
+  }
+
+  wa = container_of(w, struct uv__async, io_watcher);
+
+#if defined(__linux__)
+  if (wa->wfd == -1) {
+    uint64_t val;
+    assert(n == sizeof(val));
+    memcpy(&val, buf, sizeof(val));  /* Avoid alignment issues. */
+    wa->cb(loop, wa, val);
+    return;
+  }
+#endif
+
+  wa->cb(loop, wa, n);
+}
+
+
+void uv__async_send(struct uv__async* wa) {
+  const void* buf;
+  ssize_t len;
+  int fd;
+  int r;
+
+  buf = "";
+  len = 1;
+  fd = wa->wfd;
+
+#if defined(__linux__)
+  if (fd == -1) {
+    static const uint64_t val = 1;
+    buf = &val;
+    len = sizeof(val);
+    fd = wa->io_watcher.fd;  /* eventfd */
+  }
+#endif
+
+  do
+    r = write(fd, buf, len);
+  while (r == -1 && errno == EINTR);
+
+  if (r == len)
+    return;
+
+  if (r == -1)
+    if (errno == EAGAIN || errno == EWOULDBLOCK)
+      return;
+
+  abort();
+}
+
+
+void uv__async_init(struct uv__async* wa) {
+  wa->io_watcher.fd = -1;
+  wa->wfd = -1;
+}
+
+
+int uv__async_start(uv_loop_t* loop, struct uv__async* wa, uv__async_cb cb) {
+  int pipefd[2];
+  int err;
+
+  if (wa->io_watcher.fd != -1)
+    return 0;
+
+  err = uv__async_eventfd();
+  if (err >= 0) {
+    pipefd[0] = err;
+    pipefd[1] = -1;
+  }
+  else if (err == -ENOSYS) {
+    err = uv__make_pipe(pipefd, UV__F_NONBLOCK);
+#if defined(__linux__)
+    /* Save a file descriptor by opening one of the pipe descriptors as
+     * read/write through the procfs.  That file descriptor can then
+     * function as both ends of the pipe.
+     */
+    if (err == 0) {
+      char buf[32];
+      int fd;
+
+      snprintf(buf, sizeof(buf), "/proc/self/fd/%d", pipefd[0]);
+      fd = uv__open_cloexec(buf, O_RDWR);
+      if (fd >= 0) {
+        uv__close(pipefd[0]);
+        uv__close(pipefd[1]);
+        pipefd[0] = fd;
+        pipefd[1] = fd;
+      }
+    }
+#endif
+  }
+
+  if (err < 0)
+    return err;
+
+  uv__io_init(&wa->io_watcher, uv__async_io, pipefd[0]);
+  uv__io_start(loop, &wa->io_watcher, POLLIN);
+  wa->wfd = pipefd[1];
+  wa->cb = cb;
+
+  return 0;
+}
+
+
+void uv__async_stop(uv_loop_t* loop, struct uv__async* wa) {
+  if (wa->io_watcher.fd == -1)
+    return;
+
+  if (wa->wfd != -1) {
+    if (wa->wfd != wa->io_watcher.fd)
+      uv__close(wa->wfd);
+    wa->wfd = -1;
+  }
+
+  uv__io_stop(loop, &wa->io_watcher, POLLIN);
+  uv__close(wa->io_watcher.fd);
+  wa->io_watcher.fd = -1;
+}
+
+
+static int uv__async_eventfd() {
+#if defined(__linux__)
+  static int no_eventfd2;
+  static int no_eventfd;
+  int fd;
+
+  if (no_eventfd2)
+    goto skip_eventfd2;
+
+  fd = uv__eventfd2(0, UV__EFD_CLOEXEC | UV__EFD_NONBLOCK);
+  if (fd != -1)
+    return fd;
+
+  if (errno != ENOSYS)
+    return -errno;
+
+  no_eventfd2 = 1;
+
+skip_eventfd2:
+
+  if (no_eventfd)
+    goto skip_eventfd;
+
+  fd = uv__eventfd(0);
+  if (fd != -1) {
+    uv__cloexec(fd, 1);
+    uv__nonblock(fd, 1);
+    return fd;
+  }
+
+  if (errno != ENOSYS)
+    return -errno;
+
+  no_eventfd = 1;
+
+skip_eventfd:
+
+#endif
+
+  return -ENOSYS;
+}
diff --git a/src/unix/atomic-ops.h b/src/unix/atomic-ops.h
new file mode 100644 (file)
index 0000000..815e355
--- /dev/null
@@ -0,0 +1,88 @@
+/* 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_ATOMIC_OPS_H_
+#define UV_ATOMIC_OPS_H_
+
+#include "internal.h"  /* UV_UNUSED */
+
+#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+#include <atomic.h>
+#define __sync_val_compare_and_swap(p, o, n) atomic_cas_ptr(p, o, n)
+#endif
+
+UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
+UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval));
+UV_UNUSED(static void cpu_relax(void));
+
+/* Prefer hand-rolled assembly over the gcc builtins because the latter also
+ * issue full memory barriers.
+ */
+UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
+#if defined(__i386__) || defined(__x86_64__)
+  int out;
+  __asm__ __volatile__ ("lock; cmpxchg %2, %1;"
+                        : "=a" (out), "+m" (*(volatile int*) ptr)
+                        : "r" (newval), "0" (oldval)
+                        : "memory");
+  return out;
+#elif defined(_AIX) && defined(__xlC__)
+  const int out = (*(volatile int*) ptr);
+  __compare_and_swap(ptr, &oldval, newval);
+  return out;
+#elif defined(__MVS__)
+  return __plo_CS(ptr, (unsigned int*) ptr,
+                  oldval, (unsigned int*) &newval);
+#else
+  return __sync_val_compare_and_swap(ptr, oldval, newval);
+#endif
+}
+
+UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) {
+#if defined(__i386__) || defined(__x86_64__)
+  long out;
+  __asm__ __volatile__ ("lock; cmpxchg %2, %1;"
+                        : "=a" (out), "+m" (*(volatile long*) ptr)
+                        : "r" (newval), "0" (oldval)
+                        : "memory");
+  return out;
+#elif defined(_AIX) && defined(__xlC__)
+  const long out = (*(volatile int*) ptr);
+# if defined(__64BIT__)
+  __compare_and_swaplp(ptr, &oldval, newval);
+# else
+  __compare_and_swap(ptr, &oldval, newval);
+# endif /* if defined(__64BIT__) */
+  return out;
+#elif defined (__MVS__)
+# ifdef _LP64
+  return __plo_CSGR(ptr, (unsigned long long*) ptr,
+                    oldval, (unsigned long long*) &newval);
+# else
+  return __plo_CS(ptr, (unsigned int*) ptr,
+                  oldval, (unsigned int*) &newval);
+# endif
+#else
+  return __sync_val_compare_and_swap(ptr, oldval, newval);
+#endif
+}
+
+UV_UNUSED(static void cpu_relax(void)) {
+#if defined(__i386__) || defined(__x86_64__)
+  __asm__ __volatile__ ("rep; nop");  /* a.k.a. PAUSE */
+#endif
+}
+
+#endif  /* UV_ATOMIC_OPS_H_ */
diff --git a/src/unix/core.c b/src/unix/core.c
new file mode 100644 (file)
index 0000000..4cceb9b
--- /dev/null
@@ -0,0 +1,856 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+
+#include <stddef.h> /* NULL */
+#include <stdio.h> /* printf */
+#include <stdlib.h>
+#include <string.h> /* strerror */
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <limits.h> /* INT_MAX, PATH_MAX, IOV_MAX */
+#if !defined(__TIZENRT__)
+# include <sys/uio.h> /* writev */
+# include <pwd.h>
+#endif
+
+#ifdef __sun
+# include <sys/filio.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif
+
+#ifdef __APPLE__
+# include <mach-o/dyld.h> /* _NSGetExecutablePath */
+# include <sys/filio.h>
+# if defined(O_CLOEXEC)
+#  define UV__O_CLOEXEC O_CLOEXEC
+# endif
+#endif
+
+#if defined(__DragonFly__)      || \
+    defined(__FreeBSD__)        || \
+    defined(__FreeBSD_kernel__)
+# include <sys/sysctl.h>
+# include <sys/filio.h>
+# include <sys/wait.h>
+# define UV__O_CLOEXEC O_CLOEXEC
+# if defined(__FreeBSD__) && __FreeBSD__ >= 10
+#  define uv__accept4 accept4
+#  define UV__SOCK_NONBLOCK SOCK_NONBLOCK
+#  define UV__SOCK_CLOEXEC  SOCK_CLOEXEC
+# endif
+# if !defined(F_DUP2FD_CLOEXEC) && defined(_F_DUP2FD_CLOEXEC)
+#  define F_DUP2FD_CLOEXEC  _F_DUP2FD_CLOEXEC
+# endif
+#endif
+
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
+# include <dlfcn.h>  /* for dlsym */
+#endif
+
+#if defined(__MVS__)
+#include <sys/ioctl.h>
+#endif
+
+static int uv__run_pending(uv_loop_t* loop);
+
+/* Verify that uv_buf_t is ABI-compatible with struct iovec. */
+STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec));
+STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->base) ==
+              sizeof(((struct iovec*) 0)->iov_base));
+STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->len) ==
+              sizeof(((struct iovec*) 0)->iov_len));
+STATIC_ASSERT(offsetof(uv_buf_t, base) == offsetof(struct iovec, iov_base));
+STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len));
+
+
+void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
+  assert(!uv__is_closing(handle));
+
+  handle->flags |= UV_CLOSING;
+  handle->close_cb = close_cb;
+
+  switch (handle->type) {
+#ifdef TUV_FEATURE_PIPE
+  case UV_NAMED_PIPE:
+    uv__pipe_close((uv_pipe_t*)handle);
+    break;
+#endif
+
+  case UV_TTY:
+    uv__stream_close((uv_stream_t*)handle);
+    break;
+
+  case UV_TCP:
+    uv__tcp_close((uv_tcp_t*)handle);
+    break;
+
+  case UV_UDP:
+    uv__udp_close((uv_udp_t*)handle);
+    break;
+
+  case UV_IDLE:
+    uv__idle_close((uv_idle_t*)handle);
+    break;
+
+  case UV_ASYNC:
+    uv__async_close((uv_async_t*)handle);
+    break;
+
+  case UV_TIMER:
+    uv__timer_close((uv_timer_t*)handle);
+    break;
+
+// { TUV_CHANGES@20180803:
+//   Made signal build time configurable }
+#if TUV_FEATURE_PROCESS
+  case UV_PROCESS:
+    uv__process_close((uv_process_t*)handle);
+    break;
+#endif
+
+  case UV_POLL:
+    uv__poll_close((uv_poll_t*)handle);
+    break;
+
+#ifdef TUV_FEATURE_SIGNAL
+  case UV_SIGNAL:
+    uv__signal_close((uv_signal_t*) handle);
+    /* Signal handles may not be closed immediately. The signal code will */
+    /* itself close uv__make_close_pending whenever appropriate. */
+    return;
+#endif
+
+  default:
+    assert(0);
+  }
+
+  uv__make_close_pending(handle);
+}
+
+
+void uv__make_close_pending(uv_handle_t* handle) {
+  assert(handle->flags & UV_CLOSING);
+  assert(!(handle->flags & UV_CLOSED));
+  handle->next_closing = handle->loop->closing_handles;
+  handle->loop->closing_handles = handle;
+}
+
+int uv__getiovmax(void) {
+#if defined(IOV_MAX)
+  return IOV_MAX;
+#elif defined(_SC_IOV_MAX)
+  static int iovmax = -1;
+  if (iovmax == -1) {
+    iovmax = sysconf(_SC_IOV_MAX);
+    /* On some embedded devices (arm-linux-uclibc based ip camera),
+     * sysconf(_SC_IOV_MAX) can not get the correct value. The return
+     * value is -1 and the errno is EINPROGRESS. Degrade the value to 1.
+     */
+    if (iovmax == -1) iovmax = 1;
+  }
+  return iovmax;
+#else
+  return 1024;
+#endif
+}
+
+
+static void uv__finish_close(uv_handle_t* handle) {
+  /* Note: while the handle is in the UV_CLOSING state now, it's still possible
+   * for it to be active in the sense that uv__is_active() returns true.
+   * A good example is when the user calls uv_shutdown(), immediately followed
+   * by uv_close(). The handle is considered active at this point because the
+   * completion of the shutdown req is still pending.
+   */
+  assert(handle->flags & UV_CLOSING);
+  assert(!(handle->flags & UV_CLOSED));
+  handle->flags |= UV_CLOSED;
+
+  switch (handle->type) {
+    // case UV_PREPARE:
+    // case UV_CHECK:
+    case UV_IDLE:
+    case UV_ASYNC:
+    case UV_TIMER:
+    case UV_PROCESS:
+    // case UV_FS_EVENT:
+    // case UV_FS_POLL:
+    case UV_POLL:
+    case UV_SIGNAL:
+      break;
+
+    case UV_NAMED_PIPE:
+    case UV_TCP:
+    case UV_TTY:
+      uv__stream_destroy((uv_stream_t*)handle);
+      break;
+
+    case UV_UDP:
+      uv__udp_finish_close((uv_udp_t*)handle);
+      break;
+
+    default:
+      assert(0);
+      break;
+  }
+
+  uv__handle_unref(handle);
+  QUEUE_REMOVE(&handle->handle_queue);
+
+  if (handle->close_cb) {
+    handle->close_cb(handle);
+  }
+}
+
+
+static void uv__run_closing_handles(uv_loop_t* loop) {
+  uv_handle_t* p;
+  uv_handle_t* q;
+
+  p = loop->closing_handles;
+  loop->closing_handles = NULL;
+
+  while (p) {
+    q = p->next_closing;
+    uv__finish_close(p);
+    p = q;
+  }
+}
+
+
+int uv_is_closing(const uv_handle_t* handle) {
+  return uv__is_closing(handle);
+}
+
+
+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 (!QUEUE_EMPTY(&loop->idle_handles))
+    return 0;
+
+  if (!QUEUE_EMPTY(&loop->pending_queue))
+    return 0;
+
+  if (loop->closing_handles)
+    return 0;
+
+  return uv__next_timeout(loop);
+}
+
+
+static int uv__loop_alive(const uv_loop_t* loop) {
+  return uv__has_active_handles(loop) ||
+         uv__has_active_reqs(loop) ||
+         loop->closing_handles != NULL;
+}
+
+
+int uv_loop_alive(const uv_loop_t* loop) {
+    return uv__loop_alive(loop);
+}
+
+/* test driver */
+int uv_loop_has_active_reqs(const uv_loop_t* loop) {
+    return uv__has_active_reqs(loop);
+}
+
+
+int uv_run(uv_loop_t* loop, uv_run_mode mode) {
+  int 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__run_pending(loop);
+    uv__run_idle(loop);
+
+    timeout = 0;
+    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
+      timeout = uv_backend_timeout(loop);
+
+    uv__io_poll(loop, timeout);
+    uv__run_closing_handles(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__update_time(loop);
+      uv__run_timers(loop);
+    }
+
+    r = uv__loop_alive(loop);
+    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
+      break;
+  }
+
+  /* The if statement lets gcc compile it to a conditional store. Avoids
+   * dirtying a cache line.
+   */
+  if (loop->stop_flag != 0)
+    loop->stop_flag = 0;
+
+  return r;
+}
+
+
+void uv_update_time(uv_loop_t* loop) {
+  uv__update_time(loop);
+}
+
+
+int uv_is_active(const uv_handle_t* handle) {
+  return uv__is_active(handle);
+}
+
+
+/* Open a socket in non-blocking close-on-exec mode, atomically if possible. */
+int uv__socket(int domain, int type, int protocol) {
+  int sockfd;
+  int err;
+
+#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
+  sockfd = socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
+  if (sockfd != -1)
+    return sockfd;
+
+  if (errno != EINVAL)
+    return -errno;
+#endif
+
+  sockfd = socket(domain, type, protocol);
+  if (sockfd == -1)
+    return -errno;
+
+  err = uv__nonblock(sockfd, 1);
+  if (err == 0)
+    err = uv__cloexec(sockfd, 1);
+
+  if (err) {
+    uv__close(sockfd);
+    return err;
+  }
+
+#if defined(SO_NOSIGPIPE)
+  {
+    int on = 1;
+    setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
+  }
+#endif
+
+  return sockfd;
+}
+
+
+int uv__accept(int sockfd) {
+  int peerfd;
+  int err;
+
+  assert(sockfd >= 0);
+
+  while (1) {
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10)
+    static int no_accept4;
+
+    if (no_accept4)
+      goto skip;
+
+    peerfd = uv__accept4(sockfd,
+                         NULL,
+                         NULL,
+                         UV__SOCK_NONBLOCK|UV__SOCK_CLOEXEC);
+    if (peerfd != -1)
+      return peerfd;
+
+    if (errno == EINTR)
+      continue;
+
+    if (errno != ENOSYS)
+      return -errno;
+
+    no_accept4 = 1;
+skip:
+#endif
+
+    peerfd = accept(sockfd, NULL, NULL);
+    if (peerfd == -1) {
+      if (errno == EINTR)
+        continue;
+      return -errno;
+    }
+
+    err = uv__cloexec(peerfd, 1);
+    if (err == 0)
+      err = uv__nonblock(peerfd, 1);
+
+    if (err) {
+      uv__close(peerfd);
+      return err;
+    }
+
+    return peerfd;
+  }
+}
+
+
+int uv__close_nocheckstdio(int fd) {
+  int saved_errno;
+  int rc;
+
+  assert(fd > -1);  /* Catch uninitialized io_watcher.fd bugs. */
+
+  saved_errno = errno;
+  rc = close(fd);
+  if (rc == -1) {
+    rc = -errno;
+    if (rc == -EINTR || rc == -EINPROGRESS)
+      rc = 0;    /* The close is in progress, not an error. */
+    errno = saved_errno;
+  }
+
+  return rc;
+}
+
+
+int uv__close(int fd) {
+#if !defined(__NUTTX__) && !defined(__TIZENRT__) /* No STDERR_FILENO in nuttx */
+  assert(fd > STDERR_FILENO);  /* Catch stdio close bugs. */
+#endif
+  return uv__close_nocheckstdio(fd);
+}
+
+
+#if defined(_AIX) || \
+    defined(__APPLE__) || \
+    defined(__DragonFly__) || \
+    defined(__FreeBSD__) || \
+    defined(__FreeBSD_kernel__) || \
+    defined(__linux__)
+int uv__nonblock_ioctl(int fd, int set) {
+  int r;
+
+  do
+    r = ioctl(fd, FIONBIO, &set);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    return -errno;
+
+  return 0;
+}
+
+
+int uv__cloexec_ioctl(int fd, int set) {
+  int r;
+
+  do
+    r = ioctl(fd, set ? FIOCLEX : FIONCLEX);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    return -errno;
+
+  return 0;
+}
+#endif
+
+int uv__nonblock_fcntl(int fd, int set) {
+  int flags;
+  int r;
+
+  do
+    r = fcntl(fd, F_GETFL);
+  while (r == -1 && errno == EINTR);
+
+  if (r == -1)
+    return -errno;
+
+  /* Bail out now if already set/clear. */
+  if (!!(r & O_NONBLOCK) == !!set)
+    return 0;
+
+  if (set)
+    flags = r | O_NONBLOCK;
+  else
+    flags = r & ~O_NONBLOCK;
+
+  do
+    r = fcntl(fd, F_SETFL, flags);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    return -errno;
+
+  return 0;
+}
+
+
+int uv__cloexec_fcntl(int fd, int set) {
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+  return 0;
+#endif
+  int flags;
+  int r;
+
+  do
+    r = fcntl(fd, F_GETFD);
+  while (r == -1 && errno == EINTR);
+
+  if (r == -1)
+    return -errno;
+
+  /* Bail out now if already set/clear. */
+  if (!!(r & FD_CLOEXEC) == !!set)
+    return 0;
+
+  if (set)
+    flags = r | FD_CLOEXEC;
+  else
+    flags = r & ~FD_CLOEXEC;
+
+  do
+    r = fcntl(fd, F_SETFD, flags);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    return -errno;
+
+  return 0;
+}
+
+
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
+  return -1;
+}
+#else
+ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
+  struct cmsghdr* cmsg;
+  ssize_t rc;
+  int* pfd;
+  int* end;
+#if defined(__linux__)
+  static int no_msg_cmsg_cloexec;
+  if (no_msg_cmsg_cloexec == 0) {
+    rc = recvmsg(fd, msg, flags | 0x40000000);  /* MSG_CMSG_CLOEXEC */
+    if (rc != -1)
+      return rc;
+    if (errno != EINVAL)
+      return -errno;
+    rc = recvmsg(fd, msg, flags);
+    if (rc == -1)
+      return -errno;
+    no_msg_cmsg_cloexec = 1;
+  } else {
+    rc = recvmsg(fd, msg, flags);
+  }
+#else
+  rc = recvmsg(fd, msg, flags);
+#endif
+  if (rc == -1)
+    return -errno;
+  if (msg->msg_controllen == 0)
+    return rc;
+  for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
+    if (cmsg->cmsg_type == SCM_RIGHTS)
+      for (pfd = (int*) CMSG_DATA(cmsg),
+           end = (int*) ((char*) cmsg + cmsg->cmsg_len);
+           pfd < end;
+           pfd += 1)
+        uv__cloexec(*pfd, 1);
+  return rc;
+}
+#endif /* End of !NUTTX */
+
+
+int uv_cwd(char* buffer, size_t* size) {
+  if (buffer == NULL || size == NULL)
+    return -EINVAL;
+
+  if (getcwd(buffer, *size) == NULL)
+    return -errno;
+
+  *size = strlen(buffer);
+  if (*size > 1 && buffer[*size - 1] == '/') {
+    buffer[*size-1] = '\0';
+    (*size)--;
+  }
+
+  return 0;
+}
+
+
+int uv_chdir(const char* dir) {
+  if (chdir(dir))
+    return -errno;
+
+  return 0;
+}
+
+
+static int uv__run_pending(uv_loop_t* loop) {
+  QUEUE* q;
+  QUEUE pq;
+  uv__io_t* w;
+
+  if (QUEUE_EMPTY(&loop->pending_queue))
+    return 0;
+
+  QUEUE_MOVE(&loop->pending_queue, &pq);
+
+  while (!QUEUE_EMPTY(&pq)) {
+    q = QUEUE_HEAD(&pq);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+    w = QUEUE_DATA(q, uv__io_t, pending_queue);
+    w->cb(loop, w, POLLOUT);
+  }
+
+  return 1;
+}
+
+
+static unsigned int next_power_of_two(unsigned int val) {
+  val -= 1;
+  val |= val >> 1;
+  val |= val >> 2;
+  val |= val >> 4;
+  val |= val >> 8;
+  val |= val >> 16;
+  val += 1;
+  return val;
+}
+
+static void maybe_resize(uv_loop_t* loop, unsigned int len) {
+  uv__io_t** watchers;
+  void* fake_watcher_list;
+  void* fake_watcher_count;
+  unsigned int nwatchers;
+  unsigned int i;
+
+  if (len <= loop->nwatchers)
+    return;
+
+  /* Preserve fake watcher list and count at the end of the watchers */
+  if (loop->watchers != NULL) {
+    fake_watcher_list = loop->watchers[loop->nwatchers];
+    fake_watcher_count = loop->watchers[loop->nwatchers + 1];
+  } else {
+    fake_watcher_list = NULL;
+    fake_watcher_count = NULL;
+  }
+
+  nwatchers = next_power_of_two(len + 2) - 2;
+  watchers = uv__realloc(loop->watchers,
+                         (nwatchers + 2) * sizeof(loop->watchers[0]));
+
+  if (watchers == NULL)
+    abort();
+  for (i = loop->nwatchers; i < nwatchers; i++)
+    watchers[i] = NULL;
+  watchers[nwatchers] = fake_watcher_list;
+  watchers[nwatchers + 1] = fake_watcher_count;
+
+  loop->watchers = watchers;
+  loop->nwatchers = nwatchers;
+}
+
+
+void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) {
+  assert(cb != NULL);
+  assert(fd >= -1);
+  QUEUE_INIT(&w->pending_queue);
+  QUEUE_INIT(&w->watcher_queue);
+  w->cb = cb;
+  w->fd = fd;
+  w->events = 0;
+  w->pevents = 0;
+
+#if defined(UV_HAVE_KQUEUE)
+  w->rcount = 0;
+  w->wcount = 0;
+#endif /* defined(UV_HAVE_KQUEUE) */
+}
+
+
+void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP)));
+  assert(0 != events);
+  assert(w->fd >= 0);
+  assert(w->fd < INT_MAX);
+
+  w->pevents |= events;
+  maybe_resize(loop, w->fd + 1);
+
+#if !defined(__sun)
+  /* The event ports backend needs to rearm all file descriptors on each and
+   * every tick of the event loop but the other backends allow us to
+   * short-circuit here if the event mask is unchanged.
+   */
+  if (w->events == w->pevents) {
+    if (w->events == 0 && !QUEUE_EMPTY(&w->watcher_queue)) {
+      QUEUE_REMOVE(&w->watcher_queue);
+      QUEUE_INIT(&w->watcher_queue);
+    }
+    return;
+  }
+#endif
+
+  if (QUEUE_EMPTY(&w->watcher_queue))
+    QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+
+  if (loop->watchers[w->fd] == NULL) {
+    loop->watchers[w->fd] = w;
+    loop->nfds++;
+  }
+}
+
+
+void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP)));
+  assert(0 != events);
+
+  if (w->fd == -1)
+    return;
+
+  assert(w->fd >= 0);
+
+  /* Happens when uv__io_stop() is called on a handle that was never started. */
+  if ((unsigned) w->fd >= loop->nwatchers)
+    return;
+
+  w->pevents &= ~events;
+
+  if (w->pevents == 0) {
+    QUEUE_REMOVE(&w->watcher_queue);
+    QUEUE_INIT(&w->watcher_queue);
+
+    if (loop->watchers[w->fd] != NULL) {
+      assert(loop->watchers[w->fd] == w);
+      assert(loop->nfds > 0);
+      loop->watchers[w->fd] = NULL;
+      loop->nfds--;
+      w->events = 0;
+    }
+  }
+  else if (QUEUE_EMPTY(&w->watcher_queue))
+    QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+}
+
+
+void uv__io_close(uv_loop_t* loop, uv__io_t* w) {
+  uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP);
+  QUEUE_REMOVE(&w->pending_queue);
+
+  /* Remove stale events for this file descriptor */
+  uv__platform_invalidate_fd(loop, w->fd);
+}
+
+
+void uv__io_feed(uv_loop_t* loop, uv__io_t* w) {
+  if (QUEUE_EMPTY(&w->pending_queue))
+    QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue);
+}
+
+
+int uv__io_active(const uv__io_t* w, unsigned int events) {
+  assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP)));
+  assert(0 != events);
+  return 0 != (w->pevents & events);
+}
+
+
+int uv__open_cloexec(const char* path, int flags) {
+  int err;
+  int fd;
+
+#if defined(UV__O_CLOEXEC)
+  static int no_cloexec;
+
+  if (!no_cloexec) {
+    fd = open(path, flags | UV__O_CLOEXEC);
+    if (fd != -1)
+      return fd;
+
+    if (errno != EINVAL)
+      return -errno;
+
+    /* O_CLOEXEC not supported. */
+    no_cloexec = 1;
+  }
+#endif
+
+  fd = open(path, flags);
+  if (fd == -1)
+    return -errno;
+
+  err = uv__cloexec(fd, 1);
+  if (err) {
+    uv__close(fd);
+    return err;
+  }
+
+  return fd;
+}
+
diff --git a/src/unix/darwin-proctitle.c b/src/unix/darwin-proctitle.c
new file mode 100644 (file)
index 0000000..1142311
--- /dev/null
@@ -0,0 +1,206 @@
+/* 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 <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_IPHONE
+# include <CoreFoundation/CoreFoundation.h>
+# include <ApplicationServices/ApplicationServices.h>
+#endif
+
+
+static int uv__pthread_setname_np(const char* name) {
+  int (*dynamic_pthread_setname_np)(const char* name);
+  char namebuf[64];  /* MAXTHREADNAMESIZE */
+  int err;
+
+  /* pthread_setname_np() first appeared in OS X 10.6 and iOS 3.2. */
+  *(void **)(&dynamic_pthread_setname_np) =
+      dlsym(RTLD_DEFAULT, "pthread_setname_np");
+
+  if (dynamic_pthread_setname_np == NULL)
+    return -ENOSYS;
+
+  strncpy(namebuf, name, sizeof(namebuf) - 1);
+  namebuf[sizeof(namebuf) - 1] = '\0';
+
+  err = dynamic_pthread_setname_np(namebuf);
+  if (err)
+    return -err;
+
+  return 0;
+}
+
+
+int uv__set_process_title(const char* title) {
+#if TARGET_OS_IPHONE
+  return uv__pthread_setname_np(title);
+#else
+  CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
+                                            const char*,
+                                            CFStringEncoding);
+  CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef);
+  void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef);
+  void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef);
+  CFTypeRef (*pLSGetCurrentApplicationASN)(void);
+  OSStatus (*pLSSetApplicationInformationItem)(int,
+                                               CFTypeRef,
+                                               CFStringRef,
+                                               CFStringRef,
+                                               CFDictionaryRef*);
+  void* application_services_handle;
+  void* core_foundation_handle;
+  CFBundleRef launch_services_bundle;
+  CFStringRef* display_name_key;
+  CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef);
+  CFBundleRef (*pCFBundleGetMainBundle)(void);
+  CFBundleRef hi_services_bundle;
+  OSStatus (*pSetApplicationIsDaemon)(int);
+  CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef);
+  void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t,
+                                                                void*);
+  CFTypeRef asn;
+  int err;
+
+  err = -ENOENT;
+  application_services_handle = dlopen("/System/Library/Frameworks/"
+                                       "ApplicationServices.framework/"
+                                       "Versions/A/ApplicationServices",
+                                       RTLD_LAZY | RTLD_LOCAL);
+  core_foundation_handle = dlopen("/System/Library/Frameworks/"
+                                  "CoreFoundation.framework/"
+                                  "Versions/A/CoreFoundation",
+                                  RTLD_LAZY | RTLD_LOCAL);
+
+  if (application_services_handle == NULL || core_foundation_handle == NULL)
+    goto out;
+
+  *(void **)(&pCFStringCreateWithCString) =
+      dlsym(core_foundation_handle, "CFStringCreateWithCString");
+  *(void **)(&pCFBundleGetBundleWithIdentifier) =
+      dlsym(core_foundation_handle, "CFBundleGetBundleWithIdentifier");
+  *(void **)(&pCFBundleGetDataPointerForName) =
+      dlsym(core_foundation_handle, "CFBundleGetDataPointerForName");
+  *(void **)(&pCFBundleGetFunctionPointerForName) =
+      dlsym(core_foundation_handle, "CFBundleGetFunctionPointerForName");
+
+  if (pCFStringCreateWithCString == NULL ||
+      pCFBundleGetBundleWithIdentifier == NULL ||
+      pCFBundleGetDataPointerForName == NULL ||
+      pCFBundleGetFunctionPointerForName == NULL) {
+    goto out;
+  }
+
+#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8)
+
+  launch_services_bundle =
+      pCFBundleGetBundleWithIdentifier(S("com.apple.LaunchServices"));
+
+  if (launch_services_bundle == NULL)
+    goto out;
+
+  *(void **)(&pLSGetCurrentApplicationASN) =
+      pCFBundleGetFunctionPointerForName(launch_services_bundle,
+                                         S("_LSGetCurrentApplicationASN"));
+
+  if (pLSGetCurrentApplicationASN == NULL)
+    goto out;
+
+  *(void **)(&pLSSetApplicationInformationItem) =
+      pCFBundleGetFunctionPointerForName(launch_services_bundle,
+                                         S("_LSSetApplicationInformationItem"));
+
+  if (pLSSetApplicationInformationItem == NULL)
+    goto out;
+
+  display_name_key = pCFBundleGetDataPointerForName(launch_services_bundle,
+                                                    S("_kLSDisplayNameKey"));
+
+  if (display_name_key == NULL || *display_name_key == NULL)
+    goto out;
+
+  *(void **)(&pCFBundleGetInfoDictionary) = dlsym(core_foundation_handle,
+                                     "CFBundleGetInfoDictionary");
+  *(void **)(&pCFBundleGetMainBundle) = dlsym(core_foundation_handle,
+                                 "CFBundleGetMainBundle");
+  if (pCFBundleGetInfoDictionary == NULL || pCFBundleGetMainBundle == NULL)
+    goto out;
+
+  /* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */
+  hi_services_bundle =
+      pCFBundleGetBundleWithIdentifier(S("com.apple.HIServices"));
+  err = -ENOENT;
+  if (hi_services_bundle == NULL)
+    goto out;
+
+  *(void **)(&pSetApplicationIsDaemon) = pCFBundleGetFunctionPointerForName(
+      hi_services_bundle,
+      S("SetApplicationIsDaemon"));
+  *(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName(
+      launch_services_bundle,
+      S("_LSApplicationCheckIn"));
+  *(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) =
+      pCFBundleGetFunctionPointerForName(
+          launch_services_bundle,
+          S("_LSSetApplicationLaunchServicesServerConnectionStatus"));
+  if (pSetApplicationIsDaemon == NULL ||
+      pLSApplicationCheckIn == NULL ||
+      pLSSetApplicationLaunchServicesServerConnectionStatus == NULL) {
+    goto out;
+  }
+
+  if (pSetApplicationIsDaemon(1) != noErr)
+    goto out;
+
+  pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL);
+
+  /* Check into process manager?! */
+  pLSApplicationCheckIn(-2,
+                        pCFBundleGetInfoDictionary(pCFBundleGetMainBundle()));
+
+  asn = pLSGetCurrentApplicationASN();
+
+  err = -EINVAL;
+  if (pLSSetApplicationInformationItem(-2,  /* Magic value. */
+                                       asn,
+                                       *display_name_key,
+                                       S(title),
+                                       NULL) != noErr) {
+    goto out;
+  }
+
+  uv__pthread_setname_np(title);  /* Don't care if it fails. */
+  err = 0;
+
+out:
+  if (core_foundation_handle != NULL)
+    dlclose(core_foundation_handle);
+
+  if (application_services_handle != NULL)
+    dlclose(application_services_handle);
+
+  return err;
+#endif  /* !TARGET_OS_IPHONE */
+}
diff --git a/src/unix/darwin.c b/src/unix/darwin.c
new file mode 100644 (file)
index 0000000..cf95da2
--- /dev/null
@@ -0,0 +1,335 @@
+/* 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"
+
+#include <assert.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach-o/dyld.h> /* _NSGetExecutablePath */
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <unistd.h>  /* sysconf */
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  loop->cf_state = NULL;
+
+  if (uv__kqueue_init(loop))
+    return -errno;
+
+  return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  uv__fsevents_loop_delete(loop);
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  static mach_timebase_info_data_t info;
+
+  if ((ACCESS_ONCE(uint32_t, info.numer) == 0 ||
+       ACCESS_ONCE(uint32_t, info.denom) == 0) &&
+      mach_timebase_info(&info) != KERN_SUCCESS)
+    abort();
+
+  return mach_absolute_time() * info.numer / info.denom;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */
+  char abspath[PATH_MAX * 2 + 1];
+  char exepath[PATH_MAX + 1];
+  uint32_t exepath_size;
+  size_t abspath_size;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  exepath_size = sizeof(exepath);
+  if (_NSGetExecutablePath(exepath, &exepath_size))
+    return -EIO;
+
+  if (realpath(exepath, abspath) != abspath)
+    return -errno;
+
+  abspath_size = strlen(abspath);
+  if (abspath_size == 0)
+    return -EIO;
+
+  *size -= 1;
+  if (*size > abspath_size)
+    *size = abspath_size;
+
+  memcpy(buffer, abspath, *size);
+  buffer[*size] = '\0';
+
+  return 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  vm_statistics_data_t info;
+  mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
+
+  if (host_statistics(mach_host_self(), HOST_VM_INFO,
+                      (host_info_t)&info, &count) != KERN_SUCCESS) {
+    return -EINVAL;  /* FIXME(bnoordhuis) Translate error. */
+  }
+
+  return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE);
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  uint64_t info;
+  int which[] = {CTL_HW, HW_MEMSIZE};
+  size_t size = sizeof(info);
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  return (uint64_t) info;
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  mach_msg_type_number_t count;
+  task_basic_info_data_t info;
+  kern_return_t err;
+
+  count = TASK_BASIC_INFO_COUNT;
+  err = task_info(mach_task_self(),
+                  TASK_BASIC_INFO,
+                  (task_info_t) &info,
+                  &count);
+  (void) &err;
+  /* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than
+   * KERN_SUCCESS implies a libuv bug.
+   */
+  assert(err == KERN_SUCCESS);
+  *rss = info.resident_size;
+
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  now = time(NULL);
+  *uptime = now - info.tv_sec;
+
+  return 0;
+}
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks);
+  char model[512];
+  uint64_t cpuspeed;
+  size_t size;
+  unsigned int i;
+  natural_t numcpus;
+  mach_msg_type_number_t msg_type;
+  processor_cpu_load_info_data_t *info;
+  uv_cpu_info_t* cpu_info;
+
+  size = sizeof(model);
+  if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) &&
+      sysctlbyname("hw.model", &model, &size, NULL, 0)) {
+    return -errno;
+  }
+
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0))
+    return -errno;
+
+  if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,
+                          (processor_info_array_t*)&info,
+                          &msg_type) != KERN_SUCCESS) {
+    return -EINVAL;  /* FIXME(bnoordhuis) Translate error. */
+  }
+
+  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos)) {
+    vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
+    return -ENOMEM;
+  }
+
+  *count = numcpus;
+
+  for (i = 0; i < numcpus; i++) {
+    cpu_info = &(*cpu_infos)[i];
+
+    cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier;
+    cpu_info->cpu_times.irq = 0;
+
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = cpuspeed/1000000;
+  }
+  vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type);
+
+  return 0;
+}
+
+
+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);
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+  struct ifaddrs *addrs, *ent;
+  uv_interface_address_t* address;
+  int i;
+  struct sockaddr_dl *sa_addr;
+
+  if (getifaddrs(&addrs))
+    return -errno;
+
+  *count = 0;
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family == AF_LINK)) {
+      continue;
+    }
+
+    (*count)++;
+  }
+
+  *addresses = uv__malloc(*count * sizeof(**addresses));
+  if (!(*addresses)) {
+    freeifaddrs(addrs);
+    return -ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+      continue;
+
+    if (ent->ifa_addr == NULL)
+      continue;
+
+    /*
+     * On Mac OS X getifaddrs returns information related to Mac Addresses for
+     * various devices, such as firewire, etc. These are not relevant here.
+     */
+    if (ent->ifa_addr->sa_family == AF_LINK)
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
+
+    address++;
+  }
+
+  /* Fill in physical addresses for each interface */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family != AF_LINK)) {
+      continue;
+    }
+
+    address = *addresses;
+
+    for (i = 0; i < (*count); i++) {
+      if (strcmp(address->name, ent->ifa_name) == 0) {
+        sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
+        memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
+      }
+      address++;
+    }
+  }
+
+  freeifaddrs(addrs);
+
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+  int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
diff --git a/src/unix/dl.c b/src/unix/dl.c
new file mode 100644 (file)
index 0000000..fc1c052
--- /dev/null
@@ -0,0 +1,80 @@
+/* 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"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#include <locale.h>
+
+static int uv__dlerror(uv_lib_t* lib);
+
+
+int uv_dlopen(const char* filename, uv_lib_t* lib) {
+  dlerror(); /* Reset error status. */
+  lib->errmsg = NULL;
+  lib->handle = dlopen(filename, RTLD_LAZY);
+  return lib->handle ? 0 : uv__dlerror(lib);
+}
+
+
+void uv_dlclose(uv_lib_t* lib) {
+  uv__free(lib->errmsg);
+  lib->errmsg = NULL;
+
+  if (lib->handle) {
+    /* Ignore errors. No good way to signal them without leaking memory. */
+    dlclose(lib->handle);
+    lib->handle = NULL;
+  }
+}
+
+
+int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) {
+  dlerror(); /* Reset error status. */
+  *ptr = dlsym(lib->handle, name);
+  return uv__dlerror(lib);
+}
+
+
+const char* uv_dlerror(const uv_lib_t* lib) {
+  return lib->errmsg ? lib->errmsg : "no error";
+}
+
+
+static int uv__dlerror(uv_lib_t* lib) {
+  const char* errmsg;
+
+  uv__free(lib->errmsg);
+
+  errmsg = dlerror();
+
+  if (errmsg) {
+    lib->errmsg = uv__strdup(errmsg);
+    return -1;
+  }
+  else {
+    lib->errmsg = NULL;
+    return 0;
+  }
+}
diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c
new file mode 100644 (file)
index 0000000..cba44a3
--- /dev/null
@@ -0,0 +1,460 @@
+/* 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"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <kvm.h>
+#include <paths.h>
+#include <sys/user.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <vm/vm_param.h> /* VM_LOADAVG */
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h> /* sysconf */
+#include <fcntl.h>
+
+#undef NANOSEC
+#define NANOSEC ((uint64_t) 1e9)
+
+#ifndef CPUSTATES
+# define CPUSTATES 5U
+#endif
+#ifndef CP_USER
+# define CP_USER 0
+# define CP_NICE 1
+# define CP_SYS 2
+# define CP_IDLE 3
+# define CP_INTR 4
+#endif
+
+static char *process_title;
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  return uv__kqueue_init(loop);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
+}
+
+
+#ifdef __DragonFly__
+int uv_exepath(char* buffer, size_t* size) {
+  char abspath[PATH_MAX * 2 + 1];
+  ssize_t abspath_size;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  abspath_size = readlink("/proc/curproc/file", abspath, sizeof(abspath));
+  if (abspath_size < 0)
+    return -errno;
+
+  assert(abspath_size > 0);
+  *size -= 1;
+
+  if (*size > abspath_size)
+    *size = abspath_size;
+
+  memcpy(buffer, abspath, *size);
+  buffer[*size] = '\0';
+
+  return 0;
+}
+#else
+int uv_exepath(char* buffer, size_t* size) {
+  char abspath[PATH_MAX * 2 + 1];
+  int mib[4];
+  size_t abspath_size;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC;
+  mib[2] = KERN_PROC_PATHNAME;
+  mib[3] = -1;
+
+  abspath_size = sizeof abspath;
+  if (sysctl(mib, 4, abspath, &abspath_size, NULL, 0))
+    return -errno;
+
+  assert(abspath_size > 0);
+  abspath_size -= 1;
+  *size -= 1;
+
+  if (*size > abspath_size)
+    *size = abspath_size;
+
+  memcpy(buffer, abspath, *size);
+  buffer[*size] = '\0';
+
+  return 0;
+}
+#endif
+
+uint64_t uv_get_free_memory(void) {
+  int freecount;
+  size_t size = sizeof(freecount);
+
+  if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0))
+    return -errno;
+
+  return (uint64_t) freecount * sysconf(_SC_PAGESIZE);
+
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  unsigned long info;
+  int which[] = {CTL_HW, HW_PHYSMEM};
+
+  size_t size = sizeof(info);
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  return (uint64_t) info;
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  process_title = argc ? uv__strdup(argv[0]) : NULL;
+  return argv;
+}
+
+
+int uv_set_process_title(const char* title) {
+  int oid[4];
+
+  uv__free(process_title);
+  process_title = uv__strdup(title);
+
+  oid[0] = CTL_KERN;
+  oid[1] = KERN_PROC;
+  oid[2] = KERN_PROC_ARGS;
+  oid[3] = getpid();
+
+  sysctl(oid,
+         ARRAY_SIZE(oid),
+         NULL,
+         NULL,
+         process_title,
+         strlen(process_title) + 1);
+
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  size_t len;
+
+  if (buffer == NULL || size == 0)
+    return -EINVAL;
+
+  if (process_title) {
+    len = strlen(process_title) + 1;
+
+    if (size < len)
+      return -ENOBUFS;
+
+    memcpy(buffer, process_title, len);
+  } else {
+    len = 0;
+  }
+
+  buffer[len] = '\0';
+
+  return 0;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  kvm_t *kd = NULL;
+  struct kinfo_proc *kinfo = NULL;
+  pid_t pid;
+  int nprocs;
+  size_t page_size = getpagesize();
+
+  pid = getpid();
+
+  kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
+  if (kd == NULL) goto error;
+
+  kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs);
+  if (kinfo == NULL) goto error;
+
+#ifdef __DragonFly__
+  *rss = kinfo->kp_vm_rssize * page_size;
+#else
+  *rss = kinfo->ki_rssize * page_size;
+#endif
+
+  kvm_close(kd);
+
+  return 0;
+
+error:
+  if (kd) kvm_close(kd);
+  return -EPERM;
+}
+
+
+int uv_uptime(double* uptime) {
+  int r;
+  struct timespec sp;
+  r = clock_gettime(CLOCK_MONOTONIC, &sp);
+  if (r)
+    return -errno;
+
+  *uptime = sp.tv_sec;
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus,
+               cur = 0;
+  uv_cpu_info_t* cpu_info;
+  const char* maxcpus_key;
+  const char* cptimes_key;
+  char model[512];
+  long* cp_times;
+  int numcpus;
+  size_t size;
+  int i;
+
+#if defined(__DragonFly__)
+  /* This is not quite correct but DragonFlyBSD doesn't seem to have anything
+   * comparable to kern.smp.maxcpus or kern.cp_times (kern.cp_time is a total,
+   * not per CPU). At least this stops uv_cpu_info() from failing completely.
+   */
+  maxcpus_key = "hw.ncpu";
+  cptimes_key = "kern.cp_time";
+#else
+  maxcpus_key = "kern.smp.maxcpus";
+  cptimes_key = "kern.cp_times";
+#endif
+
+  size = sizeof(model);
+  if (sysctlbyname("hw.model", &model, &size, NULL, 0))
+    return -errno;
+
+  size = sizeof(numcpus);
+  if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
+    return -errno;
+
+  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos))
+    return -ENOMEM;
+
+  *count = numcpus;
+
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0)) {
+    uv__free(*cpu_infos);
+    return -errno;
+  }
+
+  /* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of
+   * ncpu.
+   */
+  size = sizeof(maxcpus);
+  if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) {
+    uv__free(*cpu_infos);
+    return -errno;
+  }
+
+  size = maxcpus * CPUSTATES * sizeof(long);
+
+  cp_times = uv__malloc(size);
+  if (cp_times == NULL) {
+    uv__free(*cpu_infos);
+    return -ENOMEM;
+  }
+
+  if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) {
+    uv__free(cp_times);
+    uv__free(*cpu_infos);
+    return -errno;
+  }
+
+  for (i = 0; i < numcpus; i++) {
+    cpu_info = &(*cpu_infos)[i];
+
+    cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
+    cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
+
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = cpuspeed;
+
+    cur+=CPUSTATES;
+  }
+
+  uv__free(cp_times);
+  return 0;
+}
+
+
+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);
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+  struct ifaddrs *addrs, *ent;
+  uv_interface_address_t* address;
+  int i;
+  struct sockaddr_dl *sa_addr;
+
+  if (getifaddrs(&addrs))
+    return -errno;
+
+   *count = 0;
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family == AF_LINK)) {
+      continue;
+    }
+
+    (*count)++;
+  }
+
+  *addresses = uv__malloc(*count * sizeof(**addresses));
+  if (!(*addresses)) {
+    freeifaddrs(addrs);
+    return -ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+      continue;
+
+    if (ent->ifa_addr == NULL)
+      continue;
+
+    /*
+     * On FreeBSD getifaddrs returns information related to the raw underlying
+     * devices. We're not interested in this information yet.
+     */
+    if (ent->ifa_addr->sa_family == AF_LINK)
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
+
+    address++;
+  }
+
+  /* Fill in physical addresses for each interface */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family != AF_LINK)) {
+      continue;
+    }
+
+    address = *addresses;
+
+    for (i = 0; i < (*count); i++) {
+      if (strcmp(address->name, ent->ifa_name) == 0) {
+        sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
+        memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
+      }
+      address++;
+    }
+  }
+
+  freeifaddrs(addrs);
+
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+  int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
diff --git a/src/unix/fs.c b/src/unix/fs.c
new file mode 100644 (file)
index 0000000..5971915
--- /dev/null
@@ -0,0 +1,981 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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.
+ */
+
+/* Caveat emptor: this file deviates from the libuv convention of returning
+ * negated errno codes. Most uv_fs_*() functions map directly to the system
+ * call of the same name. For more complex wrappers, it's easier to just
+ * return -1 with errno set. The dispatcher in uv__fs_work() takes care of
+ * getting the errno to the right place (req->result or as the return value.)
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h> /* PATH_MAX */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#if !defined(__TIZENRT__)
+# include <sys/uio.h> /* writev */
+# include <utime.h>
+#endif
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#if defined(__DragonFly__)        ||                                      \
+    defined(__FreeBSD__)          ||                                      \
+    defined(__FreeBSD_kernel_)    ||                                      \
+    defined(__OpenBSD__)          ||                                      \
+    defined(__NetBSD__)
+# define HAVE_PREADV 1
+#else
+# define HAVE_PREADV 0
+#endif
+
+#if defined(__linux__) || defined(__sun)
+# include <sys/sendfile.h>
+#endif
+
+#define INIT(subtype)                                                         \
+  do {                                                                        \
+    req->type = UV_FS;                                                        \
+    if (cb != NULL)                                                           \
+      uv__req_init(loop, req, UV_FS);                                         \
+    req->fs_type = UV_FS_ ## subtype;                                         \
+    req->result = 0;                                                          \
+    req->ptr = NULL;                                                          \
+    req->loop = loop;                                                         \
+    req->path = NULL;                                                         \
+    req->new_path = NULL;                                                     \
+    req->cb = cb;                                                             \
+  }                                                                           \
+  while (0)
+
+#define PATH                                                                  \
+  do {                                                                        \
+    assert(path != NULL);                                                     \
+    if (cb == NULL) {                                                         \
+      req->path = path;                                                       \
+    } else {                                                                  \
+      req->path = strdup(path);                                               \
+      if (req->path == NULL) {                                                \
+        uv__req_unregister(loop, req);                                        \
+        return -ENOMEM;                                                       \
+      }                                                                       \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define PATH2                                                                 \
+  do {                                                                        \
+    if (cb == NULL) {                                                         \
+      req->path = path;                                                       \
+      req->new_path = new_path;                                               \
+    } else {                                                                  \
+      size_t path_len;                                                        \
+      size_t new_path_len;                                                    \
+      path_len = strlen(path) + 1;                                            \
+      new_path_len = strlen(new_path) + 1;                                    \
+      req->path = uv__malloc(path_len + new_path_len);                        \
+      if (req->path == NULL) {                                                \
+        uv__req_unregister(loop, req);                                        \
+        return -ENOMEM;                                                       \
+      }                                                                       \
+      req->new_path = req->path + path_len;                                   \
+      memcpy((void*) req->path, path, path_len);                              \
+      memcpy((void*) req->new_path, new_path, new_path_len);                  \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+#define POST                                                                  \
+  do {                                                                        \
+    if (cb != NULL) {                                                         \
+      uv__work_submit(loop, &req->work_req, uv__fs_work, uv__fs_done);        \
+      return 0;                                                               \
+    }                                                                         \
+    else {                                                                    \
+      uv__fs_work(&req->work_req);                                            \
+      return req->result;                                                     \
+    }                                                                         \
+  }                                                                           \
+  while (0)
+
+
+static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
+#if defined(__linux__) || defined(__sun) || defined(__NetBSD__)
+  return fdatasync(req->file);
+#elif defined(__APPLE__) && defined(SYS_fdatasync)
+  return syscall(SYS_fdatasync, req->file);
+#else
+  return fsync(req->file);
+#endif
+}
+
+
+static ssize_t uv__fs_futime(uv_fs_t* req) {
+#if defined(__linux__)
+  /* utimesat() has nanosecond resolution but we stick to microseconds
+   * for the sake of consistency with other platforms.
+   */
+  static int no_utimesat;
+  struct timespec ts[2];
+  struct timeval tv[2];
+  char path[sizeof("/proc/self/fd/") + 3 * sizeof(int)];
+  int r;
+
+  if (no_utimesat)
+    goto skip;
+
+  ts[0].tv_sec  = req->atime;
+  ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000;
+  ts[1].tv_sec  = req->mtime;
+  ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000;
+
+  r = uv__utimesat(req->file, NULL, ts, 0);
+  if (r == 0)
+    return r;
+
+  if (errno != ENOSYS)
+    return r;
+
+  no_utimesat = 1;
+
+skip:
+
+  tv[0].tv_sec  = req->atime;
+  tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000;
+  tv[1].tv_sec  = req->mtime;
+  tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000;
+  snprintf(path, sizeof(path), "/proc/self/fd/%d", (int) req->file);
+
+  r = utimes(path, tv);
+  if (r == 0)
+    return r;
+
+  switch (errno) {
+  case ENOENT:
+    if (fcntl(req->file, F_GETFL) == -1 && errno == EBADF)
+      break;
+    /* Fall through. */
+
+  case EACCES:
+  case ENOTDIR:
+    errno = ENOSYS;
+    break;
+  }
+
+  return r;
+
+#elif defined(__APPLE__)                                                      \
+    || defined(__DragonFly__)                                                 \
+    || defined(__FreeBSD__)                                                   \
+    || defined(__FreeBSD_kernel__)                                            \
+    || defined(__NetBSD__)                                                    \
+    || defined(__OpenBSD__)                                                   \
+    || defined(__sun)
+  struct timeval tv[2];
+  tv[0].tv_sec  = req->atime;
+  tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000;
+  tv[1].tv_sec  = req->mtime;
+  tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000;
+# if defined(__sun)
+  return futimesat(req->file, NULL, tv);
+# else
+  return futimes(req->file, tv);
+# endif
+#elif defined(_AIX71)
+  struct timespec ts[2];
+  ts[0].tv_sec  = req->atime;
+  ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000;
+  ts[1].tv_sec  = req->mtime;
+  ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000;
+  return futimens(req->file, ts);
+#elif defined(__MVS__)
+  attrib_t atr;
+  memset(&atr, 0, sizeof(atr));
+  atr.att_mtimechg = 1;
+  atr.att_atimechg = 1;
+  atr.att_mtime = req->mtime;
+  atr.att_atime = req->atime;
+  return __fchattr(req->file, &atr, sizeof(atr));
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+
+
+static ssize_t uv__fs_open(uv_fs_t* req) {
+  static int no_cloexec_support;
+  int r;
+
+  /* Try O_CLOEXEC before entering locks */
+  if (no_cloexec_support == 0) {
+#ifdef O_CLOEXEC
+    r = open(req->path, req->flags | O_CLOEXEC, req->mode);
+    if (r >= 0)
+      return r;
+    if (errno != EINVAL)
+      return r;
+    no_cloexec_support = 1;
+#endif  /* O_CLOEXEC */
+  }
+
+  if (req->cb != NULL)
+    uv_rwlock_rdlock(&req->loop->cloexec_lock);
+
+  r = open(req->path, req->flags, req->mode);
+
+  /* In case of failure `uv__cloexec` will leave error in `errno`,
+   * so it is enough to just set `r` to `-1`.
+   */
+  if (r >= 0 && uv__cloexec(r, 1) != 0) {
+    r = uv__close(r);
+    if (r != 0)
+      abort();
+    r = -1;
+  }
+
+  if (req->cb != NULL)
+    uv_rwlock_rdunlock(&req->loop->cloexec_lock);
+
+  return r;
+}
+
+
+static ssize_t uv__fs_read(uv_fs_t* req) {
+#if defined(__linux__)
+  static int no_preadv;
+#endif
+  ssize_t result;
+
+#if defined(_AIX)
+  struct stat buf;
+  if(fstat(req->file, &buf))
+    return -1;
+  if(S_ISDIR(buf.st_mode)) {
+    errno = EISDIR;
+    return -1;
+  }
+#endif /* defined(_AIX) */
+  if (req->off < 0) {
+    if (req->nbufs == 1)
+      result = read(req->file, req->bufs[0].base, req->bufs[0].len);
+    else
+      result = readv(req->file, (struct iovec*) req->bufs, req->nbufs);
+  } else {
+    if (req->nbufs == 1) {
+      result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
+      goto done;
+    }
+
+#if HAVE_PREADV
+    result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
+#else
+# if defined(__linux__)
+    if (no_preadv) retry:
+# endif
+    {
+      off_t nread;
+      size_t index;
+
+      nread = 0;
+      index = 0;
+      result = 1;
+      do {
+        if (req->bufs[index].len > 0) {
+          result = pread(req->file,
+                         req->bufs[index].base,
+                         req->bufs[index].len,
+                         req->off + nread);
+          if (result > 0)
+            nread += result;
+        }
+        index++;
+      } while (index < req->nbufs && result > 0);
+      if (nread > 0)
+        result = nread;
+    }
+# if defined(__linux__)
+    else {
+      result = uv__preadv(req->file,
+                          (struct iovec*)req->bufs,
+                          req->nbufs,
+                          req->off);
+      if (result == -1 && errno == ENOSYS) {
+        no_preadv = 1;
+        goto retry;
+      }
+    }
+# endif
+#endif
+  }
+
+done:
+  return result;
+}
+
+
+#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8)
+#define UV_CONST_DIRENT uv__dirent_t
+#else
+#define UV_CONST_DIRENT const uv__dirent_t
+#endif
+
+
+static int uv__fs_scandir_filter(UV_CONST_DIRENT* dent) {
+  return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0;
+}
+
+
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+static uv__dirent_t* uv__fs_scandir_copy_entry(UV_CONST_DIRENT* dent) {
+  uv__dirent_t* ent = (uv__dirent_t*) malloc(sizeof(uv__dirent_t));
+  memcpy(ent, dent, sizeof(uv__dirent_t));
+  return ent;
+}
+
+
+static int uv__fs_scandir_sort(const void* a, const void* b) {
+    return strcmp((*(uv__dirent_t**)a)->d_name, (*(uv__dirent_t**)b)->d_name);
+}
+#else
+static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) {
+  return strcmp((*a)->d_name, (*b)->d_name);
+}
+#endif
+
+
+static ssize_t uv__fs_scandir(uv_fs_t* req) {
+  uv__dirent_t **dents = NULL;
+  int cnt = 0;
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+  DIR* dir = NULL;
+  uv__dirent_t* dent = NULL;
+  unsigned idx = 0;
+
+  dir = opendir(req->path);
+  if (dir == NULL) {
+    cnt = -1;
+    goto error;
+  }
+
+  while ((dent = readdir(dir)) != NULL) {
+    if (uv__fs_scandir_filter(dent))
+      cnt++;
+  }
+
+  /* Allocate memory for the directory entries. */
+  dents = (uv__dirent_t**) malloc(sizeof (uv__dirent_t*) * cnt);
+
+  if (cnt > 0 && dents == NULL) {
+      closedir(dir);
+      cnt = -1;
+      goto error;
+  }
+
+  rewinddir(dir);
+
+  while ((dent = readdir(dir)) != NULL) {
+    if (uv__fs_scandir_filter(dent))
+      dents[idx++] = uv__fs_scandir_copy_entry(dent);
+  }
+
+  closedir(dir);
+
+  qsort (dents, cnt, sizeof (uv__dirent_t*), uv__fs_scandir_sort);
+error:
+#else
+  cnt = scandir(req->path, &dents, uv__fs_scandir_filter, uv__fs_scandir_sort);
+#endif
+
+  /* NOTE: We will use nbufs as an index field */
+  req->nbufs = 0;
+
+  if (cnt == 0) {
+    /* Memory was allocated using the system allocator, so use free() here. */
+    if (dents != NULL) {
+      free(dents);
+      dents = NULL;
+    }
+  }
+  else if (cnt == -1)
+    return cnt;
+
+  req->ptr = dents;
+
+  return cnt;
+}
+
+
+static ssize_t uv__fs_utime(uv_fs_t* req) {
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+  return -1;
+#else
+  struct utimbuf buf;
+  buf.actime = req->atime;
+  buf.modtime = req->mtime;
+  return utime(req->path, &buf); /* TODO use utimes() where available */
+#endif
+}
+
+
+static ssize_t uv__fs_write(uv_fs_t* req) {
+#if defined(__linux__)
+  static int no_pwritev;
+#endif
+  ssize_t r;
+
+  /* Serialize writes on OS X, concurrent write() and pwrite() calls result in
+   * data loss. We can't use a per-file descriptor lock, the descriptor may be
+   * a dup().
+   */
+#if defined(__APPLE__)
+  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+  if (pthread_mutex_lock(&lock))
+    abort();
+#endif
+
+  if (req->off < 0) {
+    if (req->nbufs == 1)
+      r = write(req->file, req->bufs[0].base, req->bufs[0].len);
+    else
+      r = writev(req->file, (struct iovec*) req->bufs, req->nbufs);
+  } else {
+    if (req->nbufs == 1) {
+      r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
+      goto done;
+    }
+#if HAVE_PREADV
+    r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off);
+#else
+# if defined(__linux__)
+    if (no_pwritev) retry:
+# endif
+    {
+      off_t written;
+      size_t index;
+
+      written = 0;
+      index = 0;
+      r = 0;
+      do {
+        if (req->bufs[index].len > 0) {
+          r = pwrite(req->file,
+                     req->bufs[index].base,
+                     req->bufs[index].len,
+                     req->off + written);
+          if (r > 0)
+            written += r;
+        }
+        index++;
+      } while (index < req->nbufs && r >= 0);
+      if (written > 0)
+        r = written;
+    }
+# if defined(__linux__)
+    else {
+      r = uv__pwritev(req->file,
+                      (struct iovec*) req->bufs,
+                      req->nbufs,
+                      req->off);
+      if (r == -1 && errno == ENOSYS) {
+        no_pwritev = 1;
+        goto retry;
+      }
+    }
+# endif
+#endif
+  }
+
+done:
+#if defined(__APPLE__)
+  if (pthread_mutex_unlock(&lock))
+    abort();
+#endif
+
+  return r;
+}
+
+static void uv__to_stat(struct stat* src, uv_stat_t* dst) {
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  dst->st_dev = src->st_dev;
+#endif
+  dst->st_mode = src->st_mode;
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  dst->st_nlink = src->st_nlink;
+  dst->st_uid = src->st_uid;
+  dst->st_gid = src->st_gid;
+  dst->st_rdev = src->st_rdev;
+  dst->st_ino = src->st_ino;
+#endif
+  dst->st_size = src->st_size;
+  dst->st_blksize = src->st_blksize;
+  dst->st_blocks = src->st_blocks;
+
+#if defined(__APPLE__)
+  dst->st_atim.tv_sec = src->st_atimespec.tv_sec;
+  dst->st_atim.tv_nsec = src->st_atimespec.tv_nsec;
+  dst->st_mtim.tv_sec = src->st_mtimespec.tv_sec;
+  dst->st_mtim.tv_nsec = src->st_mtimespec.tv_nsec;
+  dst->st_ctim.tv_sec = src->st_ctimespec.tv_sec;
+  dst->st_ctim.tv_nsec = src->st_ctimespec.tv_nsec;
+  dst->st_birthtim.tv_sec = src->st_birthtimespec.tv_sec;
+  dst->st_birthtim.tv_nsec = src->st_birthtimespec.tv_nsec;
+  dst->st_flags = src->st_flags;
+  dst->st_gen = src->st_gen;
+#elif defined(__ANDROID__)
+  dst->st_atim.tv_sec = src->st_atime;
+  dst->st_atim.tv_nsec = src->st_atimensec;
+  dst->st_mtim.tv_sec = src->st_mtime;
+  dst->st_mtim.tv_nsec = src->st_mtimensec;
+  dst->st_ctim.tv_sec = src->st_ctime;
+  dst->st_ctim.tv_nsec = src->st_ctimensec;
+  dst->st_birthtim.tv_sec = src->st_ctime;
+  dst->st_birthtim.tv_nsec = src->st_ctimensec;
+  dst->st_flags = 0;
+  dst->st_gen = 0;
+#elif !defined(_AIX) && !defined(__NUTTX__) && \
+    (defined(_GNU_SOURCE)    || \
+    defined(_BSD_SOURCE)     || \
+    defined(_SVID_SOURCE)    || \
+    defined(_XOPEN_SOURCE)   || \
+    defined(_DEFAULT_SOURCE))
+  dst->st_atim.tv_sec = src->st_atim.tv_sec;
+  dst->st_atim.tv_nsec = src->st_atim.tv_nsec;
+  dst->st_mtim.tv_sec = src->st_mtim.tv_sec;
+  dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec;
+  dst->st_ctim.tv_sec = src->st_ctim.tv_sec;
+  dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec;
+# if defined(__DragonFly__)  || \
+     defined(__FreeBSD__)    || \
+     defined(__OpenBSD__)    || \
+     defined(__NetBSD__)
+  dst->st_birthtim.tv_sec = src->st_birthtim.tv_sec;
+  dst->st_birthtim.tv_nsec = src->st_birthtim.tv_nsec;
+  dst->st_flags = src->st_flags;
+  dst->st_gen = src->st_gen;
+# else
+  dst->st_birthtim.tv_sec = src->st_ctim.tv_sec;
+  dst->st_birthtim.tv_nsec = src->st_ctim.tv_nsec;
+  dst->st_flags = 0;
+  dst->st_gen = 0;
+# endif
+#else
+  dst->st_atim.tv_sec = src->st_atime;
+  dst->st_atim.tv_nsec = 0;
+  dst->st_mtim.tv_sec = src->st_mtime;
+  dst->st_mtim.tv_nsec = 0;
+  dst->st_ctim.tv_sec = src->st_ctime;
+  dst->st_ctim.tv_nsec = 0;
+  dst->st_birthtim.tv_sec = src->st_ctime;
+  dst->st_birthtim.tv_nsec = 0;
+  dst->st_flags = 0;
+  dst->st_gen = 0;
+#endif
+}
+
+
+static int uv__fs_stat(const char *path, uv_stat_t *buf) {
+  struct stat pbuf;
+  int ret;
+
+  ret = stat(path, &pbuf);
+  if (ret == 0)
+    uv__to_stat(&pbuf, buf);
+
+  return ret;
+}
+
+
+static int uv__fs_fstat(int fd, uv_stat_t *buf) {
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+  return -1;
+#else
+  struct stat pbuf;
+  int ret;
+
+  ret = fstat(fd, &pbuf);
+  if (ret == 0)
+    uv__to_stat(&pbuf, buf);
+
+  return ret;
+#endif
+}
+
+
+typedef ssize_t (*uv__fs_buf_iter_processor)(uv_fs_t* req);
+static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process) {
+  unsigned int iovmax;
+  unsigned int nbufs;
+  uv_buf_t* bufs;
+  ssize_t total;
+  ssize_t result;
+
+  iovmax = uv__getiovmax();
+  nbufs = req->nbufs;
+  bufs = req->bufs;
+  total = 0;
+
+  while (nbufs > 0) {
+    req->nbufs = nbufs;
+    if (req->nbufs > iovmax)
+      req->nbufs = iovmax;
+
+    result = process(req);
+    if (result <= 0) {
+      if (total == 0)
+        total = result;
+      break;
+    }
+
+    if (req->off >= 0)
+      req->off += result;
+
+    req->bufs += req->nbufs;
+    nbufs -= req->nbufs;
+    total += result;
+  }
+
+  if (errno == EINTR && total == -1)
+    return total;
+
+  if (bufs != req->bufsml)
+    uv__free(bufs);
+
+  req->bufs = NULL;
+  req->nbufs = 0;
+
+  return total;
+}
+
+
+static void uv__fs_work(struct uv__work* w) {
+  int retry_on_eintr;
+  uv_fs_t* req;
+  ssize_t r;
+
+  req = container_of(w, uv_fs_t, work_req);
+  retry_on_eintr = !(req->fs_type == UV_FS_CLOSE);
+
+  do {
+    errno = 0;
+
+#define X(type, action)                                                       \
+  case UV_FS_ ## type:                                                        \
+    r = action;                                                               \
+    break;
+
+    switch (req->fs_type) {
+    X(CLOSE, close(req->file));
+    X(FDATASYNC, uv__fs_fdatasync(req));
+    X(FSTAT, uv__fs_fstat(req->file, &req->statbuf));
+    X(FSYNC, fsync(req->file));
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+    X(FTRUNCATE, ftruncate(req->file, req->off));
+#endif
+    X(FUTIME, uv__fs_futime(req));
+    X(MKDIR, mkdir(req->path, req->mode));
+    X(OPEN, uv__fs_open(req));
+    X(READ, uv__fs_buf_iter(req, uv__fs_read));
+    X(SCANDIR, uv__fs_scandir(req));
+    X(RENAME, rename(req->path, req->new_path));
+    X(RMDIR, rmdir(req->path));
+    X(STAT, uv__fs_stat(req->path, &req->statbuf));
+    X(UNLINK, unlink(req->path));
+    X(UTIME, uv__fs_utime(req));
+    X(WRITE, uv__fs_buf_iter(req, uv__fs_write));
+    default: abort();
+    }
+#undef X
+  } while (r == -1 && errno == EINTR && retry_on_eintr);
+
+  if (r == -1)
+    req->result = -errno;
+  else
+    req->result = r;
+
+  if (r == 0 && (req->fs_type == UV_FS_STAT ||
+                 req->fs_type == UV_FS_FSTAT ||
+                 req->fs_type == UV_FS_LSTAT)) {
+    req->ptr = &req->statbuf;
+  }
+}
+
+
+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 == -ECANCELED) {
+    assert(req->result == 0);
+    req->result = -ECANCELED;
+  }
+
+  req->cb(req);
+}
+
+
+int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(CLOSE);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(FDATASYNC);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(FSTAT);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
+  INIT(FSYNC);
+  req->file = file;
+  POST;
+}
+
+
+int uv_fs_ftruncate(uv_loop_t* loop,
+                    uv_fs_t* req,
+                    uv_file file,
+                    int64_t off,
+                    uv_fs_cb cb) {
+  INIT(FTRUNCATE);
+  req->file = file;
+  req->off = off;
+  POST;
+}
+
+
+int uv_fs_futime(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 uv_file file,
+                 double atime,
+                 double mtime,
+                 uv_fs_cb cb) {
+  INIT(FUTIME);
+  req->file = file;
+  req->atime = atime;
+  req->mtime = mtime;
+  POST;
+}
+
+
+int uv_fs_mkdir(uv_loop_t* loop,
+                uv_fs_t* req,
+                const char* path,
+                int mode,
+                uv_fs_cb cb) {
+  INIT(MKDIR);
+  PATH;
+  req->mode = mode;
+  POST;
+}
+
+
+int uv_fs_open(uv_loop_t* loop,
+               uv_fs_t* req,
+               const char* path,
+               int flags,
+               int mode,
+               uv_fs_cb cb) {
+  INIT(OPEN);
+  PATH;
+  req->flags = flags;
+  req->mode = mode;
+  POST;
+}
+
+
+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 off,
+               uv_fs_cb cb) {
+  if (bufs == NULL || nbufs == 0)
+    return -EINVAL;
+
+  INIT(READ);
+  req->file = file;
+
+  req->nbufs = nbufs;
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->bufs == NULL) {
+    if (cb != NULL)
+      uv__req_unregister(loop, req);
+    return -ENOMEM;
+  }
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->off = off;
+  POST;
+}
+
+
+int uv_fs_scandir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  int flags,
+                  uv_fs_cb cb) {
+  INIT(SCANDIR);
+  PATH;
+  req->flags = flags;
+  POST;
+}
+
+
+int uv_fs_rename(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 const char* new_path,
+                 uv_fs_cb cb) {
+  INIT(RENAME);
+  PATH2;
+  POST;
+}
+
+
+int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  INIT(RMDIR);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  INIT(STAT);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  INIT(UNLINK);
+  PATH;
+  POST;
+}
+
+
+int uv_fs_utime(uv_loop_t* loop,
+                uv_fs_t* req,
+                const char* path,
+                double atime,
+                double mtime,
+                uv_fs_cb cb) {
+  INIT(UTIME);
+  PATH;
+  req->atime = atime;
+  req->mtime = mtime;
+  POST;
+}
+
+
+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 off,
+                uv_fs_cb cb) {
+  if (bufs == NULL || nbufs == 0)
+    return -EINVAL;
+
+  INIT(WRITE);
+  req->file = file;
+
+  req->nbufs = nbufs;
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = uv__malloc(nbufs * sizeof(*bufs));
+
+  if (req->bufs == NULL) {
+    if (cb != NULL)
+      uv__req_unregister(loop, req);
+    return -ENOMEM;
+  }
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->off = off;
+  POST;
+}
+
+
+void uv_fs_req_cleanup(uv_fs_t* req) {
+  /* Only necessary for asychronous requests, i.e., requests with a callback.
+   * Synchronous ones don't copy their arguments and have req->path and
+   * req->new_path pointing to user-owned memory.  UV_FS_MKDTEMP is the
+   * exception to the rule, it always allocates memory.
+   */
+  if (req->path != NULL && (req->cb != NULL || req->fs_type == UV_FS_MKDTEMP))
+    uv__free((void*) req->path);  /* Memory is shared with req->new_path. */
+
+  req->path = NULL;
+  req->new_path = NULL;
+
+  if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
+    uv__fs_scandir_cleanup(req);
+
+  if (req->ptr != &req->statbuf)
+    uv__free(req->ptr);
+  req->ptr = NULL;
+}
diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c
new file mode 100644 (file)
index 0000000..d331a13
--- /dev/null
@@ -0,0 +1,904 @@
+/* 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"
+
+#if TARGET_OS_IPHONE
+
+/* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
+
+int uv__fsevents_init(uv_fs_event_t* handle) {
+  return 0;
+}
+
+
+int uv__fsevents_close(uv_fs_event_t* handle) {
+  return 0;
+}
+
+
+void uv__fsevents_loop_delete(uv_loop_t* loop) {
+}
+
+#else /* TARGET_OS_IPHONE */
+
+#include <dlfcn.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#include <CoreFoundation/CFRunLoop.h>
+#include <CoreServices/CoreServices.h>
+
+/* These are macros to avoid "initializer element is not constant" errors
+ * with old versions of gcc.
+ */
+#define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod |         \
+                           kFSEventStreamEventFlagItemModified |              \
+                           kFSEventStreamEventFlagItemInodeMetaMod |          \
+                           kFSEventStreamEventFlagItemChangeOwner |           \
+                           kFSEventStreamEventFlagItemXattrMod)
+
+#define kFSEventsRenamed  (kFSEventStreamEventFlagItemCreated |               \
+                           kFSEventStreamEventFlagItemRemoved |               \
+                           kFSEventStreamEventFlagItemRenamed)
+
+#define kFSEventsSystem   (kFSEventStreamEventFlagUserDropped |               \
+                           kFSEventStreamEventFlagKernelDropped |             \
+                           kFSEventStreamEventFlagEventIdsWrapped |           \
+                           kFSEventStreamEventFlagHistoryDone |               \
+                           kFSEventStreamEventFlagMount |                     \
+                           kFSEventStreamEventFlagUnmount |                   \
+                           kFSEventStreamEventFlagRootChanged)
+
+typedef struct uv__fsevents_event_s uv__fsevents_event_t;
+typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
+typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
+
+enum uv__cf_loop_signal_type_e {
+  kUVCFLoopSignalRegular,
+  kUVCFLoopSignalClosing
+};
+typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
+
+struct uv__cf_loop_signal_s {
+  QUEUE member;
+  uv_fs_event_t* handle;
+  uv__cf_loop_signal_type_t type;
+};
+
+struct uv__fsevents_event_s {
+  QUEUE member;
+  int events;
+  char path[1];
+};
+
+struct uv__cf_loop_state_s {
+  CFRunLoopRef loop;
+  CFRunLoopSourceRef signal_source;
+  int fsevent_need_reschedule;
+  FSEventStreamRef fsevent_stream;
+  uv_sem_t fsevent_sem;
+  uv_mutex_t fsevent_mutex;
+  void* fsevent_handles[2];
+  unsigned int fsevent_handle_count;
+};
+
+/* Forward declarations */
+static void uv__cf_loop_cb(void* arg);
+static void* uv__cf_loop_runner(void* arg);
+static int uv__cf_loop_signal(uv_loop_t* loop,
+                              uv_fs_event_t* handle,
+                              uv__cf_loop_signal_type_t type);
+
+/* Lazy-loaded by uv__fsevents_global_init(). */
+static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
+                                    const void**,
+                                    CFIndex,
+                                    const CFArrayCallBacks*);
+static void (*pCFRelease)(CFTypeRef);
+static void (*pCFRunLoopAddSource)(CFRunLoopRef,
+                                   CFRunLoopSourceRef,
+                                   CFStringRef);
+static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
+static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
+                                      CFRunLoopSourceRef,
+                                      CFStringRef);
+static void (*pCFRunLoopRun)(void);
+static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
+                                                    CFIndex,
+                                                    CFRunLoopSourceContext*);
+static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
+static void (*pCFRunLoopStop)(CFRunLoopRef);
+static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
+static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
+    CFAllocatorRef,
+    const char*);
+static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
+static CFStringRef (*pkCFRunLoopDefaultMode);
+static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
+                                                FSEventStreamCallback,
+                                                FSEventStreamContext*,
+                                                CFArrayRef,
+                                                FSEventStreamEventId,
+                                                CFTimeInterval,
+                                                FSEventStreamCreateFlags);
+static void (*pFSEventStreamFlushSync)(FSEventStreamRef);
+static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
+static void (*pFSEventStreamRelease)(FSEventStreamRef);
+static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
+                                                 CFRunLoopRef,
+                                                 CFStringRef);
+static Boolean (*pFSEventStreamStart)(FSEventStreamRef);
+static void (*pFSEventStreamStop)(FSEventStreamRef);
+
+#define UV__FSEVENTS_PROCESS(handle, block)                                   \
+    do {                                                                      \
+      QUEUE events;                                                           \
+      QUEUE* q;                                                               \
+      uv__fsevents_event_t* event;                                            \
+      int err;                                                                \
+      uv_mutex_lock(&(handle)->cf_mutex);                                     \
+      /* Split-off all events and empty original queue */                     \
+      QUEUE_MOVE(&(handle)->cf_events, &events);                              \
+      /* Get error (if any) and zero original one */                          \
+      err = (handle)->cf_error;                                               \
+      (handle)->cf_error = 0;                                                 \
+      uv_mutex_unlock(&(handle)->cf_mutex);                                   \
+      /* Loop through events, deallocating each after processing */           \
+      while (!QUEUE_EMPTY(&events)) {                                         \
+        q = QUEUE_HEAD(&events);                                              \
+        event = QUEUE_DATA(q, uv__fsevents_event_t, member);                  \
+        QUEUE_REMOVE(q);                                                      \
+        /* NOTE: Checking uv__is_active() is required here, because handle    \
+         * callback may close handle and invoking it after it will lead to    \
+         * incorrect behaviour */                                             \
+        if (!uv__is_closing((handle)) && uv__is_active((handle)))             \
+          block                                                               \
+        /* Free allocated data */                                             \
+        uv__free(event);                                                      \
+      }                                                                       \
+      if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle)))   \
+        (handle)->cb((handle), NULL, 0, err);                                 \
+    } while (0)
+
+
+/* Runs in UV loop's thread, when there're events to report to handle */
+static void uv__fsevents_cb(uv_async_t* cb) {
+  uv_fs_event_t* handle;
+
+  handle = cb->data;
+
+  UV__FSEVENTS_PROCESS(handle, {
+    handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
+  });
+}
+
+
+/* Runs in CF thread, pushed event into handle's event list */
+static void uv__fsevents_push_event(uv_fs_event_t* handle,
+                                    QUEUE* events,
+                                    int err) {
+  assert(events != NULL || err != 0);
+  uv_mutex_lock(&handle->cf_mutex);
+
+  /* Concatenate two queues */
+  if (events != NULL)
+    QUEUE_ADD(&handle->cf_events, events);
+
+  /* Propagate error */
+  if (err != 0)
+    handle->cf_error = err;
+  uv_mutex_unlock(&handle->cf_mutex);
+
+  uv_async_send(handle->cf_cb);
+}
+
+
+/* Runs in CF thread, when there're events in FSEventStream */
+static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
+                                  void* info,
+                                  size_t numEvents,
+                                  void* eventPaths,
+                                  const FSEventStreamEventFlags eventFlags[],
+                                  const FSEventStreamEventId eventIds[]) {
+  size_t i;
+  int len;
+  char** paths;
+  char* path;
+  char* pos;
+  uv_fs_event_t* handle;
+  QUEUE* q;
+  uv_loop_t* loop;
+  uv__cf_loop_state_t* state;
+  uv__fsevents_event_t* event;
+  QUEUE head;
+
+  loop = info;
+  state = loop->cf_state;
+  assert(state != NULL);
+  paths = eventPaths;
+
+  /* For each handle */
+  uv_mutex_lock(&state->fsevent_mutex);
+  QUEUE_FOREACH(q, &state->fsevent_handles) {
+    handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+    QUEUE_INIT(&head);
+
+    /* Process and filter out events */
+    for (i = 0; i < numEvents; i++) {
+      /* Ignore system events */
+      if (eventFlags[i] & kFSEventsSystem)
+        continue;
+
+      path = paths[i];
+      len = strlen(path);
+
+      /* Filter out paths that are outside handle's request */
+      if (strncmp(path, handle->realpath, handle->realpath_len) != 0)
+        continue;
+
+      if (handle->realpath_len > 1 || *handle->realpath != '/') {
+        path += handle->realpath_len;
+        len -= handle->realpath_len;
+
+        /* Skip forward slash */
+        if (*path != '\0') {
+          path++;
+          len--;
+        }
+      }
+
+#ifdef MAC_OS_X_VERSION_10_7
+      /* Ignore events with path equal to directory itself */
+      if (len == 0)
+        continue;
+#endif /* MAC_OS_X_VERSION_10_7 */
+
+      /* Do not emit events from subdirectories (without option set) */
+      if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != 0) {
+        pos = strchr(path + 1, '/');
+        if (pos != NULL)
+          continue;
+      }
+
+#ifndef MAC_OS_X_VERSION_10_7
+      path = "";
+      len = 0;
+#endif /* MAC_OS_X_VERSION_10_7 */
+
+      event = uv__malloc(sizeof(*event) + len);
+      if (event == NULL)
+        break;
+
+      memset(event, 0, sizeof(*event));
+      memcpy(event->path, path, len + 1);
+
+      if ((eventFlags[i] & kFSEventsModified) != 0 &&
+          (eventFlags[i] & kFSEventsRenamed) == 0)
+        event->events = UV_CHANGE;
+      else
+        event->events = UV_RENAME;
+
+      QUEUE_INSERT_TAIL(&head, &event->member);
+    }
+
+    if (!QUEUE_EMPTY(&head))
+      uv__fsevents_push_event(handle, &head, 0);
+  }
+  uv_mutex_unlock(&state->fsevent_mutex);
+}
+
+
+/* Runs in CF thread */
+static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
+  uv__cf_loop_state_t* state;
+  FSEventStreamContext ctx;
+  FSEventStreamRef ref;
+  CFAbsoluteTime latency;
+  FSEventStreamCreateFlags flags;
+
+  /* Initialize context */
+  ctx.version = 0;
+  ctx.info = loop;
+  ctx.retain = NULL;
+  ctx.release = NULL;
+  ctx.copyDescription = NULL;
+
+  latency = 0.05;
+
+  /* Explanation of selected flags:
+   * 1. NoDefer - without this flag, events that are happening continuously
+   *    (i.e. each event is happening after time interval less than `latency`,
+   *    counted from previous event), will be deferred and passed to callback
+   *    once they'll either fill whole OS buffer, or when this continuous stream
+   *    will stop (i.e. there'll be delay between events, bigger than
+   *    `latency`).
+   *    Specifying this flag will invoke callback after `latency` time passed
+   *    since event.
+   * 2. FileEvents - fire callback for file changes too (by default it is firing
+   *    it only for directory changes).
+   */
+  flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
+
+  /*
+   * NOTE: It might sound like a good idea to remember last seen StreamEventId,
+   * but in reality one dir might have last StreamEventId less than, the other,
+   * that is being watched now. Which will cause FSEventStream API to report
+   * changes to files from the past.
+   */
+  ref = pFSEventStreamCreate(NULL,
+                             &uv__fsevents_event_cb,
+                             &ctx,
+                             paths,
+                             kFSEventStreamEventIdSinceNow,
+                             latency,
+                             flags);
+  assert(ref != NULL);
+
+  state = loop->cf_state;
+  pFSEventStreamScheduleWithRunLoop(ref,
+                                    state->loop,
+                                    *pkCFRunLoopDefaultMode);
+  if (!pFSEventStreamStart(ref)) {
+    pFSEventStreamInvalidate(ref);
+    pFSEventStreamRelease(ref);
+    return -EMFILE;
+  }
+
+  state->fsevent_stream = ref;
+  return 0;
+}
+
+
+/* Runs in CF thread */
+static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
+  uv__cf_loop_state_t* state;
+
+  state = loop->cf_state;
+
+  if (state->fsevent_stream == NULL)
+    return;
+
+  /* Flush all accumulated events */
+  pFSEventStreamFlushSync(state->fsevent_stream);
+
+  /* Stop emitting events */
+  pFSEventStreamStop(state->fsevent_stream);
+
+  /* Release stream */
+  pFSEventStreamInvalidate(state->fsevent_stream);
+  pFSEventStreamRelease(state->fsevent_stream);
+  state->fsevent_stream = NULL;
+}
+
+
+/* Runs in CF thread, when there're new fsevent handles to add to stream */
+static void uv__fsevents_reschedule(uv_fs_event_t* handle,
+                                    uv__cf_loop_signal_type_t type) {
+  uv__cf_loop_state_t* state;
+  QUEUE* q;
+  uv_fs_event_t* curr;
+  CFArrayRef cf_paths;
+  CFStringRef* paths;
+  unsigned int i;
+  int err;
+  unsigned int path_count;
+
+  state = handle->loop->cf_state;
+  paths = NULL;
+  cf_paths = NULL;
+  err = 0;
+  /* NOTE: `i` is used in deallocation loop below */
+  i = 0;
+
+  /* Optimization to prevent O(n^2) time spent when starting to watch
+   * many files simultaneously
+   */
+  uv_mutex_lock(&state->fsevent_mutex);
+  if (state->fsevent_need_reschedule == 0) {
+    uv_mutex_unlock(&state->fsevent_mutex);
+    goto final;
+  }
+  state->fsevent_need_reschedule = 0;
+  uv_mutex_unlock(&state->fsevent_mutex);
+
+  /* Destroy previous FSEventStream */
+  uv__fsevents_destroy_stream(handle->loop);
+
+  /* Any failure below will be a memory failure */
+  err = -ENOMEM;
+
+  /* Create list of all watched paths */
+  uv_mutex_lock(&state->fsevent_mutex);
+  path_count = state->fsevent_handle_count;
+  if (path_count != 0) {
+    paths = uv__malloc(sizeof(*paths) * path_count);
+    if (paths == NULL) {
+      uv_mutex_unlock(&state->fsevent_mutex);
+      goto final;
+    }
+
+    q = &state->fsevent_handles;
+    for (; i < path_count; i++) {
+      q = QUEUE_NEXT(q);
+      assert(q != &state->fsevent_handles);
+      curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+
+      assert(curr->realpath != NULL);
+      paths[i] =
+          pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
+      if (paths[i] == NULL) {
+        uv_mutex_unlock(&state->fsevent_mutex);
+        goto final;
+      }
+    }
+  }
+  uv_mutex_unlock(&state->fsevent_mutex);
+  err = 0;
+
+  if (path_count != 0) {
+    /* Create new FSEventStream */
+    cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
+    if (cf_paths == NULL) {
+      err = -ENOMEM;
+      goto final;
+    }
+    err = uv__fsevents_create_stream(handle->loop, cf_paths);
+  }
+
+final:
+  /* Deallocate all paths in case of failure */
+  if (err != 0) {
+    if (cf_paths == NULL) {
+      while (i != 0)
+        pCFRelease(paths[--i]);
+      uv__free(paths);
+    } else {
+      /* CFArray takes ownership of both strings and original C-array */
+      pCFRelease(cf_paths);
+    }
+
+    /* Broadcast error to all handles */
+    uv_mutex_lock(&state->fsevent_mutex);
+    QUEUE_FOREACH(q, &state->fsevent_handles) {
+      curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
+      uv__fsevents_push_event(curr, NULL, err);
+    }
+    uv_mutex_unlock(&state->fsevent_mutex);
+  }
+
+  /*
+   * Main thread will block until the removal of handle from the list,
+   * we must tell it when we're ready.
+   *
+   * NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
+   */
+  if (type == kUVCFLoopSignalClosing)
+    uv_sem_post(&state->fsevent_sem);
+}
+
+
+static int uv__fsevents_global_init(void) {
+  static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+  static void* core_foundation_handle;
+  static void* core_services_handle;
+  int err;
+
+  err = 0;
+  pthread_mutex_lock(&global_init_mutex);
+  if (core_foundation_handle != NULL)
+    goto out;
+
+  /* The libraries are never unloaded because we currently don't have a good
+   * mechanism for keeping a reference count. It's unlikely to be an issue
+   * but if it ever becomes one, we can turn the dynamic library handles into
+   * per-event loop properties and have the dynamic linker keep track for us.
+   */
+  err = -ENOSYS;
+  core_foundation_handle = dlopen("/System/Library/Frameworks/"
+                                  "CoreFoundation.framework/"
+                                  "Versions/A/CoreFoundation",
+                                  RTLD_LAZY | RTLD_LOCAL);
+  if (core_foundation_handle == NULL)
+    goto out;
+
+  core_services_handle = dlopen("/System/Library/Frameworks/"
+                                "CoreServices.framework/"
+                                "Versions/A/CoreServices",
+                                RTLD_LAZY | RTLD_LOCAL);
+  if (core_services_handle == NULL)
+    goto out;
+
+  err = -ENOENT;
+#define V(handle, symbol)                                                     \
+  do {                                                                        \
+    *(void **)(&p ## symbol) = dlsym((handle), #symbol);                      \
+    if (p ## symbol == NULL)                                                  \
+      goto out;                                                               \
+  }                                                                           \
+  while (0)
+  V(core_foundation_handle, CFArrayCreate);
+  V(core_foundation_handle, CFRelease);
+  V(core_foundation_handle, CFRunLoopAddSource);
+  V(core_foundation_handle, CFRunLoopGetCurrent);
+  V(core_foundation_handle, CFRunLoopRemoveSource);
+  V(core_foundation_handle, CFRunLoopRun);
+  V(core_foundation_handle, CFRunLoopSourceCreate);
+  V(core_foundation_handle, CFRunLoopSourceSignal);
+  V(core_foundation_handle, CFRunLoopStop);
+  V(core_foundation_handle, CFRunLoopWakeUp);
+  V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
+  V(core_foundation_handle, CFStringGetSystemEncoding);
+  V(core_foundation_handle, kCFRunLoopDefaultMode);
+  V(core_services_handle, FSEventStreamCreate);
+  V(core_services_handle, FSEventStreamFlushSync);
+  V(core_services_handle, FSEventStreamInvalidate);
+  V(core_services_handle, FSEventStreamRelease);
+  V(core_services_handle, FSEventStreamScheduleWithRunLoop);
+  V(core_services_handle, FSEventStreamStart);
+  V(core_services_handle, FSEventStreamStop);
+#undef V
+  err = 0;
+
+out:
+  if (err && core_services_handle != NULL) {
+    dlclose(core_services_handle);
+    core_services_handle = NULL;
+  }
+
+  if (err && core_foundation_handle != NULL) {
+    dlclose(core_foundation_handle);
+    core_foundation_handle = NULL;
+  }
+
+  pthread_mutex_unlock(&global_init_mutex);
+  return err;
+}
+
+
+/* Runs in UV loop */
+static int uv__fsevents_loop_init(uv_loop_t* loop) {
+  CFRunLoopSourceContext ctx;
+  uv__cf_loop_state_t* state;
+  pthread_attr_t attr_storage;
+  pthread_attr_t* attr;
+  int err;
+
+  if (loop->cf_state != NULL)
+    return 0;
+
+  err = uv__fsevents_global_init();
+  if (err)
+    return err;
+
+  state = uv__calloc(1, sizeof(*state));
+  if (state == NULL)
+    return -ENOMEM;
+
+  err = uv_mutex_init(&loop->cf_mutex);
+  if (err)
+    goto fail_mutex_init;
+
+  err = uv_sem_init(&loop->cf_sem, 0);
+  if (err)
+    goto fail_sem_init;
+
+  QUEUE_INIT(&loop->cf_signals);
+
+  err = uv_sem_init(&state->fsevent_sem, 0);
+  if (err)
+    goto fail_fsevent_sem_init;
+
+  err = uv_mutex_init(&state->fsevent_mutex);
+  if (err)
+    goto fail_fsevent_mutex_init;
+
+  QUEUE_INIT(&state->fsevent_handles);
+  state->fsevent_need_reschedule = 0;
+  state->fsevent_handle_count = 0;
+
+  memset(&ctx, 0, sizeof(ctx));
+  ctx.info = loop;
+  ctx.perform = uv__cf_loop_cb;
+  state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
+  if (state->signal_source == NULL) {
+    err = -ENOMEM;
+    goto fail_signal_source_create;
+  }
+
+  /* In the unlikely event that pthread_attr_init() fails, create the thread
+   * with the default stack size. We'll use a little more address space but
+   * that in itself is not a fatal error.
+   */
+  attr = &attr_storage;
+  if (pthread_attr_init(attr))
+    attr = NULL;
+
+  if (attr != NULL)
+    if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN))
+      abort();
+
+  loop->cf_state = state;
+
+  /* uv_thread_t is an alias for pthread_t. */
+  err = -pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop);
+
+  if (attr != NULL)
+    pthread_attr_destroy(attr);
+
+  if (err)
+    goto fail_thread_create;
+
+  /* Synchronize threads */
+  uv_sem_wait(&loop->cf_sem);
+  return 0;
+
+fail_thread_create:
+  loop->cf_state = NULL;
+
+fail_signal_source_create:
+  uv_mutex_destroy(&state->fsevent_mutex);
+
+fail_fsevent_mutex_init:
+  uv_sem_destroy(&state->fsevent_sem);
+
+fail_fsevent_sem_init:
+  uv_sem_destroy(&loop->cf_sem);
+
+fail_sem_init:
+  uv_mutex_destroy(&loop->cf_mutex);
+
+fail_mutex_init:
+  uv__free(state);
+  return err;
+}
+
+
+/* Runs in UV loop */
+void uv__fsevents_loop_delete(uv_loop_t* loop) {
+  uv__cf_loop_signal_t* s;
+  uv__cf_loop_state_t* state;
+  QUEUE* q;
+
+  if (loop->cf_state == NULL)
+    return;
+
+  if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0)
+    abort();
+
+  uv_thread_join(&loop->cf_thread);
+  uv_sem_destroy(&loop->cf_sem);
+  uv_mutex_destroy(&loop->cf_mutex);
+
+  /* Free any remaining data */
+  while (!QUEUE_EMPTY(&loop->cf_signals)) {
+    q = QUEUE_HEAD(&loop->cf_signals);
+    s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
+    QUEUE_REMOVE(q);
+    uv__free(s);
+  }
+
+  /* Destroy state */
+  state = loop->cf_state;
+  uv_sem_destroy(&state->fsevent_sem);
+  uv_mutex_destroy(&state->fsevent_mutex);
+  pCFRelease(state->signal_source);
+  uv__free(state);
+  loop->cf_state = NULL;
+}
+
+
+/* Runs in CF thread. This is the CF loop's body */
+static void* uv__cf_loop_runner(void* arg) {
+  uv_loop_t* loop;
+  uv__cf_loop_state_t* state;
+
+  loop = arg;
+  state = loop->cf_state;
+  state->loop = pCFRunLoopGetCurrent();
+
+  pCFRunLoopAddSource(state->loop,
+                      state->signal_source,
+                      *pkCFRunLoopDefaultMode);
+
+  uv_sem_post(&loop->cf_sem);
+
+  pCFRunLoopRun();
+  pCFRunLoopRemoveSource(state->loop,
+                         state->signal_source,
+                         *pkCFRunLoopDefaultMode);
+
+  return NULL;
+}
+
+
+/* Runs in CF thread, executed after `uv__cf_loop_signal()` */
+static void uv__cf_loop_cb(void* arg) {
+  uv_loop_t* loop;
+  uv__cf_loop_state_t* state;
+  QUEUE* item;
+  QUEUE split_head;
+  uv__cf_loop_signal_t* s;
+
+  loop = arg;
+  state = loop->cf_state;
+
+  uv_mutex_lock(&loop->cf_mutex);
+  QUEUE_MOVE(&loop->cf_signals, &split_head);
+  uv_mutex_unlock(&loop->cf_mutex);
+
+  while (!QUEUE_EMPTY(&split_head)) {
+    item = QUEUE_HEAD(&split_head);
+    QUEUE_REMOVE(item);
+
+    s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
+
+    /* This was a termination signal */
+    if (s->handle == NULL)
+      pCFRunLoopStop(state->loop);
+    else
+      uv__fsevents_reschedule(s->handle, s->type);
+
+    uv__free(s);
+  }
+}
+
+
+/* Runs in UV loop to notify CF thread */
+int uv__cf_loop_signal(uv_loop_t* loop,
+                       uv_fs_event_t* handle,
+                       uv__cf_loop_signal_type_t type) {
+  uv__cf_loop_signal_t* item;
+  uv__cf_loop_state_t* state;
+
+  item = uv__malloc(sizeof(*item));
+  if (item == NULL)
+    return -ENOMEM;
+
+  item->handle = handle;
+  item->type = type;
+
+  uv_mutex_lock(&loop->cf_mutex);
+  QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
+  uv_mutex_unlock(&loop->cf_mutex);
+
+  state = loop->cf_state;
+  assert(state != NULL);
+  pCFRunLoopSourceSignal(state->signal_source);
+  pCFRunLoopWakeUp(state->loop);
+
+  return 0;
+}
+
+
+/* Runs in UV loop to initialize handle */
+int uv__fsevents_init(uv_fs_event_t* handle) {
+  int err;
+  uv__cf_loop_state_t* state;
+
+  err = uv__fsevents_loop_init(handle->loop);
+  if (err)
+    return err;
+
+  /* Get absolute path to file */
+  handle->realpath = realpath(handle->path, NULL);
+  if (handle->realpath == NULL)
+    return -errno;
+  handle->realpath_len = strlen(handle->realpath);
+
+  /* Initialize event queue */
+  QUEUE_INIT(&handle->cf_events);
+  handle->cf_error = 0;
+
+  /*
+   * Events will occur in other thread.
+   * Initialize callback for getting them back into event loop's thread
+   */
+  handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
+  if (handle->cf_cb == NULL) {
+    err = -ENOMEM;
+    goto fail_cf_cb_malloc;
+  }
+
+  handle->cf_cb->data = handle;
+  uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
+  handle->cf_cb->flags |= UV__HANDLE_INTERNAL;
+  uv_unref((uv_handle_t*) handle->cf_cb);
+
+  err = uv_mutex_init(&handle->cf_mutex);
+  if (err)
+    goto fail_cf_mutex_init;
+
+  /* Insert handle into the list */
+  state = handle->loop->cf_state;
+  uv_mutex_lock(&state->fsevent_mutex);
+  QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
+  state->fsevent_handle_count++;
+  state->fsevent_need_reschedule = 1;
+  uv_mutex_unlock(&state->fsevent_mutex);
+
+  /* Reschedule FSEventStream */
+  assert(handle != NULL);
+  err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular);
+  if (err)
+    goto fail_loop_signal;
+
+  return 0;
+
+fail_loop_signal:
+  uv_mutex_destroy(&handle->cf_mutex);
+
+fail_cf_mutex_init:
+  uv__free(handle->cf_cb);
+  handle->cf_cb = NULL;
+
+fail_cf_cb_malloc:
+  uv__free(handle->realpath);
+  handle->realpath = NULL;
+  handle->realpath_len = 0;
+
+  return err;
+}
+
+
+/* Runs in UV loop to de-initialize handle */
+int uv__fsevents_close(uv_fs_event_t* handle) {
+  int err;
+  uv__cf_loop_state_t* state;
+
+  if (handle->cf_cb == NULL)
+    return -EINVAL;
+
+  /* Remove handle from  the list */
+  state = handle->loop->cf_state;
+  uv_mutex_lock(&state->fsevent_mutex);
+  QUEUE_REMOVE(&handle->cf_member);
+  state->fsevent_handle_count--;
+  state->fsevent_need_reschedule = 1;
+  uv_mutex_unlock(&state->fsevent_mutex);
+
+  /* Reschedule FSEventStream */
+  assert(handle != NULL);
+  err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing);
+  if (err)
+    return -err;
+
+  /* Wait for deinitialization */
+  uv_sem_wait(&state->fsevent_sem);
+
+  uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
+  handle->cf_cb = NULL;
+
+  /* Free data in queue */
+  UV__FSEVENTS_PROCESS(handle, {
+    /* NOP */
+  });
+
+  uv_mutex_destroy(&handle->cf_mutex);
+  uv__free(handle->realpath);
+  handle->realpath = NULL;
+  handle->realpath_len = 0;
+
+  return 0;
+}
+
+#endif /* TARGET_OS_IPHONE */
diff --git a/src/unix/getaddrinfo.c b/src/unix/getaddrinfo.c
new file mode 100644 (file)
index 0000000..aa30b59
--- /dev/null
@@ -0,0 +1,227 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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.
+ */
+
+/* Expose glibc-specific EAI_* error codes. Needs to be defined before we
+ * include any headers.
+ */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include "uv.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <stddef.h> /* NULL */
+#include <stdlib.h>
+#include <string.h>
+
+/* EAI_* constants. */
+#include <netdb.h>
+
+
+int uv__getaddrinfo_translate_error(int sys_err) {
+  switch (sys_err) {
+  case 0: return 0;
+#if defined(EAI_ADDRFAMILY)
+  case EAI_ADDRFAMILY: return UV_EAI_ADDRFAMILY;
+#endif
+#if defined(EAI_AGAIN)
+  case EAI_AGAIN: return UV_EAI_AGAIN;
+#endif
+#if defined(EAI_BADFLAGS)
+  case EAI_BADFLAGS: return UV_EAI_BADFLAGS;
+#endif
+#if defined(EAI_BADHINTS)
+  case EAI_BADHINTS: return UV_EAI_BADHINTS;
+#endif
+#if defined(EAI_CANCELED)
+  case EAI_CANCELED: return UV_EAI_CANCELED;
+#endif
+#if defined(EAI_FAIL)
+  case EAI_FAIL: return UV_EAI_FAIL;
+#endif
+#if defined(EAI_FAMILY)
+  case EAI_FAMILY: return UV_EAI_FAMILY;
+#endif
+#if defined(EAI_MEMORY)
+  case EAI_MEMORY: return UV_EAI_MEMORY;
+#endif
+#if defined(EAI_NODATA)
+  case EAI_NODATA: return UV_EAI_NODATA;
+#endif
+#if defined(EAI_NONAME)
+# if !defined(EAI_NODATA) || EAI_NODATA != EAI_NONAME
+  case EAI_NONAME: return UV_EAI_NONAME;
+# endif
+#endif
+#if defined(EAI_OVERFLOW)
+  case EAI_OVERFLOW: return UV_EAI_OVERFLOW;
+#endif
+#if defined(EAI_PROTOCOL)
+  case EAI_PROTOCOL: return UV_EAI_PROTOCOL;
+#endif
+#if defined(EAI_SERVICE)
+  case EAI_SERVICE: return UV_EAI_SERVICE;
+#endif
+#if defined(EAI_SOCKTYPE)
+  case EAI_SOCKTYPE: return UV_EAI_SOCKTYPE;
+#endif
+#if defined(EAI_SYSTEM)
+  case EAI_SYSTEM: return -errno;
+#endif
+  }
+  assert(!"unknown EAI_* error code");
+  abort();
+  return 0;  /* Pacify compiler. */
+}
+
+
+static void uv__getaddrinfo_work(struct uv__work* w) {
+  uv_getaddrinfo_t* req;
+  int err;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+#if defined(__NUTTX__)
+  err = 0;
+#else
+  /* Only IPv4 is supported now. (Not support IPv6.)*/
+  if (req->hints) {
+    req->hints->ai_family = AF_INET;
+  }
+  err = getaddrinfo(req->hostname, req->service, req->hints, &req->addrinfo);
+#endif
+  req->retcode = uv__getaddrinfo_translate_error(err);
+}
+
+
+static void uv__getaddrinfo_done(struct uv__work* w, int status) {
+  uv_getaddrinfo_t* req;
+
+  req = container_of(w, uv_getaddrinfo_t, work_req);
+  uv__req_unregister(req->loop, req);
+
+  /* See initialization in uv_getaddrinfo(). */
+  if (req->hints)
+    uv__free(req->hints);
+  else if (req->service)
+    uv__free(req->service);
+  else if (req->hostname)
+    uv__free(req->hostname);
+  else
+    assert(0);
+
+  req->hints = NULL;
+  req->service = NULL;
+  req->hostname = NULL;
+
+  if (status == -ECANCELED) {
+    assert(req->retcode == 0);
+    req->retcode = UV_EAI_CANCELED;
+  }
+
+  if (req->cb)
+    req->cb(req, req->retcode, req->addrinfo);
+}
+
+
+int uv_getaddrinfo(uv_loop_t* loop,
+                   uv_getaddrinfo_t* req,
+                   uv_getaddrinfo_cb cb,
+                   const char* hostname,
+                   const char* service,
+                   const struct addrinfo* hints) {
+  size_t hostname_len;
+  size_t service_len;
+  size_t hints_len;
+  size_t len;
+  char* buf;
+
+  if (req == NULL || (hostname == NULL && service == NULL))
+    return -EINVAL;
+
+  hostname_len = hostname ? strlen(hostname) + 1 : 0;
+  service_len = service ? strlen(service) + 1 : 0;
+  hints_len = hints ? sizeof(*hints) : 0;
+  buf = uv__malloc(hostname_len + service_len + hints_len);
+
+  if (buf == NULL)
+    return -ENOMEM;
+
+  uv__req_init(loop, req, UV_GETADDRINFO);
+  req->loop = loop;
+  req->cb = cb;
+  req->addrinfo = NULL;
+  req->hints = NULL;
+  req->service = NULL;
+  req->hostname = NULL;
+  req->retcode = 0;
+
+  /* order matters, see uv_getaddrinfo_done() */
+  len = 0;
+
+  if (hints) {
+    req->hints = memcpy(buf + len, hints, sizeof(*hints));
+    len += sizeof(*hints);
+  }
+
+  if (service) {
+    req->service = memcpy(buf + len, service, service_len);
+    len += service_len;
+  }
+
+  if (hostname)
+    req->hostname = memcpy(buf + len, hostname, hostname_len);
+
+  if (cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    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;
+  }
+}
+
+
+void uv_freeaddrinfo(struct addrinfo* ai) {
+#if !defined(__NUTTX__)
+  if (ai)
+    freeaddrinfo(ai);
+#endif
+}
diff --git a/src/unix/getnameinfo.c b/src/unix/getnameinfo.c
new file mode 100644 (file)
index 0000000..daa798a
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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 <stdio.h>
+#include <string.h>
+
+#include "uv.h"
+#include "internal.h"
+
+
+static void uv__getnameinfo_work(struct uv__work* w) {
+  uv_getnameinfo_t* req;
+  int err;
+  socklen_t salen;
+
+  req = container_of(w, uv_getnameinfo_t, work_req);
+
+  if (req->storage.ss_family == AF_INET)
+    salen = sizeof(struct sockaddr_in);
+  else if (req->storage.ss_family == AF_INET6)
+    salen = sizeof(struct sockaddr_in6);
+  else
+    abort();
+
+  err = getnameinfo((struct sockaddr*) &req->storage,
+                    salen,
+                    req->host,
+                    sizeof(req->host),
+                    req->service,
+                    sizeof(req->service),
+                    req->flags);
+  req->retcode = uv__getaddrinfo_translate_error(err);
+}
+
+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 == -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(loop, (uv_req_t*)req, UV_GETNAMEINFO);
+
+  req->getnameinfo_cb = getnameinfo_cb;
+  req->flags = flags;
+  req->type = UV_GETNAMEINFO;
+  req->loop = loop;
+  req->retcode = 0;
+
+  if (getnameinfo_cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    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/src/unix/internal.h b/src/unix/internal.h
new file mode 100644 (file)
index 0000000..fdab195
--- /dev/null
@@ -0,0 +1,348 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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_UNIX_INTERNAL_H_
+#define UV_UNIX_INTERNAL_H_
+
+#include "uv-common.h"
+
+#include <assert.h>
+#include <stdlib.h> /* abort */
+#include <string.h> /* strrchr */
+#include <fcntl.h>  /* O_CLOEXEC, may be */
+#include <stdio.h>
+
+#if defined(__STRICT_ANSI__)
+# define inline __inline
+#endif
+
+#if defined(__linux__)
+# include "linux-syscalls.h"
+#endif /* __linux__ */
+
+#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 */
+
+#if defined(__APPLE__) && !TARGET_OS_IPHONE
+# include <AvailabilityMacros.h>
+#endif
+
+#if defined(__ANDROID__)
+int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset);
+# ifdef pthread_sigmask
+# undef pthread_sigmask
+# endif
+# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset)
+#endif
+
+#define ACCESS_ONCE(type, var)                                                \
+  (*(volatile type*) &(var))
+
+#define ROUND_UP(a, b)                                                        \
+  ((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a))
+
+#define UNREACHABLE()                                                         \
+  do {                                                                        \
+    assert(0 && "unreachable code");                                          \
+    abort();                                                                  \
+  }                                                                           \
+  while (0)
+
+#define SAVE_ERRNO(block)                                                     \
+  do {                                                                        \
+    int _saved_errno = errno;                                                 \
+    do { block; } while (0);                                                  \
+    errno = _saved_errno;                                                     \
+  }                                                                           \
+  while (0)
+
+/* The __clang__ and __INTEL_COMPILER checks are superfluous because they
+ * define __GNUC__. They are here to convey to you, dear reader, that these
+ * macros are enabled when compiling with clang or icc.
+ */
+#if defined(__clang__) ||                                                     \
+    defined(__GNUC__) ||                                                      \
+    defined(__INTEL_COMPILER) ||                                              \
+    defined(__SUNPRO_C)
+# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration
+# define UV_UNUSED(declaration)     __attribute__((unused)) declaration
+#else
+# define UV_DESTRUCTOR(declaration) declaration
+# define UV_UNUSED(declaration)     declaration
+#endif
+
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+# undef UV_DESTRUCTOR
+# define UV_DESTRUCTOR(declaration) UV_DESTRUCTOR_not_supported##declaration
+#endif
+
+/* Leans on the fact that, on Linux, POLLRDHUP == EPOLLRDHUP. */
+#ifdef POLLRDHUP
+# define UV__POLLRDHUP POLLRDHUP
+#else
+# define UV__POLLRDHUP 0x2000
+#endif
+
+#if !defined(O_CLOEXEC) && defined(__FreeBSD__)
+/*
+ * It may be that we are just missing `__POSIX_VISIBLE >= 200809`.
+ * Try using fixed value const and give up, if it doesn't work
+ */
+# define O_CLOEXEC 0x00100000
+#endif
+
+typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
+
+/* handle flags */
+enum {
+  UV_CLOSING              = 0x01,   /* uv_close() called but not finished. */
+  UV_CLOSED               = 0x02,   /* close(2) finished. */
+  UV_STREAM_READING       = 0x04,   /* uv_read_start() called. */
+  UV_STREAM_SHUTTING      = 0x08,   /* uv_shutdown() called but not complete. */
+  UV_STREAM_SHUT          = 0x10,   /* Write side closed. */
+  UV_STREAM_READABLE      = 0x20,   /* The stream is readable */
+  UV_STREAM_WRITABLE      = 0x40,   /* The stream is writable */
+  UV_STREAM_BLOCKING      = 0x80,   /* Synchronous writes. */
+  UV_STREAM_READ_PARTIAL  = 0x100,  /* read(2) read less than requested. */
+  UV_STREAM_READ_EOF      = 0x200,  /* read(2) read EOF. */
+  UV_TCP_NODELAY          = 0x400,  /* Disable Nagle. */
+  UV_TCP_KEEPALIVE        = 0x800,  /* Turn on keep-alive. */
+  UV_TCP_SINGLE_ACCEPT    = 0x1000, /* Only accept() when idle. */
+  UV_HANDLE_IPV6          = 0x10000, /* Handle is bound to a IPv6 socket. */
+  UV_UDP_PROCESSING       = 0x20000, /* Handle is running the send callback queue. */
+  UV_HANDLE_BOUND         = 0x40000  /* Handle is bound to an address and port */
+};
+
+/* loop flags */
+enum {
+  UV_LOOP_BLOCK_SIGPROF = 1
+};
+
+typedef enum {
+  UV_CLOCK_PRECISE = 0,  /* Use the highest resolution clock available. */
+  UV_CLOCK_FAST = 1      /* Use the fastest clock with <= 1ms granularity. */
+} uv_clocktype_t;
+
+struct uv__stream_queued_fds_s {
+  unsigned int size;
+  unsigned int offset;
+  int fds[1];
+};
+
+
+#if defined(_AIX) || \
+    defined(__APPLE__) || \
+    defined(__DragonFly__) || \
+    defined(__FreeBSD__) || \
+    defined(__FreeBSD_kernel__) || \
+    defined(__linux__)
+#define uv__cloexec uv__cloexec_ioctl
+#define uv__nonblock uv__nonblock_ioctl
+#else
+#define uv__cloexec uv__cloexec_fcntl
+#define uv__nonblock uv__nonblock_fcntl
+#endif
+
+/* core */
+int uv__cloexec_ioctl(int fd, int set);
+int uv__cloexec_fcntl(int fd, int set);
+int uv__nonblock_ioctl(int fd, int set);
+int uv__nonblock_fcntl(int fd, int set);
+int uv__close(int fd);
+int uv__close_nocheckstdio(int fd);
+int uv__socket(int domain, int type, int protocol);
+int uv__dup(int fd);
+#if defined(__NUTTX__) || defined(__TIZENRT__) /* No msghdr in nuttx */
+struct msghdr;
+#endif
+ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
+void uv__make_close_pending(uv_handle_t* handle);
+int uv__getiovmax(void);
+
+void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd);
+void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+void uv__io_close(uv_loop_t* loop, uv__io_t* w);
+void uv__io_feed(uv_loop_t* loop, uv__io_t* w);
+int uv__io_active(const uv__io_t* w, unsigned int events);
+int uv__io_check_fd(uv_loop_t* loop, int fd);
+void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */
+
+/* async */
+void uv__async_send(struct uv__async* wa);
+void uv__async_init(struct uv__async* wa);
+int uv__async_start(uv_loop_t* loop, struct uv__async* wa, uv__async_cb cb);
+void uv__async_stop(uv_loop_t* loop, struct uv__async* wa);
+
+/* loop */
+void uv__run_idle(uv_loop_t* loop);
+void uv__run_check(uv_loop_t* loop);
+void uv__run_prepare(uv_loop_t* loop);
+
+/* stream */
+void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream,
+    uv_handle_type type);
+int uv__stream_open(uv_stream_t*, int fd, int flags);
+void uv__stream_destroy(uv_stream_t* stream);
+#if defined(__APPLE__)
+int uv__stream_try_select(uv_stream_t* stream, int* fd);
+#endif /* defined(__APPLE__) */
+void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+int uv__accept(int sockfd);
+int uv__dup2_cloexec(int oldfd, int newfd);
+int uv__open_cloexec(const char* path, int flags);
+
+/* tcp */
+int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
+int uv__tcp_nodelay(int fd, int on);
+int uv__tcp_keepalive(int fd, int on, unsigned int delay);
+
+/* pipe */
+int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
+
+/* timer */
+void uv__run_timers(uv_loop_t* loop);
+int uv__next_timeout(const uv_loop_t* loop);
+
+/* signal */
+void uv__signal_close(uv_signal_t* handle);
+void uv__signal_global_once_init(void);
+void uv__signal_loop_cleanup(uv_loop_t* loop);
+
+/* platform specific */
+uint64_t uv__hrtime(uv_clocktype_t type);
+int uv__kqueue_init(uv_loop_t* loop);
+int uv__platform_loop_init(uv_loop_t* loop);
+void uv__platform_loop_delete(uv_loop_t* loop);
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd);
+
+/* various */
+void uv__async_close(uv_async_t* handle);
+void uv__check_close(uv_check_t* handle);
+void uv__fs_event_close(uv_fs_event_t* handle);
+void uv__idle_close(uv_idle_t* handle);
+void uv__pipe_close(uv_pipe_t* handle);
+void uv__poll_close(uv_poll_t* handle);
+void uv__prepare_close(uv_prepare_t* handle);
+void uv__process_close(uv_process_t* handle);
+void uv__stream_close(uv_stream_t* handle);
+void uv__tcp_close(uv_tcp_t* handle);
+void uv__timer_close(uv_timer_t* handle);
+void uv__udp_close(uv_udp_t* handle);
+void uv__udp_finish_close(uv_udp_t* handle);
+uv_handle_type uv__handle_type(int fd);
+FILE* uv__open_file(const char* path);
+int uv__getpwuid_r(uv_passwd_t* pwd);
+
+
+#if defined(__APPLE__)
+int uv___stream_fd(const uv_stream_t* handle);
+#define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle)))
+#else
+#define uv__stream_fd(handle) ((handle)->io_watcher.fd)
+#endif /* defined(__APPLE__) */
+
+#ifdef UV__O_NONBLOCK
+# define UV__F_NONBLOCK UV__O_NONBLOCK
+#else
+# define UV__F_NONBLOCK 1
+#endif
+
+int uv__make_socketpair(int fds[2], int flags);
+int uv__make_pipe(int fds[2], int flags);
+
+#if defined(__APPLE__)
+
+int uv__fsevents_init(uv_fs_event_t* handle);
+int uv__fsevents_close(uv_fs_event_t* handle);
+void uv__fsevents_loop_delete(uv_loop_t* loop);
+
+/* OSX < 10.7 has no file events, polyfill them */
+#ifndef MAC_OS_X_VERSION_10_7
+
+static const int kFSEventStreamCreateFlagFileEvents = 0x00000010;
+static const int kFSEventStreamEventFlagItemCreated = 0x00000100;
+static const int kFSEventStreamEventFlagItemRemoved = 0x00000200;
+static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400;
+static const int kFSEventStreamEventFlagItemRenamed = 0x00000800;
+static const int kFSEventStreamEventFlagItemModified = 0x00001000;
+static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000;
+static const int kFSEventStreamEventFlagItemChangeOwner = 0x00004000;
+static const int kFSEventStreamEventFlagItemXattrMod = 0x00008000;
+static const int kFSEventStreamEventFlagItemIsFile = 0x00010000;
+static const int kFSEventStreamEventFlagItemIsDir = 0x00020000;
+static const int kFSEventStreamEventFlagItemIsSymlink = 0x00040000;
+
+#endif /* __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 */
+
+#endif /* defined(__APPLE__) */
+
+UV_UNUSED(static void uv__req_init(uv_loop_t* loop,
+                                   uv_req_t* req,
+                                   uv_req_type type)) {
+  req->type = type;
+  uv__req_register(loop, req);
+}
+#define uv__req_init(loop, req, type) \
+  uv__req_init((loop), (uv_req_t*)(req), (type))
+
+UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
+  /* Use a fast time source if available.  We only need millisecond precision.
+   */
+  loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
+}
+
+UV_UNUSED(static char* uv__basename_r(const char* path)) {
+  char* s;
+
+  s = strrchr(path, '/');
+  if (s == NULL)
+    return (char*) path;
+
+  return s + 1;
+}
+
+#endif /* UV_UNIX_INTERNAL_H_ */
diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c
new file mode 100644 (file)
index 0000000..fffd462
--- /dev/null
@@ -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 "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags);
+
+
+int uv__kqueue_init(uv_loop_t* loop) {
+  loop->backend_fd = kqueue();
+  if (loop->backend_fd == -1)
+    return -errno;
+
+  uv__cloexec(loop->backend_fd, 1);
+
+  return 0;
+}
+
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct kevent ev;
+  int rc;
+
+  rc = 0;
+  EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+  if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
+    rc = -errno;
+
+  EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+  if (rc == 0)
+    if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
+      abort();
+
+  return rc;
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  struct kevent events[1024];
+  struct kevent* ev;
+  struct timespec spec;
+  unsigned int nevents;
+  unsigned int revents;
+  QUEUE* q;
+  uv__io_t* w;
+  sigset_t* pset;
+  sigset_t set;
+  uint64_t base;
+  uint64_t diff;
+  int have_signals;
+  int filter;
+  int fflags;
+  int count;
+  int nfds;
+  int fd;
+  int op;
+  int i;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  nevents = 0;
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int) loop->nwatchers);
+
+    if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) {
+      filter = EVFILT_READ;
+      fflags = 0;
+      op = EV_ADD;
+
+      if (w->cb == uv__fs_event) {
+        filter = EVFILT_VNODE;
+        fflags = NOTE_ATTRIB | NOTE_WRITE  | NOTE_RENAME
+               | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
+        op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */
+      }
+
+      EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0);
+
+      if (++nevents == ARRAY_SIZE(events)) {
+        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+          abort();
+        nevents = 0;
+      }
+    }
+
+    if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) {
+      EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+
+      if (++nevents == ARRAY_SIZE(events)) {
+        if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL))
+          abort();
+        nevents = 0;
+      }
+    }
+
+    w->events = w->pevents;
+  }
+
+  pset = NULL;
+  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
+    pset = &set;
+    sigemptyset(pset);
+    sigaddset(pset, SIGPROF);
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 48; /* Benchmarks suggest this gives the best throughput. */
+
+  for (;; nevents = 0) {
+    if (timeout != -1) {
+      spec.tv_sec = timeout / 1000;
+      spec.tv_nsec = (timeout % 1000) * 1000000;
+    }
+
+    if (pset != NULL)
+      pthread_sigmask(SIG_BLOCK, pset, NULL);
+
+    nfds = kevent(loop->backend_fd,
+                  events,
+                  nevents,
+                  events,
+                  ARRAY_SIZE(events),
+                  timeout == -1 ? NULL : &spec);
+
+    if (pset != NULL)
+      pthread_sigmask(SIG_UNBLOCK, pset, NULL);
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfds == 0) {
+      assert(timeout != -1);
+      return;
+    }
+
+    if (nfds == -1) {
+      if (errno != EINTR)
+        abort();
+
+      if (timeout == 0)
+        return;
+
+      if (timeout == -1)
+        continue;
+
+      /* Interrupted by a signal. Update timeout and poll again. */
+      goto update_timeout;
+    }
+
+    have_signals = 0;
+    nevents = 0;
+
+    assert(loop->watchers != NULL);
+    loop->watchers[loop->nwatchers] = (void*) events;
+    loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
+    for (i = 0; i < nfds; i++) {
+      ev = events + i;
+      fd = ev->ident;
+      /* Skip invalidated events, see uv__platform_invalidate_fd */
+      if (fd == -1)
+        continue;
+      w = loop->watchers[fd];
+
+      if (w == NULL) {
+        /* File descriptor that we've stopped watching, disarm it. */
+        /* TODO batch up */
+        struct kevent events[1];
+
+        EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
+        if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
+          if (errno != EBADF && errno != ENOENT)
+            abort();
+
+        continue;
+      }
+
+      if (ev->filter == EVFILT_VNODE) {
+        assert(w->events == POLLIN);
+        assert(w->pevents == POLLIN);
+        w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */
+        nevents++;
+        continue;
+      }
+
+      revents = 0;
+
+      if (ev->filter == EVFILT_READ) {
+        if (w->pevents & POLLIN) {
+          revents |= POLLIN;
+          w->rcount = ev->data;
+        } else {
+          /* TODO batch up */
+          struct kevent events[1];
+          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
+          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
+            if (errno != ENOENT)
+              abort();
+        }
+      }
+
+      if (ev->filter == EVFILT_WRITE) {
+        if (w->pevents & POLLOUT) {
+          revents |= POLLOUT;
+          w->wcount = ev->data;
+        } else {
+          /* TODO batch up */
+          struct kevent events[1];
+          EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0);
+          if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL))
+            if (errno != ENOENT)
+              abort();
+        }
+      }
+
+      if (ev->flags & EV_ERROR)
+        revents |= POLLERR;
+
+      if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP))
+        revents |= UV__POLLRDHUP;
+
+      if (revents == 0)
+        continue;
+
+      /* Run signal watchers last.  This also affects child process watchers
+       * because those are implemented in terms of signal watchers.
+       */
+      if (w == &loop->signal_io_watcher)
+        have_signals = 1;
+      else
+        w->cb(loop, w, revents);
+
+      nevents++;
+    }
+
+    if (have_signals != 0)
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+
+    loop->watchers[loop->nwatchers] = NULL;
+    loop->watchers[loop->nwatchers + 1] = NULL;
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0) {
+      if (nfds == ARRAY_SIZE(events) && --count != 0) {
+        /* Poll for more events but don't block this time. */
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+
+    if (timeout == 0)
+      return;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    diff = loop->time - base;
+    if (diff >= (uint64_t) timeout)
+      return;
+
+    timeout -= diff;
+  }
+}
+
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  struct kevent* events;
+  uintptr_t i;
+  uintptr_t nfds;
+
+  assert(loop->watchers != NULL);
+
+  events = (struct kevent*) loop->watchers[loop->nwatchers];
+  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
+  if (events == NULL)
+    return;
+
+  /* Invalidate events with same file descriptor */
+  for (i = 0; i < nfds; i++)
+    if ((int) events[i].ident == fd)
+      events[i].ident = -1;
+}
+
+
+static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
+  uv_fs_event_t* handle;
+  struct kevent ev;
+  int events;
+  const char* path;
+#if defined(F_GETPATH)
+  /* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */
+  char pathbuf[MAXPATHLEN];
+#endif
+
+  handle = container_of(w, uv_fs_event_t, event_watcher);
+
+  if (fflags & (NOTE_ATTRIB | NOTE_EXTEND))
+    events = UV_CHANGE;
+  else
+    events = UV_RENAME;
+
+  path = NULL;
+#if defined(F_GETPATH)
+  /* Also works when the file has been unlinked from the file system. Passing
+   * in the path when the file has been deleted is arguably a little strange
+   * but it's consistent with what the inotify backend does.
+   */
+  if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0)
+    path = uv__basename_r(pathbuf);
+#endif
+  handle->cb(handle, path, events, 0);
+
+  if (handle->event_watcher.fd == -1)
+    return;
+
+  /* Watcher operates in one-shot mode, re-arm it. */
+  fflags = NOTE_ATTRIB | NOTE_WRITE  | NOTE_RENAME
+         | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE;
+
+  EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0);
+
+  if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
+    abort();
+}
+
+
+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);
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+#if defined(__APPLE__)
+  struct stat statbuf;
+#endif /* defined(__APPLE__) */
+  int fd;
+
+  if (uv__is_active(handle))
+    return -EINVAL;
+
+  /* TODO open asynchronously - but how do we report back errors? */
+  fd = open(path, O_RDONLY);
+  if (fd == -1)
+    return -errno;
+
+  uv__handle_start(handle);
+  uv__io_init(&handle->event_watcher, uv__fs_event, fd);
+  handle->path = uv__strdup(path);
+  handle->cb = cb;
+
+#if defined(__APPLE__)
+  /* Nullify field to perform checks later */
+  handle->cf_cb = NULL;
+  handle->realpath = NULL;
+  handle->realpath_len = 0;
+  handle->cf_flags = flags;
+
+  if (fstat(fd, &statbuf))
+    goto fallback;
+  /* FSEvents works only with directories */
+  if (!(statbuf.st_mode & S_IFDIR))
+    goto fallback;
+
+  /* The fallback fd is no longer needed */
+  uv__close(fd);
+  handle->event_watcher.fd = -1;
+
+  return uv__fsevents_init(handle);
+
+fallback:
+#endif /* defined(__APPLE__) */
+
+  uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
+
+  return 0;
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  if (!uv__is_active(handle))
+    return 0;
+
+  uv__handle_stop(handle);
+
+#if defined(__APPLE__)
+  if (uv__fsevents_close(handle))
+#endif /* defined(__APPLE__) */
+  {
+    uv__io_close(handle->loop, &handle->event_watcher);
+  }
+
+  uv__free(handle->path);
+  handle->path = NULL;
+
+  if (handle->event_watcher.fd != -1) {
+    /* When FSEvents is used, we don't use the event_watcher's fd under certain
+     * confitions. (see uv_fs_event_start) */
+    uv__close(handle->event_watcher.fd);
+    handle->event_watcher.fd = -1;
+  }
+
+  return 0;
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+}
diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c
new file mode 100644 (file)
index 0000000..a9b137b
--- /dev/null
@@ -0,0 +1,484 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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.
+ */
+
+/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their
+ * EPOLL* counterparts.  We use the POLL* variants in this file because that
+ * is what libuv uses elsewhere and it avoids a dependency on <sys/epoll.h>.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <net/if.h>
+#include <sys/param.h>
+#include <sys/prctl.h>
+#include <sys/sysinfo.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#define HAVE_IFADDRS_H 1
+
+#ifdef __UCLIBC__
+# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
+#  undef HAVE_IFADDRS_H
+# endif
+#endif
+
+#ifdef HAVE_IFADDRS_H
+# if defined(__ANDROID__)
+#  include "android-ifaddrs.h"
+# else
+#  include <ifaddrs.h>
+# endif
+# include <sys/socket.h>
+# include <net/ethernet.h>
+# include <netpacket/packet.h>
+#endif /* HAVE_IFADDRS_H */
+
+/* Available from 2.6.32 onwards. */
+#ifndef CLOCK_MONOTONIC_COARSE
+# define CLOCK_MONOTONIC_COARSE 6
+#endif
+
+/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
+ * include that file because it conflicts with <time.h>. We'll just have to
+ * define it ourselves.
+ */
+#ifndef CLOCK_BOOTTIME
+# define CLOCK_BOOTTIME 7
+#endif
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  int fd;
+
+  fd = uv__epoll_create1(UV__EPOLL_CLOEXEC);
+
+  /* epoll_create1() can fail either because it's not implemented (old kernel)
+   * or because it doesn't understand the EPOLL_CLOEXEC flag.
+   */
+  if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
+    fd = uv__epoll_create(256);
+
+    if (fd != -1)
+      uv__cloexec(fd, 1);
+  }
+
+  loop->backend_fd = fd;
+  loop->inotify_fd = -1;
+  loop->inotify_watchers = NULL;
+
+  if (fd == -1)
+    return -errno;
+
+  return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  if (loop->inotify_fd == -1) return;
+  uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN);
+  uv__close(loop->inotify_fd);
+  loop->inotify_fd = -1;
+}
+
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  struct uv__epoll_event* events;
+  struct uv__epoll_event dummy;
+  uintptr_t i;
+  uintptr_t nfds;
+
+  assert(loop->watchers != NULL);
+
+  events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers];
+  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
+  if (events != NULL)
+    /* Invalidate events with same file descriptor */
+    for (i = 0; i < nfds; i++)
+      if ((int) events[i].data == fd)
+        events[i].data = -1;
+
+  /* Remove the file descriptor from the epoll.
+   * This avoids a problem where the same file description remains open
+   * in another process, causing repeated junk epoll events.
+   *
+   * We pass in a dummy epoll_event, to work around a bug in old kernels.
+   */
+  if (loop->backend_fd >= 0) {
+    /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
+     * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
+     */
+    memset(&dummy, 0, sizeof(dummy));
+    uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &dummy);
+  }
+}
+
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct uv__epoll_event e;
+  int rc;
+
+  e.events = POLLIN;
+  e.data = -1;
+
+  rc = 0;
+  if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_ADD, fd, &e))
+    if (errno != EEXIST)
+      rc = -errno;
+
+  if (rc == 0)
+    if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &e))
+      abort();
+
+  return rc;
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes
+   * effectively infinite on 32 bits architectures.  To avoid blocking
+   * indefinitely, we cap the timeout and poll again if necessary.
+   *
+   * Note that "30 minutes" is a simplification because it depends on
+   * the value of CONFIG_HZ.  The magic constant assumes CONFIG_HZ=1200,
+   * that being the largest value I have seen in the wild (and only once.)
+   */
+  static const int max_safe_timeout = 1789569;
+  static int no_epoll_pwait;
+  static int no_epoll_wait;
+  struct uv__epoll_event events[1024];
+  struct uv__epoll_event* pe;
+  struct uv__epoll_event e;
+  int real_timeout;
+  QUEUE* q;
+  uv__io_t* w;
+  sigset_t sigset;
+  uint64_t sigmask;
+  uint64_t base;
+  int have_signals;
+  int nevents;
+  int count;
+  int nfds;
+  int fd;
+  int op;
+  int i;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int) loop->nwatchers);
+
+    e.events = w->pevents;
+    e.data = w->fd;
+
+    if (w->events == 0)
+      op = UV__EPOLL_CTL_ADD;
+    else
+      op = UV__EPOLL_CTL_MOD;
+
+    /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching
+     * events, skip the syscall and squelch the events after epoll_wait().
+     */
+    if (uv__epoll_ctl(loop->backend_fd, op, w->fd, &e)) {
+      if (errno != EEXIST)
+        abort();
+
+      assert(op == UV__EPOLL_CTL_ADD);
+
+      /* We've reactivated a file descriptor that's been watched before. */
+      if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_MOD, w->fd, &e))
+        abort();
+    }
+
+    w->events = w->pevents;
+  }
+
+  sigmask = 0;
+  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGPROF);
+    sigmask |= 1 << (SIGPROF - 1);
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 48; /* Benchmarks suggest this gives the best throughput. */
+  real_timeout = timeout;
+
+  for (;;) {
+    /* See the comment for max_safe_timeout for an explanation of why
+     * this is necessary.  Executive summary: kernel bug workaround.
+     */
+    if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout)
+      timeout = max_safe_timeout;
+
+    if (sigmask != 0 && no_epoll_pwait != 0)
+      if (pthread_sigmask(SIG_BLOCK, &sigset, NULL))
+        abort();
+
+    if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) {
+      nfds = uv__epoll_pwait(loop->backend_fd,
+                             events,
+                             ARRAY_SIZE(events),
+                             timeout,
+                             sigmask);
+      if (nfds == -1 && errno == ENOSYS)
+        no_epoll_pwait = 1;
+    } else {
+      nfds = uv__epoll_wait(loop->backend_fd,
+                            events,
+                            ARRAY_SIZE(events),
+                            timeout);
+      if (nfds == -1 && errno == ENOSYS)
+        no_epoll_wait = 1;
+    }
+
+    if (sigmask != 0 && no_epoll_pwait != 0)
+      if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL))
+        abort();
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfds == 0) {
+      assert(timeout != -1);
+
+      if (timeout == 0)
+        return;
+
+      /* We may have been inside the system call for longer than |timeout|
+       * milliseconds so we need to update the timestamp to avoid drift.
+       */
+      goto update_timeout;
+    }
+
+    if (nfds == -1) {
+      if (errno == ENOSYS) {
+        /* epoll_wait() or epoll_pwait() failed, try the other system call. */
+        assert(no_epoll_wait == 0 || no_epoll_pwait == 0);
+        continue;
+      }
+
+      if (errno != EINTR)
+        abort();
+
+      if (timeout == -1)
+        continue;
+
+      if (timeout == 0)
+        return;
+
+      /* Interrupted by a signal. Update timeout and poll again. */
+      goto update_timeout;
+    }
+
+    have_signals = 0;
+    nevents = 0;
+
+    assert(loop->watchers != NULL);
+    loop->watchers[loop->nwatchers] = (void*) events;
+    loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
+    for (i = 0; i < nfds; i++) {
+      pe = events + i;
+      fd = pe->data;
+
+      /* Skip invalidated events, see uv__platform_invalidate_fd */
+      if (fd == -1)
+        continue;
+
+      assert(fd >= 0);
+      assert((unsigned) fd < loop->nwatchers);
+
+      w = loop->watchers[fd];
+
+      if (w == NULL) {
+        /* File descriptor that we've stopped watching, disarm it.
+         *
+         * Ignore all errors because we may be racing with another thread
+         * when the file descriptor is closed.
+         */
+        uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, pe);
+        continue;
+      }
+
+      /* Give users only events they're interested in. Prevents spurious
+       * callbacks when previous callback invocation in this loop has stopped
+       * the current watcher. Also, filters out events that users has not
+       * requested us to watch.
+       */
+      pe->events &= w->pevents | POLLERR | POLLHUP;
+
+      /* Work around an epoll quirk where it sometimes reports just the
+       * EPOLLERR or EPOLLHUP event.  In order to force the event loop to
+       * move forward, we merge in the read/write events that the watcher
+       * is interested in; uv__read() and uv__write() will then deal with
+       * the error or hangup in the usual fashion.
+       *
+       * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
+       * reads the available data, calls uv_read_stop(), then sometime later
+       * calls uv_read_start() again.  By then, libuv has forgotten about the
+       * hangup and the kernel won't report EPOLLIN again because there's
+       * nothing left to read.  If anything, libuv is to blame here.  The
+       * current hack is just a quick bandaid; to properly fix it, libuv
+       * needs to remember the error/hangup event.  We should get that for
+       * free when we switch over to edge-triggered I/O.
+       */
+      if (pe->events == POLLERR || pe->events == POLLHUP)
+        pe->events |= w->pevents & (POLLIN | POLLOUT);
+
+      if (pe->events != 0) {
+        /* Run signal watchers last.  This also affects child process watchers
+         * because those are implemented in terms of signal watchers.
+         */
+        if (w == &loop->signal_io_watcher)
+          have_signals = 1;
+        else
+          w->cb(loop, w, pe->events);
+
+        nevents++;
+      }
+    }
+
+    if (have_signals != 0)
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+
+    loop->watchers[loop->nwatchers] = NULL;
+    loop->watchers[loop->nwatchers + 1] = NULL;
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0) {
+      if (nfds == ARRAY_SIZE(events) && --count != 0) {
+        /* Poll for more events but don't block this time. */
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+
+    if (timeout == 0)
+      return;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    real_timeout -= (loop->time - base);
+    if (real_timeout <= 0)
+      return;
+
+    timeout = real_timeout;
+  }
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  static clock_t fast_clock_id = -1;
+  struct timespec t;
+  clock_t clock_id;
+
+  /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
+   * millisecond granularity or better.  CLOCK_MONOTONIC_COARSE is
+   * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
+   * decide to make a costly system call.
+   */
+  /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
+   * when it has microsecond granularity or better (unlikely).
+   */
+  if (type == UV_CLOCK_FAST && fast_clock_id == -1) {
+    if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 &&
+        t.tv_nsec <= 1 * 1000 * 1000) {
+      fast_clock_id = CLOCK_MONOTONIC_COARSE;
+    } else {
+      fast_clock_id = CLOCK_MONOTONIC;
+    }
+  }
+
+  clock_id = CLOCK_MONOTONIC;
+  if (type == UV_CLOCK_FAST)
+    clock_id = fast_clock_id;
+
+  if (clock_gettime(clock_id, &t))
+    return 0;  /* Not really possible. */
+
+  return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
+}
+
+// { TUV_CHANGES@20180803:
+//   Made signal build time configurable }
+#if TUV_FEATURE_PROCESS
+int uv_exepath(char* buffer, size_t* size) {
+  ssize_t n;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  n = *size - 1;
+  if (n > 0)
+    n = readlink("/proc/self/exe", buffer, n);
+
+  if (n == -1)
+    return -errno;
+
+  buffer[n] = '\0';
+  *size = n;
+
+  return 0;
+}
+#endif /* TUV_FEATURE_PROCESS */
diff --git a/src/unix/linux-inotify.c b/src/unix/linux-inotify.c
new file mode 100644 (file)
index 0000000..4708c05
--- /dev/null
@@ -0,0 +1,285 @@
+/* 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 "tree.h"
+#include "internal.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+struct watcher_list {
+  RB_ENTRY(watcher_list) entry;
+  QUEUE watchers;
+  int iterating;
+  char* path;
+  int wd;
+};
+
+struct watcher_root {
+  struct watcher_list* rbh_root;
+};
+#define CAST(p) ((struct watcher_root*)(p))
+
+
+static int compare_watchers(const struct watcher_list* a,
+                            const struct watcher_list* b) {
+  if (a->wd < b->wd) return -1;
+  if (a->wd > b->wd) return 1;
+  return 0;
+}
+
+
+RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers)
+
+
+static void uv__inotify_read(uv_loop_t* loop,
+                             uv__io_t* w,
+                             unsigned int revents);
+
+
+static int new_inotify_fd(void) {
+  int err;
+  int fd;
+
+  fd = uv__inotify_init1(UV__IN_NONBLOCK | UV__IN_CLOEXEC);
+  if (fd != -1)
+    return fd;
+
+  if (errno != ENOSYS)
+    return -errno;
+
+  fd = uv__inotify_init();
+  if (fd == -1)
+    return -errno;
+
+  err = uv__cloexec(fd, 1);
+  if (err == 0)
+    err = uv__nonblock(fd, 1);
+
+  if (err) {
+    uv__close(fd);
+    return err;
+  }
+
+  return fd;
+}
+
+
+static int init_inotify(uv_loop_t* loop) {
+  int err;
+
+  if (loop->inotify_fd != -1)
+    return 0;
+
+  err = new_inotify_fd();
+  if (err < 0)
+    return err;
+
+  loop->inotify_fd = err;
+  uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd);
+  uv__io_start(loop, &loop->inotify_read_watcher, POLLIN);
+
+  return 0;
+}
+
+
+static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) {
+  struct watcher_list w;
+  w.wd = wd;
+  return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w);
+}
+
+static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
+  /* if the watcher_list->watchers is being iterated over, we can't free it. */
+  if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) {
+    /* No watchers left for this path. Clean up. */
+    RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w);
+    uv__inotify_rm_watch(loop->inotify_fd, w->wd);
+    uv__free(w);
+  }
+}
+
+static void uv__inotify_read(uv_loop_t* loop,
+                             uv__io_t* dummy,
+                             unsigned int events) {
+  const struct uv__inotify_event* e;
+  struct watcher_list* w;
+  uv_fs_event_t* h;
+  QUEUE queue;
+  QUEUE* q;
+  const char* path;
+  ssize_t size;
+  const char *p;
+  /* needs to be large enough for sizeof(inotify_event) + strlen(path) */
+  char buf[4096];
+
+  while (1) {
+    do
+      size = read(loop->inotify_fd, buf, sizeof(buf));
+    while (size == -1 && errno == EINTR);
+
+    if (size == -1) {
+      assert(errno == EAGAIN || errno == EWOULDBLOCK);
+      break;
+    }
+
+    assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */
+
+    /* Now we have one or more inotify_event structs. */
+    for (p = buf; p < buf + size; p += sizeof(*e) + e->len) {
+      e = (const struct uv__inotify_event*)p;
+
+      events = 0;
+      if (e->mask & (UV__IN_ATTRIB|UV__IN_MODIFY))
+        events |= UV_CHANGE;
+      if (e->mask & ~(UV__IN_ATTRIB|UV__IN_MODIFY))
+        events |= UV_RENAME;
+
+      w = find_watcher(loop, e->wd);
+      if (w == NULL)
+        continue; /* Stale event, no watchers left. */
+
+      /* inotify does not return the filename when monitoring a single file
+       * for modifications. Repurpose the filename for API compatibility.
+       * I'm not convinced this is a good thing, maybe it should go.
+       */
+      path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path);
+
+      /* We're about to iterate over the queue and call user's callbacks.
+       * What can go wrong?
+       * A callback could call uv_fs_event_stop()
+       * and the queue can change under our feet.
+       * So, we use QUEUE_MOVE() trick to safely iterate over the queue.
+       * And we don't free the watcher_list until we're done iterating.
+       *
+       * First,
+       * tell uv_fs_event_stop() (that could be called from a user's callback)
+       * not to free watcher_list.
+       */
+      w->iterating = 1;
+      QUEUE_MOVE(&w->watchers, &queue);
+      while (!QUEUE_EMPTY(&queue)) {
+        q = QUEUE_HEAD(&queue);
+        h = QUEUE_DATA(q, uv_fs_event_t, watchers);
+
+        QUEUE_REMOVE(q);
+        QUEUE_INSERT_TAIL(&w->watchers, q);
+
+        h->cb(h, path, events, 0);
+      }
+      /* done iterating, time to (maybe) free empty watcher_list */
+      w->iterating = 0;
+      maybe_free_watcher_list(w, loop);
+    }
+  }
+}
+
+
+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);
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+  struct watcher_list* w;
+  int events;
+  int err;
+  int wd;
+
+  if (uv__is_active(handle))
+    return -EINVAL;
+
+  err = init_inotify(handle->loop);
+  if (err)
+    return err;
+
+  events = UV__IN_ATTRIB
+         | UV__IN_CREATE
+         | UV__IN_MODIFY
+         | UV__IN_DELETE
+         | UV__IN_DELETE_SELF
+         | UV__IN_MOVE_SELF
+         | UV__IN_MOVED_FROM
+         | UV__IN_MOVED_TO;
+
+  wd = uv__inotify_add_watch(handle->loop->inotify_fd, path, events);
+  if (wd == -1)
+    return -errno;
+
+  w = find_watcher(handle->loop, wd);
+  if (w)
+    goto no_insert;
+
+  w = uv__malloc(sizeof(*w) + strlen(path) + 1);
+  if (w == NULL)
+    return -ENOMEM;
+
+  w->wd = wd;
+  w->path = strcpy((char*)(w + 1), path);
+  QUEUE_INIT(&w->watchers);
+  w->iterating = 0;
+  RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
+
+no_insert:
+  uv__handle_start(handle);
+  QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers);
+  handle->path = w->path;
+  handle->cb = cb;
+  handle->wd = wd;
+
+  return 0;
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  struct watcher_list* w;
+
+  if (!uv__is_active(handle))
+    return 0;
+
+  w = find_watcher(handle->loop, handle->wd);
+  assert(w != NULL);
+
+  handle->wd = -1;
+  handle->path = NULL;
+  uv__handle_stop(handle);
+  QUEUE_REMOVE(&handle->watchers);
+
+  maybe_free_watcher_list(w, handle->loop);
+
+  return 0;
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+}
diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c
new file mode 100644 (file)
index 0000000..d7d0937
--- /dev/null
@@ -0,0 +1,416 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "linux-syscalls.h"
+#include <unistd.h>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#if defined(__has_feature)
+# if __has_feature(memory_sanitizer)
+#  define MSAN_ACTIVE 1
+#  include <sanitizer/msan_interface.h>
+# endif
+#endif
+
+#if defined(__i386__)
+# ifndef __NR_socketcall
+#  define __NR_socketcall 102
+# endif
+#endif
+
+#if defined(__arm__)
+# if defined(__thumb__) || defined(__ARM_EABI__)
+#  define UV_SYSCALL_BASE 0
+# else
+#  define UV_SYSCALL_BASE 0x900000
+# endif
+#endif /* __arm__ */
+
+#ifndef __NR_accept4
+# if defined(__x86_64__)
+#  define __NR_accept4 288
+# elif defined(__i386__)
+   /* Nothing. Handled through socketcall(). */
+# elif defined(__arm__)
+#  define __NR_accept4 (UV_SYSCALL_BASE + 366)
+# endif
+#endif /* __NR_accept4 */
+
+#ifndef __NR_eventfd
+# if defined(__x86_64__)
+#  define __NR_eventfd 284
+# elif defined(__i386__)
+#  define __NR_eventfd 323
+# elif defined(__arm__)
+#  define __NR_eventfd (UV_SYSCALL_BASE + 351)
+# endif
+#endif /* __NR_eventfd */
+
+#ifndef __NR_eventfd2
+# if defined(__x86_64__)
+#  define __NR_eventfd2 290
+# elif defined(__i386__)
+#  define __NR_eventfd2 328
+# elif defined(__arm__)
+#  define __NR_eventfd2 (UV_SYSCALL_BASE + 356)
+# endif
+#endif /* __NR_eventfd2 */
+
+#ifndef __NR_epoll_create
+# if defined(__x86_64__)
+#  define __NR_epoll_create 213
+# elif defined(__i386__)
+#  define __NR_epoll_create 254
+# elif defined(__arm__)
+#  define __NR_epoll_create (UV_SYSCALL_BASE + 250)
+# endif
+#endif /* __NR_epoll_create */
+
+#ifndef __NR_epoll_create1
+# if defined(__x86_64__)
+#  define __NR_epoll_create1 291
+# elif defined(__i386__)
+#  define __NR_epoll_create1 329
+# elif defined(__arm__)
+#  define __NR_epoll_create1 (UV_SYSCALL_BASE + 357)
+# endif
+#endif /* __NR_epoll_create1 */
+
+#ifndef __NR_epoll_ctl
+# if defined(__x86_64__)
+#  define __NR_epoll_ctl 233 /* used to be 214 */
+# elif defined(__i386__)
+#  define __NR_epoll_ctl 255
+# elif defined(__arm__)
+#  define __NR_epoll_ctl (UV_SYSCALL_BASE + 251)
+# endif
+#endif /* __NR_epoll_ctl */
+
+#ifndef __NR_epoll_wait
+# if defined(__x86_64__)
+#  define __NR_epoll_wait 232 /* used to be 215 */
+# elif defined(__i386__)
+#  define __NR_epoll_wait 256
+# elif defined(__arm__)
+#  define __NR_epoll_wait (UV_SYSCALL_BASE + 252)
+# endif
+#endif /* __NR_epoll_wait */
+
+#ifndef __NR_epoll_pwait
+# if defined(__x86_64__)
+#  define __NR_epoll_pwait 281
+# elif defined(__i386__)
+#  define __NR_epoll_pwait 319
+# elif defined(__arm__)
+#  define __NR_epoll_pwait (UV_SYSCALL_BASE + 346)
+# endif
+#endif /* __NR_epoll_pwait */
+
+#ifndef __NR_inotify_init
+# if defined(__x86_64__)
+#  define __NR_inotify_init 253
+# elif defined(__i386__)
+#  define __NR_inotify_init 291
+# elif defined(__arm__)
+#  define __NR_inotify_init (UV_SYSCALL_BASE + 316)
+# endif
+#endif /* __NR_inotify_init */
+
+#ifndef __NR_inotify_init1
+# if defined(__x86_64__)
+#  define __NR_inotify_init1 294
+# elif defined(__i386__)
+#  define __NR_inotify_init1 332
+# elif defined(__arm__)
+#  define __NR_inotify_init1 (UV_SYSCALL_BASE + 360)
+# endif
+#endif /* __NR_inotify_init1 */
+
+#ifndef __NR_inotify_add_watch
+# if defined(__x86_64__)
+#  define __NR_inotify_add_watch 254
+# elif defined(__i386__)
+#  define __NR_inotify_add_watch 292
+# elif defined(__arm__)
+#  define __NR_inotify_add_watch (UV_SYSCALL_BASE + 317)
+# endif
+#endif /* __NR_inotify_add_watch */
+
+#ifndef __NR_inotify_rm_watch
+# if defined(__x86_64__)
+#  define __NR_inotify_rm_watch 255
+# elif defined(__i386__)
+#  define __NR_inotify_rm_watch 293
+# elif defined(__arm__)
+#  define __NR_inotify_rm_watch (UV_SYSCALL_BASE + 318)
+# endif
+#endif /* __NR_inotify_rm_watch */
+
+#ifndef __NR_pipe2
+# if defined(__x86_64__)
+#  define __NR_pipe2 293
+# elif defined(__i386__)
+#  define __NR_pipe2 331
+# elif defined(__arm__)
+#  define __NR_pipe2 (UV_SYSCALL_BASE + 359)
+# endif
+#endif /* __NR_pipe2 */
+
+#ifndef __NR_recvmmsg
+# if defined(__x86_64__)
+#  define __NR_recvmmsg 299
+# elif defined(__i386__)
+#  define __NR_recvmmsg 337
+# elif defined(__arm__)
+#  define __NR_recvmmsg (UV_SYSCALL_BASE + 365)
+# endif
+#endif /* __NR_recvmsg */
+
+#ifndef __NR_sendmmsg
+# if defined(__x86_64__)
+#  define __NR_sendmmsg 307
+# elif defined(__i386__)
+#  define __NR_sendmmsg 345
+# elif defined(__arm__)
+#  define __NR_sendmmsg (UV_SYSCALL_BASE + 374)
+# endif
+#endif /* __NR_sendmmsg */
+
+#ifndef __NR_utimensat
+# if defined(__x86_64__)
+#  define __NR_utimensat 280
+# elif defined(__i386__)
+#  define __NR_utimensat 320
+# elif defined(__arm__)
+#  define __NR_utimensat (UV_SYSCALL_BASE + 348)
+# endif
+#endif /* __NR_utimensat */
+
+#ifndef __NR_preadv
+# if defined(__x86_64__)
+#  define __NR_preadv 295
+# elif defined(__i386__)
+#  define __NR_preadv 333
+# elif defined(__arm__)
+#  define __NR_preadv (UV_SYSCALL_BASE + 361)
+# endif
+#endif /* __NR_preadv */
+
+#ifndef __NR_pwritev
+# if defined(__x86_64__)
+#  define __NR_pwritev 296
+# elif defined(__i386__)
+#  define __NR_pwritev 334
+# elif defined(__arm__)
+#  define __NR_pwritev (UV_SYSCALL_BASE + 362)
+# endif
+#endif /* __NR_pwritev */
+
+#ifndef __NR_dup3
+# if defined(__x86_64__)
+#  define __NR_dup3 292
+# elif defined(__i386__)
+#  define __NR_dup3 330
+# elif defined(__arm__)
+#  define __NR_dup3 (UV_SYSCALL_BASE + 358)
+# endif
+#endif /* __NR_pwritev */
+
+
+int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
+#if defined(__i386__)
+  unsigned long args[4];
+  int r;
+
+  args[0] = (unsigned long) fd;
+  args[1] = (unsigned long) addr;
+  args[2] = (unsigned long) addrlen;
+  args[3] = (unsigned long) flags;
+
+  r = syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args);
+
+  /* socketcall() raises EINVAL when SYS_ACCEPT4 is not supported but so does
+   * a bad flags argument. Try to distinguish between the two cases.
+   */
+  if (r == -1)
+    if (errno == EINVAL)
+      if ((flags & ~(UV__SOCK_CLOEXEC|UV__SOCK_NONBLOCK)) == 0)
+        errno = ENOSYS;
+
+  return r;
+#elif defined(__NR_accept4)
+  return syscall(__NR_accept4, fd, addr, addrlen, flags);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__eventfd(unsigned int count) {
+#if defined(__NR_eventfd)
+  return syscall(__NR_eventfd, count);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__eventfd2(unsigned int count, int flags) {
+#if defined(__NR_eventfd2)
+  return syscall(__NR_eventfd2, count, flags);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__epoll_create(int size) {
+#if defined(__NR_epoll_create)
+  return syscall(__NR_epoll_create, size);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__epoll_create1(int flags) {
+#if defined(__NR_epoll_create1)
+  return syscall(__NR_epoll_create1, flags);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event* events) {
+#if defined(__NR_epoll_ctl)
+  return syscall(__NR_epoll_ctl, epfd, op, fd, events);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__epoll_wait(int epfd,
+                   struct uv__epoll_event* events,
+                   int nevents,
+                   int timeout) {
+#if defined(__NR_epoll_wait)
+  int result;
+  result = syscall(__NR_epoll_wait, epfd, events, nevents, timeout);
+#if MSAN_ACTIVE
+  if (result > 0)
+    __msan_unpoison(events, sizeof(events[0]) * result);
+#endif
+  return result;
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__epoll_pwait(int epfd,
+                    struct uv__epoll_event* events,
+                    int nevents,
+                    int timeout,
+                    uint64_t sigmask) {
+#if defined(__NR_epoll_pwait)
+  int result;
+  result = syscall(__NR_epoll_pwait,
+                   epfd,
+                   events,
+                   nevents,
+                   timeout,
+                   &sigmask,
+                   sizeof(sigmask));
+#if MSAN_ACTIVE
+  if (result > 0)
+    __msan_unpoison(events, sizeof(events[0]) * result);
+#endif
+  return result;
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__pipe2(int pipefd[2], int flags) {
+#if defined(__NR_pipe2)
+  int result;
+  result = syscall(__NR_pipe2, pipefd, flags);
+#if MSAN_ACTIVE
+  if (!result)
+    __msan_unpoison(pipefd, sizeof(int[2]));
+#endif
+  return result;
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+int uv__utimesat(int dirfd,
+                 const char* path,
+                 const struct timespec times[2],
+                 int flags)
+{
+#if defined(__NR_utimensat)
+  return syscall(__NR_utimensat, dirfd, path, times, flags);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
+#if defined(__NR_preadv)
+  return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
+
+
+ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) {
+#if defined(__NR_pwritev)
+  return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h
new file mode 100644 (file)
index 0000000..4c095e9
--- /dev/null
@@ -0,0 +1,151 @@
+/* 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_LINUX_SYSCALL_H_
+#define UV_LINUX_SYSCALL_H_
+
+#undef  _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <stdint.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#if defined(__alpha__)
+# define UV__O_CLOEXEC        0x200000
+#elif defined(__hppa__)
+# define UV__O_CLOEXEC        0x200000
+#elif defined(__sparc__)
+# define UV__O_CLOEXEC        0x400000
+#else
+# define UV__O_CLOEXEC        0x80000
+#endif
+
+#if defined(__alpha__)
+# define UV__O_NONBLOCK       0x4
+#elif defined(__hppa__)
+# define UV__O_NONBLOCK       O_NONBLOCK
+#elif defined(__mips__)
+# define UV__O_NONBLOCK       0x80
+#elif defined(__sparc__)
+# define UV__O_NONBLOCK       0x4000
+#else
+# define UV__O_NONBLOCK       0x800
+#endif
+
+#define UV__EFD_CLOEXEC       UV__O_CLOEXEC
+#define UV__EFD_NONBLOCK      UV__O_NONBLOCK
+
+#define UV__IN_CLOEXEC        UV__O_CLOEXEC
+#define UV__IN_NONBLOCK       UV__O_NONBLOCK
+
+#define UV__SOCK_CLOEXEC      UV__O_CLOEXEC
+#if defined(SOCK_NONBLOCK)
+# define UV__SOCK_NONBLOCK    SOCK_NONBLOCK
+#else
+# define UV__SOCK_NONBLOCK    UV__O_NONBLOCK
+#endif
+
+/* epoll flags */
+#define UV__EPOLL_CLOEXEC     UV__O_CLOEXEC
+#define UV__EPOLL_CTL_ADD     1
+#define UV__EPOLL_CTL_DEL     2
+#define UV__EPOLL_CTL_MOD     3
+
+/* inotify flags */
+#define UV__IN_ACCESS         0x001
+#define UV__IN_MODIFY         0x002
+#define UV__IN_ATTRIB         0x004
+#define UV__IN_CLOSE_WRITE    0x008
+#define UV__IN_CLOSE_NOWRITE  0x010
+#define UV__IN_OPEN           0x020
+#define UV__IN_MOVED_FROM     0x040
+#define UV__IN_MOVED_TO       0x080
+#define UV__IN_CREATE         0x100
+#define UV__IN_DELETE         0x200
+#define UV__IN_DELETE_SELF    0x400
+#define UV__IN_MOVE_SELF      0x800
+
+#if defined(__x86_64__)
+struct uv__epoll_event {
+  uint32_t events;
+  uint64_t data;
+} __attribute__((packed));
+#else
+struct uv__epoll_event {
+  uint32_t events;
+  uint64_t data;
+};
+#endif
+
+struct uv__inotify_event {
+  int32_t wd;
+  uint32_t mask;
+  uint32_t cookie;
+  uint32_t len;
+  /* char name[0]; */
+};
+
+struct uv__mmsghdr {
+  struct msghdr msg_hdr;
+  unsigned int msg_len;
+};
+
+int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags);
+int uv__eventfd(unsigned int count);
+int uv__epoll_create(int size);
+int uv__epoll_create1(int flags);
+int uv__epoll_ctl(int epfd, int op, int fd, struct uv__epoll_event *ev);
+int uv__epoll_wait(int epfd,
+                   struct uv__epoll_event* events,
+                   int nevents,
+                   int timeout);
+int uv__epoll_pwait(int epfd,
+                    struct uv__epoll_event* events,
+                    int nevents,
+                    int timeout,
+                    uint64_t sigmask);
+int uv__eventfd2(unsigned int count, int flags);
+int uv__inotify_init(void);
+int uv__inotify_init1(int flags);
+int uv__inotify_add_watch(int fd, const char* path, uint32_t mask);
+int uv__inotify_rm_watch(int fd, int32_t wd);
+int uv__pipe2(int pipefd[2], int flags);
+int uv__recvmmsg(int fd,
+                 struct uv__mmsghdr* mmsg,
+                 unsigned int vlen,
+                 unsigned int flags,
+                 struct timespec* timeout);
+int uv__sendmmsg(int fd,
+                 struct uv__mmsghdr* mmsg,
+                 unsigned int vlen,
+                 unsigned int flags);
+int uv__utimesat(int dirfd,
+                 const char* path,
+                 const struct timespec times[2],
+                 int flags);
+ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
+ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
+int uv__dup3(int oldfd, int newfd, int flags);
+
+#endif /* UV_LINUX_SYSCALL_H_ */
diff --git a/src/unix/loop-watcher.c b/src/unix/loop-watcher.c
new file mode 100644 (file)
index 0000000..7b5bedb
--- /dev/null
@@ -0,0 +1,81 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+
+#define UV_LOOP_WATCHER_DEFINE(name, type)                                    \
+  int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) {              \
+    uv__handle_init(loop, (uv_handle_t*)handle, UV_##type);                   \
+    handle->name##_cb = NULL;                                                 \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+  int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) {           \
+    if (uv__is_active(handle)) return 0;                                      \
+    if (cb == NULL) return -EINVAL;                                           \
+    QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue);         \
+    handle->name##_cb = cb;                                                   \
+    uv__handle_start(handle);                                                 \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+  int uv_##name##_stop(uv_##name##_t* handle) {                               \
+    if (!uv__is_active(handle)) return 0;                                     \
+    QUEUE_REMOVE(&handle->queue);                                             \
+    uv__handle_stop(handle);                                                  \
+    return 0;                                                                 \
+  }                                                                           \
+                                                                              \
+  void uv__run_##name(uv_loop_t* loop) {                                      \
+    uv_##name##_t* h;                                                         \
+    QUEUE queue;                                                              \
+    QUEUE* q;                                                                 \
+    QUEUE_MOVE(&loop->name##_handles, &queue);                                \
+    while (!QUEUE_EMPTY(&queue)) {                                            \
+      q = QUEUE_HEAD(&queue);                                                 \
+      h = QUEUE_DATA(q, uv_##name##_t, queue);                                \
+      QUEUE_REMOVE(q);                                                        \
+      QUEUE_INSERT_TAIL(&loop->name##_handles, q);                            \
+      h->name##_cb(h);                                                        \
+    }                                                                         \
+  }                                                                           \
+                                                                              \
+  void uv__##name##_close(uv_##name##_t* handle) {                            \
+    uv_##name##_stop(handle);                                                 \
+  }
+
+UV_LOOP_WATCHER_DEFINE(idle, IDLE)
diff --git a/src/unix/loop.c b/src/unix/loop.c
new file mode 100644 (file)
index 0000000..16b3b0f
--- /dev/null
@@ -0,0 +1,172 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "tree.h"
+#include "internal.h"
+#include "heap-inl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int uv_loop_init(uv_loop_t* loop) {
+  void* saved_data;
+  int err;
+
+#ifdef TUV_FEATURE_SIGNAL
+  uv__signal_global_once_init();
+#endif
+
+  saved_data = loop->data;
+  memset(loop, 0, sizeof(*loop));
+  loop->data = saved_data;
+
+  heap_init((struct heap*) &loop->timer_heap);
+  QUEUE_INIT(&loop->wq);
+  QUEUE_INIT(&loop->active_reqs);
+  QUEUE_INIT(&loop->idle_handles);
+  QUEUE_INIT(&loop->async_handles);
+  QUEUE_INIT(&loop->check_handles);
+  QUEUE_INIT(&loop->prepare_handles);
+  QUEUE_INIT(&loop->handle_queue);
+
+  loop->nfds = 0;
+  loop->watchers = NULL;
+  loop->nwatchers = 0;
+  QUEUE_INIT(&loop->pending_queue);
+  QUEUE_INIT(&loop->watcher_queue);
+
+  loop->closing_handles = NULL;
+  uv__update_time(loop);
+  uv__async_init(&loop->async_watcher);
+#ifdef TUV_FEATURE_SIGNAL
+  loop->signal_pipefd[0] = -1;
+  loop->signal_pipefd[1] = -1;
+#endif
+  loop->backend_fd = -1;
+  loop->emfile_fd = -1;
+
+  loop->timer_counter = 0;
+  loop->stop_flag = 0;
+
+  err = uv__platform_loop_init(loop);
+  if (err)
+    return err;
+
+#ifdef TUV_FEATURE_SIGNAL
+  err = uv_signal_init(loop, &loop->child_watcher);
+  if (err)
+    goto fail_signal_init;
+#endif
+
+  uv__handle_unref(&loop->child_watcher);
+  loop->child_watcher.flags |= UV__HANDLE_INTERNAL;
+  QUEUE_INIT(&loop->process_handles);
+
+  err = uv_rwlock_init(&loop->cloexec_lock);
+  if (err)
+    goto fail_rwlock_init;
+
+  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;
+
+  return 0;
+
+fail_async_init:
+  uv_mutex_destroy(&loop->wq_mutex);
+
+fail_mutex_init:
+  uv_rwlock_destroy(&loop->cloexec_lock);
+
+fail_rwlock_init:
+#ifdef TUV_FEATURE_SIGNAL
+  uv__signal_loop_cleanup(loop);
+#endif
+
+fail_signal_init:
+  uv__platform_loop_delete(loop);
+
+  return err;
+}
+
+
+void uv__loop_close(uv_loop_t* loop) {
+#ifdef TUV_FEATURE_SIGNAL
+  uv__signal_loop_cleanup(loop);
+#endif
+  uv__platform_loop_delete(loop);
+  uv__async_stop(loop, &loop->async_watcher);
+
+  if (loop->emfile_fd != -1) {
+    uv__close(loop->emfile_fd);
+    loop->emfile_fd = -1;
+  }
+
+  if (loop->backend_fd != -1) {
+    uv__close(loop->backend_fd);
+    loop->backend_fd = -1;
+  }
+
+  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);
+
+  /*
+   * Note that all thread pool stuff is finished at this point and
+   * it is safe to just destroy rw lock
+   */
+  uv_rwlock_destroy(&loop->cloexec_lock);
+
+#if 0
+  assert(QUEUE_EMPTY(&loop->pending_queue));
+  assert(QUEUE_EMPTY(&loop->watcher_queue));
+  assert(loop->nfds == 0);
+#endif
+
+  uv__free(loop->watchers);
+  loop->watchers = NULL;
+  loop->nwatchers = 0;
+}
diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c
new file mode 100644 (file)
index 0000000..4a9e6cb
--- /dev/null
@@ -0,0 +1,380 @@
+/* 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"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include <kvm.h>
+#include <paths.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <uvm/uvm_extern.h>
+
+#include <unistd.h>
+#include <time.h>
+
+#undef NANOSEC
+#define NANOSEC ((uint64_t) 1e9)
+
+static char *process_title;
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  return uv__kqueue_init(loop);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) == -1) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  int mib[4];
+  size_t cb;
+  pid_t mypid;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  mypid = getpid();
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC_ARGS;
+  mib[2] = mypid;
+  mib[3] = KERN_PROC_ARGV;
+
+  cb = *size;
+  if (sysctl(mib, 4, buffer, &cb, NULL, 0))
+    return -errno;
+  *size = strlen(buffer);
+
+  return 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct uvmexp info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_UVMEXP};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
+}
+
+
+uint64_t uv_get_total_memory(void) {
+#if defined(HW_PHYSMEM64)
+  uint64_t info;
+  int which[] = {CTL_HW, HW_PHYSMEM64};
+#else
+  unsigned int info;
+  int which[] = {CTL_HW, HW_PHYSMEM};
+#endif
+  size_t size = sizeof(info);
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  return (uint64_t) info;
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  process_title = argc ? uv__strdup(argv[0]) : NULL;
+  return argv;
+}
+
+
+int uv_set_process_title(const char* title) {
+  if (process_title) uv__free(process_title);
+
+  process_title = uv__strdup(title);
+  setproctitle("%s", title);
+
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  size_t len;
+
+  if (buffer == NULL || size == 0)
+    return -EINVAL;
+
+  if (process_title) {
+    len = strlen(process_title) + 1;
+
+    if (size < len)
+      return -ENOBUFS;
+
+    memcpy(buffer, process_title, len);
+  } else {
+    len = 0;
+  }
+
+  buffer[len] = '\0';
+
+  return 0;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  kvm_t *kd = NULL;
+  struct kinfo_proc2 *kinfo = NULL;
+  pid_t pid;
+  int nprocs;
+  int max_size = sizeof(struct kinfo_proc2);
+  int page_size;
+
+  page_size = getpagesize();
+  pid = getpid();
+
+  kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
+
+  if (kd == NULL) goto error;
+
+  kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs);
+  if (kinfo == NULL) goto error;
+
+  *rss = kinfo->p_vm_rssize * page_size;
+
+  kvm_close(kd);
+
+  return 0;
+
+error:
+  if (kd) kvm_close(kd);
+  return -EPERM;
+}
+
+
+int uv_uptime(double* uptime) {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  now = time(NULL);
+
+  *uptime = (double)(now - info.tv_sec);
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK);
+  unsigned int multiplier = ((uint64_t)1000L / ticks);
+  unsigned int cur = 0;
+  uv_cpu_info_t* cpu_info;
+  u_int64_t* cp_times;
+  char model[512];
+  u_int64_t cpuspeed;
+  int numcpus;
+  size_t size;
+  int i;
+
+  size = sizeof(model);
+  if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) &&
+      sysctlbyname("hw.model", &model, &size, NULL, 0)) {
+    return -errno;
+  }
+
+  size = sizeof(numcpus);
+  if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0))
+    return -errno;
+  *count = numcpus;
+
+  /* Only i386 and amd64 have machdep.tsc_freq */
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0))
+    cpuspeed = 0;
+
+  size = numcpus * CPUSTATES * sizeof(*cp_times);
+  cp_times = uv__malloc(size);
+  if (cp_times == NULL)
+    return -ENOMEM;
+
+  if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0))
+    return -errno;
+
+  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos)) {
+    uv__free(cp_times);
+    uv__free(*cpu_infos);
+    return -ENOMEM;
+  }
+
+  for (i = 0; i < numcpus; i++) {
+    cpu_info = &(*cpu_infos)[i];
+    cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
+    cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6);
+    cur += CPUSTATES;
+  }
+  uv__free(cp_times);
+  return 0;
+}
+
+
+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);
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+  struct ifaddrs *addrs, *ent;
+  uv_interface_address_t* address;
+  int i;
+  struct sockaddr_dl *sa_addr;
+
+  if (getifaddrs(&addrs))
+    return -errno;
+
+  *count = 0;
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family != PF_INET)) {
+      continue;
+    }
+    (*count)++;
+  }
+
+  *addresses = uv__malloc(*count * sizeof(**addresses));
+
+  if (!(*addresses)) {
+    freeifaddrs(addrs);
+    return -ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+      continue;
+
+    if (ent->ifa_addr == NULL)
+      continue;
+
+    if (ent->ifa_addr->sa_family != PF_INET)
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
+
+    address++;
+  }
+
+  /* Fill in physical addresses for each interface */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family != AF_LINK)) {
+      continue;
+    }
+
+    address = *addresses;
+
+    for (i = 0; i < (*count); i++) {
+      if (strcmp(address->name, ent->ifa_name) == 0) {
+        sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
+        memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
+      }
+      address++;
+    }
+  }
+
+  freeifaddrs(addrs);
+
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
diff --git a/src/unix/nuttx.c b/src/unix/nuttx.c
new file mode 100644 (file)
index 0000000..8c33a4a
--- /dev/null
@@ -0,0 +1,278 @@
+/* Copyright 2015-present Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+
+#include <uv.h>
+#include "unix/internal.h"
+
+//-----------------------------------------------------------------------------
+// loop
+int uv__platform_loop_init(uv_loop_t* loop) {
+  loop->npollfds = 0;
+  return 0;
+}
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  loop->npollfds = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  int i;
+  int nfd = loop->npollfds;
+  for (i = 0; i < nfd; ++i) {
+    struct pollfd* pfd = &loop->pollfds[i];
+    if (fd == pfd->fd) {
+      pfd->fd = -1;
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+
+/* should not clear handle structure with memset as
+ * *data can be set before calling init function */
+
+//-----------------------------------------------------------------------------
+
+int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen) {
+  return 0;
+}
+
+ssize_t readv(int fd, const struct iovec* iiovec, int count) {
+  ssize_t result = 0;
+  ssize_t total = 0;
+  int idx;
+
+  for (idx = 0; idx < count; ++idx) {
+    result = read(fd, iiovec[idx].iov_base, iiovec[idx].iov_len);
+    if (result < 0) {
+      return result;
+    } else {
+      total += result;
+    }
+  }
+  return total;
+}
+
+
+ssize_t writev(int fd, const struct iovec* iiovec, int count) {
+  ssize_t result = 0;
+  ssize_t total = 0;
+  int idx;
+
+  for (idx = 0; idx < count; ++idx) {
+    result = write(fd, iiovec[idx].iov_base, iiovec[idx].iov_len);
+    if (result < 0) {
+      return result;
+    } else {
+      total += result;
+    }
+  }
+  return total;
+}
+
+// From nuttx_clock.c
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  uint64_t ret = (((uint64_t) ts.tv_sec) * ((uint64_t) 1e9) +
+                   (uint64_t) ts.tv_nsec);
+  return ret;
+}
+
+// From nuttx_io.c
+static void uv__add_pollfd(uv_loop_t* loop, struct pollfd* pe) {
+  int i;
+  bool exist = false;
+  int free_idx = -1;
+  for (i = 0; i < loop->npollfds; ++i) {
+    struct pollfd* cur = &loop->pollfds[i];
+    if (cur->fd == pe->fd) {
+      cur->events = pe->events;
+      exist = true;
+      break;
+    }
+    if (cur->fd == -1) {
+      free_idx = i;
+    }
+  }
+  if (!exist) {
+    if (free_idx == -1) {
+      free_idx = loop->npollfds++;
+      if (free_idx >= TUV_POLL_EVENTS_SIZE)
+      {
+        TDLOG("uv__add_pollfd abort, because loop->npollfds (%d) reached maximum size", free_idx);
+        ABORT();
+      }
+    }
+    struct pollfd* cur = &loop->pollfds[free_idx];
+
+    cur->fd = pe->fd;
+    cur->events = pe->events;
+    cur->revents = 0;
+    cur->sem = 0;
+    cur->priv = 0;
+  }
+}
+
+
+static void uv__rem_pollfd(uv_loop_t* loop, struct pollfd* pe) {
+  int i = 0;
+  while (i < loop->npollfds) {
+    struct pollfd* cur = &loop->pollfds[i];
+    if (cur->fd == pe->fd) {
+      *cur = loop->pollfds[--loop->npollfds];
+    } else {
+      ++i;
+    }
+  }
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  struct pollfd pfd;
+  struct pollfd* pe;
+  QUEUE* q;
+  uv__io_t* w;
+  uint64_t base;
+  uint64_t diff;
+  int nevents;
+  int count;
+  int nfd;
+  int i;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int)loop->nwatchers);
+
+    pfd.fd = w->fd;
+    pfd.events = w->pevents;
+    uv__add_pollfd(loop, &pfd);
+
+    w->events = w->pevents;
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 5;
+
+  for (;;) {
+    nfd = poll(loop->pollfds, loop->npollfds, timeout);
+
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfd == 0) {
+      assert(timeout != -1);
+      return;
+    }
+
+    if (nfd == -1) {
+      int err = get_errno();
+      if (err == EAGAIN) {
+        set_errno(0);
+      }
+      else if (err != EINTR) {
+        // poll of which the watchers is null should be removed
+        TDLOG("uv__io_poll abort for errno(%d)", err);
+        goto handle_poll;
+      }
+      if (timeout == -1) {
+        continue;
+      }
+      if (timeout == 0) {
+        return;
+      }
+      goto update_timeout;
+    }
+
+handle_poll:
+    nevents = 0;
+
+    for (i = 0; i < loop->npollfds; ++i) {
+      pe = &loop->pollfds[i];
+      w = loop->watchers[pe->fd];
+
+      if (w == NULL) {
+        uv__rem_pollfd(loop, pe);
+        --i;
+        continue;
+      }
+
+      if (pe->fd >= 0 && (pe->revents & (POLLIN | POLLOUT | POLLHUP))) {    
+        w->cb(loop, w, pe->revents);
+        ++nevents;
+      }
+    }
+
+    if (nevents != 0) {
+      if (--count != 0) {
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+    if (timeout == 0) {
+      return;
+    }
+    if (timeout == -1) {
+      continue;
+    }
+update_timeout:
+    assert(timeout > 0);
+
+    diff = loop->time - base;
+    if (diff >= (uint64_t)timeout) {
+      return;
+    }
+    timeout -= diff;
+  }
+}
diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c
new file mode 100644 (file)
index 0000000..909288c
--- /dev/null
@@ -0,0 +1,396 @@
+/* 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"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/sched.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#undef NANOSEC
+#define NANOSEC ((uint64_t) 1e9)
+
+
+static char *process_title;
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  return uv__kqueue_init(loop);
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
+}
+
+
+void uv_loadavg(double avg[3]) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
+
+  avg[0] = (double) info.ldavg[0] / info.fscale;
+  avg[1] = (double) info.ldavg[1] / info.fscale;
+  avg[2] = (double) info.ldavg[2] / info.fscale;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  int mib[4];
+  char **argsbuf = NULL;
+  char **argsbuf_tmp;
+  size_t argsbuf_size = 100U;
+  size_t exepath_size;
+  pid_t mypid;
+  int err;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  mypid = getpid();
+  for (;;) {
+    err = -ENOMEM;
+    argsbuf_tmp = uv__realloc(argsbuf, argsbuf_size);
+    if (argsbuf_tmp == NULL)
+      goto out;
+    argsbuf = argsbuf_tmp;
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC_ARGS;
+    mib[2] = mypid;
+    mib[3] = KERN_PROC_ARGV;
+    if (sysctl(mib, 4, argsbuf, &argsbuf_size, NULL, 0) == 0) {
+      break;
+    }
+    if (errno != ENOMEM) {
+      err = -errno;
+      goto out;
+    }
+    argsbuf_size *= 2U;
+  }
+
+  if (argsbuf[0] == NULL) {
+    err = -EINVAL;  /* FIXME(bnoordhuis) More appropriate error. */
+    goto out;
+  }
+
+  *size -= 1;
+  exepath_size = strlen(argsbuf[0]);
+  if (*size > exepath_size)
+    *size = exepath_size;
+
+  memcpy(buffer, argsbuf[0], *size);
+  buffer[*size] = '\0';
+  err = 0;
+
+out:
+  uv__free(argsbuf);
+
+  return err;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct uvmexp info;
+  size_t size = sizeof(info);
+  int which[] = {CTL_VM, VM_UVMEXP};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  uint64_t info;
+  int which[] = {CTL_HW, HW_PHYSMEM64};
+  size_t size = sizeof(info);
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  return (uint64_t) info;
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  process_title = argc ? uv__strdup(argv[0]) : NULL;
+  return argv;
+}
+
+
+int uv_set_process_title(const char* title) {
+  uv__free(process_title);
+  process_title = uv__strdup(title);
+  setproctitle(title);
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  size_t len;
+
+  if (buffer == NULL || size == 0)
+    return -EINVAL;
+
+  if (process_title) {
+    len = strlen(process_title) + 1;
+
+    if (size < len)
+      return -ENOBUFS;
+
+    memcpy(buffer, process_title, len);
+  } else {
+    len = 0;
+  }
+
+  buffer[len] = '\0';
+
+  return 0;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  struct kinfo_proc kinfo;
+  size_t page_size = getpagesize();
+  size_t size = sizeof(struct kinfo_proc);
+  int mib[6];
+
+  mib[0] = CTL_KERN;
+  mib[1] = KERN_PROC;
+  mib[2] = KERN_PROC_PID;
+  mib[3] = getpid();
+  mib[4] = sizeof(struct kinfo_proc);
+  mib[5] = 1;
+
+  if (sysctl(mib, 6, &kinfo, &size, NULL, 0) < 0)
+    return -errno;
+
+  *rss = kinfo.p_vm_rssize * page_size;
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0))
+    return -errno;
+
+  now = time(NULL);
+
+  *uptime = (double)(now - info.tv_sec);
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed;
+  uint64_t info[CPUSTATES];
+  char model[512];
+  int numcpus = 1;
+  int which[] = {CTL_HW,HW_MODEL,0};
+  size_t size;
+  int i;
+  uv_cpu_info_t* cpu_info;
+
+  size = sizeof(model);
+  if (sysctl(which, 2, &model, &size, NULL, 0))
+    return -errno;
+
+  which[1] = HW_NCPU;
+  size = sizeof(numcpus);
+  if (sysctl(which, 2, &numcpus, &size, NULL, 0))
+    return -errno;
+
+  *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos))
+    return -ENOMEM;
+
+  *count = numcpus;
+
+  which[1] = HW_CPUSPEED;
+  size = sizeof(cpuspeed);
+  if (sysctl(which, 2, &cpuspeed, &size, NULL, 0)) {
+    uv__free(*cpu_infos);
+    return -errno;
+  }
+
+  size = sizeof(info);
+  which[0] = CTL_KERN;
+  which[1] = KERN_CPTIME2;
+  for (i = 0; i < numcpus; i++) {
+    which[2] = i;
+    size = sizeof(info);
+    if (sysctl(which, 3, &info, &size, NULL, 0)) {
+      uv__free(*cpu_infos);
+      return -errno;
+    }
+
+    cpu_info = &(*cpu_infos)[i];
+
+    cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(info[CP_NICE]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier;
+    cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier;
+
+    cpu_info->model = uv__strdup(model);
+    cpu_info->speed = cpuspeed;
+  }
+
+  return 0;
+}
+
+
+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);
+}
+
+
+int uv_interface_addresses(uv_interface_address_t** addresses,
+  int* count) {
+  struct ifaddrs *addrs, *ent;
+  uv_interface_address_t* address;
+  int i;
+  struct sockaddr_dl *sa_addr;
+
+  if (getifaddrs(&addrs) != 0)
+    return -errno;
+
+   *count = 0;
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family != PF_INET)) {
+      continue;
+    }
+    (*count)++;
+  }
+
+  *addresses = uv__malloc(*count * sizeof(**addresses));
+
+  if (!(*addresses)) {
+    freeifaddrs(addrs);
+    return -ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+      continue;
+
+    if (ent->ifa_addr == NULL)
+      continue;
+
+    if (ent->ifa_addr->sa_family != PF_INET)
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
+
+    address++;
+  }
+
+  /* Fill in physical addresses for each interface */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family != AF_LINK)) {
+      continue;
+    }
+
+    address = *addresses;
+
+    for (i = 0; i < (*count); i++) {
+      if (strcmp(address->name, ent->ifa_name) == 0) {
+        sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
+        memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
+      }
+      address++;
+    }
+  }
+
+  freeifaddrs(addrs);
+
+  return 0;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+  int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
diff --git a/src/unix/os390.c b/src/unix/os390.c
new file mode 100644 (file)
index 0000000..bcdbc4b
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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 "internal.h"
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  struct pollfd p[1];
+  int rv;
+
+  p[0].fd = fd;
+  p[0].events = POLLIN;
+
+  do
+    rv = poll(p, 1, 0);
+  while (rv == -1 && errno == EINTR);
+
+  if (rv == -1)
+    abort();
+
+  if (p[0].revents & POLLNVAL)
+    return -1;
+
+  return 0;
+}
diff --git a/src/unix/pipe.c b/src/unix/pipe.c
new file mode 100644 (file)
index 0000000..b73994c
--- /dev/null
@@ -0,0 +1,298 @@
+/* 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"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+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->shutdown_req = NULL;
+  handle->connect_req = NULL;
+  handle->pipe_fname = NULL;
+  handle->ipc = ipc;
+  return 0;
+}
+
+
+int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
+  struct sockaddr_un saddr;
+  const char* pipe_fname;
+  int sockfd;
+  int err;
+
+  pipe_fname = NULL;
+  sockfd = -1;
+
+  /* Already bound? */
+  if (uv__stream_fd(handle) >= 0)
+    return -EINVAL;
+
+  /* Make a copy of the file name, it outlives this function's scope. */
+  pipe_fname = uv__strdup(name);
+  if (pipe_fname == NULL)
+    return -ENOMEM;
+
+  /* We've got a copy, don't touch the original any more. */
+  name = NULL;
+
+  err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
+  if (err < 0)
+    goto err_socket;
+  sockfd = err;
+
+  memset(&saddr, 0, sizeof saddr);
+  strncpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path) - 1);
+  saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
+  saddr.sun_family = AF_UNIX;
+
+  if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
+    err = -errno;
+    /* Convert ENOENT to EACCES for compatibility with Windows. */
+    if (err == -ENOENT)
+      err = -EACCES;
+    goto err_bind;
+  }
+
+  /* Success. */
+  handle->flags |= UV_HANDLE_BOUND;
+  handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */
+  handle->io_watcher.fd = sockfd;
+  return 0;
+
+err_bind:
+  uv__close(sockfd);
+
+err_socket:
+  uv__free((void*)pipe_fname);
+  return err;
+}
+
+
+int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
+  if (uv__stream_fd(handle) == -1)
+    return -EINVAL;
+
+#if defined(__MVS__)
+  /* On zOS, backlog=0 has undefined behaviour */
+  if (backlog == 0)
+    backlog = 1;
+  else if (backlog < 0)
+    backlog = SOMAXCONN;
+#endif
+
+  if (listen(uv__stream_fd(handle), backlog))
+    return -errno;
+
+  handle->connection_cb = cb;
+  handle->io_watcher.cb = uv__server_io;
+  uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
+  return 0;
+}
+
+
+void uv__pipe_close(uv_pipe_t* handle) {
+  if (handle->pipe_fname) {
+    /*
+     * Unlink the file system entity before closing the file descriptor.
+     * Doing it the other way around introduces a race where our process
+     * unlinks a socket with the same name that's just been created by
+     * another thread or process.
+     */
+    unlink(handle->pipe_fname);
+    uv__free((void*)handle->pipe_fname);
+    handle->pipe_fname = NULL;
+  }
+
+  uv__stream_close((uv_stream_t*)handle);
+}
+
+
+int uv_pipe_open(uv_pipe_t* handle, uv_file fd) {
+  int err;
+
+  err = uv__nonblock(fd, 1);
+  if (err)
+    return err;
+
+#if defined(__APPLE__)
+  err = uv__stream_try_select((uv_stream_t*) handle, &fd);
+  if (err)
+    return err;
+#endif /* defined(__APPLE__) */
+
+  return uv__stream_open((uv_stream_t*)handle,
+                         fd,
+                         UV_STREAM_READABLE | UV_STREAM_WRITABLE);
+}
+
+
+void uv_pipe_connect(uv_connect_t* req,
+                    uv_pipe_t* handle,
+                    const char* name,
+                    uv_connect_cb cb) {
+  struct sockaddr_un saddr;
+  int new_sock;
+  int err;
+  int r;
+
+  new_sock = (uv__stream_fd(handle) == -1);
+
+  if (new_sock) {
+    err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
+    if (err < 0)
+      goto out;
+    handle->io_watcher.fd = err;
+  }
+
+  memset(&saddr, 0, sizeof saddr);
+  strncpy(saddr.sun_path, name, sizeof(saddr.sun_path) - 1);
+  saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
+  saddr.sun_family = AF_UNIX;
+
+  do {
+    r = connect(uv__stream_fd(handle),
+                (struct sockaddr*)&saddr, sizeof saddr);
+  }
+  while (r == -1 && errno == EINTR);
+
+  if (r == -1 && errno != EINPROGRESS) {
+    err = -errno;
+    goto out;
+  }
+
+  err = 0;
+  if (new_sock) {
+    err = uv__stream_open((uv_stream_t*)handle,
+                          uv__stream_fd(handle),
+                          UV_STREAM_READABLE | UV_STREAM_WRITABLE);
+  }
+
+  if (err == 0)
+    uv__io_start(handle->loop, &handle->io_watcher, POLLIN | POLLOUT);
+
+out:
+  handle->delayed_error = err;
+  handle->connect_req = req;
+
+  uv__req_init(handle->loop, req, UV_CONNECT);
+  req->handle = (uv_stream_t*)handle;
+  req->cb = cb;
+  QUEUE_INIT(&req->queue);
+
+  /* Force callback to run on next tick in case of error. */
+  if (err)
+    uv__io_feed(handle->loop, &handle->io_watcher);
+
+}
+
+
+typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
+
+
+static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
+                                    uv__peersockfunc func,
+                                    char* buffer,
+                                    size_t* size) {
+  struct sockaddr_un sa;
+  socklen_t addrlen;
+  int err;
+
+  addrlen = sizeof(sa);
+  memset(&sa, 0, addrlen);
+  err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen);
+  if (err < 0) {
+    *size = 0;
+    return -errno;
+  }
+
+#if defined(__linux__)
+  if (sa.sun_path[0] == 0)
+    /* Linux abstract namespace */
+    addrlen -= offsetof(struct sockaddr_un, sun_path);
+  else
+#endif
+    addrlen = strlen(sa.sun_path);
+
+
+  if (addrlen >= *size) {
+    *size = addrlen + 1;
+    return UV_ENOBUFS;
+  }
+
+  memcpy(buffer, sa.sun_path, addrlen);
+  *size = addrlen;
+
+  /* only null-terminate if it's not an abstract socket */
+  if (buffer[0] != '\0')
+    buffer[addrlen] = '\0';
+
+  return 0;
+}
+
+
+int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  return uv__pipe_getsockpeername(handle, getsockname, buffer, size);
+}
+
+
+int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
+  return uv__pipe_getsockpeername(handle, getpeername, buffer, size);
+}
+
+
+void uv_pipe_pending_instances(uv_pipe_t* handle, int count) {
+}
+
+
+int uv_pipe_pending_count(uv_pipe_t* handle) {
+  uv__stream_queued_fds_t* queued_fds;
+
+  if (!handle->ipc)
+    return 0;
+
+  if (handle->accepted_fd == -1)
+    return 0;
+
+  if (handle->queued_fds == NULL)
+    return 1;
+
+  queued_fds = handle->queued_fds;
+  return queued_fds->offset + 1;
+}
+
+
+uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
+  if (!handle->ipc)
+    return UV_UNKNOWN_HANDLE;
+
+  if (handle->accepted_fd == -1)
+    return UV_UNKNOWN_HANDLE;
+  else
+    return uv__handle_type(handle->accepted_fd);
+}
diff --git a/src/unix/poll.c b/src/unix/poll.c
new file mode 100644 (file)
index 0000000..7b8e008
--- /dev/null
@@ -0,0 +1,149 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+
+static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  uv_poll_t* handle;
+  int pevents;
+
+  handle = container_of(w, uv_poll_t, io_watcher);
+
+  if (events & POLLERR) {
+    uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP);
+    uv__handle_stop(handle);
+    handle->poll_cb(handle, -EBADF, 0);
+    return;
+  }
+
+  pevents = 0;
+  if (events & POLLIN)
+    pevents |= UV_READABLE;
+  if (events & POLLOUT)
+    pevents |= UV_WRITABLE;
+  if (events & UV__POLLRDHUP)
+    pevents |= UV_DISCONNECT;
+
+  handle->poll_cb(handle, 0, pevents);
+}
+
+
+int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) {
+  int err;
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  err = uv__io_check_fd(loop, fd);
+  if (err)
+    return err;
+#endif
+
+  /* If ioctl(FIONBIO) reports ENOTTY, try fcntl(F_GETFL) + fcntl(F_SETFL).
+   * Workaround for e.g. kqueue fds not supporting ioctls.
+   */
+  err = uv__nonblock(fd, 1);
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  if (err == -ENOTTY)
+    if (uv__nonblock == uv__nonblock_ioctl)
+      err = uv__nonblock_fcntl(fd, 1);
+#endif
+
+  if (err)
+    return err;
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL);
+  uv__io_init(&handle->io_watcher, uv__poll_io, fd);
+  handle->poll_cb = NULL;
+  return 0;
+}
+
+
+int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle,
+    uv_os_sock_t socket) {
+  return uv_poll_init(loop, handle, socket);
+}
+
+
+static void uv__poll_stop(uv_poll_t* handle) {
+  uv__io_stop(handle->loop,
+              &handle->io_watcher,
+              POLLIN | POLLOUT | UV__POLLRDHUP);
+  uv__handle_stop(handle);
+}
+
+
+int uv_poll_stop(uv_poll_t* handle) {
+  assert(!uv__is_closing(handle));
+  uv__poll_stop(handle);
+  return 0;
+}
+
+
+int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) {
+  int events;
+
+  assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0);
+  assert(!uv__is_closing(handle));
+
+  uv__poll_stop(handle);
+
+  if (pevents == 0)
+    return 0;
+
+  events = 0;
+  if (pevents & UV_READABLE)
+    events |= POLLIN;
+  if (pevents & UV_WRITABLE)
+    events |= POLLOUT;
+  if (pevents & UV_DISCONNECT)
+    events |= UV__POLLRDHUP;
+
+  uv__io_start(handle->loop, &handle->io_watcher, events);
+  uv__handle_start(handle);
+  handle->poll_cb = poll_cb;
+
+  return 0;
+}
+
+
+void uv__poll_close(uv_poll_t* handle) {
+  uv__poll_stop(handle);
+}
diff --git a/src/unix/process.c b/src/unix/process.c
new file mode 100644 (file)
index 0000000..ba06d85
--- /dev/null
@@ -0,0 +1,606 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+
+#if defined(__APPLE__) && !TARGET_OS_IPHONE
+# include <crt_externs.h>
+# define environ (*_NSGetEnviron())
+#else
+extern char **environ;
+#endif
+
+#if defined(__linux__) || defined(__GLIBC__)
+# include <grp.h>
+#endif
+
+// { TUV_CHANGES@20180803:
+//   Made signal build time configurable }
+#if TUV_FEATURE_PROCESS
+static void uv__chld(uv_signal_t* handle, int signum) {
+  uv_process_t* process;
+  uv_loop_t* loop;
+  int exit_status;
+  int term_signal;
+  int status;
+  pid_t pid;
+  QUEUE pending;
+  QUEUE* q;
+  QUEUE* h;
+
+  assert(signum == SIGCHLD);
+
+  QUEUE_INIT(&pending);
+  loop = handle->loop;
+
+  h = &loop->process_handles;
+  q = QUEUE_HEAD(h);
+  while (q != h) {
+    process = QUEUE_DATA(q, uv_process_t, queue);
+    q = QUEUE_NEXT(q);
+
+    do
+      pid = waitpid(process->pid, &status, WNOHANG);
+    while (pid == -1 && errno == EINTR);
+
+    if (pid == 0)
+      continue;
+
+    if (pid == -1) {
+      if (errno != ECHILD)
+        abort();
+      continue;
+    }
+
+    process->status = status;
+    QUEUE_REMOVE(&process->queue);
+    QUEUE_INSERT_TAIL(&pending, &process->queue);
+  }
+
+  h = &pending;
+  q = QUEUE_HEAD(h);
+  while (q != h) {
+    process = QUEUE_DATA(q, uv_process_t, queue);
+    q = QUEUE_NEXT(q);
+
+    QUEUE_REMOVE(&process->queue);
+    QUEUE_INIT(&process->queue);
+    uv__handle_stop(process);
+
+    if (process->exit_cb == NULL)
+      continue;
+
+    exit_status = 0;
+    if (WIFEXITED(process->status))
+      exit_status = WEXITSTATUS(process->status);
+
+    term_signal = 0;
+    if (WIFSIGNALED(process->status))
+      term_signal = WTERMSIG(process->status);
+
+    process->exit_cb(process, exit_status, term_signal);
+  }
+  assert(QUEUE_EMPTY(&pending));
+}
+
+
+int uv__make_socketpair(int fds[2], int flags) {
+#if defined(__linux__)
+  static int no_cloexec;
+
+  if (no_cloexec)
+    goto skip;
+
+  if (socketpair(AF_UNIX, SOCK_STREAM | UV__SOCK_CLOEXEC | flags, 0, fds) == 0)
+    return 0;
+
+  /* Retry on EINVAL, it means SOCK_CLOEXEC is not supported.
+   * Anything else is a genuine error.
+   */
+  if (errno != EINVAL)
+    return -errno;
+
+  no_cloexec = 1;
+
+skip:
+#endif
+
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
+    return -errno;
+
+  uv__cloexec(fds[0], 1);
+  uv__cloexec(fds[1], 1);
+
+  if (flags & UV__F_NONBLOCK) {
+    uv__nonblock(fds[0], 1);
+    uv__nonblock(fds[1], 1);
+  }
+
+  return 0;
+}
+#endif /* TUV_FEATURE_PROCESS */
+
+int uv__make_pipe(int fds[2], int flags) {
+#if defined(__linux__)
+  static int no_pipe2;
+
+  if (no_pipe2)
+    goto skip;
+
+  if (uv__pipe2(fds, flags | UV__O_CLOEXEC) == 0)
+    return 0;
+
+  if (errno != ENOSYS)
+    return -errno;
+
+  no_pipe2 = 1;
+
+skip:
+#endif
+
+  if (pipe(fds))
+    return -errno;
+
+  uv__cloexec(fds[0], 1);
+  uv__cloexec(fds[1], 1);
+
+  if (flags & UV__F_NONBLOCK) {
+    uv__nonblock(fds[0], 1);
+    uv__nonblock(fds[1], 1);
+  }
+
+  return 0;
+}
+
+
+// { TUV_CHANGES@20180803:
+//   Made signal build time configurable }
+#if TUV_FEATURE_PROCESS
+/*
+ * Used for initializing stdio streams like options.stdin_stream. Returns
+ * zero on success. See also the cleanup section in uv_spawn().
+ */
+static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
+  int mask;
+  int fd;
+
+  mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
+
+  switch (container->flags & mask) {
+  case UV_IGNORE:
+    return 0;
+
+  case UV_CREATE_PIPE:
+    assert(container->data.stream != NULL);
+    if (container->data.stream->type != UV_NAMED_PIPE)
+      return -EINVAL;
+    else
+      return uv__make_socketpair(fds, 0);
+
+  case UV_INHERIT_FD:
+  case UV_INHERIT_STREAM:
+    if (container->flags & UV_INHERIT_FD)
+      fd = container->data.fd;
+    else
+      fd = uv__stream_fd(container->data.stream);
+
+    if (fd == -1)
+      return -EINVAL;
+
+    fds[1] = fd;
+    return 0;
+
+  default:
+    assert(0 && "Unexpected flags");
+    return -EINVAL;
+  }
+}
+
+
+static int uv__process_open_stream(uv_stdio_container_t* container,
+                                   int pipefds[2],
+                                   int writable) {
+  int flags;
+  int err;
+
+  if (!(container->flags & UV_CREATE_PIPE) || pipefds[0] < 0)
+    return 0;
+
+  err = uv__close(pipefds[1]);
+  if (err != 0)
+    abort();
+
+  pipefds[1] = -1;
+  uv__nonblock(pipefds[0], 1);
+
+  if (container->data.stream->type == UV_NAMED_PIPE &&
+      ((uv_pipe_t*)container->data.stream)->ipc)
+    flags = UV_STREAM_READABLE | UV_STREAM_WRITABLE;
+  else if (writable)
+    flags = UV_STREAM_WRITABLE;
+  else
+    flags = UV_STREAM_READABLE;
+
+  return uv__stream_open(container->data.stream, pipefds[0], flags);
+}
+
+
+static void uv__process_close_stream(uv_stdio_container_t* container) {
+  if (!(container->flags & UV_CREATE_PIPE)) return;
+  uv__stream_close((uv_stream_t*)container->data.stream);
+}
+
+
+static void uv__write_int(int fd, int val) {
+  ssize_t n;
+
+  do
+    n = write(fd, &val, sizeof(val));
+  while (n == -1 && errno == EINTR);
+
+  if (n == -1 && errno == EPIPE)
+    return; /* parent process has quit */
+
+  assert(n == sizeof(val));
+}
+
+
+#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH))
+/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be
+ * avoided. Since this isn't called on those targets, the function
+ * doesn't even need to be defined for them.
+ */
+static void uv__process_child_init(const uv_process_options_t* options,
+                                   int stdio_count,
+                                   int (*pipes)[2],
+                                   int error_fd) {
+  int close_fd;
+  int use_fd;
+  int fd;
+
+  if (options->flags & UV_PROCESS_DETACHED)
+    setsid();
+
+  /* First duplicate low numbered fds, since it's not safe to duplicate them,
+   * they could get replaced. Example: swapping stdout and stderr; without
+   * this fd 2 (stderr) would be duplicated into fd 1, thus making both
+   * stdout and stderr go to the same fd, which was not the intention. */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < 0 || use_fd >= fd)
+      continue;
+    pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count);
+    if (pipes[fd][1] == -1) {
+      uv__write_int(error_fd, -errno);
+      _exit(127);
+    }
+  }
+
+  for (fd = 0; fd < stdio_count; fd++) {
+    close_fd = pipes[fd][0];
+    use_fd = pipes[fd][1];
+
+    if (use_fd < 0) {
+      if (fd >= 3)
+        continue;
+      else {
+        /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is
+         * set
+         */
+        use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR);
+        close_fd = use_fd;
+
+        if (use_fd == -1) {
+          uv__write_int(error_fd, -errno);
+          _exit(127);
+        }
+      }
+    }
+
+    if (fd == use_fd)
+      uv__cloexec(use_fd, 0);
+    else
+      fd = dup2(use_fd, fd);
+
+    if (fd == -1) {
+      uv__write_int(error_fd, -errno);
+      _exit(127);
+    }
+
+    if (fd <= 2)
+      uv__nonblock(fd, 0);
+
+    if (close_fd >= stdio_count)
+      uv__close(close_fd);
+  }
+
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+
+    if (use_fd >= stdio_count)
+      uv__close(use_fd);
+  }
+
+  if (options->cwd != NULL && chdir(options->cwd)) {
+    uv__write_int(error_fd, -errno);
+    _exit(127);
+  }
+
+  if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) {
+    /* When dropping privileges from root, the `setgroups` call will
+     * remove any extraneous groups. If we don't call this, then
+     * even though our uid has dropped, we may still have groups
+     * that enable us to do super-user things. This will fail if we
+     * aren't root, so don't bother checking the return value, this
+     * is just done as an optimistic privilege dropping function.
+     */
+    SAVE_ERRNO(setgroups(0, NULL));
+  }
+
+  if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) {
+    uv__write_int(error_fd, -errno);
+    _exit(127);
+  }
+
+  if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) {
+    uv__write_int(error_fd, -errno);
+    _exit(127);
+  }
+
+  if (options->env != NULL) {
+    environ = options->env;
+  }
+
+  execvp(options->file, options->args);
+  uv__write_int(error_fd, -errno);
+  _exit(127);
+}
+#endif
+
+
+int uv_spawn(uv_loop_t* loop,
+             uv_process_t* process,
+             const uv_process_options_t* options) {
+#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)
+  /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */
+  return -ENOSYS;
+#else
+  int signal_pipe[2] = { -1, -1 };
+  int (*pipes)[2];
+  int stdio_count;
+  ssize_t r;
+  pid_t pid;
+  int err;
+  int exec_errorno;
+  int i;
+  int status;
+
+  assert(options->file != NULL);
+  assert(!(options->flags & ~(UV_PROCESS_DETACHED |
+                              UV_PROCESS_SETGID |
+                              UV_PROCESS_SETUID |
+                              UV_PROCESS_WINDOWS_HIDE |
+                              UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
+
+  uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
+  QUEUE_INIT(&process->queue);
+
+  stdio_count = options->stdio_count;
+  if (stdio_count < 3)
+    stdio_count = 3;
+
+  err = -ENOMEM;
+  pipes = uv__malloc(stdio_count * sizeof(*pipes));
+  if (pipes == NULL)
+    goto error;
+
+  for (i = 0; i < stdio_count; i++) {
+    pipes[i][0] = -1;
+    pipes[i][1] = -1;
+  }
+
+  for (i = 0; i < options->stdio_count; i++) {
+    err = uv__process_init_stdio(options->stdio + i, pipes[i]);
+    if (err)
+      goto error;
+  }
+
+  /* This pipe is used by the parent to wait until
+   * the child has called `execve()`. We need this
+   * to avoid the following race condition:
+   *
+   *    if ((pid = fork()) > 0) {
+   *      kill(pid, SIGTERM);
+   *    }
+   *    else if (pid == 0) {
+   *      execve("/bin/cat", argp, envp);
+   *    }
+   *
+   * The parent sends a signal immediately after forking.
+   * Since the child may not have called `execve()` yet,
+   * there is no telling what process receives the signal,
+   * our fork or /bin/cat.
+   *
+   * To avoid ambiguity, we create a pipe with both ends
+   * marked close-on-exec. Then, after the call to `fork()`,
+   * the parent polls the read end until it EOFs or errors with EPIPE.
+   */
+  err = uv__make_pipe(signal_pipe, 0);
+  if (err)
+    goto error;
+
+  uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
+
+  /* Acquire write lock to prevent opening new fds in worker threads */
+  uv_rwlock_wrlock(&loop->cloexec_lock);
+  pid = fork();
+
+  if (pid == -1) {
+    err = -errno;
+    uv_rwlock_wrunlock(&loop->cloexec_lock);
+    uv__close(signal_pipe[0]);
+    uv__close(signal_pipe[1]);
+    goto error;
+  }
+
+  if (pid == 0) {
+    uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
+    abort();
+  }
+
+  /* Release lock in parent process */
+  uv_rwlock_wrunlock(&loop->cloexec_lock);
+  uv__close(signal_pipe[1]);
+
+  process->status = 0;
+  exec_errorno = 0;
+  do
+    r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
+  while (r == -1 && errno == EINTR);
+
+  if (r == 0)
+    ; /* okay, EOF */
+  else if (r == sizeof(exec_errorno)) {
+    do
+      err = waitpid(pid, &status, 0); /* okay, read errorno */
+    while (err == -1 && errno == EINTR);
+    assert(err == pid);
+  } else if (r == -1 && errno == EPIPE) {
+    do
+      err = waitpid(pid, &status, 0); /* okay, got EPIPE */
+    while (err == -1 && errno == EINTR);
+    assert(err == pid);
+  } else
+    abort();
+
+  uv__close_nocheckstdio(signal_pipe[0]);
+
+  for (i = 0; i < options->stdio_count; i++) {
+    err = uv__process_open_stream(options->stdio + i, pipes[i], i == 0);
+    if (err == 0)
+      continue;
+
+    while (i--)
+      uv__process_close_stream(options->stdio + i);
+
+    goto error;
+  }
+
+  /* Only activate this handle if exec() happened successfully */
+  if (exec_errorno == 0) {
+    QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
+    uv__handle_start(process);
+  }
+
+  process->pid = pid;
+  process->exit_cb = options->exit_cb;
+
+  uv__free(pipes);
+  return exec_errorno;
+
+error:
+  if (pipes != NULL) {
+    for (i = 0; i < stdio_count; i++) {
+      if (i < options->stdio_count)
+        if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM))
+          continue;
+      if (pipes[i][0] != -1)
+        uv__close_nocheckstdio(pipes[i][0]);
+      if (pipes[i][1] != -1)
+        uv__close_nocheckstdio(pipes[i][1]);
+    }
+    uv__free(pipes);
+  }
+
+  return err;
+#endif
+}
+
+
+int uv_process_kill(uv_process_t* process, int signum) {
+  return uv_kill(process->pid, signum);
+}
+
+
+int uv_kill(int pid, int signum) {
+  if (kill(pid, signum))
+    return -errno;
+  else
+    return 0;
+}
+
+
+void uv__process_close(uv_process_t* handle) {
+  QUEUE_REMOVE(&handle->queue);
+  uv__handle_stop(handle);
+  if (QUEUE_EMPTY(&handle->loop->process_handles))
+    uv_signal_stop(&handle->loop->child_watcher);
+}
+
+#endif /* TUV_FEATURE_PROCESS */
+
+#else /* defined(__NUTTX__) || defined(__TIZENRT__) */
+
+int uv__make_pipe(int fds[2], int flags) {
+  if (pipe(fds))
+    return -errno;
+
+  uv__cloexec(fds[0], 1);
+  uv__cloexec(fds[1], 1);
+
+  if (flags & UV__F_NONBLOCK) {
+    uv__nonblock(fds[0], 1);
+    uv__nonblock(fds[1], 1);
+  }
+
+  return 0;
+}
+
+#endif /* !defined(__NUTTX__) && !defined(__TIZENRT__) */
diff --git a/src/unix/proctitle.c b/src/unix/proctitle.c
new file mode 100644 (file)
index 0000000..08d875f
--- /dev/null
@@ -0,0 +1,105 @@
+/* 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"
+
+#include <stdlib.h>
+#include <string.h>
+
+extern void uv__set_process_title(const char* title);
+
+static void* args_mem;
+
+static struct {
+  char* str;
+  size_t len;
+} process_title;
+
+
+char** uv_setup_args(int argc, char** argv) {
+  char** new_argv;
+  size_t size;
+  char* s;
+  int i;
+
+  if (argc <= 0)
+    return argv;
+
+  /* Calculate how much memory we need for the argv strings. */
+  size = 0;
+  for (i = 0; i < argc; i++)
+    size += strlen(argv[i]) + 1;
+
+  process_title.str = argv[0];
+  process_title.len = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0];
+  assert(process_title.len + 1 == size);  /* argv memory should be adjacent. */
+
+  /* Add space for the argv pointers. */
+  size += (argc + 1) * sizeof(char*);
+
+  new_argv = uv__malloc(size);
+  if (new_argv == NULL)
+    return argv;
+  args_mem = new_argv;
+
+  /* Copy over the strings and set up the pointer table. */
+  s = (char*) &new_argv[argc + 1];
+  for (i = 0; i < argc; i++) {
+    size = strlen(argv[i]) + 1;
+    memcpy(s, argv[i], size);
+    new_argv[i] = s;
+    s += size;
+  }
+  new_argv[i] = NULL;
+
+  return new_argv;
+}
+
+
+int uv_set_process_title(const char* title) {
+  if (process_title.len == 0)
+    return 0;
+
+  /* No need to terminate, byte after is always '\0'. */
+  strncpy(process_title.str, title, process_title.len);
+  uv__set_process_title(title);
+
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  if (buffer == NULL || size == 0)
+    return -EINVAL;
+  else if (size <= process_title.len)
+    return -ENOBUFS;
+
+  memcpy(buffer, process_title.str, process_title.len + 1);
+  buffer[process_title.len] = '\0';
+
+  return 0;
+}
+
+
+UV_DESTRUCTOR(static void free_args_mem(void)) {
+  uv__free(args_mem);  /* Keep valgrind happy. */
+  args_mem = NULL;
+}
diff --git a/src/unix/pthread-barrier.c b/src/unix/pthread-barrier.c
new file mode 100644 (file)
index 0000000..f57bf25
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+Copyright (c) 2016, Kari Tristan Helgason <kthelgason@gmail.com>
+
+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.
+*/
+#include "uv-common.h"
+#include "pthread-barrier.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+/* TODO: support barrier_attr */
+int pthread_barrier_init(pthread_barrier_t* barrier,
+                         const void* barrier_attr,
+                         unsigned count) {
+  int rc;
+  _uv_barrier* b;
+
+  if (barrier == NULL || count == 0)
+    return EINVAL;
+
+  if (barrier_attr != NULL)
+    return ENOTSUP;
+
+  b = uv__malloc(sizeof(*b));
+  if (b == NULL)
+    return ENOMEM;
+
+  b->in = 0;
+  b->out = 0;
+  b->threshold = count;
+
+  if ((rc = pthread_mutex_init(&b->mutex, NULL)) != 0)
+    goto error2;
+  if ((rc = pthread_cond_init(&b->cond, NULL)) != 0)
+    goto error;
+
+  barrier->b = b;
+  return 0;
+
+error:
+  pthread_mutex_destroy(&b->mutex);
+error2:
+  uv__free(b);
+  return rc;
+}
+
+int pthread_barrier_wait(pthread_barrier_t* barrier) {
+  int rc;
+  _uv_barrier* b;
+
+  if (barrier == NULL || barrier->b == NULL)
+    return EINVAL;
+
+  b = barrier->b;
+  /* Lock the mutex*/
+  if ((rc = pthread_mutex_lock(&b->mutex)) != 0)
+    return rc;
+
+  /* Increment the count. If this is the first thread to reach the threshold,
+     wake up waiters, unlock the mutex, then return
+     PTHREAD_BARRIER_SERIAL_THREAD. */
+  if (++b->in == b->threshold) {
+    b->in = 0;
+    b->out = b->threshold - 1;
+    assert(pthread_cond_signal(&b->cond) == 0);
+
+    pthread_mutex_unlock(&b->mutex);
+    return PTHREAD_BARRIER_SERIAL_THREAD;
+  }
+  /* Otherwise, wait for other threads until in is set to 0,
+     then return 0 to indicate this is not the first thread. */
+  do {
+    if ((rc = pthread_cond_wait(&b->cond, &b->mutex)) != 0)
+      break;
+  } while (b->in != 0);
+
+  /* mark thread exit */
+  b->out--;
+  pthread_cond_signal(&b->cond);
+  pthread_mutex_unlock(&b->mutex);
+  return rc;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t* barrier) {
+  int rc;
+  _uv_barrier* b;
+
+  if (barrier == NULL || barrier->b == NULL)
+    return EINVAL;
+
+  b = barrier->b;
+
+  if ((rc = pthread_mutex_lock(&b->mutex)) != 0)
+    return rc;
+
+  if (b->in > 0 || b->out > 0)
+    rc = EBUSY;
+
+  pthread_mutex_unlock(&b->mutex);
+
+  if (rc)
+    return rc;
+
+  pthread_cond_destroy(&b->cond);
+  pthread_mutex_destroy(&b->mutex);
+  uv__free(barrier->b);
+  barrier->b = NULL;
+  return 0;
+}
diff --git a/src/unix/pthread-fixes.c b/src/unix/pthread-fixes.c
new file mode 100644 (file)
index 0000000..fb17995
--- /dev/null
@@ -0,0 +1,56 @@
+/* Copyright (c) 2013, Sony Mobile Communications AB
+ * Copyright (c) 2012, Google Inc.
+   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 Google Inc. nor the names of its
+   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.
+*/
+
+/* Android versions < 4.1 have a broken pthread_sigmask. */
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+
+int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) {
+  static int workaround;
+  int err;
+
+  if (workaround) {
+    return sigprocmask(how, set, oset);
+  } else {
+    err = pthread_sigmask(how, set, oset);
+    if (err) {
+      if (err == EINVAL && sigprocmask(how, set, oset) == 0) {
+        workaround = 1;
+        return 0;
+      } else {
+        return -1;
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/src/unix/signal.c b/src/unix/signal.c
new file mode 100644 (file)
index 0000000..dbd8f86
--- /dev/null
@@ -0,0 +1,467 @@
+/* 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"
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+typedef struct {
+  uv_signal_t* handle;
+  int signum;
+} uv__signal_msg_t;
+
+RB_HEAD(uv__signal_tree_s, uv_signal_s);
+
+
+static int uv__signal_unlock(void);
+static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2);
+static void uv__signal_stop(uv_signal_t* handle);
+
+
+static uv_once_t uv__signal_global_init_guard = UV_ONCE_INIT;
+static struct uv__signal_tree_s uv__signal_tree =
+    RB_INITIALIZER(uv__signal_tree);
+static int uv__signal_lock_pipefd[2];
+
+
+RB_GENERATE_STATIC(uv__signal_tree_s,
+                   uv_signal_s, tree_entry,
+                   uv__signal_compare)
+
+
+static void uv__signal_global_init(void) {
+  if (uv__make_pipe(uv__signal_lock_pipefd, 0))
+    abort();
+
+  if (uv__signal_unlock())
+    abort();
+}
+
+
+void uv__signal_global_once_init(void) {
+  uv_once(&uv__signal_global_init_guard, uv__signal_global_init);
+}
+
+
+
+static int uv__signal_lock(void) {
+  int r;
+  char data;
+
+  do {
+    r = read(uv__signal_lock_pipefd[0], &data, sizeof data);
+  } while (r < 0 && errno == EINTR);
+
+  return (r < 0) ? -1 : 0;
+}
+
+
+static int uv__signal_unlock(void) {
+  int r;
+  char data = 42;
+
+  do {
+    r = write(uv__signal_lock_pipefd[1], &data, sizeof data);
+  } while (r < 0 && errno == EINTR);
+
+  return (r < 0) ? -1 : 0;
+}
+
+
+static void uv__signal_block_and_lock(sigset_t* saved_sigmask) {
+  sigset_t new_mask;
+
+  if (sigfillset(&new_mask))
+    abort();
+
+  if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask))
+    abort();
+
+  if (uv__signal_lock())
+    abort();
+}
+
+
+static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) {
+  if (uv__signal_unlock())
+    abort();
+
+  if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL))
+    abort();
+}
+
+
+static uv_signal_t* uv__signal_first_handle(int signum) {
+  /* This function must be called with the signal lock held. */
+  uv_signal_t lookup;
+  uv_signal_t* handle;
+
+  lookup.signum = signum;
+  lookup.loop = NULL;
+
+  handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup);
+
+  if (handle != NULL && handle->signum == signum)
+    return handle;
+
+  return NULL;
+}
+
+
+static void uv__signal_handler(int signum) {
+  uv__signal_msg_t msg;
+  uv_signal_t* handle;
+  int saved_errno;
+
+  saved_errno = errno;
+  memset(&msg, 0, sizeof msg);
+
+  if (uv__signal_lock()) {
+    errno = saved_errno;
+    return;
+  }
+
+  for (handle = uv__signal_first_handle(signum);
+       handle != NULL && handle->signum == signum;
+       handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
+    int r;
+
+    msg.signum = signum;
+    msg.handle = handle;
+
+    /* write() should be atomic for small data chunks, so the entire message
+     * should be written at once. In theory the pipe could become full, in
+     * which case the user is out of luck.
+     */
+    do {
+      r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg);
+    } while (r == -1 && errno == EINTR);
+
+    assert(r == sizeof msg ||
+           (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)));
+
+    if (r != -1)
+      handle->caught_signals++;
+  }
+
+  uv__signal_unlock();
+  errno = saved_errno;
+}
+
+
+static int uv__signal_register_handler(int signum) {
+  /* When this function is called, the signal lock must be held. */
+  struct sigaction sa;
+
+  /* XXX use a separate signal stack? */
+  memset(&sa, 0, sizeof(sa));
+  if (sigfillset(&sa.sa_mask))
+    abort();
+  sa.sa_handler = uv__signal_handler;
+
+  /* XXX save old action so we can restore it later on? */
+  if (sigaction(signum, &sa, NULL))
+    return -errno;
+
+  return 0;
+}
+
+
+static void uv__signal_unregister_handler(int signum) {
+  /* When this function is called, the signal lock must be held. */
+  struct sigaction sa;
+
+  memset(&sa, 0, sizeof(sa));
+  sa.sa_handler = SIG_DFL;
+
+  /* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a
+   * signal implies that it was successfully registered earlier, so EINVAL
+   * should never happen.
+   */
+  if (sigaction(signum, &sa, NULL))
+    abort();
+}
+
+
+static int uv__signal_loop_once_init(uv_loop_t* loop) {
+  int err;
+
+  /* Return if already initialized. */
+  if (loop->signal_pipefd[0] != -1)
+    return 0;
+
+  err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK);
+  if (err)
+    return err;
+
+  uv__io_init(&loop->signal_io_watcher,
+              uv__signal_event,
+              loop->signal_pipefd[0]);
+  uv__io_start(loop, &loop->signal_io_watcher, POLLIN);
+
+  return 0;
+}
+
+
+void uv__signal_loop_cleanup(uv_loop_t* loop) {
+  QUEUE* q;
+
+  /* Stop all the signal watchers that are still attached to this loop. This
+   * ensures that the (shared) signal tree doesn't contain any invalid entries
+   * entries, and that signal handlers are removed when appropriate.
+   * It's safe to use QUEUE_FOREACH here because the handles and the handle
+   * queue are not modified by uv__signal_stop().
+   */
+  QUEUE_FOREACH(q, &loop->handle_queue) {
+    uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue);
+
+    if (handle->type == UV_SIGNAL)
+      uv__signal_stop((uv_signal_t*) handle);
+  }
+
+  if (loop->signal_pipefd[0] != -1) {
+    uv__close(loop->signal_pipefd[0]);
+    loop->signal_pipefd[0] = -1;
+  }
+
+  if (loop->signal_pipefd[1] != -1) {
+    uv__close(loop->signal_pipefd[1]);
+    loop->signal_pipefd[1] = -1;
+  }
+}
+
+
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+  int err;
+
+  err = uv__signal_loop_once_init(loop);
+  if (err)
+    return err;
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
+  handle->signum = 0;
+  handle->caught_signals = 0;
+  handle->dispatched_signals = 0;
+
+  return 0;
+}
+
+
+void uv__signal_close(uv_signal_t* handle) {
+
+  uv__signal_stop(handle);
+
+  /* If there are any caught signals "trapped" in the signal pipe, we can't
+   * call the close callback yet. Otherwise, add the handle to the finish_close
+   * queue.
+   */
+  if (handle->caught_signals == handle->dispatched_signals) {
+    uv__make_close_pending((uv_handle_t*) handle);
+  }
+}
+
+
+int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
+  sigset_t saved_sigmask;
+  int err;
+
+  assert(!uv__is_closing(handle));
+
+  /* If the user supplies signum == 0, then return an error already. If the
+   * signum is otherwise invalid then uv__signal_register will find out
+   * eventually.
+   */
+  if (signum == 0)
+    return -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
+   * 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) {
+    uv__signal_stop(handle);
+  }
+
+  uv__signal_block_and_lock(&saved_sigmask);
+
+  /* If at this point there are no active signal watchers for this signum (in
+   * any of the loops), it's time to try and register a handler for it here.
+   */
+  if (uv__signal_first_handle(signum) == NULL) {
+    err = uv__signal_register_handler(signum);
+    if (err) {
+      /* Registering the signal handler failed. Must be an invalid signal. */
+      uv__signal_unlock_and_unblock(&saved_sigmask);
+      return err;
+    }
+  }
+
+  handle->signum = signum;
+  RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle);
+
+  uv__signal_unlock_and_unblock(&saved_sigmask);
+
+  handle->signal_cb = signal_cb;
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+static void uv__signal_event(uv_loop_t* loop,
+                             uv__io_t* w,
+                             unsigned int events) {
+  uv__signal_msg_t* msg;
+  uv_signal_t* handle;
+  char buf[sizeof(uv__signal_msg_t) * 32];
+  size_t bytes, end, i;
+  int r;
+
+  bytes = 0;
+  end = 0;
+
+  do {
+    r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes);
+
+    if (r == -1 && errno == EINTR)
+      continue;
+
+    if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+      /* If there are bytes in the buffer already (which really is extremely
+       * unlikely if possible at all) we can't exit the function here. We'll
+       * spin until more bytes are read instead.
+       */
+      if (bytes > 0)
+        continue;
+
+      /* Otherwise, there was nothing there. */
+      return;
+    }
+
+    /* Other errors really should never happen. */
+    if (r == -1)
+      abort();
+
+    bytes += r;
+
+    /* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */
+    end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t);
+
+    for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) {
+      msg = (uv__signal_msg_t*) (buf + i);
+      handle = msg->handle;
+
+      if (msg->signum == handle->signum) {
+        assert(!(handle->flags & UV_CLOSING));
+        handle->signal_cb(handle, handle->signum);
+      }
+
+      handle->dispatched_signals++;
+
+      /* If uv_close was called while there were caught signals that were not
+       * yet dispatched, the uv__finish_close was deferred. Make close pending
+       * now if this has happened.
+       */
+      if ((handle->flags & UV_CLOSING) &&
+          (handle->caught_signals == handle->dispatched_signals)) {
+        uv__make_close_pending((uv_handle_t*) handle);
+      }
+    }
+
+    bytes -= end;
+
+    /* If there are any "partial" messages left, move them to the start of the
+     * the buffer, and spin. This should not happen.
+     */
+    if (bytes) {
+      memmove(buf, buf + end, bytes);
+      continue;
+    }
+  } while (end == sizeof buf);
+}
+
+
+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 (w1->loop < w2->loop) return -1;
+  if (w1->loop > w2->loop) return 1;
+
+  if (w1 < w2) return -1;
+  if (w1 > w2) return 1;
+
+  return 0;
+}
+
+
+int uv_signal_stop(uv_signal_t* handle) {
+  assert(!uv__is_closing(handle));
+  uv__signal_stop(handle);
+  return 0;
+}
+
+
+static void uv__signal_stop(uv_signal_t* handle) {
+  uv_signal_t* removed_handle;
+  sigset_t saved_sigmask;
+
+  /* If the watcher wasn't started, this is a no-op. */
+  if (handle->signum == 0)
+    return;
+
+  uv__signal_block_and_lock(&saved_sigmask);
+
+  removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle);
+  assert(removed_handle == handle);
+  (void) removed_handle;
+
+  /* Check if there are other active signal watchers observing this signal. If
+   * not, unregister the signal handler.
+   */
+  if (uv__signal_first_handle(handle->signum) == NULL)
+    uv__signal_unregister_handler(handle->signum);
+
+  uv__signal_unlock_and_unblock(&saved_sigmask);
+
+  handle->signum = 0;
+  uv__handle_stop(handle);
+}
diff --git a/src/unix/spinlock.h b/src/unix/spinlock.h
new file mode 100644 (file)
index 0000000..a20c83c
--- /dev/null
@@ -0,0 +1,53 @@
+/* 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_SPINLOCK_H_
+#define UV_SPINLOCK_H_
+
+#include "internal.h"  /* ACCESS_ONCE, UV_UNUSED */
+#include "atomic-ops.h"
+
+#define UV_SPINLOCK_INITIALIZER { 0 }
+
+typedef struct {
+  int lock;
+} uv_spinlock_t;
+
+UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock));
+UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock));
+UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock));
+UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock));
+
+UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) {
+  ACCESS_ONCE(int, spinlock->lock) = 0;
+}
+
+UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) {
+  while (!uv_spinlock_trylock(spinlock)) cpu_relax();
+}
+
+UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) {
+  ACCESS_ONCE(int, spinlock->lock) = 0;
+}
+
+UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) {
+  /* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing.
+   * Not really critical until we have locks that are (frequently) contended
+   * for by several threads.
+   */
+  return 0 == cmpxchgi(&spinlock->lock, 0, 1);
+}
+
+#endif  /* UV_SPINLOCK_H_ */
diff --git a/src/unix/stream.c b/src/unix/stream.c
new file mode 100644 (file)
index 0000000..f09abae
--- /dev/null
@@ -0,0 +1,1692 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#if !defined(__TIZENRT__)
+# include <sys/uio.h>
+#endif
+
+#include <sys/un.h>
+#include <unistd.h>
+#include <limits.h> /* IOV_MAX */
+
+#if defined(__APPLE__)
+# include <sys/event.h>
+# include <sys/time.h>
+# include <sys/select.h>
+
+/* Forward declaration */
+typedef struct uv__stream_select_s uv__stream_select_t;
+
+struct uv__stream_select_s {
+  uv_stream_t* stream;
+  uv_thread_t thread;
+  uv_sem_t close_sem;
+  uv_sem_t async_sem;
+  uv_async_t async;
+  int events;
+  int fake_fd;
+  int int_fd;
+  int fd;
+  fd_set* sread;
+  size_t sread_sz;
+  fd_set* swrite;
+  size_t swrite_sz;
+};
+#endif /* defined(__APPLE__) */
+
+static void uv__stream_connect(uv_stream_t*);
+static void uv__write(uv_stream_t* stream);
+static void uv__read(uv_stream_t* stream);
+static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
+static void uv__write_callbacks(uv_stream_t* stream);
+static size_t uv__write_req_size(uv_write_t* req);
+
+
+void uv__stream_init(uv_loop_t* loop,
+                     uv_stream_t* stream,
+                     uv_handle_type type) {
+  int err;
+
+  uv__handle_init(loop, (uv_handle_t*)stream, type);
+  stream->read_cb = NULL;
+  stream->alloc_cb = NULL;
+  stream->close_cb = NULL;
+  stream->connection_cb = NULL;
+  stream->connect_req = NULL;
+  stream->shutdown_req = NULL;
+  stream->accepted_fd = -1;
+  stream->queued_fds = NULL;
+  stream->delayed_error = 0;
+  QUEUE_INIT(&stream->write_queue);
+  QUEUE_INIT(&stream->write_completed_queue);
+  stream->write_queue_size = 0;
+
+  if (loop->emfile_fd == -1) {
+    err = uv__open_cloexec("/dev/null", O_RDONLY);
+    if (err < 0)
+        /* In the rare case that "/dev/null" isn't mounted open "/"
+         * instead.
+         */
+        err = uv__open_cloexec("/", O_RDONLY);
+    if (err >= 0)
+      loop->emfile_fd = err;
+  }
+
+#if defined(__APPLE__)
+  stream->select = NULL;
+#endif /* defined(__APPLE_) */
+
+  uv__io_init(&stream->io_watcher, uv__stream_io, -1);
+}
+
+
+static void uv__stream_osx_interrupt_select(uv_stream_t* stream) {
+#if defined(__APPLE__)
+  /* Notify select() thread about state change */
+  uv__stream_select_t* s;
+  int r;
+
+  s = stream->select;
+  if (s == NULL)
+    return;
+
+  /* Interrupt select() loop
+   * NOTE: fake_fd and int_fd are socketpair(), thus writing to one will
+   * emit read event on other side
+   */
+  do
+    r = write(s->fake_fd, "x", 1);
+  while (r == -1 && errno == EINTR);
+
+  assert(r == 1);
+#else  /* !defined(__APPLE__) */
+  /* No-op on any other platform */
+#endif  /* !defined(__APPLE__) */
+}
+
+
+#if defined(__APPLE__)
+static void uv__stream_osx_select(void* arg) {
+  uv_stream_t* stream;
+  uv__stream_select_t* s;
+  char buf[1024];
+  int events;
+  int fd;
+  int r;
+  int max_fd;
+
+  stream = arg;
+  s = stream->select;
+  fd = s->fd;
+
+  if (fd > s->int_fd)
+    max_fd = fd;
+  else
+    max_fd = s->int_fd;
+
+  while (1) {
+    /* Terminate on semaphore */
+    if (uv_sem_trywait(&s->close_sem) == 0)
+      break;
+
+    /* Watch fd using select(2) */
+    memset(s->sread, 0, s->sread_sz);
+    memset(s->swrite, 0, s->swrite_sz);
+
+    if (uv__io_active(&stream->io_watcher, POLLIN))
+      FD_SET(fd, s->sread);
+    if (uv__io_active(&stream->io_watcher, POLLOUT))
+      FD_SET(fd, s->swrite);
+    FD_SET(s->int_fd, s->sread);
+
+    /* Wait indefinitely for fd events */
+    r = select(max_fd + 1, s->sread, s->swrite, NULL, NULL);
+    if (r == -1) {
+      if (errno == EINTR)
+        continue;
+
+      /* XXX: Possible?! */
+      abort();
+    }
+
+    /* Ignore timeouts */
+    if (r == 0)
+      continue;
+
+    /* Empty socketpair's buffer in case of interruption */
+    if (FD_ISSET(s->int_fd, s->sread))
+      while (1) {
+        r = read(s->int_fd, buf, sizeof(buf));
+
+        if (r == sizeof(buf))
+          continue;
+
+        if (r != -1)
+          break;
+
+        if (errno == EAGAIN || errno == EWOULDBLOCK)
+          break;
+
+        if (errno == EINTR)
+          continue;
+
+        abort();
+      }
+
+    /* Handle events */
+    events = 0;
+    if (FD_ISSET(fd, s->sread))
+      events |= POLLIN;
+    if (FD_ISSET(fd, s->swrite))
+      events |= POLLOUT;
+
+    assert(events != 0 || FD_ISSET(s->int_fd, s->sread));
+    if (events != 0) {
+      ACCESS_ONCE(int, s->events) = events;
+
+      uv_async_send(&s->async);
+      uv_sem_wait(&s->async_sem);
+
+      /* Should be processed at this stage */
+      assert((s->events == 0) || (stream->flags & UV_CLOSING));
+    }
+  }
+}
+
+
+static void uv__stream_osx_select_cb(uv_async_t* handle) {
+  uv__stream_select_t* s;
+  uv_stream_t* stream;
+  int events;
+
+  s = container_of(handle, uv__stream_select_t, async);
+  stream = s->stream;
+
+  /* Get and reset stream's events */
+  events = s->events;
+  ACCESS_ONCE(int, s->events) = 0;
+
+  assert(events != 0);
+  assert(events == (events & (POLLIN | POLLOUT)));
+
+  /* Invoke callback on event-loop */
+  if ((events & POLLIN) && uv__io_active(&stream->io_watcher, POLLIN))
+    uv__stream_io(stream->loop, &stream->io_watcher, POLLIN);
+
+  if ((events & POLLOUT) && uv__io_active(&stream->io_watcher, POLLOUT))
+    uv__stream_io(stream->loop, &stream->io_watcher, POLLOUT);
+
+  if (stream->flags & UV_CLOSING)
+    return;
+
+  /* NOTE: It is important to do it here, otherwise `select()` might be called
+   * before the actual `uv__read()`, leading to the blocking syscall
+   */
+  uv_sem_post(&s->async_sem);
+}
+
+
+static void uv__stream_osx_cb_close(uv_handle_t* async) {
+  uv__stream_select_t* s;
+
+  s = container_of(async, uv__stream_select_t, async);
+  uv__free(s);
+}
+
+
+int uv__stream_try_select(uv_stream_t* stream, int* fd) {
+  /*
+   * kqueue doesn't work with some files from /dev mount on osx.
+   * select(2) in separate thread for those fds
+   */
+
+  struct kevent filter[1];
+  struct kevent events[1];
+  struct timespec timeout;
+  uv__stream_select_t* s;
+  int fds[2];
+  int err;
+  int ret;
+  int kq;
+  int old_fd;
+  int max_fd;
+  size_t sread_sz;
+  size_t swrite_sz;
+
+  kq = kqueue();
+  if (kq == -1) {
+    perror("(libuv) kqueue()");
+    return -errno;
+  }
+
+  EV_SET(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+
+  /* Use small timeout, because we only want to capture EINVALs */
+  timeout.tv_sec = 0;
+  timeout.tv_nsec = 1;
+
+  do
+    ret = kevent(kq, filter, 1, events, 1, &timeout);
+  while (ret == -1 && errno == EINTR);
+
+  uv__close(kq);
+
+  if (ret == -1)
+    return -errno;
+
+  if (ret == 0 || (events[0].flags & EV_ERROR) == 0 || events[0].data != EINVAL)
+    return 0;
+
+  /* At this point we definitely know that this fd won't work with kqueue */
+
+  /*
+   * Create fds for io watcher and to interrupt the select() loop.
+   * NOTE: do it ahead of malloc below to allocate enough space for fd_sets
+   */
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
+    return -errno;
+
+  max_fd = *fd;
+  if (fds[1] > max_fd)
+    max_fd = fds[1];
+
+  sread_sz = ROUND_UP(max_fd + 1, sizeof(uint32_t) * NBBY) / NBBY;
+  swrite_sz = sread_sz;
+
+  s = uv__malloc(sizeof(*s) + sread_sz + swrite_sz);
+  if (s == NULL) {
+    err = -ENOMEM;
+    goto failed_malloc;
+  }
+
+  s->events = 0;
+  s->fd = *fd;
+  s->sread = (fd_set*) ((char*) s + sizeof(*s));
+  s->sread_sz = sread_sz;
+  s->swrite = (fd_set*) ((char*) s->sread + sread_sz);
+  s->swrite_sz = swrite_sz;
+
+  err = uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb);
+  if (err)
+    goto failed_async_init;
+
+  s->async.flags |= UV__HANDLE_INTERNAL;
+  uv__handle_unref(&s->async);
+
+  err = uv_sem_init(&s->close_sem, 0);
+  if (err != 0)
+    goto failed_close_sem_init;
+
+  err = uv_sem_init(&s->async_sem, 0);
+  if (err != 0)
+    goto failed_async_sem_init;
+
+  s->fake_fd = fds[0];
+  s->int_fd = fds[1];
+
+  old_fd = *fd;
+  s->stream = stream;
+  stream->select = s;
+  *fd = s->fake_fd;
+
+  err = uv_thread_create(&s->thread, uv__stream_osx_select, stream);
+  if (err != 0)
+    goto failed_thread_create;
+
+  return 0;
+
+failed_thread_create:
+  s->stream = NULL;
+  stream->select = NULL;
+  *fd = old_fd;
+
+  uv_sem_destroy(&s->async_sem);
+
+failed_async_sem_init:
+  uv_sem_destroy(&s->close_sem);
+
+failed_close_sem_init:
+  uv__close(fds[0]);
+  uv__close(fds[1]);
+  uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close);
+  return err;
+
+failed_async_init:
+  uv__free(s);
+
+failed_malloc:
+  uv__close(fds[0]);
+  uv__close(fds[1]);
+
+  return err;
+}
+#endif /* defined(__APPLE__) */
+
+
+int uv__stream_open(uv_stream_t* stream, int fd, int flags) {
+#if defined(__APPLE__)
+  int enable;
+#endif
+
+  if (!(stream->io_watcher.fd == -1 || stream->io_watcher.fd == fd))
+    return -EBUSY;
+
+  assert(fd >= 0);
+  stream->flags |= flags;
+
+  if (stream->type == UV_TCP) {
+    if ((stream->flags & UV_TCP_NODELAY) && uv__tcp_nodelay(fd, 1))
+      return -errno;
+
+    /* TODO Use delay the user passed in. */
+    if ((stream->flags & UV_TCP_KEEPALIVE) && uv__tcp_keepalive(fd, 1, 60))
+      return -errno;
+  }
+
+#if defined(__APPLE__)
+  enable = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) &&
+      errno != ENOTSOCK &&
+      errno != EINVAL) {
+    return -errno;
+  }
+#endif
+
+  stream->io_watcher.fd = fd;
+
+  return 0;
+}
+
+
+void uv__stream_flush_write_queue(uv_stream_t* stream, int error) {
+  uv_write_t* req;
+  QUEUE* q;
+  while (!QUEUE_EMPTY(&stream->write_queue)) {
+    q = QUEUE_HEAD(&stream->write_queue);
+    QUEUE_REMOVE(q);
+
+    req = QUEUE_DATA(q, uv_write_t, queue);
+    req->error = error;
+
+    QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue);
+  }
+}
+
+
+void uv__stream_destroy(uv_stream_t* stream) {
+  assert(!uv__io_active(&stream->io_watcher, POLLIN | POLLOUT));
+  assert(stream->flags & UV_CLOSED);
+
+  if (stream->connect_req) {
+    uv__req_unregister(stream->loop, stream->connect_req);
+    stream->connect_req->cb(stream->connect_req, -ECANCELED);
+    stream->connect_req = NULL;
+  }
+
+  uv__stream_flush_write_queue(stream, -ECANCELED);
+  uv__write_callbacks(stream);
+
+  if (stream->shutdown_req) {
+    /* The ECANCELED error code is a lie, the shutdown(2) syscall is a
+     * fait accompli at this point. Maybe we should revisit this in v0.11.
+     * A possible reason for leaving it unchanged is that it informs the
+     * callee that the handle has been destroyed.
+     */
+    uv__req_unregister(stream->loop, stream->shutdown_req);
+    stream->shutdown_req->cb(stream->shutdown_req, -ECANCELED);
+    stream->shutdown_req = NULL;
+  }
+
+  assert(stream->write_queue_size == 0);
+}
+
+
+/* Implements a best effort approach to mitigating accept() EMFILE errors.
+ * We have a spare file descriptor stashed away that we close to get below
+ * the EMFILE limit. Next, we accept all pending connections and close them
+ * immediately to signal the clients that we're overloaded - and we are, but
+ * we still keep on trucking.
+ *
+ * There is one caveat: it's not reliable in a multi-threaded environment.
+ * The file descriptor limit is per process. Our party trick fails if another
+ * thread opens a file or creates a socket in the time window between us
+ * calling close() and accept().
+ */
+static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) {
+  int err;
+  int emfile_fd;
+
+  if (loop->emfile_fd == -1)
+    return -EMFILE;
+
+  uv__close(loop->emfile_fd);
+  loop->emfile_fd = -1;
+
+  do {
+    err = uv__accept(accept_fd);
+    if (err >= 0)
+      uv__close(err);
+  } while (err >= 0 || err == -EINTR);
+
+  emfile_fd = uv__open_cloexec("/", O_RDONLY);
+  if (emfile_fd >= 0)
+    loop->emfile_fd = emfile_fd;
+
+  return err;
+}
+
+
+#if defined(UV_HAVE_KQUEUE)
+# define UV_DEC_BACKLOG(w) w->rcount--;
+#else
+# define UV_DEC_BACKLOG(w) /* no-op */
+#endif /* defined(UV_HAVE_KQUEUE) */
+
+
+void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  uv_stream_t* stream;
+  int err;
+
+  stream = container_of(w, uv_stream_t, io_watcher);
+  assert(events == POLLIN);
+  assert(stream->accepted_fd == -1);
+  assert(!(stream->flags & UV_CLOSING));
+
+  uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
+
+  /* connection_cb can close the server socket while we're
+   * in the loop so check it on each iteration.
+   */
+  while (uv__stream_fd(stream) != -1) {
+    assert(stream->accepted_fd == -1);
+
+#if defined(UV_HAVE_KQUEUE)
+    if (w->rcount <= 0)
+      return;
+#endif /* defined(UV_HAVE_KQUEUE) */
+
+    err = uv__accept(uv__stream_fd(stream));
+    if (err < 0) {
+      if (err == -EAGAIN || err == -EWOULDBLOCK)
+        return;  /* Not an error. */
+
+      if (err == -ECONNABORTED)
+        continue;  /* Ignore. Nothing we can do about that. */
+
+      if (err == -EMFILE || err == -ENFILE) {
+        err = uv__emfile_trick(loop, uv__stream_fd(stream));
+        if (err == -EAGAIN || err == -EWOULDBLOCK)
+          break;
+      }
+
+      stream->connection_cb(stream, err);
+      continue;
+    }
+
+    UV_DEC_BACKLOG(w)
+    stream->accepted_fd = err;
+    stream->connection_cb(stream, 0);
+
+    if (stream->accepted_fd != -1) {
+      /* The user hasn't yet accepted called uv_accept() */
+      uv__io_stop(loop, &stream->io_watcher, POLLIN);
+      return;
+    }
+
+    if (stream->type == UV_TCP && (stream->flags & UV_TCP_SINGLE_ACCEPT)) {
+      /* Give other processes a chance to accept connections. */
+      struct timespec timeout = { 0, 1 };
+      nanosleep(&timeout, NULL);
+    }
+  }
+}
+
+
+#undef UV_DEC_BACKLOG
+
+
+int uv_accept(uv_stream_t* server, uv_stream_t* client) {
+  int err;
+
+  assert(server->loop == client->loop);
+
+  if (server->accepted_fd == -1)
+    return -EAGAIN;
+
+  switch (client->type) {
+#ifdef TUV_FEATURE_PIPE
+    case UV_NAMED_PIPE:
+#endif
+    case UV_TCP:
+      err = uv__stream_open(client,
+                            server->accepted_fd,
+                            UV_STREAM_READABLE | UV_STREAM_WRITABLE);
+      if (err) {
+        /* TODO handle error */
+        uv__close(server->accepted_fd);
+        goto done;
+      }
+      break;
+
+    case UV_UDP:
+      err = uv_udp_open((uv_udp_t*) client, server->accepted_fd);
+      if (err) {
+        uv__close(server->accepted_fd);
+        goto done;
+      }
+      break;
+
+    default:
+      return -EINVAL;
+  }
+
+  client->flags |= UV_HANDLE_BOUND;
+
+done:
+  /* Process queued fds */
+  if (server->queued_fds != NULL) {
+    uv__stream_queued_fds_t* queued_fds;
+
+    queued_fds = server->queued_fds;
+
+    /* Read first */
+    server->accepted_fd = queued_fds->fds[0];
+
+    /* All read, free */
+    assert(queued_fds->offset > 0);
+    if (--queued_fds->offset == 0) {
+      uv__free(queued_fds);
+      server->queued_fds = NULL;
+    } else {
+      /* Shift rest */
+      memmove(queued_fds->fds,
+              queued_fds->fds + 1,
+              queued_fds->offset * sizeof(*queued_fds->fds));
+    }
+  } else {
+    server->accepted_fd = -1;
+    if (err == 0)
+      uv__io_start(server->loop, &server->io_watcher, POLLIN);
+  }
+  return err;
+}
+
+
+int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
+  int err;
+
+  switch (stream->type) {
+  case UV_TCP:
+    err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb);
+    break;
+
+#ifdef TUV_FEATURE_PIPE
+  case UV_NAMED_PIPE:
+    err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb);
+    break;
+#endif
+
+  default:
+    err = -EINVAL;
+  }
+
+  if (err == 0)
+    uv__handle_start(stream);
+
+  return err;
+}
+
+
+static void uv__drain(uv_stream_t* stream) {
+  uv_shutdown_t* req;
+  int err;
+
+  assert(QUEUE_EMPTY(&stream->write_queue));
+  uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+  uv__stream_osx_interrupt_select(stream);
+
+  /* Shutdown? */
+  if ((stream->flags & UV_STREAM_SHUTTING) &&
+      !(stream->flags & UV_CLOSING) &&
+      !(stream->flags & UV_STREAM_SHUT)) {
+    assert(stream->shutdown_req);
+
+    req = stream->shutdown_req;
+    stream->shutdown_req = NULL;
+    stream->flags &= ~UV_STREAM_SHUTTING;
+    uv__req_unregister(stream->loop, req);
+
+    err = 0;
+    if (shutdown(uv__stream_fd(stream), SHUT_WR))
+      err = -errno;
+
+    if (err == 0)
+      stream->flags |= UV_STREAM_SHUT;
+
+    if (req->cb != NULL)
+      req->cb(req, err);
+  }
+}
+
+
+static size_t uv__write_req_size(uv_write_t* req) {
+  size_t size;
+
+  assert(req->bufs != NULL);
+  size = uv__count_bufs(req->bufs + req->write_index,
+                        req->nbufs - req->write_index);
+  assert(req->handle->write_queue_size >= size);
+
+  return size;
+}
+
+
+static void uv__write_req_finish(uv_write_t* req) {
+  uv_stream_t* stream = req->handle;
+
+  /* Pop the req off tcp->write_queue. */
+  QUEUE_REMOVE(&req->queue);
+
+  /* Only free when there was no error. On error, we touch up write_queue_size
+   * right before making the callback. The reason we don't do that right away
+   * is that a write_queue_size > 0 is our only way to signal to the user that
+   * they should stop writing - which they should if we got an error. Something
+   * to revisit in future revisions of the libuv API.
+   */
+  if (req->error == 0) {
+    if (req->bufs != req->bufsml)
+      uv__free(req->bufs);
+    req->bufs = NULL;
+  }
+
+  /* Add it to the write_completed_queue where it will have its
+   * callback called in the near future.
+   */
+  QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue);
+  uv__io_feed(stream->loop, &stream->io_watcher);
+}
+
+
+static int uv__handle_fd(uv_handle_t* handle) {
+  switch (handle->type) {
+    case UV_NAMED_PIPE:
+    case UV_TCP:
+      return ((uv_stream_t*) handle)->io_watcher.fd;
+
+    case UV_UDP:
+      return ((uv_udp_t*) handle)->io_watcher.fd;
+
+    default:
+      return -1;
+  }
+}
+
+static void uv__write(uv_stream_t* stream) {
+  struct iovec* iov;
+  QUEUE* q;
+  uv_write_t* req;
+  int iovmax;
+  int iovcnt;
+  ssize_t n;
+
+start:
+
+  assert(uv__stream_fd(stream) >= 0);
+
+  if (QUEUE_EMPTY(&stream->write_queue))
+    return;
+
+  q = QUEUE_HEAD(&stream->write_queue);
+  req = QUEUE_DATA(q, uv_write_t, queue);
+  assert(req->handle == stream);
+
+  /*
+   * Cast to iovec. We had to have our own uv_buf_t instead of iovec
+   * because Windows's WSABUF is not an iovec.
+   */
+  assert(sizeof(uv_buf_t) == sizeof(struct iovec));
+  iov = (struct iovec*) &(req->bufs[req->write_index]);
+  iovcnt = req->nbufs - req->write_index;
+
+  iovmax = uv__getiovmax();
+
+  /* Limit iov count to avoid EINVALs from writev() */
+  if (iovcnt > iovmax)
+    iovcnt = iovmax;
+
+  /*
+   * Now do the actual writev. Note that we've been updating the pointers
+   * inside the iov each time we write. So there is no need to offset it.
+   */
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  if (req->send_handle) {
+    struct msghdr msg;
+    struct cmsghdr *cmsg;
+    int fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle);
+    char scratch[64] = {0};
+
+    assert(fd_to_send >= 0);
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = iovcnt;
+    msg.msg_flags = 0;
+
+    msg.msg_control = (void*) scratch;
+    msg.msg_controllen = CMSG_SPACE(sizeof(fd_to_send));
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
+
+    /* silence aliasing warning */
+    {
+      void* pv = CMSG_DATA(cmsg);
+      int* pi = pv;
+      *pi = fd_to_send;
+    }
+
+    do {
+      n = sendmsg(uv__stream_fd(stream), &msg, 0);
+    }
+#if defined(__APPLE__)
+    /*
+     * Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
+     * EPROTOTYPE can be returned while trying to write to a socket that is
+     * shutting down. If we retry the write, we should get the expected EPIPE
+     * instead.
+     */
+    while (n == -1 && (errno == EINTR || errno == EPROTOTYPE));
+#else
+    while (n == -1 && errno == EINTR);
+#endif
+  } else {
+#endif
+    do {
+      if (iovcnt == 1) {
+        n = write(uv__stream_fd(stream), iov[0].iov_base, iov[0].iov_len);
+      } else {
+        n = writev(uv__stream_fd(stream), iov, iovcnt);
+      }
+    }
+#if defined(__APPLE__)
+    /*
+     * Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
+     * EPROTOTYPE can be returned while trying to write to a socket that is
+     * shutting down. If we retry the write, we should get the expected EPIPE
+     * instead.
+     */
+    while (n == -1 && (errno == EINTR || errno == EPROTOTYPE));
+#else
+    while (n == -1 && errno == EINTR);
+#endif
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  }
+#endif
+
+  if (n < 0) {
+    if (errno != EAGAIN && errno != EWOULDBLOCK) {
+      /* Error */
+      req->error = -errno;
+      uv__write_req_finish(req);
+      uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+      if (!uv__io_active(&stream->io_watcher, POLLIN))
+        uv__handle_stop(stream);
+      uv__stream_osx_interrupt_select(stream);
+      return;
+    } else if (stream->flags & UV_STREAM_BLOCKING) {
+      /* If this is a blocking stream, try again. */
+      goto start;
+    }
+  } else {
+    /* Successful write */
+
+    while (n >= 0) {
+      uv_buf_t* buf = &(req->bufs[req->write_index]);
+      size_t len = buf->len;
+
+      assert(req->write_index < req->nbufs);
+
+      if ((size_t)n < len) {
+        buf->base += n;
+        buf->len -= n;
+        stream->write_queue_size -= n;
+        n = 0;
+
+        /* There is more to write. */
+        if (stream->flags & UV_STREAM_BLOCKING) {
+          /*
+           * If we're blocking then we should not be enabling the write
+           * watcher - instead we need to try again.
+           */
+          goto start;
+        } else {
+          /* Break loop and ensure the watcher is pending. */
+          break;
+        }
+
+      } else {
+        /* Finished writing the buf at index req->write_index. */
+        req->write_index++;
+
+        assert((size_t)n >= len);
+        n -= len;
+
+        assert(stream->write_queue_size >= len);
+        stream->write_queue_size -= len;
+
+        if (req->write_index == req->nbufs) {
+          /* Then we're done! */
+          assert(n == 0);
+          uv__write_req_finish(req);
+          /* TODO: start trying to write the next request. */
+          return;
+        }
+      }
+    }
+  }
+
+  /* Either we've counted n down to zero or we've got EAGAIN. */
+  assert(n == 0 || n == -1);
+
+  /* Only non-blocking streams should use the write_watcher. */
+  assert(!(stream->flags & UV_STREAM_BLOCKING));
+
+  /* We're not done. */
+  uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
+
+  /* Notify select() thread about state change */
+  uv__stream_osx_interrupt_select(stream);
+}
+
+
+static void uv__write_callbacks(uv_stream_t* stream) {
+  uv_write_t* req;
+  QUEUE* q;
+
+  while (!QUEUE_EMPTY(&stream->write_completed_queue)) {
+    /* Pop a req off write_completed_queue. */
+    q = QUEUE_HEAD(&stream->write_completed_queue);
+    req = QUEUE_DATA(q, uv_write_t, queue);
+    QUEUE_REMOVE(q);
+    uv__req_unregister(stream->loop, req);
+
+    if (req->bufs != NULL) {
+      stream->write_queue_size -= uv__write_req_size(req);
+      if (req->bufs != req->bufsml)
+        uv__free(req->bufs);
+      req->bufs = NULL;
+    }
+
+    /* NOTE: call callback AFTER freeing the request data. */
+    if (req->cb)
+      req->cb(req, req->error);
+  }
+
+  assert(QUEUE_EMPTY(&stream->write_completed_queue));
+}
+
+
+uv_handle_type uv__handle_type(int fd) {
+  struct sockaddr_storage ss;
+  socklen_t sslen;
+  socklen_t len;
+  int type;
+
+  memset(&ss, 0, sizeof(ss));
+  sslen = sizeof(ss);
+
+  if (getsockname(fd, (struct sockaddr*)&ss, &sslen))
+    return UV_UNKNOWN_HANDLE;
+
+  len = sizeof type;
+
+  if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len))
+    return UV_UNKNOWN_HANDLE;
+
+  if (type == SOCK_STREAM) {
+#if defined(_AIX) || defined(__DragonFly__)
+    /* on AIX/DragonFly the getsockname call returns an empty sa structure
+     * for sockets of type AF_UNIX.  For all other types it will
+     * return a properly filled in structure.
+     */
+    if (sslen == 0)
+      return UV_NAMED_PIPE;
+#endif
+    switch (ss.ss_family) {
+
+#ifdef AF_UNIX
+      case AF_UNIX:
+        return UV_NAMED_PIPE;
+#endif
+      case AF_INET:
+      case AF_INET6:
+        return UV_TCP;
+      }
+  }
+
+  if (type == SOCK_DGRAM &&
+      (ss.ss_family == AF_INET || ss.ss_family == AF_INET6))
+    return UV_UDP;
+
+  return UV_UNKNOWN_HANDLE;
+}
+
+
+static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) {
+  stream->flags |= UV_STREAM_READ_EOF;
+  uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
+  if (!uv__io_active(&stream->io_watcher, POLLOUT))
+    uv__handle_stop(stream);
+  uv__stream_osx_interrupt_select(stream);
+  stream->read_cb(stream, UV_EOF, buf);
+  stream->flags &= ~UV_STREAM_READING;
+}
+
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
+  uv__stream_queued_fds_t* queued_fds;
+  unsigned int queue_size;
+
+  queued_fds = stream->queued_fds;
+  if (queued_fds == NULL) {
+    queue_size = 8;
+    queued_fds = uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) +
+                            sizeof(*queued_fds));
+    if (queued_fds == NULL)
+      return -ENOMEM;
+    queued_fds->size = queue_size;
+    queued_fds->offset = 0;
+    stream->queued_fds = queued_fds;
+
+    /* Grow */
+  } else if (queued_fds->size == queued_fds->offset) {
+    queue_size = queued_fds->size + 8;
+    queued_fds = uv__realloc(queued_fds,
+                             (queue_size - 1) * sizeof(*queued_fds->fds) +
+                              sizeof(*queued_fds));
+
+    /*
+     * Allocation failure, report back.
+     * NOTE: if it is fatal - sockets will be closed in uv__stream_close
+     */
+    if (queued_fds == NULL)
+      return -ENOMEM;
+    queued_fds->size = queue_size;
+    stream->queued_fds = queued_fds;
+  }
+
+  /* Put fd in a queue */
+  queued_fds->fds[queued_fds->offset++] = fd;
+
+  return 0;
+}
+#endif
+
+
+#define UV__CMSG_FD_COUNT 64
+#define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int))
+
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
+  struct cmsghdr* cmsg;
+
+  for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+    char* start;
+    char* end;
+    int err;
+    void* pv;
+    int* pi;
+    unsigned int i;
+    unsigned int count;
+
+    if (cmsg->cmsg_type != SCM_RIGHTS) {
+      fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n",
+          cmsg->cmsg_type);
+      continue;
+    }
+
+    /* silence aliasing warning */
+    pv = CMSG_DATA(cmsg);
+    pi = pv;
+
+    /* Count available fds */
+    start = (char*) cmsg;
+    end = (char*) cmsg + cmsg->cmsg_len;
+    count = 0;
+    while (start + CMSG_LEN(count * sizeof(*pi)) < end)
+      count++;
+    assert(start + CMSG_LEN(count * sizeof(*pi)) == end);
+
+    for (i = 0; i < count; i++) {
+      /* Already has accepted fd, queue now */
+      if (stream->accepted_fd != -1) {
+        err = uv__stream_queue_fd(stream, pi[i]);
+        if (err != 0) {
+          /* Close rest */
+          for (; i < count; i++)
+            uv__close(pi[i]);
+          return err;
+        }
+      } else {
+        stream->accepted_fd = pi[i];
+      }
+    }
+  }
+
+  return 0;
+}
+#endif
+
+
+#ifdef __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wgnu-folding-constant"
+#endif
+
+static void uv__read(uv_stream_t* stream) {
+  uv_buf_t buf;
+  ssize_t nread;
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  struct msghdr msg;
+  char cmsg_space[CMSG_SPACE(UV__CMSG_FD_SIZE)];
+#endif
+  int count;
+  int err;
+  int is_ipc;
+
+  stream->flags &= ~UV_STREAM_READ_PARTIAL;
+
+  /* Prevent loop starvation when the data comes in as fast as (or faster than)
+   * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
+   */
+  count = 32;
+
+  is_ipc = stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) stream)->ipc;
+#if defined(__NUTTX__) || defined(__TIZENRT__) /* No IPC support for now */
+  assert(!is_ipc);
+#endif
+
+  /* XXX: Maybe instead of having UV_STREAM_READING we just test if
+   * tcp->read_cb is NULL or not?
+   */
+  while (stream->read_cb
+      && (stream->flags & UV_STREAM_READING)
+      && (count-- > 0)) {
+    assert(stream->alloc_cb != NULL);
+
+    buf = uv_buf_init(NULL, 0);
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+    stream->alloc_cb((uv_handle_t*)stream, 2 * 1024, &buf);
+#else
+    stream->alloc_cb((uv_handle_t*)stream, 64 * 1024, &buf);
+#endif
+    if (buf.base == NULL || buf.len == 0) {
+      /* User indicates it can't or won't handle the read. */
+      stream->read_cb(stream, UV_ENOBUFS, &buf);
+      return;
+    }
+
+    assert(buf.base != NULL);
+    assert(uv__stream_fd(stream) >= 0);
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+    if (!is_ipc) {
+#endif
+      do {
+        nread = read(uv__stream_fd(stream), buf.base, buf.len);
+      }
+      while (nread < 0 && errno == EINTR);
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+    } else {
+      /* ipc uses recvmsg */
+      msg.msg_flags = 0;
+      msg.msg_iov = (struct iovec*) &buf;
+      msg.msg_iovlen = 1;
+      msg.msg_name = NULL;
+      msg.msg_namelen = 0;
+      /* Set up to receive a descriptor even if one isn't in the message */
+      msg.msg_controllen = sizeof(cmsg_space);
+      msg.msg_control = cmsg_space;
+
+      do {
+        nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);
+      }
+      while (nread < 0 && errno == EINTR);
+    }
+#endif
+
+    if (nread < 0) {
+      /* Error */
+      if (errno == EAGAIN || errno == EWOULDBLOCK) {
+        /* Wait for the next one. */
+        if (stream->flags & UV_STREAM_READING) {
+          uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
+          uv__stream_osx_interrupt_select(stream);
+        }
+        stream->read_cb(stream, 0, &buf);
+      } else {
+        /* Error. User should call uv_close(). */
+        stream->read_cb(stream, -errno, &buf);
+        if (stream->flags & UV_STREAM_READING) {
+          stream->flags &= ~UV_STREAM_READING;
+          uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
+          if (!uv__io_active(&stream->io_watcher, POLLOUT))
+            uv__handle_stop(stream);
+          uv__stream_osx_interrupt_select(stream);
+        }
+      }
+      return;
+    } else if (nread == 0) {
+      uv__stream_eof(stream, &buf);
+      return;
+    } else {
+      /* Successful read */
+      ssize_t buflen = buf.len;
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+      if (is_ipc) {
+        err = uv__stream_recv_cmsg(stream, &msg);
+        if (err != 0) {
+          stream->read_cb(stream, err, &buf);
+          return;
+        }
+      }
+#endif
+      stream->read_cb(stream, nread, &buf);
+
+      /* Return if we didn't fill the buffer, there is no more data to read. */
+      if (nread < buflen) {
+        stream->flags |= UV_STREAM_READ_PARTIAL;
+        return;
+      }
+    }
+  }
+}
+
+
+#ifdef __clang__
+# pragma clang diagnostic pop
+#endif
+
+#undef UV__CMSG_FD_COUNT
+#undef UV__CMSG_FD_SIZE
+
+
+int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
+  assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE) &&
+         "uv_shutdown (unix) only supports uv_handle_t right now");
+
+  if (!(stream->flags & UV_STREAM_WRITABLE) ||
+      stream->flags & UV_STREAM_SHUT ||
+      stream->flags & UV_STREAM_SHUTTING ||
+      uv__is_closing(stream)) {
+    return -ENOTCONN;
+  }
+
+  assert(uv__stream_fd(stream) >= 0);
+
+  /* Initialize request */
+  uv__req_init(stream->loop, req, UV_SHUTDOWN);
+  req->handle = stream;
+  req->cb = cb;
+  stream->shutdown_req = req;
+  stream->flags |= UV_STREAM_SHUTTING;
+
+  uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
+  uv__stream_osx_interrupt_select(stream);
+
+  return 0;
+}
+
+
+static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
+  uv_stream_t* stream;
+
+  stream = container_of(w, uv_stream_t, io_watcher);
+
+  assert(stream->type == UV_TCP ||
+         stream->type == UV_NAMED_PIPE ||
+         stream->type == UV_TTY);
+  assert(!(stream->flags & UV_CLOSING));
+
+  if (stream->connect_req) {
+    uv__stream_connect(stream);
+    return;
+  }
+
+  assert(uv__stream_fd(stream) >= 0);
+
+  /* Ignore POLLHUP here. Even it it's set, there may still be data to read. */
+  if (events & (POLLIN | POLLERR | POLLHUP))
+    uv__read(stream);
+
+  if (uv__stream_fd(stream) == -1)
+    return;  /* read_cb closed stream. */
+
+  /* Short-circuit iff POLLHUP is set, the user is still interested in read
+   * events and uv__read() reported a partial read but not EOF. If the EOF
+   * flag is set, uv__read() called read_cb with err=UV_EOF and we don't
+   * have to do anything. If the partial read flag is not set, we can't
+   * report the EOF yet because there is still data to read.
+   */
+  if ((events & POLLHUP) &&
+      (stream->flags & UV_STREAM_READING) &&
+      (stream->flags & UV_STREAM_READ_PARTIAL) &&
+      !(stream->flags & UV_STREAM_READ_EOF)) {
+    uv_buf_t buf = { NULL, 0 };
+    uv__stream_eof(stream, &buf);
+  }
+
+  if (uv__stream_fd(stream) == -1)
+    return;  /* read_cb closed stream. */
+
+  if (events & (POLLOUT | POLLERR | POLLHUP)) {
+    uv__write(stream);
+    uv__write_callbacks(stream);
+
+    /* Write queue drained. */
+    if (QUEUE_EMPTY(&stream->write_queue))
+      uv__drain(stream);
+  }
+}
+
+
+/**
+ * We get called here from directly following a call to connect(2).
+ * In order to determine if we've errored out or succeeded must call
+ * getsockopt.
+ */
+static void uv__stream_connect(uv_stream_t* stream) {
+  int error;
+  uv_connect_t* req = stream->connect_req;
+  socklen_t errorsize = sizeof(int);
+
+  assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE);
+  assert(req);
+
+  if (stream->delayed_error) {
+    /* To smooth over the differences between unixes errors that
+     * were reported synchronously on the first connect can be delayed
+     * until the next tick--which is now.
+     */
+    error = stream->delayed_error;
+    stream->delayed_error = 0;
+  } else {
+    /* Normal situation: we need to get the socket error from the kernel. */
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+    // getsockopt for SO_ERROR is not supported in NuttX
+    error = 0;
+#else
+    assert(uv__stream_fd(stream) >= 0);
+    getsockopt(uv__stream_fd(stream),
+               SOL_SOCKET,
+               SO_ERROR,
+               &error,
+               &errorsize);
+    error = -error;
+#endif
+  }
+
+  if (error == -EINPROGRESS)
+    return;
+
+  stream->connect_req = NULL;
+  uv__req_unregister(stream->loop, req);
+
+  if (error < 0 || QUEUE_EMPTY(&stream->write_queue)) {
+    uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+  }
+
+  if (req->cb)
+    req->cb(req, error);
+
+  if (uv__stream_fd(stream) == -1)
+    return;
+
+  if (error < 0) {
+    uv__stream_flush_write_queue(stream, -ECANCELED);
+    uv__write_callbacks(stream);
+  }
+}
+
+
+int uv_write2(uv_write_t* req,
+              uv_stream_t* stream,
+              const uv_buf_t bufs[],
+              unsigned int nbufs,
+              uv_stream_t* send_handle,
+              uv_write_cb cb) {
+  int empty_queue;
+
+  assert(nbufs > 0);
+  assert((stream->type == UV_TCP ||
+          stream->type == UV_NAMED_PIPE ||
+          stream->type == UV_TTY) &&
+         "uv_write (unix) does not yet support other types of streams");
+
+  if (uv__stream_fd(stream) < 0)
+    return -EBADF;
+
+  if (send_handle) {
+    if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc)
+      return -EINVAL;
+
+    /* XXX We abuse uv_write2() to send over UDP handles to child processes.
+     * Don't call uv__stream_fd() on those handles, it's a macro that on OS X
+     * evaluates to a function that operates on a uv_stream_t with a couple of
+     * OS X specific fields. On other Unices it does (handle)->io_watcher.fd,
+     * which works but only by accident.
+     */
+    if (uv__handle_fd((uv_handle_t*) send_handle) < 0)
+      return -EBADF;
+  }
+
+  /* It's legal for write_queue_size > 0 even when the write_queue is empty;
+   * it means there are error-state requests in the write_completed_queue that
+   * will touch up write_queue_size later, see also uv__write_req_finish().
+   * We could check that write_queue is empty instead but that implies making
+   * a write() syscall when we know that the handle is in error mode.
+   */
+  empty_queue = (stream->write_queue_size == 0);
+
+  /* Initialize the req */
+  uv__req_init(stream->loop, req, UV_WRITE);
+  req->cb = cb;
+  req->handle = stream;
+  req->error = 0;
+  req->send_handle = send_handle;
+  QUEUE_INIT(&req->queue);
+
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
+
+  if (req->bufs == NULL)
+    return -ENOMEM;
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
+  req->nbufs = nbufs;
+  req->write_index = 0;
+  stream->write_queue_size += uv__count_bufs(bufs, nbufs);
+
+  /* Append the request to write_queue. */
+  QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue);
+
+  /* If the queue was empty when this function began, we should attempt to
+   * do the write immediately. Otherwise start the write_watcher and wait
+   * for the fd to become writable.
+   */
+  if (stream->connect_req) {
+    /* Still connecting, do nothing. */
+  }
+  else if (empty_queue) {
+    uv__write(stream);
+  }
+  else {
+    /*
+     * blocking streams should never have anything in the queue.
+     * if this assert fires then somehow the blocking stream isn't being
+     * sufficiently flushed in uv__write.
+     */
+    assert(!(stream->flags & UV_STREAM_BLOCKING));
+    uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
+    uv__stream_osx_interrupt_select(stream);
+  }
+
+  return 0;
+}
+
+
+/* The buffers to be written must remain valid until the callback is called.
+ * This is not required for the uv_buf_t array.
+ */
+int uv_write(uv_write_t* req,
+             uv_stream_t* handle,
+             const uv_buf_t bufs[],
+             unsigned int nbufs,
+             uv_write_cb cb) {
+  return uv_write2(req, handle, bufs, nbufs, NULL, cb);
+}
+
+
+void uv_try_write_cb(uv_write_t* req, int status) {
+  /* Should not be called */
+  abort();
+}
+
+
+int uv_try_write(uv_stream_t* stream,
+                 const uv_buf_t bufs[],
+                 unsigned int nbufs) {
+  int r;
+  int has_pollout;
+  size_t written;
+  size_t req_size;
+  uv_write_t req;
+
+  /* Connecting or already writing some data */
+  if (stream->connect_req != NULL || stream->write_queue_size != 0)
+    return -EAGAIN;
+
+  has_pollout = uv__io_active(&stream->io_watcher, POLLOUT);
+
+  r = uv_write(&req, stream, bufs, nbufs, uv_try_write_cb);
+  if (r != 0)
+    return r;
+
+  /* Remove not written bytes from write queue size */
+  written = uv__count_bufs(bufs, nbufs);
+  if (req.bufs != NULL)
+    req_size = uv__write_req_size(&req);
+  else
+    req_size = 0;
+  written -= req_size;
+  stream->write_queue_size -= req_size;
+
+  /* Unqueue request, regardless of immediateness */
+  QUEUE_REMOVE(&req.queue);
+  uv__req_unregister(stream->loop, &req);
+  if (req.bufs != req.bufsml)
+    uv__free(req.bufs);
+  req.bufs = NULL;
+
+  /* Do not poll for writable, if we wasn't before calling this */
+  if (!has_pollout) {
+    uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+    uv__stream_osx_interrupt_select(stream);
+  }
+
+  if (written == 0 && req_size != 0)
+    return -EAGAIN;
+  else
+    return written;
+}
+
+
+int uv_read_start(uv_stream_t* stream,
+                  uv_alloc_cb alloc_cb,
+                  uv_read_cb read_cb) {
+  assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE ||
+      stream->type == UV_TTY);
+
+  if (stream->flags & UV_CLOSING)
+    return -EINVAL;
+
+  /* The UV_STREAM_READING flag is irrelevant of the state of the tcp - it just
+   * expresses the desired state of the user.
+   */
+  stream->flags |= UV_STREAM_READING;
+
+  /* TODO: try to do the read inline? */
+  /* TODO: keep track of tcp state. If we've gotten a EOF then we should
+   * not start the IO watcher.
+   */
+  assert(uv__stream_fd(stream) >= 0);
+  assert(alloc_cb);
+
+  stream->read_cb = read_cb;
+  stream->alloc_cb = alloc_cb;
+
+  uv__io_start(stream->loop, &stream->io_watcher, POLLIN);
+  uv__handle_start(stream);
+  uv__stream_osx_interrupt_select(stream);
+
+  return 0;
+}
+
+
+int uv_read_stop(uv_stream_t* stream) {
+  if (!(stream->flags & UV_STREAM_READING))
+    return 0;
+
+  stream->flags &= ~UV_STREAM_READING;
+  uv__io_stop(stream->loop, &stream->io_watcher, POLLIN);
+  if (!uv__io_active(&stream->io_watcher, POLLOUT))
+    uv__handle_stop(stream);
+  uv__stream_osx_interrupt_select(stream);
+
+  stream->read_cb = NULL;
+  stream->alloc_cb = NULL;
+  return 0;
+}
+
+
+int uv_is_readable(const uv_stream_t* stream) {
+  return !!(stream->flags & UV_STREAM_READABLE);
+}
+
+
+int uv_is_writable(const uv_stream_t* stream) {
+  return !!(stream->flags & UV_STREAM_WRITABLE);
+}
+
+
+#if defined(__APPLE__)
+int uv___stream_fd(const uv_stream_t* handle) {
+  const uv__stream_select_t* s;
+
+  assert(handle->type == UV_TCP ||
+         handle->type == UV_TTY ||
+         handle->type == UV_NAMED_PIPE);
+
+  s = handle->select;
+  if (s != NULL)
+    return s->fd;
+
+  return handle->io_watcher.fd;
+}
+#endif /* defined(__APPLE__) */
+
+
+void uv__stream_close(uv_stream_t* handle) {
+  unsigned int i;
+  uv__stream_queued_fds_t* queued_fds;
+
+#if defined(__APPLE__)
+  /* Terminate select loop first */
+  if (handle->select != NULL) {
+    uv__stream_select_t* s;
+
+    s = handle->select;
+
+    uv_sem_post(&s->close_sem);
+    uv_sem_post(&s->async_sem);
+    uv__stream_osx_interrupt_select(handle);
+    uv_thread_join(&s->thread);
+    uv_sem_destroy(&s->close_sem);
+    uv_sem_destroy(&s->async_sem);
+    uv__close(s->fake_fd);
+    uv__close(s->int_fd);
+    uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close);
+
+    handle->select = NULL;
+  }
+#endif /* defined(__APPLE__) */
+
+  uv__io_close(handle->loop, &handle->io_watcher);
+  uv_read_stop(handle);
+  uv__handle_stop(handle);
+
+  if (handle->io_watcher.fd != -1) {
+    /* Don't close stdio file descriptors.  Nothing good comes from it. */
+    if (handle->io_watcher.fd > STDERR_FILENO)
+      uv__close(handle->io_watcher.fd);
+    handle->io_watcher.fd = -1;
+  }
+
+  if (handle->accepted_fd != -1) {
+    uv__close(handle->accepted_fd);
+    handle->accepted_fd = -1;
+  }
+
+  /* Close all queued fds */
+  if (handle->queued_fds != NULL) {
+    queued_fds = handle->queued_fds;
+    for (i = 0; i < queued_fds->offset; i++)
+      uv__close(queued_fds->fds[i]);
+    uv__free(handle->queued_fds);
+    handle->queued_fds = NULL;
+  }
+
+  assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT));
+}
+
+
+#ifdef TUV_FEATURE_PIPE
+int uv_stream_set_blocking(uv_stream_t* handle, int blocking) {
+  /* Don't need to check the file descriptor, uv__nonblock()
+   * will fail with EBADF if it's not valid.
+   */
+  return uv__nonblock(uv__stream_fd(handle), !blocking);
+}
+#endif
diff --git a/src/unix/sunos.c b/src/unix/sunos.c
new file mode 100644 (file)
index 0000000..3e7a759
--- /dev/null
@@ -0,0 +1,821 @@
+/* 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"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#ifndef SUNOS_NO_IFADDRS
+# include <ifaddrs.h>
+#endif
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_arp.h>
+#include <sys/sockio.h>
+
+#include <sys/loadavg.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <kstat.h>
+#include <fcntl.h>
+
+#include <sys/port.h>
+#include <port.h>
+
+#define PORT_FIRED 0x69
+#define PORT_UNUSED 0x0
+#define PORT_LOADED 0x99
+#define PORT_DELETED -1
+
+#if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64)
+#define PROCFS_FILE_OFFSET_BITS_HACK 1
+#undef _FILE_OFFSET_BITS
+#else
+#define PROCFS_FILE_OFFSET_BITS_HACK 0
+#endif
+
+#include <procfs.h>
+
+#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1)
+#define _FILE_OFFSET_BITS 64
+#endif
+
+
+int uv__platform_loop_init(uv_loop_t* loop) {
+  int err;
+  int fd;
+
+  loop->fs_fd = -1;
+  loop->backend_fd = -1;
+
+  fd = port_create();
+  if (fd == -1)
+    return -errno;
+
+  err = uv__cloexec(fd, 1);
+  if (err) {
+    uv__close(fd);
+    return err;
+  }
+  loop->backend_fd = fd;
+
+  return 0;
+}
+
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  if (loop->fs_fd != -1) {
+    uv__close(loop->fs_fd);
+    loop->fs_fd = -1;
+  }
+
+  if (loop->backend_fd != -1) {
+    uv__close(loop->backend_fd);
+    loop->backend_fd = -1;
+  }
+}
+
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  struct port_event* events;
+  uintptr_t i;
+  uintptr_t nfds;
+
+  assert(loop->watchers != NULL);
+
+  events = (struct port_event*) loop->watchers[loop->nwatchers];
+  nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
+  if (events == NULL)
+    return;
+
+  /* Invalidate events with same file descriptor */
+  for (i = 0; i < nfds; i++)
+    if ((int) events[i].portev_object == fd)
+      events[i].portev_object = -1;
+}
+
+
+int uv__io_check_fd(uv_loop_t* loop, int fd) {
+  if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0))
+    return -errno;
+
+  if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd))
+    abort();
+
+  return 0;
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  struct port_event events[1024];
+  struct port_event* pe;
+  struct timespec spec;
+  QUEUE* q;
+  uv__io_t* w;
+  sigset_t* pset;
+  sigset_t set;
+  uint64_t base;
+  uint64_t diff;
+  unsigned int nfds;
+  unsigned int i;
+  int saved_errno;
+  int have_signals;
+  int nevents;
+  int count;
+  int err;
+  int fd;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+
+    if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0))
+      abort();
+
+    w->events = w->pevents;
+  }
+
+  pset = NULL;
+  if (loop->flags & UV_LOOP_BLOCK_SIGPROF) {
+    pset = &set;
+    sigemptyset(pset);
+    sigaddset(pset, SIGPROF);
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 48; /* Benchmarks suggest this gives the best throughput. */
+
+  for (;;) {
+    if (timeout != -1) {
+      spec.tv_sec = timeout / 1000;
+      spec.tv_nsec = (timeout % 1000) * 1000000;
+    }
+
+    /* Work around a kernel bug where nfds is not updated. */
+    events[0].portev_source = 0;
+
+    nfds = 1;
+    saved_errno = 0;
+
+    if (pset != NULL)
+      pthread_sigmask(SIG_BLOCK, pset, NULL);
+
+    err = port_getn(loop->backend_fd,
+                    events,
+                    ARRAY_SIZE(events),
+                    &nfds,
+                    timeout == -1 ? NULL : &spec);
+
+    if (pset != NULL)
+      pthread_sigmask(SIG_UNBLOCK, pset, NULL);
+
+    if (err) {
+      /* Work around another kernel bug: port_getn() may return events even
+       * on error.
+       */
+      if (errno == EINTR || errno == ETIME)
+        saved_errno = errno;
+      else
+        abort();
+    }
+
+    /* Update loop->time unconditionally. It's tempting to skip the update when
+     * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
+     * operating system didn't reschedule our process while in the syscall.
+     */
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (events[0].portev_source == 0) {
+      if (timeout == 0)
+        return;
+
+      if (timeout == -1)
+        continue;
+
+      goto update_timeout;
+    }
+
+    if (nfds == 0) {
+      assert(timeout != -1);
+      return;
+    }
+
+    have_signals = 0;
+    nevents = 0;
+
+    assert(loop->watchers != NULL);
+    loop->watchers[loop->nwatchers] = (void*) events;
+    loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
+    for (i = 0; i < nfds; i++) {
+      pe = events + i;
+      fd = pe->portev_object;
+
+      /* Skip invalidated events, see uv__platform_invalidate_fd */
+      if (fd == -1)
+        continue;
+
+      assert(fd >= 0);
+      assert((unsigned) fd < loop->nwatchers);
+
+      w = loop->watchers[fd];
+
+      /* File descriptor that we've stopped watching, ignore. */
+      if (w == NULL)
+        continue;
+
+      /* Run signal watchers last.  This also affects child process watchers
+       * because those are implemented in terms of signal watchers.
+       */
+      if (w == &loop->signal_io_watcher)
+        have_signals = 1;
+      else
+        w->cb(loop, w, pe->portev_events);
+
+      nevents++;
+
+      if (w != loop->watchers[fd])
+        continue;  /* Disabled by callback. */
+
+      /* Events Ports operates in oneshot mode, rearm timer on next run. */
+      if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue))
+        QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
+    }
+
+    if (have_signals != 0)
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+
+    loop->watchers[loop->nwatchers] = NULL;
+    loop->watchers[loop->nwatchers + 1] = NULL;
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
+    if (nevents != 0) {
+      if (nfds == ARRAY_SIZE(events) && --count != 0) {
+        /* Poll for more events but don't block this time. */
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+
+    if (saved_errno == ETIME) {
+      assert(timeout != -1);
+      return;
+    }
+
+    if (timeout == 0)
+      return;
+
+    if (timeout == -1)
+      continue;
+
+update_timeout:
+    assert(timeout > 0);
+
+    diff = loop->time - base;
+    if (diff >= (uint64_t) timeout)
+      return;
+
+    timeout -= diff;
+  }
+}
+
+
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  return gethrtime();
+}
+
+
+/*
+ * We could use a static buffer for the path manipulations that we need outside
+ * of the function, but this function could be called by multiple consumers and
+ * we don't want to potentially create a race condition in the use of snprintf.
+ */
+int uv_exepath(char* buffer, size_t* size) {
+  ssize_t res;
+  char buf[128];
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return -EINVAL;
+
+  snprintf(buf, sizeof(buf), "/proc/%lu/path/a.out", (unsigned long) getpid());
+
+  res = *size - 1;
+  if (res > 0)
+    res = readlink(buf, buffer, res);
+
+  if (res == -1)
+    return -errno;
+
+  buffer[res] = '\0';
+  *size = res;
+  return 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES);
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES);
+}
+
+
+void uv_loadavg(double avg[3]) {
+  (void) getloadavg(avg, 3);
+}
+
+
+#if defined(PORT_SOURCE_FILE)
+
+static int uv__fs_event_rearm(uv_fs_event_t *handle) {
+  if (handle->fd == -1)
+    return -EBADF;
+
+  if (port_associate(handle->loop->fs_fd,
+                     PORT_SOURCE_FILE,
+                     (uintptr_t) &handle->fo,
+                     FILE_ATTRIB | FILE_MODIFIED,
+                     handle) == -1) {
+    return -errno;
+  }
+  handle->fd = PORT_LOADED;
+
+  return 0;
+}
+
+
+static void uv__fs_event_read(uv_loop_t* loop,
+                              uv__io_t* w,
+                              unsigned int revents) {
+  uv_fs_event_t *handle = NULL;
+  timespec_t timeout;
+  port_event_t pe;
+  int events;
+  int r;
+
+  (void) w;
+  (void) revents;
+
+  do {
+    uint_t n = 1;
+
+    /*
+     * Note that our use of port_getn() here (and not port_get()) is deliberate:
+     * there is a bug in event ports (Sun bug 6456558) whereby a zeroed timeout
+     * causes port_get() to return success instead of ETIME when there aren't
+     * actually any events (!); by using port_getn() in lieu of port_get(),
+     * we can at least workaround the bug by checking for zero returned events
+     * and treating it as we would ETIME.
+     */
+    do {
+      memset(&timeout, 0, sizeof timeout);
+      r = port_getn(loop->fs_fd, &pe, 1, &n, &timeout);
+    }
+    while (r == -1 && errno == EINTR);
+
+    if ((r == -1 && errno == ETIME) || n == 0)
+      break;
+
+    handle = (uv_fs_event_t*) pe.portev_user;
+    assert((r == 0) && "unexpected port_get() error");
+
+    events = 0;
+    if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED))
+      events |= UV_CHANGE;
+    if (pe.portev_events & ~(FILE_ATTRIB | FILE_MODIFIED))
+      events |= UV_RENAME;
+    assert(events != 0);
+    handle->fd = PORT_FIRED;
+    handle->cb(handle, NULL, events, 0);
+
+    if (handle->fd != PORT_DELETED) {
+      r = uv__fs_event_rearm(handle);
+      if (r != 0)
+        handle->cb(handle, NULL, 0, r);
+    }
+  }
+  while (handle->fd != PORT_DELETED);
+}
+
+
+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);
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* path,
+                      unsigned int flags) {
+  int portfd;
+  int first_run;
+  int err;
+
+  if (uv__is_active(handle))
+    return -EINVAL;
+
+  first_run = 0;
+  if (handle->loop->fs_fd == -1) {
+    portfd = port_create();
+    if (portfd == -1)
+      return -errno;
+    handle->loop->fs_fd = portfd;
+    first_run = 1;
+  }
+
+  uv__handle_start(handle);
+  handle->path = uv__strdup(path);
+  handle->fd = PORT_UNUSED;
+  handle->cb = cb;
+
+  memset(&handle->fo, 0, sizeof handle->fo);
+  handle->fo.fo_name = handle->path;
+  err = uv__fs_event_rearm(handle);
+  if (err != 0)
+    return err;
+
+  if (first_run) {
+    uv__io_init(&handle->loop->fs_event_watcher, uv__fs_event_read, portfd);
+    uv__io_start(handle->loop, &handle->loop->fs_event_watcher, POLLIN);
+  }
+
+  return 0;
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  if (!uv__is_active(handle))
+    return 0;
+
+  if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) {
+    port_dissociate(handle->loop->fs_fd,
+                    PORT_SOURCE_FILE,
+                    (uintptr_t) &handle->fo);
+  }
+
+  handle->fd = PORT_DELETED;
+  uv__free(handle->path);
+  handle->path = NULL;
+  handle->fo.fo_name = NULL;
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  uv_fs_event_stop(handle);
+}
+
+#else /* !defined(PORT_SOURCE_FILE) */
+
+int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
+  return -ENOSYS;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle,
+                      uv_fs_event_cb cb,
+                      const char* filename,
+                      unsigned int flags) {
+  return -ENOSYS;
+}
+
+
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  return -ENOSYS;
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  UNREACHABLE();
+}
+
+#endif /* defined(PORT_SOURCE_FILE) */
+
+
+char** uv_setup_args(int argc, char** argv) {
+  return argv;
+}
+
+
+int uv_set_process_title(const char* title) {
+  return 0;
+}
+
+
+int uv_get_process_title(char* buffer, size_t size) {
+  if (buffer == NULL || size == 0)
+    return -EINVAL;
+
+  buffer[0] = '\0';
+  return 0;
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  psinfo_t psinfo;
+  int err;
+  int fd;
+
+  fd = open("/proc/self/psinfo", O_RDONLY);
+  if (fd == -1)
+    return -errno;
+
+  /* FIXME(bnoordhuis) Handle EINTR. */
+  err = -EINVAL;
+  if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) {
+    *rss = (size_t)psinfo.pr_rssize * 1024;
+    err = 0;
+  }
+  uv__close(fd);
+
+  return err;
+}
+
+
+int uv_uptime(double* uptime) {
+  kstat_ctl_t   *kc;
+  kstat_t       *ksp;
+  kstat_named_t *knp;
+
+  long hz = sysconf(_SC_CLK_TCK);
+
+  kc = kstat_open();
+  if (kc == NULL)
+    return -EPERM;
+
+  ksp = kstat_lookup(kc, (char*) "unix", 0, (char*) "system_misc");
+  if (kstat_read(kc, ksp, NULL) == -1) {
+    *uptime = -1;
+  } else {
+    knp = (kstat_named_t*)  kstat_data_lookup(ksp, (char*) "clk_intr");
+    *uptime = knp->value.ul / hz;
+  }
+  kstat_close(kc);
+
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  int           lookup_instance;
+  kstat_ctl_t   *kc;
+  kstat_t       *ksp;
+  kstat_named_t *knp;
+  uv_cpu_info_t* cpu_info;
+
+  kc = kstat_open();
+  if (kc == NULL)
+    return -EPERM;
+
+  /* Get count of cpus */
+  lookup_instance = 0;
+  while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) {
+    lookup_instance++;
+  }
+
+  *cpu_infos = uv__malloc(lookup_instance * sizeof(**cpu_infos));
+  if (!(*cpu_infos)) {
+    kstat_close(kc);
+    return -ENOMEM;
+  }
+
+  *count = lookup_instance;
+
+  cpu_info = *cpu_infos;
+  lookup_instance = 0;
+  while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) {
+    if (kstat_read(kc, ksp, NULL) == -1) {
+      cpu_info->speed = 0;
+      cpu_info->model = NULL;
+    } else {
+      knp = kstat_data_lookup(ksp, (char*) "clock_MHz");
+      assert(knp->data_type == KSTAT_DATA_INT32 ||
+             knp->data_type == KSTAT_DATA_INT64);
+      cpu_info->speed = (knp->data_type == KSTAT_DATA_INT32) ? knp->value.i32
+                                                             : knp->value.i64;
+
+      knp = kstat_data_lookup(ksp, (char*) "brand");
+      assert(knp->data_type == KSTAT_DATA_STRING);
+      cpu_info->model = uv__strdup(KSTAT_NAMED_STR_PTR(knp));
+    }
+
+    lookup_instance++;
+    cpu_info++;
+  }
+
+  cpu_info = *cpu_infos;
+  lookup_instance = 0;
+  for (;;) {
+    ksp = kstat_lookup(kc, (char*) "cpu", lookup_instance, (char*) "sys");
+
+    if (ksp == NULL)
+      break;
+
+    if (kstat_read(kc, ksp, NULL) == -1) {
+      cpu_info->cpu_times.user = 0;
+      cpu_info->cpu_times.nice = 0;
+      cpu_info->cpu_times.sys = 0;
+      cpu_info->cpu_times.idle = 0;
+      cpu_info->cpu_times.irq = 0;
+    } else {
+      knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_user");
+      assert(knp->data_type == KSTAT_DATA_UINT64);
+      cpu_info->cpu_times.user = knp->value.ui64;
+
+      knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_kernel");
+      assert(knp->data_type == KSTAT_DATA_UINT64);
+      cpu_info->cpu_times.sys = knp->value.ui64;
+
+      knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_idle");
+      assert(knp->data_type == KSTAT_DATA_UINT64);
+      cpu_info->cpu_times.idle = knp->value.ui64;
+
+      knp = kstat_data_lookup(ksp, (char*) "intr");
+      assert(knp->data_type == KSTAT_DATA_UINT64);
+      cpu_info->cpu_times.irq = knp->value.ui64;
+      cpu_info->cpu_times.nice = 0;
+    }
+
+    lookup_instance++;
+    cpu_info++;
+  }
+
+  kstat_close(kc);
+
+  return 0;
+}
+
+
+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);
+}
+
+/*
+ * Inspired By:
+ * https://blogs.oracle.com/paulie/entry/retrieving_mac_address_in_solaris
+ * http://www.pauliesworld.org/project/getmac.c
+ */
+static int uv__set_phys_addr(uv_interface_address_t* address,
+                             struct ifaddrs* ent) {
+
+  struct sockaddr_dl* sa_addr;
+  int sockfd;
+  int i;
+  struct arpreq arpreq;
+
+  /* This appears to only work as root */
+  sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
+  memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
+  for (i = 0; i < sizeof(address->phys_addr); i++) {
+    if (address->phys_addr[i] != 0)
+      return 0;
+  }
+  memset(&arpreq, 0, sizeof(arpreq));
+  if (address->address.address4.sin_family == AF_INET) {
+    struct sockaddr_in* sin = ((struct sockaddr_in*)&arpreq.arp_pa);
+    sin->sin_addr.s_addr = address->address.address4.sin_addr.s_addr;
+  } else if (address->address.address4.sin_family == AF_INET6) {
+    struct sockaddr_in6* sin = ((struct sockaddr_in6*)&arpreq.arp_pa);
+    memcpy(sin->sin6_addr.s6_addr,
+           address->address.address6.sin6_addr.s6_addr,
+           sizeof(address->address.address6.sin6_addr.s6_addr));
+  } else {
+    return 0;
+  }
+
+  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+  if (sockfd < 0)
+    return -errno;
+
+  if (ioctl(sockfd, SIOCGARP, (char*)&arpreq) == -1) {
+    uv__close(sockfd);
+    return -errno;
+  }
+  memcpy(address->phys_addr, arpreq.arp_ha.sa_data, sizeof(address->phys_addr));
+  uv__close(sockfd);
+  return 0;
+}
+
+int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+#ifdef SUNOS_NO_IFADDRS
+  return -ENOSYS;
+#else
+  uv_interface_address_t* address;
+  struct ifaddrs* addrs;
+  struct ifaddrs* ent;
+  int i;
+
+  if (getifaddrs(&addrs))
+    return -errno;
+
+  *count = 0;
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family == PF_PACKET)) {
+      continue;
+    }
+
+    (*count)++;
+  }
+
+  *addresses = uv__malloc(*count * sizeof(**addresses));
+  if (!(*addresses)) {
+    freeifaddrs(addrs);
+    return -ENOMEM;
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
+      continue;
+
+    if (ent->ifa_addr == NULL)
+      continue;
+
+    address->name = uv__strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
+    }
+
+    if (ent->ifa_netmask->sa_family == AF_INET6) {
+      address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
+    } else {
+      address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
+    }
+
+    address->is_internal = !!((ent->ifa_flags & IFF_PRIVATE) ||
+                           (ent->ifa_flags & IFF_LOOPBACK));
+
+    uv__set_phys_addr(address, ent);
+    address++;
+  }
+
+  freeifaddrs(addrs);
+
+  return 0;
+#endif  /* SUNOS_NO_IFADDRS */
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses,
+  int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    uv__free(addresses[i].name);
+  }
+
+  uv__free(addresses);
+}
diff --git a/src/unix/tcp.c b/src/unix/tcp.c
new file mode 100644 (file)
index 0000000..c423dcb
--- /dev/null
@@ -0,0 +1,395 @@
+/* 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"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+
+static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) {
+  int sockfd;
+  int err;
+
+  if (domain == AF_UNSPEC || uv__stream_fd(handle) != -1) {
+    handle->flags |= flags;
+    return 0;
+  }
+
+  err = uv__socket(domain, SOCK_STREAM, 0);
+  if (err < 0)
+    return err;
+  sockfd = err;
+
+  err = uv__stream_open((uv_stream_t*) handle, sockfd, flags);
+  if (err) {
+    uv__close(sockfd);
+    return err;
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, 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 -EINVAL;
+
+  if (flags & ~0xFF)
+    return -EINVAL;
+
+  uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
+
+  /* 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) {
+    int err = maybe_new_socket(tcp, domain, 0);
+    if (err) {
+      QUEUE_REMOVE(&tcp->handle_queue);
+      return err;
+    }
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
+  return uv_tcp_init_ex(loop, tcp, AF_UNSPEC);
+}
+
+
+int uv__tcp_bind(uv_tcp_t* tcp,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+  int on;
+
+  /* Cannot set IPv6-only mode on non-IPv6 socket. */
+  if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
+    return -EINVAL;
+
+  err = maybe_new_socket(tcp,
+                         addr->sa_family,
+                         UV_STREAM_READABLE | UV_STREAM_WRITABLE);
+  if (err)
+    return err;
+
+  on = 1;
+  if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+    return -errno;
+
+#ifdef IPV6_V6ONLY
+  if (addr->sa_family == AF_INET6) {
+    on = (flags & UV_TCP_IPV6ONLY) != 0;
+    if (setsockopt(tcp->io_watcher.fd,
+                   IPPROTO_IPV6,
+                   IPV6_V6ONLY,
+                   &on,
+                   sizeof on) == -1) {
+#if defined(__MVS__)
+      if (errno == EOPNOTSUPP)
+        return -EINVAL;
+#endif
+      return -errno;
+    }
+  }
+#endif
+
+  errno = 0;
+  if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) {
+    if (errno == EAFNOSUPPORT)
+      /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
+       * socket created with AF_INET to an AF_INET6 address or vice versa. */
+      return -EINVAL;
+    return -errno;
+  }
+  tcp->delayed_error = -errno;
+
+  tcp->flags |= UV_HANDLE_BOUND;
+  if (addr->sa_family == AF_INET6)
+    tcp->flags |= UV_HANDLE_IPV6;
+
+  return 0;
+}
+
+
+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;
+  int r;
+
+  assert(handle->type == UV_TCP);
+
+  if (handle->connect_req != NULL)
+    return -EALREADY;  /* FIXME(bnoordhuis) -EINVAL or maybe -EBUSY. */
+
+  err = maybe_new_socket(handle,
+                         addr->sa_family,
+                         UV_STREAM_READABLE | UV_STREAM_WRITABLE);
+  if (err)
+    return err;
+
+  handle->delayed_error = 0;
+
+  do {
+    errno = 0;
+    r = connect(uv__stream_fd(handle), addr, addrlen);
+  } while (r == -1 && errno == EINTR);
+
+  /* We not only check the return value, but also check the errno != 0.
+   * Because in rare cases connect() will return -1 but the errno
+   * is 0 (for example, on Android 4.3, OnePlus phone A0001_12_150227)
+   * and actually the tcp three-way handshake is completed.
+   */
+  if (r == -1 && errno != 0) {
+    if (errno == EINPROGRESS)
+      ; /* not an error */
+    else if (errno == ECONNREFUSED)
+    /* If we get a ECONNREFUSED wait until the next tick to report the
+     * error. Solaris wants to report immediately--other unixes want to
+     * wait.
+     */
+      handle->delayed_error = -errno;
+    else
+      return -errno;
+  }
+
+  uv__req_init(handle->loop, req, UV_CONNECT);
+  req->cb = cb;
+  req->handle = (uv_stream_t*) handle;
+  QUEUE_INIT(&req->queue);
+  handle->connect_req = req;
+
+  uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
+
+  if (handle->delayed_error)
+    uv__io_feed(handle->loop, &handle->io_watcher);
+
+  return 0;
+}
+
+
+int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
+  int err;
+
+  err = uv__nonblock(sock, 1);
+  if (err)
+    return err;
+
+  return uv__stream_open((uv_stream_t*)handle,
+                         sock,
+                         UV_STREAM_READABLE | UV_STREAM_WRITABLE);
+}
+
+
+int uv_tcp_getsockname(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+  socklen_t socklen;
+
+  if (handle->delayed_error)
+    return handle->delayed_error;
+
+  if (uv__stream_fd(handle) < 0)
+    return -EINVAL;  /* FIXME(bnoordhuis) -EBADF */
+
+  /* sizeof(socklen_t) != sizeof(int) on some systems. */
+  socklen = (socklen_t) *namelen;
+
+  if (getsockname(uv__stream_fd(handle), name, &socklen))
+    return -errno;
+
+  *namelen = (int) socklen;
+  return 0;
+}
+
+
+int uv_tcp_getpeername(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+  socklen_t socklen;
+
+  if (handle->delayed_error)
+    return handle->delayed_error;
+
+  if (uv__stream_fd(handle) < 0)
+    return -EINVAL;  /* FIXME(bnoordhuis) -EBADF */
+
+  /* sizeof(socklen_t) != sizeof(int) on some systems. */
+  socklen = (socklen_t) *namelen;
+
+  if (getpeername(uv__stream_fd(handle), name, &socklen))
+    return -errno;
+
+  *namelen = (int) socklen;
+  return 0;
+}
+
+
+int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
+  static int single_accept = -1;
+  int err;
+
+  if (tcp->delayed_error)
+    return tcp->delayed_error;
+
+  if (single_accept == -1) {
+    const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
+    single_accept = (val != NULL && atoi(val) != 0);  /* Off by default. */
+  }
+
+  if (single_accept)
+    tcp->flags |= UV_TCP_SINGLE_ACCEPT;
+
+  err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE);
+  if (err)
+    return err;
+
+#ifdef __MVS__
+  /* on zOS the listen call does not bind automatically
+     if the socket is unbound. Hence the manual binding to
+     an arbitrary port is required to be done manually
+  */
+
+  if (!(tcp->flags & UV_HANDLE_BOUND)) {
+    struct sockaddr_storage saddr;
+    socklen_t slen  = sizeof(saddr);
+    memset(&saddr, 0, sizeof(saddr));
+
+    if (getsockname(tcp->io_watcher.fd, (struct sockaddr*) &saddr, &slen))
+      return -errno;
+
+    if (bind(tcp->io_watcher.fd, (struct sockaddr*) &saddr, slen))
+      return -errno;
+
+    tcp->flags |= UV_HANDLE_BOUND;
+  }
+#endif
+
+  if (listen(tcp->io_watcher.fd, backlog))
+    return -errno;
+
+  tcp->connection_cb = cb;
+  tcp->flags |= UV_HANDLE_BOUND;
+
+  /* Start listening for connections. */
+  tcp->io_watcher.cb = uv__server_io;
+  uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
+
+  return 0;
+}
+
+
+int uv__tcp_nodelay(int fd, int on) {
+  if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)))
+    return -errno;
+  return 0;
+}
+
+
+int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
+  if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
+    return -errno;
+
+#ifdef TCP_KEEPIDLE
+  if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
+    return -errno;
+#endif
+
+  /* Solaris/SmartOS, if you don't support keep-alive,
+   * then don't advertise it in your system headers...
+   */
+  /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */
+#if defined(TCP_KEEPALIVE) && !defined(__sun)
+  if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
+    return -errno;
+#endif
+
+  return 0;
+}
+
+
+int uv_tcp_nodelay(uv_tcp_t* handle, int on) {
+  int err;
+
+  if (uv__stream_fd(handle) != -1) {
+    err = uv__tcp_nodelay(uv__stream_fd(handle), on);
+    if (err)
+      return err;
+  }
+
+  if (on)
+    handle->flags |= UV_TCP_NODELAY;
+  else
+    handle->flags &= ~UV_TCP_NODELAY;
+
+  return 0;
+}
+
+
+int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
+  int err;
+
+  if (uv__stream_fd(handle) != -1) {
+    err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay);
+    if (err)
+      return err;
+  }
+
+  if (on)
+    handle->flags |= UV_TCP_KEEPALIVE;
+  else
+    handle->flags &= ~UV_TCP_KEEPALIVE;
+
+  /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge
+   *      uv_tcp_t with an int that's almost never used...
+   */
+
+  return 0;
+}
+
+
+int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
+  if (enable)
+    handle->flags &= ~UV_TCP_SINGLE_ACCEPT;
+  else
+    handle->flags |= UV_TCP_SINGLE_ACCEPT;
+  return 0;
+}
+
+
+void uv__tcp_close(uv_tcp_t* handle) {
+  uv__stream_close((uv_stream_t*)handle);
+}
diff --git a/src/unix/thread.c b/src/unix/thread.c
new file mode 100644 (file)
index 0000000..3cc11be
--- /dev/null
@@ -0,0 +1,615 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+
+#include <pthread.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <unistd.h>  /* getpagesize() */
+
+#include <limits.h>
+
+#ifdef __MVS__
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#endif
+
+#undef NANOSEC
+#define NANOSEC ((uint64_t) 1e9)
+
+
+int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  int err;
+  pthread_attr_t* attr;
+#if defined(__APPLE__)
+  pthread_attr_t attr_storage;
+  struct rlimit lim;
+#endif
+
+  /* On OSX threads other than the main thread are created with a reduced stack
+   * size by default, adjust it to RLIMIT_STACK.
+   */
+#if defined(__APPLE__)
+  if (getrlimit(RLIMIT_STACK, &lim))
+    abort();
+
+  attr = &attr_storage;
+  if (pthread_attr_init(attr))
+    abort();
+
+  if (lim.rlim_cur != RLIM_INFINITY) {
+    /* pthread_attr_setstacksize() expects page-aligned values. */
+    lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
+
+    if (lim.rlim_cur >= PTHREAD_STACK_MIN)
+      if (pthread_attr_setstacksize(attr, lim.rlim_cur))
+        abort();
+  }
+#else
+  attr = NULL;
+#endif
+
+  err = pthread_create(tid, attr, (void*(*)(void*)) entry, arg);
+
+  if (attr != NULL)
+    pthread_attr_destroy(attr);
+
+  return -err;
+}
+
+
+uv_thread_t uv_thread_self(void) {
+  return pthread_self();
+}
+
+int uv_thread_join(uv_thread_t *tid) {
+  return -pthread_join(*tid, NULL);
+}
+
+
+int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
+  return pthread_equal(*t1, *t2);
+}
+
+
+int uv_mutex_init(uv_mutex_t* mutex) {
+#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
+  return -pthread_mutex_init(mutex, NULL);
+#else
+  pthread_mutexattr_t attr;
+  int err;
+
+  if (pthread_mutexattr_init(&attr))
+    abort();
+
+  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK))
+    abort();
+
+  err = pthread_mutex_init(mutex, &attr);
+
+  if (pthread_mutexattr_destroy(&attr))
+    abort();
+
+  return -err;
+#endif
+}
+
+
+void uv_mutex_destroy(uv_mutex_t* mutex) {
+  if (pthread_mutex_destroy(mutex))
+    abort();
+}
+
+
+void uv_mutex_lock(uv_mutex_t* mutex) {
+  if (pthread_mutex_lock(mutex))
+    abort();
+}
+
+
+int uv_mutex_trylock(uv_mutex_t* mutex) {
+  int err;
+
+  err = pthread_mutex_trylock(mutex);
+  if (err) {
+    if (err != EBUSY && err != EAGAIN)
+      abort();
+    return -EBUSY;
+  }
+
+  return 0;
+}
+
+
+void uv_mutex_unlock(uv_mutex_t* mutex) {
+  if (pthread_mutex_unlock(mutex))
+    abort();
+}
+
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+
+// No pthread_rwlock_t. Use pthread_mutex_t
+
+int uv_rwlock_init(uv_rwlock_t* rwlock) {
+  return uv_mutex_init(rwlock);
+}
+
+
+void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
+  uv_mutex_destroy(rwlock);
+}
+
+
+void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
+  uv_mutex_lock(rwlock);
+}
+
+
+int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
+  return uv_mutex_trylock(rwlock);
+}
+
+
+void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
+  uv_mutex_unlock(rwlock);
+}
+
+
+void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
+  uv_mutex_lock(rwlock);
+}
+
+
+int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
+  return uv_mutex_trylock(rwlock);
+}
+
+
+void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
+  uv_mutex_unlock(rwlock);
+}
+
+#else
+
+int uv_rwlock_init(uv_rwlock_t* rwlock) {
+  return -pthread_rwlock_init(rwlock, NULL);
+}
+
+
+void uv_rwlock_destroy(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_destroy(rwlock))
+    abort();
+}
+
+
+void uv_rwlock_rdlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_rdlock(rwlock))
+    abort();
+}
+
+
+int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) {
+  int err;
+
+  err = pthread_rwlock_tryrdlock(rwlock);
+  if (err) {
+    if (err != EBUSY && err != EAGAIN)
+      abort();
+    return -EBUSY;
+  }
+
+  return 0;
+}
+
+
+void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_unlock(rwlock))
+    abort();
+}
+
+
+void uv_rwlock_wrlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_wrlock(rwlock))
+    abort();
+}
+
+
+int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) {
+  int err;
+
+  err = pthread_rwlock_trywrlock(rwlock);
+  if (err) {
+    if (err != EBUSY && err != EAGAIN)
+      abort();
+    return -EBUSY;
+  }
+
+  return 0;
+}
+
+
+void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) {
+  if (pthread_rwlock_unlock(rwlock))
+    abort();
+}
+#endif /* End of !NUTTX */
+
+
+void uv_once(uv_once_t* guard, void (*callback)(void)) {
+  if (pthread_once(guard, callback))
+    abort();
+}
+
+#if defined(__APPLE__) && defined(__MACH__)
+
+int uv_sem_init(uv_sem_t* sem, unsigned int value) {
+  kern_return_t err;
+
+  err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value);
+  if (err == KERN_SUCCESS)
+    return 0;
+  if (err == KERN_INVALID_ARGUMENT)
+    return -EINVAL;
+  if (err == KERN_RESOURCE_SHORTAGE)
+    return -ENOMEM;
+
+  abort();
+  return -EINVAL;  /* Satisfy the compiler. */
+}
+
+
+void uv_sem_destroy(uv_sem_t* sem) {
+  if (semaphore_destroy(mach_task_self(), *sem))
+    abort();
+}
+
+
+void uv_sem_post(uv_sem_t* sem) {
+  if (semaphore_signal(*sem))
+    abort();
+}
+
+
+void uv_sem_wait(uv_sem_t* sem) {
+  int r;
+
+  do
+    r = semaphore_wait(*sem);
+  while (r == KERN_ABORTED);
+
+  if (r != KERN_SUCCESS)
+    abort();
+}
+
+
+int uv_sem_trywait(uv_sem_t* sem) {
+  mach_timespec_t interval;
+  kern_return_t err;
+
+  interval.tv_sec = 0;
+  interval.tv_nsec = 0;
+
+  err = semaphore_timedwait(*sem, interval);
+  if (err == KERN_SUCCESS)
+    return 0;
+  if (err == KERN_OPERATION_TIMED_OUT)
+    return -EAGAIN;
+
+  abort();
+  return -EINVAL;  /* Satisfy the compiler. */
+}
+
+#elif defined(__MVS__)
+
+int uv_sem_init(uv_sem_t* sem, unsigned int value) {
+  uv_sem_t semid;
+  struct sembuf buf;
+  int err;
+
+  buf.sem_num = 0;
+  buf.sem_op = value;
+  buf.sem_flg = 0;
+
+  semid = semget(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR);
+  if (semid == -1)
+    return -errno;
+
+  if (-1 == semop(semid, &buf, 1)) {
+    err = errno;
+    if (-1 == semctl(*sem, 0, IPC_RMID))
+      abort();
+    return -err;
+  }
+
+  *sem = semid;
+  return 0;
+}
+
+void uv_sem_destroy(uv_sem_t* sem) {
+  if (-1 == semctl(*sem, 0, IPC_RMID))
+    abort();
+}
+
+void uv_sem_post(uv_sem_t* sem) {
+  struct sembuf buf;
+
+  buf.sem_num = 0;
+  buf.sem_op = 1;
+  buf.sem_flg = 0;
+
+  if (-1 == semop(*sem, &buf, 1))
+    abort();
+}
+
+void uv_sem_wait(uv_sem_t* sem) {
+  struct sembuf buf;
+  int op_status;
+
+  buf.sem_num = 0;
+  buf.sem_op = -1;
+  buf.sem_flg = 0;
+
+  do
+    op_status = semop(*sem, &buf, 1);
+  while (op_status == -1 && errno == EINTR);
+
+  if (op_status)
+    abort();
+}
+
+int uv_sem_trywait(uv_sem_t* sem) {
+  struct sembuf buf;
+  int op_status;
+
+  buf.sem_num = 0;
+  buf.sem_op = -1;
+  buf.sem_flg = IPC_NOWAIT;
+
+  do
+    op_status = semop(*sem, &buf, 1);
+  while (op_status == -1 && errno == EINTR);
+
+  if (op_status) {
+    if (errno == EAGAIN)
+      return -EAGAIN;
+    abort();
+  }
+
+  return 0;
+}
+
+#else /* !(defined(__APPLE__) && defined(__MACH__)) */
+
+int uv_sem_init(uv_sem_t* sem, unsigned int value) {
+  if (sem_init(sem, 0, value))
+    return -errno;
+  return 0;
+}
+
+
+void uv_sem_destroy(uv_sem_t* sem) {
+  if (sem_destroy(sem))
+    abort();
+}
+
+
+void uv_sem_post(uv_sem_t* sem) {
+  if (sem_post(sem))
+    abort();
+}
+
+
+void uv_sem_wait(uv_sem_t* sem) {
+  int r;
+
+  do
+    r = sem_wait(sem);
+  while (r == -1 && errno == EINTR);
+
+  if (r)
+    abort();
+}
+
+
+int uv_sem_trywait(uv_sem_t* sem) {
+  int r;
+
+  do
+    r = sem_trywait(sem);
+  while (r == -1 && errno == EINTR);
+
+  if (r) {
+    if (errno == EAGAIN)
+      return -EAGAIN;
+    abort();
+  }
+
+  return 0;
+}
+
+#endif /* defined(__APPLE__) && defined(__MACH__) */
+
+
+#if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__)
+
+int uv_cond_init(uv_cond_t* cond) {
+  return -pthread_cond_init(cond, NULL);
+}
+
+#else /* !(defined(__APPLE__) && defined(__MACH__)) */
+
+int uv_cond_init(uv_cond_t* cond) {
+  pthread_condattr_t attr;
+  int err;
+
+  err = pthread_condattr_init(&attr);
+  if (err)
+    return -err;
+
+#if !(defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) \
+    && !(defined(__NUTTX__) || defined(__TIZENRT__))
+  err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+  if (err)
+    goto error2;
+#endif
+
+  err = pthread_cond_init(cond, &attr);
+  if (err)
+    goto error2;
+
+  err = pthread_condattr_destroy(&attr);
+  if (err)
+    goto error;
+
+  return 0;
+
+error:
+  pthread_cond_destroy(cond);
+error2:
+  pthread_condattr_destroy(&attr);
+  return -err;
+}
+
+#endif /* defined(__APPLE__) && defined(__MACH__) */
+
+void uv_cond_destroy(uv_cond_t* cond) {
+#if defined(__APPLE__) && defined(__MACH__)
+  /* It has been reported that destroying condition variables that have been
+   * signalled but not waited on can sometimes result in application crashes.
+   * See https://codereview.chromium.org/1323293005.
+   */
+  pthread_mutex_t mutex;
+  struct timespec ts;
+  int err;
+
+  if (pthread_mutex_init(&mutex, NULL))
+    abort();
+
+  if (pthread_mutex_lock(&mutex))
+    abort();
+
+  ts.tv_sec = 0;
+  ts.tv_nsec = 1;
+
+  err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts);
+  if (err != 0 && err != ETIMEDOUT)
+    abort();
+
+  if (pthread_mutex_unlock(&mutex))
+    abort();
+
+  if (pthread_mutex_destroy(&mutex))
+    abort();
+#endif /* defined(__APPLE__) && defined(__MACH__) */
+
+  if (pthread_cond_destroy(cond))
+    abort();
+}
+
+void uv_cond_signal(uv_cond_t* cond) {
+  if (pthread_cond_signal(cond))
+    abort();
+}
+
+void uv_cond_broadcast(uv_cond_t* cond) {
+  if (pthread_cond_broadcast(cond))
+    abort();
+}
+
+void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
+  if (pthread_cond_wait(cond, mutex))
+    abort();
+}
+
+
+int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
+  int r;
+  struct timespec ts;
+
+#if defined(__APPLE__) && defined(__MACH__)
+  ts.tv_sec = timeout / NANOSEC;
+  ts.tv_nsec = timeout % NANOSEC;
+  r = pthread_cond_timedwait_relative_np(cond, mutex, &ts);
+#else
+  timeout += uv__hrtime(UV_CLOCK_PRECISE);
+  ts.tv_sec = timeout / NANOSEC;
+  ts.tv_nsec = timeout % NANOSEC;
+#if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
+  /*
+   * The bionic pthread implementation doesn't support CLOCK_MONOTONIC,
+   * but has this alternative function instead.
+   */
+  r = pthread_cond_timedwait_monotonic_np(cond, mutex, &ts);
+#else
+  r = pthread_cond_timedwait(cond, mutex, &ts);
+#endif /* __ANDROID__ */
+#endif
+
+
+  if (r == 0)
+    return 0;
+
+  if (r == ETIMEDOUT)
+    return -ETIMEDOUT;
+
+  abort();
+  return -EINVAL;  /* Satisfy the compiler. */
+}
+
+
+#ifdef TUV_FEATURE_PIPE
+int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
+  return -pthread_barrier_init(barrier, NULL, count);
+}
+
+
+void uv_barrier_destroy(uv_barrier_t* barrier) {
+  if (pthread_barrier_destroy(barrier))
+    abort();
+}
+
+
+int uv_barrier_wait(uv_barrier_t* barrier) {
+  int r = pthread_barrier_wait(barrier);
+  if (r && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    abort();
+  return r == PTHREAD_BARRIER_SERIAL_THREAD;
+}
+#endif
diff --git a/src/unix/timer.c b/src/unix/timer.c
new file mode 100644 (file)
index 0000000..c8ee07e
--- /dev/null
@@ -0,0 +1,210 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+#include "heap-inl.h"
+
+#include <assert.h>
+#include <limits.h>
+
+
+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;
+
+#ifndef TUV_ENABLE_MEMORY_CONSTRAINTS
+  /* Compare start_id when both have the same timeout. start_id is
+   * allocated with loop->timer_counter in uv_timer_start().
+   */
+  if (a->start_id < b->start_id)
+    return 1;
+  if (b->start_id < a->start_id)
+    return 0;
+#endif
+
+  return 0;
+}
+
+
+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;
+}
+
+void tuv_timer_deinit(uv_loop_t* loop, uv_timer_t* handle) {
+  QUEUE* q;
+  uv_handle_t* h;
+  uv_handle_t* ht;
+  ht = (uv_handle_t*)handle;
+
+  QUEUE_FOREACH(q, &loop->handle_queue) {
+    h = QUEUE_DATA(q, uv_handle_t, handle_queue);
+    if (h == ht) {
+      tuv__handle_deinit(ht);
+      break;
+    }
+  }
+}
+
+int uv_timer_start(uv_timer_t* handle,
+                   uv_timer_cb cb,
+                   uint64_t timeout,
+                   uint64_t repeat) {
+  uint64_t clamped_timeout;
+
+  if (cb == NULL)
+    return -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;
+#ifndef TUV_ENABLE_MEMORY_CONSTRAINTS
+  /* start_id is the second index to be compared in uv__timer_cmp() */
+  handle->start_id = handle->loop->timer_counter++;
+#endif
+
+  heap_insert((struct heap*) &handle->loop->timer_heap,
+              (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;
+
+#ifdef TUV_ENABLE_MEMORY_CONSTRAINTS
+  heap_remove((struct heap*) &handle->loop->timer_heap,
+              (struct heap_node*) &handle->heap_node);
+#else /* original libuv code */
+  heap_remove((struct heap*) &handle->loop->timer_heap,
+              (struct heap_node*) &handle->heap_node,
+              timer_less_than);
+#endif
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_timer_again(uv_timer_t* handle) {
+  if (handle->timer_cb == NULL)
+    return -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((const struct heap*) &loop->timer_heap);
+  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 diff;
+}
+
+
+void uv__run_timers(uv_loop_t* loop) {
+  struct heap_node* heap_node;
+  uv_timer_t* handle;
+
+  for (;;) {
+    heap_node = heap_min((struct heap*) &loop->timer_heap);
+    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/src/unix/tizenrt.c b/src/unix/tizenrt.c
new file mode 100644 (file)
index 0000000..b8b8e34
--- /dev/null
@@ -0,0 +1,274 @@
+/* Copyright 2017-present Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+
+#include <uv.h>
+#include "unix/internal.h"
+
+//-----------------------------------------------------------------------------
+// loop
+int uv__platform_loop_init(uv_loop_t* loop) {
+  loop->npollfds = 0;
+  return 0;
+}
+
+void uv__platform_loop_delete(uv_loop_t* loop) {
+  loop->npollfds = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
+  int i;
+  int nfd = loop->npollfds;
+  for (i = 0; i < nfd; ++i) {
+    struct pollfd* pfd = &loop->pollfds[i];
+    if (fd == pfd->fd) {
+      pfd->fd = -1;
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+
+/* should not clear handle structure with memset as
+ * *data can be set before calling init function */
+
+//-----------------------------------------------------------------------------
+
+ssize_t readv(int fd, const struct iovec* iiovec, int count) {
+  ssize_t result = 0;
+  ssize_t total = 0;
+  int idx;
+
+  for (idx = 0; idx < count; ++idx) {
+    result = read(fd, iiovec[idx].iov_base, iiovec[idx].iov_len);
+    if (result < 0) {
+      return result;
+    } else {
+      total += result;
+    }
+  }
+  return total;
+}
+
+
+ssize_t writev(int fd, const struct iovec* iiovec, int count) {
+  ssize_t result = 0;
+  ssize_t total = 0;
+  int idx;
+
+  for (idx = 0; idx < count; ++idx) {
+    result = write(fd, iiovec[idx].iov_base, iiovec[idx].iov_len);
+    if (result < 0) {
+      return result;
+    } else {
+      total += result;
+    }
+  }
+  return total;
+}
+
+// From nuttx_clock.c
+uint64_t uv__hrtime(uv_clocktype_t type) {
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  uint64_t ret = (((uint64_t) ts.tv_sec) * ((uint64_t) 1e9) +
+                   (uint64_t) ts.tv_nsec);
+  return ret;
+}
+
+// From nuttx_io.c
+static void uv__add_pollfd(uv_loop_t* loop, struct pollfd* pe) {
+  int i;
+  bool exist = false;
+  int free_idx = -1;
+  for (i = 0; i < loop->npollfds; ++i) {
+    struct pollfd* cur = &loop->pollfds[i];
+    if (cur->fd == pe->fd) {
+      cur->events = pe->events;
+      exist = true;
+      break;
+    }
+    if (cur->fd == -1) {
+      free_idx = i;
+    }
+  }
+  if (!exist) {
+    if (free_idx == -1) {
+      free_idx = loop->npollfds++;
+      if (free_idx >= TUV_POLL_EVENTS_SIZE)
+      {
+        TDLOG("uv__add_pollfd abort, because loop->npollfds (%d) reached maximum size", free_idx);
+        ABORT();
+      }
+    }
+    struct pollfd* cur = &loop->pollfds[free_idx];
+
+    cur->fd = pe->fd;
+    cur->events = pe->events;
+    cur->revents = 0;
+    cur->sem = 0;
+    cur->priv = 0;
+  }
+}
+
+
+static void uv__rem_pollfd(uv_loop_t* loop, struct pollfd* pe) {
+  int i = 0;
+  while (i < loop->npollfds) {
+    struct pollfd* cur = &loop->pollfds[i];
+    if (cur->fd == pe->fd) {
+      *cur = loop->pollfds[--loop->npollfds];
+    } else {
+      ++i;
+    }
+  }
+}
+
+
+void uv__io_poll(uv_loop_t* loop, int timeout) {
+  struct pollfd pfd;
+  struct pollfd* pe;
+  QUEUE* q;
+  uv__io_t* w;
+  uint64_t base;
+  uint64_t diff;
+  int nevents;
+  int count;
+  int nfd;
+  int i;
+
+  if (loop->nfds == 0) {
+    assert(QUEUE_EMPTY(&loop->watcher_queue));
+    return;
+  }
+
+  while (!QUEUE_EMPTY(&loop->watcher_queue)) {
+    q = QUEUE_HEAD(&loop->watcher_queue);
+    QUEUE_REMOVE(q);
+    QUEUE_INIT(q);
+
+    w = QUEUE_DATA(q, uv__io_t, watcher_queue);
+    assert(w->pevents != 0);
+    assert(w->fd >= 0);
+    assert(w->fd < (int)loop->nwatchers);
+
+    pfd.fd = w->fd;
+    pfd.events = w->pevents;
+    uv__add_pollfd(loop, &pfd);
+
+    w->events = w->pevents;
+  }
+
+  assert(timeout >= -1);
+  base = loop->time;
+  count = 5;
+
+  for (;;) {
+    nfd = poll(loop->pollfds, loop->npollfds, timeout);
+
+    SAVE_ERRNO(uv__update_time(loop));
+
+    if (nfd == 0) {
+      assert(timeout != -1);
+      return;
+    }
+
+    if (nfd == -1) {
+      int err = get_errno();
+      if (err == EAGAIN) {
+        set_errno(0);
+      }
+      else if (err != EINTR) {
+        // poll of which the watchers is null should be removed
+        TDLOG("uv__io_poll abort for errno(%d)", err);
+        goto handle_poll;
+      }
+      if (timeout == -1) {
+        continue;
+      }
+      if (timeout == 0) {
+        return;
+      }
+      goto update_timeout;
+    }
+
+handle_poll:
+    nevents = 0;
+
+    for (i = 0; i < loop->npollfds; ++i) {
+      pe = &loop->pollfds[i];
+      w = loop->watchers[pe->fd];
+
+      if (w == NULL) {
+        uv__rem_pollfd(loop, pe);
+        --i;
+        continue;
+      }
+
+      if (pe->fd >= 0 && (pe->revents & (POLLIN | POLLOUT | POLLHUP))) {    
+        w->cb(loop, w, pe->revents);
+        ++nevents;
+      }
+    }
+
+    if (nevents != 0) {
+      if (--count != 0) {
+        timeout = 0;
+        continue;
+      }
+      return;
+    }
+    if (timeout == 0) {
+      return;
+    }
+    if (timeout == -1) {
+      continue;
+    }
+update_timeout:
+    assert(timeout > 0);
+
+    diff = loop->time - base;
+    if (diff >= (uint64_t)timeout) {
+      return;
+    }
+    timeout -= diff;
+  }
+}
diff --git a/src/unix/tty.c b/src/unix/tty.c
new file mode 100644 (file)
index 0000000..b2d37f4
--- /dev/null
@@ -0,0 +1,336 @@
+/* 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"
+#include "spinlock.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <termios.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#if defined(__MVS__) && !defined(IMAXBEL)
+#define IMAXBEL 0
+#endif
+
+static int orig_termios_fd = -1;
+static struct termios orig_termios;
+static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
+
+static int uv__tty_is_slave(const int fd) {
+  int result;
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+  int dummy;
+
+  result = ioctl(fd, TIOCGPTN, &dummy) != 0;
+#elif defined(__APPLE__)
+  char dummy[256];
+
+  result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
+#else
+  /* Fallback to ptsname
+   */
+  result = ptsname(fd) == NULL;
+#endif
+  return result;
+}
+
+int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) {
+  uv_handle_type type;
+  int flags;
+  int newfd;
+  int r;
+  int saved_flags;
+  char path[256];
+
+  /* File descriptors that refer to files cannot be monitored with epoll.
+   * That restriction also applies to character devices like /dev/random
+   * (but obviously not /dev/tty.)
+   */
+  type = uv_guess_handle(fd);
+  if (type == UV_FILE || type == UV_UNKNOWN_HANDLE)
+    return -EINVAL;
+
+  flags = 0;
+  newfd = -1;
+
+  /* Reopen the file descriptor when it refers to a tty. This lets us put the
+   * tty in non-blocking mode without affecting other processes that share it
+   * with us.
+   *
+   * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also
+   * affects fd 1 of `cat` because both file descriptors refer to the same
+   * struct file in the kernel. When we reopen our fd 0, it points to a
+   * different struct file, hence changing its properties doesn't affect
+   * other processes.
+   */
+  if (type == UV_TTY) {
+    /* Reopening a pty in master mode won't work either because the reopened
+     * pty will be in slave mode (*BSD) or reopening will allocate a new
+     * master/slave pair (Linux). Therefore check if the fd points to a
+     * slave device.
+     */
+    if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
+      r = uv__open_cloexec(path, O_RDWR);
+    else
+      r = -1;
+
+    if (r < 0) {
+      /* fallback to using blocking writes */
+      if (!readable)
+        flags |= UV_STREAM_BLOCKING;
+      goto skip;
+    }
+
+    newfd = r;
+
+    r = uv__dup2_cloexec(newfd, fd);
+    if (r < 0 && r != -EINVAL) {
+      /* EINVAL means newfd == fd which could conceivably happen if another
+       * thread called close(fd) between our calls to isatty() and open().
+       * That's a rather unlikely event but let's handle it anyway.
+       */
+      uv__close(newfd);
+      return r;
+    }
+
+    fd = newfd;
+  }
+
+#if defined(__APPLE__)
+  /* Save the fd flags in case we need to restore them due to an error. */
+  do
+    saved_flags = fcntl(fd, F_GETFL);
+  while (saved_flags == -1 && errno == EINTR);
+
+  if (saved_flags == -1) {
+    if (newfd != -1)
+      uv__close(newfd);
+    return -errno;
+  }
+#endif
+
+  /* Pacify the compiler. */
+  (void) &saved_flags;
+
+skip:
+  uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
+
+  /* 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 (!(flags & UV_STREAM_BLOCKING))
+    uv__nonblock(fd, 1);
+
+#if defined(__APPLE__)
+  r = uv__stream_try_select((uv_stream_t*) tty, &fd);
+  if (r) {
+    int rc = r;
+    if (newfd != -1)
+      uv__close(newfd);
+    QUEUE_REMOVE(&tty->handle_queue);
+    do
+      r = fcntl(fd, F_SETFL, saved_flags);
+    while (r == -1 && errno == EINTR);
+    return rc;
+  }
+#endif
+
+  if (readable)
+    flags |= UV_STREAM_READABLE;
+  else
+    flags |= UV_STREAM_WRITABLE;
+
+  uv__stream_open((uv_stream_t*) tty, fd, flags);
+  tty->mode = UV_TTY_MODE_NORMAL;
+
+  return 0;
+}
+
+static void uv__tty_make_raw(struct termios* tio) {
+  assert(tio != NULL);
+
+#if defined __sun || defined __MVS__
+  /*
+   * This implementation of cfmakeraw for Solaris and derivatives is taken from
+   * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
+   */
+  tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR |
+                    IGNCR | ICRNL | IXON);
+  tio->c_oflag &= ~OPOST;
+  tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+  tio->c_cflag &= ~(CSIZE | PARENB);
+  tio->c_cflag |= CS8;
+#else
+  cfmakeraw(tio);
+#endif /* #ifdef __sun */
+}
+
+int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
+  struct termios tmp;
+  int fd;
+
+  if (tty->mode == (int) mode)
+    return 0;
+
+  fd = uv__stream_fd(tty);
+  if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) {
+    if (tcgetattr(fd, &tty->orig_termios))
+      return -errno;
+
+    /* This is used for uv_tty_reset_mode() */
+    uv_spinlock_lock(&termios_spinlock);
+    if (orig_termios_fd == -1) {
+      orig_termios = tty->orig_termios;
+      orig_termios_fd = fd;
+    }
+    uv_spinlock_unlock(&termios_spinlock);
+  }
+
+  tmp = tty->orig_termios;
+  switch (mode) {
+    case UV_TTY_MODE_NORMAL:
+      break;
+    case UV_TTY_MODE_RAW:
+      tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+      tmp.c_oflag |= (ONLCR);
+      tmp.c_cflag |= (CS8);
+      tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+      tmp.c_cc[VMIN] = 1;
+      tmp.c_cc[VTIME] = 0;
+      break;
+    case UV_TTY_MODE_IO:
+      uv__tty_make_raw(&tmp);
+      break;
+  }
+
+  /* Apply changes after draining */
+  if (tcsetattr(fd, TCSADRAIN, &tmp))
+    return -errno;
+
+  tty->mode = mode;
+  return 0;
+}
+
+
+int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
+  struct winsize ws;
+  int err;
+
+  do
+    err = ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws);
+  while (err == -1 && errno == EINTR);
+
+  if (err == -1)
+    return -errno;
+
+  *width = ws.ws_col;
+  *height = ws.ws_row;
+
+  return 0;
+}
+
+
+uv_handle_type uv_guess_handle(uv_file file) {
+  struct sockaddr sa;
+  struct stat s;
+  socklen_t len;
+  int type;
+
+  if (file < 0)
+    return UV_UNKNOWN_HANDLE;
+
+  if (isatty(file))
+    return UV_TTY;
+
+  if (fstat(file, &s))
+    return UV_UNKNOWN_HANDLE;
+
+  if (S_ISREG(s.st_mode))
+    return UV_FILE;
+
+  if (S_ISCHR(s.st_mode))
+    return UV_FILE;  /* XXX UV_NAMED_PIPE? */
+
+  if (S_ISFIFO(s.st_mode))
+    return UV_NAMED_PIPE;
+
+  if (!S_ISSOCK(s.st_mode))
+    return UV_UNKNOWN_HANDLE;
+
+  len = sizeof(type);
+  if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
+    return UV_UNKNOWN_HANDLE;
+
+  len = sizeof(sa);
+  if (getsockname(file, &sa, &len))
+    return UV_UNKNOWN_HANDLE;
+
+  if (type == SOCK_DGRAM)
+    if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
+      return UV_UDP;
+
+  if (type == SOCK_STREAM) {
+#if defined(_AIX) || defined(__DragonFly__)
+    /* on AIX/DragonFly the getsockname call returns an empty sa structure
+     * for sockets of type AF_UNIX.  For all other types it will
+     * return a properly filled in structure.
+     */
+    if (len == 0)
+      return UV_NAMED_PIPE;
+#endif /* defined(_AIX) || defined(__DragonFly__) */
+
+    if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
+      return UV_TCP;
+    if (sa.sa_family == AF_UNIX)
+      return UV_NAMED_PIPE;
+  }
+
+  return UV_UNKNOWN_HANDLE;
+}
+
+
+/* This function is async signal-safe, meaning that it's safe to call from
+ * inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s
+ * critical section when the signal was raised.
+ */
+int uv_tty_reset_mode(void) {
+  int saved_errno;
+  int err;
+
+  saved_errno = errno;
+  if (!uv_spinlock_trylock(&termios_spinlock))
+    return -EBUSY;  /* In uv_tty_set_mode(). */
+
+  err = 0;
+  if (orig_termios_fd != -1)
+    if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios))
+      err = -errno;
+
+  uv_spinlock_unlock(&termios_spinlock);
+  errno = saved_errno;
+
+  return err;
+}
diff --git a/src/unix/udp.c b/src/unix/udp.c
new file mode 100644 (file)
index 0000000..8d10afc
--- /dev/null
@@ -0,0 +1,1006 @@
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Copyright 2015-2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if defined(__MVS__)
+#include <xti.h>
+#endif
+
+#if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP)
+# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+
+#if defined(IPV6_LEAVE_GROUP) && !defined(IPV6_DROP_MEMBERSHIP)
+# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+#endif
+
+#if defined(__TIZENRT__)
+/* Although TizenRT does not support IPv6, in order to minimize
+ * libtuv source change, I added following macro definitions.
+ */
+#define IPV6_UNICAST_HOPS   16
+#define IPV6_MULTICAST_IF   17
+#define IPV6_MULTICAST_HOPS 18
+#define IPV6_MULTICAST_LOOP 19
+#endif
+
+static void uv__udp_run_completed(uv_udp_t* handle);
+static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
+static void uv__udp_recvmsg(uv_udp_t* handle);
+static void uv__udp_sendmsg(uv_udp_t* handle);
+static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
+                                       int domain,
+                                       unsigned int flags);
+
+
+void uv__udp_close(uv_udp_t* handle) {
+  uv__io_close(handle->loop, &handle->io_watcher);
+  uv__handle_stop(handle);
+
+  if (handle->io_watcher.fd != -1) {
+    uv__close(handle->io_watcher.fd);
+    handle->io_watcher.fd = -1;
+  }
+}
+
+
+void uv__udp_finish_close(uv_udp_t* handle) {
+  uv_udp_send_t* req;
+  QUEUE* q;
+
+  assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT));
+  assert(handle->io_watcher.fd == -1);
+
+  while (!QUEUE_EMPTY(&handle->write_queue)) {
+    q = QUEUE_HEAD(&handle->write_queue);
+    QUEUE_REMOVE(q);
+
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    req->status = -ECANCELED;
+    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+  }
+
+  uv__udp_run_completed(handle);
+
+  assert(handle->send_queue_size == 0);
+  assert(handle->send_queue_count == 0);
+
+  /* Now tear down the handle. */
+  handle->recv_cb = NULL;
+  handle->alloc_cb = NULL;
+  /* but _do not_ touch close_cb */
+}
+
+
+static void uv__udp_run_completed(uv_udp_t* handle) {
+  uv_udp_send_t* req;
+  QUEUE* q;
+
+  assert(!(handle->flags & UV_UDP_PROCESSING));
+  handle->flags |= UV_UDP_PROCESSING;
+
+  while (!QUEUE_EMPTY(&handle->write_completed_queue)) {
+    q = QUEUE_HEAD(&handle->write_completed_queue);
+    QUEUE_REMOVE(q);
+
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    uv__req_unregister(handle->loop, req);
+
+    handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
+    handle->send_queue_count--;
+
+    if (req->bufs != req->bufsml)
+      uv__free(req->bufs);
+    req->bufs = NULL;
+
+    if (req->send_cb == NULL)
+      continue;
+
+    /* req->status >= 0 == bytes written
+     * req->status <  0 == errno
+     */
+    if (req->status >= 0)
+      req->send_cb(req, 0);
+    else
+      req->send_cb(req, req->status);
+  }
+
+  if (QUEUE_EMPTY(&handle->write_queue)) {
+    /* Pending queue and completion queue empty, stop watcher. */
+    uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT);
+    if (!uv__io_active(&handle->io_watcher, POLLIN))
+      uv__handle_stop(handle);
+  }
+
+  handle->flags &= ~UV_UDP_PROCESSING;
+}
+
+
+static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
+  uv_udp_t* handle;
+
+  handle = container_of(w, uv_udp_t, io_watcher);
+  assert(handle->type == UV_UDP);
+
+  if (revents & POLLIN)
+    uv__udp_recvmsg(handle);
+
+  if (revents & POLLOUT) {
+    uv__udp_sendmsg(handle);
+    uv__udp_run_completed(handle);
+  }
+}
+
+
+static void uv__udp_recvmsg(uv_udp_t* handle) {
+  struct sockaddr_storage peer;
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  struct msghdr h;
+#else
+  socklen_t addrlen;
+#endif
+  ssize_t nread;
+  uv_buf_t buf;
+  int flags;
+  int count;
+
+  assert(handle->recv_cb != NULL);
+  assert(handle->alloc_cb != NULL);
+
+  /* Prevent loop starvation when the data comes in as fast as (or faster than)
+   * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
+   */
+  count = 32;
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  memset(&h, 0, sizeof(h));
+  h.msg_name = &peer;
+#endif
+
+  do {
+    buf = uv_buf_init(NULL, 0);
+    handle->alloc_cb((uv_handle_t*) handle, 64 * 1024, &buf);
+    if (buf.base == NULL || buf.len == 0) {
+      handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0);
+      return;
+    }
+    assert(buf.base != NULL);
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+    h.msg_namelen = sizeof(peer);
+    h.msg_iov = (void*) &buf;
+    h.msg_iovlen = 1;
+
+    do {
+      nread = recvmsg(handle->io_watcher.fd, &h, 0);
+    }
+    while (nread == -1 && errno == EINTR);
+#else
+    addrlen = sizeof(peer);
+    do {
+      nread = recvfrom(handle->io_watcher.fd, buf.base, buf.len, 0,
+                       (struct sockaddr*)&peer, &addrlen);
+    }
+    while (nread == -1 && errno == EINTR);
+#endif
+
+    if (nread == -1) {
+      if (errno == EAGAIN || errno == EWOULDBLOCK)
+        handle->recv_cb(handle, 0, &buf, NULL, 0);
+      else
+        handle->recv_cb(handle, -errno, &buf, NULL, 0);
+    }
+    else {
+      const struct sockaddr *addr;
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+      if (h.msg_namelen == 0)
+#else
+      if (addrlen == 0)
+#endif
+        addr = NULL;
+      else
+        addr = (const struct sockaddr*) &peer;
+
+      flags = 0;
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+      if (h.msg_flags & MSG_TRUNC)
+#else
+      if (buf.len > nread)
+#endif
+        flags |= UV_UDP_PARTIAL;
+
+      handle->recv_cb(handle, nread, &buf, addr, flags);
+    }
+  }
+  /* recv_cb callback may decide to pause or close the handle */
+  while (nread != -1
+      && count-- > 0
+      && handle->io_watcher.fd != -1
+      && handle->recv_cb != NULL);
+}
+
+
+static void uv__udp_sendmsg(uv_udp_t* handle) {
+  uv_udp_send_t* req;
+  QUEUE* q;
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  struct msghdr h;
+#endif
+  ssize_t size;
+
+  while (!QUEUE_EMPTY(&handle->write_queue)) {
+    q = QUEUE_HEAD(&handle->write_queue);
+    assert(q != NULL);
+
+    req = QUEUE_DATA(q, uv_udp_send_t, queue);
+    assert(req != NULL);
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+    memset(&h, 0, sizeof h);
+    h.msg_name = &req->addr;
+    h.msg_namelen = (req->addr.ss_family == AF_INET6 ?
+      sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+    h.msg_iov = (struct iovec*) req->bufs;
+    h.msg_iovlen = req->nbufs;
+
+    do {
+      size = sendmsg(handle->io_watcher.fd, &h, 0);
+    } while (size == -1 && errno == EINTR);
+#else
+    assert(req->nbufs == 1);
+    do {
+      size = sendto(handle->io_watcher.fd, req->bufs->base,
+                    req->bufs->len, 0, (struct sockaddr*)&req->addr,
+                    sizeof(req->addr));
+    } while (size == -1 && errno == EINTR);
+#endif
+    if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+      break;
+
+    req->status = (size == -1 ? -errno : size);
+
+    /* Sending a datagram is an atomic operation: either all data
+     * is written or nothing is (and EMSGSIZE is raised). That is
+     * why we don't handle partial writes. Just pop the request
+     * off the write queue and onto the completed queue, done.
+     */
+    QUEUE_REMOVE(&req->queue);
+    QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue);
+    uv__io_feed(handle->loop, &handle->io_watcher);
+  }
+}
+
+
+/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
+ * refinements for programs that use multicast.
+ *
+ * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
+ * are different from the BSDs: it _shares_ the port rather than steal it
+ * from the current listener.  While useful, it's not something we can emulate
+ * on other platforms so we don't enable it.
+ */
+static int uv__set_reuse(int fd) {
+  int yes;
+
+#if defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__TIZENRT__)
+  yes = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
+    return -errno;
+#else
+  yes = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
+    return -errno;
+#endif
+
+  return 0;
+}
+
+
+int uv__udp_bind(uv_udp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int addrlen,
+                 unsigned int flags) {
+  int err;
+  int yes;
+  int fd;
+
+  /* Check for bad flags. */
+  if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR))
+    return -EINVAL;
+
+  /* Cannot set IPv6-only mode on non-IPv6 socket. */
+  if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6)
+    return -EINVAL;
+
+  fd = handle->io_watcher.fd;
+  if (fd == -1) {
+    err = uv__socket(addr->sa_family, SOCK_DGRAM, 0);
+    if (err < 0)
+      return err;
+    fd = err;
+    handle->io_watcher.fd = fd;
+  }
+
+  if (flags & UV_UDP_REUSEADDR) {
+    err = uv__set_reuse(fd);
+    if (err)
+      goto out;
+  }
+
+  if (flags & UV_UDP_IPV6ONLY) {
+#ifdef IPV6_V6ONLY
+    yes = 1;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) {
+      err = -errno;
+      goto out;
+    }
+#else
+    err = -ENOTSUP;
+    goto out;
+#endif
+  }
+
+  if (bind(fd, addr, addrlen)) {
+    err = -errno;
+    if (errno == EAFNOSUPPORT)
+      /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
+       * socket created with AF_INET to an AF_INET6 address or vice versa. */
+      err = -EINVAL;
+    goto out;
+  }
+
+  if (addr->sa_family == AF_INET6)
+    handle->flags |= UV_HANDLE_IPV6;
+
+  handle->flags |= UV_HANDLE_BOUND;
+
+  return 0;
+
+out:
+  uv__close(handle->io_watcher.fd);
+  handle->io_watcher.fd = -1;
+  return err;
+}
+
+
+static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
+                                       int domain,
+                                       unsigned int flags) {
+  unsigned char taddr[sizeof(struct sockaddr_in6)];
+  socklen_t addrlen;
+
+  if (handle->io_watcher.fd != -1)
+    return 0;
+
+  switch (domain) {
+  case AF_INET:
+  {
+    struct sockaddr_in* addr = (void*)&taddr;
+    memset(addr, 0, sizeof *addr);
+    addr->sin_family = AF_INET;
+    addr->sin_addr.s_addr = INADDR_ANY;
+    addrlen = sizeof *addr;
+    break;
+  }
+#if !defined(__TIZENRT__) || defined(CONFIG_NET_IPv6)
+  case AF_INET6:
+  {
+    struct sockaddr_in6* addr = (void*)&taddr;
+    memset(addr, 0, sizeof *addr);
+    addr->sin6_family = AF_INET6;
+    addr->sin6_addr = in6addr_any;
+    addrlen = sizeof *addr;
+    break;
+  }
+#endif
+  default:
+    assert(0 && "unsupported address family");
+    abort();
+  }
+
+  return uv__udp_bind(handle, (const struct sockaddr*) &taddr, addrlen, flags);
+}
+
+
+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 err;
+  int empty_queue;
+
+  assert(nbufs > 0);
+
+  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+  if (err)
+    return err;
+
+  /* It's legal for send_queue_count > 0 even when the write_queue is empty;
+   * it means there are error-state requests in the write_completed_queue that
+   * will touch up send_queue_size/count later.
+   */
+  empty_queue = (handle->send_queue_count == 0);
+
+  uv__req_init(handle->loop, req, UV_UDP_SEND);
+  assert(addrlen <= sizeof(req->addr));
+  memcpy(&req->addr, addr, addrlen);
+  req->send_cb = send_cb;
+  req->handle = handle;
+  req->nbufs = nbufs;
+
+  req->bufs = req->bufsml;
+  if (nbufs > ARRAY_SIZE(req->bufsml))
+    req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
+
+  if (req->bufs == NULL) {
+    uv__req_unregister(handle->loop, req);
+    return -ENOMEM;
+  }
+
+  memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0]));
+  handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs);
+  handle->send_queue_count++;
+  QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue);
+  uv__handle_start(handle);
+
+  if (empty_queue && !(handle->flags & UV_UDP_PROCESSING)) {
+    uv__udp_sendmsg(handle);
+  } else {
+    uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
+  }
+
+  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) {
+  int err;
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  struct msghdr h;
+#endif
+  ssize_t size;
+
+  assert(nbufs > 0);
+
+  /* already sending a message */
+  if (handle->send_queue_count != 0)
+    return -EAGAIN;
+
+  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+  if (err)
+    return err;
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  memset(&h, 0, sizeof h);
+  h.msg_name = (struct sockaddr*) addr;
+  h.msg_namelen = addrlen;
+  h.msg_iov = (struct iovec*) bufs;
+  h.msg_iovlen = nbufs;
+
+  do {
+    size = sendmsg(handle->io_watcher.fd, &h, 0);
+  } while (size == -1 && errno == EINTR);
+#else
+  assert(nbufs == 1);
+  do {
+    size = sendto(handle->io_watcher.fd, bufs->base, bufs->len,
+                  0, addr, addrlen);
+  } while (size == -1 && errno == EINTR);
+#endif
+
+  if (size == -1) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK)
+      return -EAGAIN;
+    else
+      return -errno;
+  }
+
+  return size;
+}
+
+
+#if !defined(__NUTTX__)
+static int uv__udp_set_membership4(uv_udp_t* handle,
+                                   const struct sockaddr_in* multicast_addr,
+                                   const char* interface_addr,
+                                   uv_membership membership) {
+  struct ip_mreq mreq;
+  int optname;
+  int 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 -EINVAL;
+  }
+
+  if (setsockopt(handle->io_watcher.fd,
+                 IPPROTO_IP,
+                 optname,
+                 &mreq,
+                 sizeof(mreq))) {
+#if defined(__MVS__)
+  if (errno == ENXIO)
+    return -ENODEV;
+#endif
+    return -errno;
+  }
+
+  return 0;
+}
+#endif /* !defined(__NUTTX__) */
+
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+static int uv__udp_set_membership6(uv_udp_t* handle,
+                                   const struct sockaddr_in6* multicast_addr,
+                                   const char* interface_addr,
+                                   uv_membership membership) {
+  int optname;
+  struct ipv6_mreq mreq;
+  struct sockaddr_in6 addr6;
+
+  memset(&mreq, 0, sizeof mreq);
+
+  if (interface_addr) {
+    if (uv_ip6_addr(interface_addr, 0, &addr6))
+      return -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 -EINVAL;
+  }
+
+  if (setsockopt(handle->io_watcher.fd,
+                 IPPROTO_IPV6,
+                 optname,
+                 &mreq,
+                 sizeof(mreq))) {
+#if defined(__MVS__)
+  if (errno == ENXIO)
+    return -ENODEV;
+#endif
+    return -errno;
+  }
+
+  return 0;
+}
+#endif /* !defined(__NUTTX__) && !defined(__TIZENRT__) */
+
+
+int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
+  int domain;
+  int err;
+  int fd;
+
+  /* Use the lower 8 bits for the domain */
+  domain = flags & 0xFF;
+  if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
+    return -EINVAL;
+
+  if (flags & ~0xFF)
+    return -EINVAL;
+
+  if (domain != AF_UNSPEC) {
+    err = uv__socket(domain, SOCK_DGRAM, 0);
+    if (err < 0)
+      return err;
+    fd = err;
+  } else {
+    fd = -1;
+  }
+
+  uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP);
+  handle->alloc_cb = NULL;
+  handle->recv_cb = NULL;
+  handle->send_queue_size = 0;
+  handle->send_queue_count = 0;
+  uv__io_init(&handle->io_watcher, uv__udp_io, fd);
+  QUEUE_INIT(&handle->write_queue);
+  QUEUE_INIT(&handle->write_completed_queue);
+  return 0;
+}
+
+
+int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) {
+  return uv_udp_init_ex(loop, handle, AF_UNSPEC);
+}
+
+
+int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
+  int err;
+
+  /* Check for already active socket. */
+  if (handle->io_watcher.fd != -1)
+    return -EBUSY;
+
+  err = uv__nonblock(sock, 1);
+  if (err)
+    return err;
+
+  err = uv__set_reuse(sock);
+  if (err)
+    return err;
+
+  handle->io_watcher.fd = sock;
+  return 0;
+}
+
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+int uv_udp_set_membership(uv_udp_t* handle,
+                          const char* multicast_addr,
+                          const char* interface_addr,
+                          uv_membership membership) {
+  int err;
+  struct sockaddr_in addr4;
+  struct sockaddr_in6 addr6;
+
+  if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) {
+    err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
+    if (err)
+      return err;
+    return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
+  } else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) {
+    err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
+    if (err)
+      return err;
+    return uv__udp_set_membership6(handle, &addr6, interface_addr, membership);
+  } else {
+    return -EINVAL;
+  }
+}
+#endif
+
+
+#if defined(__TIZENRT__)
+int uv_udp_set_membership(uv_udp_t* handle,
+                          const char* multicast_addr,
+                          const char* interface_addr,
+                          uv_membership membership) {
+  int err;
+  struct sockaddr_in addr4;
+
+  if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) {
+    err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
+    if (err)
+      return err;
+    return uv__udp_set_membership4(handle, &addr4, interface_addr, membership);
+  } else {
+    return -EINVAL;
+  }
+}
+#endif /* defined(__TIZENRT__) */
+
+static int uv__setsockopt(uv_udp_t* handle,
+                         int option4,
+                         int option6,
+                         const void* val,
+                         size_t size) {
+  int r;
+
+  if (handle->flags & UV_HANDLE_IPV6)
+    r = setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IPV6,
+                   option6,
+                   val,
+                   size);
+  else
+    r = setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IP,
+                   option4,
+                   val,
+                   size);
+  if (r)
+    return -errno;
+
+  return 0;
+}
+
+static int uv__setsockopt_maybe_char(uv_udp_t* handle,
+                                     int option4,
+                                     int option6,
+                                     int val) {
+#if defined(__sun) || defined(_AIX) || defined(__MVS__)
+  char arg = val;
+#elif defined(__OpenBSD__)
+  unsigned char arg = val;
+#else
+  int arg = val;
+#endif
+
+  if (val < 0 || val > 255)
+    return -EINVAL;
+
+  return uv__setsockopt(handle, option4, option6, &arg, sizeof(arg));
+}
+
+
+int uv_udp_set_broadcast(uv_udp_t* handle, int on) {
+  if (setsockopt(handle->io_watcher.fd,
+                 SOL_SOCKET,
+                 SO_BROADCAST,
+                 &on,
+                 sizeof(on))) {
+    return -errno;
+  }
+
+  return 0;
+}
+
+
+#if !defined(__NUTTX__)
+int uv_udp_set_ttl(uv_udp_t* handle, int ttl) {
+  if (ttl < 1 || ttl > 255)
+    return -EINVAL;
+
+#if defined(__MVS__)
+  if (!(handle->flags & UV_HANDLE_IPV6))
+    return -ENOTSUP;  /* zOS does not support setting ttl for IPv4 */
+#endif
+
+/*
+ * On Solaris and derivatives such as SmartOS, the length of socket options
+ * is sizeof(int) for IP_TTL and IPV6_UNICAST_HOPS,
+ * so hardcode the size of these options on this platform,
+ * and use the general uv__setsockopt_maybe_char call on other platforms.
+ */
+#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \
+    defined(__MVS__)
+
+  return uv__setsockopt(handle,
+                        IP_TTL,
+                        IPV6_UNICAST_HOPS,
+                        &ttl,
+                        sizeof(ttl));
+#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
+          defined(__MVS__) */
+
+  return uv__setsockopt_maybe_char(handle,
+                                   IP_TTL,
+                                   IPV6_UNICAST_HOPS,
+                                   ttl);
+}
+
+
+int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) {
+/*
+ * On Solaris and derivatives such as SmartOS, the length of socket options
+ * is sizeof(int) for IPV6_MULTICAST_HOPS and sizeof(char) for
+ * IP_MULTICAST_TTL, so hardcode the size of the option in the IPv6 case,
+ * and use the general uv__setsockopt_maybe_char call otherwise.
+ */
+#if defined(__sun) || defined(_AIX) || defined(__MVS__)
+  if (handle->flags & UV_HANDLE_IPV6)
+    return uv__setsockopt(handle,
+                          IP_MULTICAST_TTL,
+                          IPV6_MULTICAST_HOPS,
+                          &ttl,
+                          sizeof(ttl));
+#endif /* defined(__sun) || defined(_AIX) || defined(__MVS__) */
+
+  return uv__setsockopt_maybe_char(handle,
+                                   IP_MULTICAST_TTL,
+                                   IPV6_MULTICAST_HOPS,
+                                   ttl);
+}
+
+
+int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) {
+/*
+ * On Solaris and derivatives such as SmartOS, the length of socket options
+ * is sizeof(int) for IPV6_MULTICAST_LOOP and sizeof(char) for
+ * IP_MULTICAST_LOOP, so hardcode the size of the option in the IPv6 case,
+ * and use the general uv__setsockopt_maybe_char call otherwise.
+ */
+#if defined(__sun) || defined(_AIX) || defined(__MVS__)
+  if (handle->flags & UV_HANDLE_IPV6)
+    return uv__setsockopt(handle,
+                          IP_MULTICAST_LOOP,
+                          IPV6_MULTICAST_LOOP,
+                          &on,
+                          sizeof(on));
+#endif /* defined(__sun) || defined(_AIX) || defined(__MVS__) */
+
+  return uv__setsockopt_maybe_char(handle,
+                                   IP_MULTICAST_LOOP,
+                                   IPV6_MULTICAST_LOOP,
+                                   on);
+}
+
+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 -EINVAL;
+  }
+
+  if (addr_st.ss_family == AF_INET) {
+    if (setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IP,
+                   IP_MULTICAST_IF,
+                   (void*) &addr4->sin_addr,
+                   sizeof(addr4->sin_addr)) == -1) {
+      return -errno;
+    }
+  } else if (addr_st.ss_family == AF_INET6) {
+    if (setsockopt(handle->io_watcher.fd,
+                   IPPROTO_IPV6,
+                   IPV6_MULTICAST_IF,
+                   &addr6->sin6_scope_id,
+                   sizeof(addr6->sin6_scope_id)) == -1) {
+      return -errno;
+    }
+  } else {
+    assert(0 && "unexpected address family");
+    abort();
+  }
+
+  return 0;
+}
+#endif
+
+int uv_udp_getsockname(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+  socklen_t socklen;
+
+  if (handle->io_watcher.fd == -1)
+    return -EINVAL;  /* FIXME(bnoordhuis) -EBADF */
+
+  /* sizeof(socklen_t) != sizeof(int) on some systems. */
+  socklen = (socklen_t) *namelen;
+
+  if (getsockname(handle->io_watcher.fd, name, &socklen))
+    return -errno;
+
+  *namelen = (int) socklen;
+  return 0;
+}
+
+
+int uv__udp_recv_start(uv_udp_t* handle,
+                       uv_alloc_cb alloc_cb,
+                       uv_udp_recv_cb recv_cb) {
+  int err;
+
+  if (alloc_cb == NULL || recv_cb == NULL)
+    return -EINVAL;
+
+  if (uv__io_active(&handle->io_watcher, POLLIN))
+    return -EALREADY;  /* FIXME(bnoordhuis) Should be -EBUSY. */
+
+  err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0);
+  if (err)
+    return err;
+
+  handle->alloc_cb = alloc_cb;
+  handle->recv_cb = recv_cb;
+
+  uv__io_start(handle->loop, &handle->io_watcher, POLLIN);
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+
+int uv__udp_recv_stop(uv_udp_t* handle) {
+  uv__io_stop(handle->loop, &handle->io_watcher, POLLIN);
+
+  if (!uv__io_active(&handle->io_watcher, POLLOUT))
+    uv__handle_stop(handle);
+
+  handle->alloc_cb = NULL;
+  handle->recv_cb = NULL;
+
+  return 0;
+}
diff --git a/src/uv-common.c b/src/uv-common.c
new file mode 100644 (file)
index 0000000..5c12d82
--- /dev/null
@@ -0,0 +1,504 @@
+/* Copyright 2015-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <unistd.h> /* usleep */
+# include <net/if.h> /* if_nametoindex */
+#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,
+};
+
+#if defined(__APPLE__) || defined(_WIN32) || defined(TUV_FEATURE_PIPE)
+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);
+}
+#endif
+
+void* uv__malloc(size_t size) {
+  return uv__allocator.local_malloc(size);
+}
+
+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) {
+  return uv__allocator.local_realloc(ptr, size);
+}
+
+uv_buf_t uv_buf_init(char* base, unsigned int len) {
+  uv_buf_t buf;
+  buf.base = base;
+  buf.len = len;
+  return buf;
+}
+
+
+#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)
+    default:
+      /* TUV_CHANGES@20170517: To figure out what error name is missing */
+      TDLOG("uv_err_name for (%d) is missing. Add err name string\n", err);
+      return "uv_err_name: Unknown error";
+  }
+}
+#undef UV_ERR_NAME_GEN
+
+
+#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)
+    default:
+        return "Unknown error";
+    }
+}
+#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);
+  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);
+
+  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);
+#elif !defined(__NUTTX__) && !defined(__TIZENRT__)
+    /* No if_nametoindex in nuttx */
+    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_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_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) {
+  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_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) {
+  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_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);
+  }
+}
+
+
+void uv_ref(uv_handle_t* handle) {
+  uv__handle_ref(handle);
+}
+
+
+void uv_unref(uv_handle_t* handle) {
+  uv__handle_unref(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;
+}
+
+
+/* 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 = uv__get_nbufs(req);
+
+  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;
+#ifdef HAVE_DIRENT_TYPES
+  switch (dent->d_type) {
+    case UV__DT_DIR:
+      ent->type = UV_DIRENT_DIR;
+      break;
+    case UV__DT_FILE:
+      ent->type = UV_DIRENT_FILE;
+      break;
+    case UV__DT_LINK:
+      ent->type = UV_DIRENT_LINK;
+      break;
+    case UV__DT_FIFO:
+      ent->type = UV_DIRENT_FIFO;
+      break;
+    case UV__DT_SOCKET:
+      ent->type = UV_DIRENT_SOCKET;
+      break;
+    case UV__DT_CHAR:
+      ent->type = UV_DIRENT_CHAR;
+      break;
+    case UV__DT_BLOCK:
+      ent->type = UV_DIRENT_BLOCK;
+      break;
+    default:
+      ent->type = UV_DIRENT_UNKNOWN;
+  }
+#else
+  ent->type = UV_DIRENT_UNKNOWN;
+#endif
+
+  return 0;
+}
+
+
+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;
+}
+
+
+int uv_loop_close(uv_loop_t* loop) {
+  QUEUE* q;
+  uv_handle_t* h;
+#ifndef NDEBUG
+  void* saved_data;
+#endif
+
+  if (!QUEUE_EMPTY(&(loop)->active_reqs))
+    return UV_EBUSY;
+
+  tuv_async_deinit(loop, &loop->wq_async);
+
+  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;
+}
+
+/* Pause the calling thread for a number of milliseconds. */
+void uv_sleep(int msec) {
+#ifdef _WIN32
+  Sleep(msec);
+#else
+  usleep(msec * 1000);
+#endif
+}
diff --git a/src/uv-common.h b/src/uv-common.h
new file mode 100644 (file)
index 0000000..e52b529
--- /dev/null
@@ -0,0 +1,249 @@
+/* Copyright 2015-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "stdint-msvc2008.h"
+#else
+# include <stdint.h>
+#endif
+
+#include "uv.h"
+#include "tree.h"
+#include "queue.h"
+
+#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)])
+
+#ifndef _WIN32
+enum {
+  UV__HANDLE_INTERNAL = 0x8000,
+  UV__HANDLE_ACTIVE   = 0x4000,
+  UV__HANDLE_REF      = 0x2000,
+  UV__HANDLE_CLOSING  = 0 /* no-op on unix */
+};
+#else
+# define UV__HANDLE_INTERNAL  0x80
+# define UV__HANDLE_ACTIVE    0x40
+# define UV__HANDLE_REF       0x20
+# define UV__HANDLE_CLOSING   0x01
+#endif
+
+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_bind(uv_udp_t* handle,
+                 const struct sockaddr* addr,
+                 unsigned int  addrlen,
+                 unsigned int flags);
+
+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. */
+
+void uv__work_submit(uv_loop_t* loop,
+                     struct uv__work *w,
+                     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);
+
+#define uv__has_active_reqs(loop)                                             \
+  (QUEUE_EMPTY(&(loop)->active_reqs) == 0)
+
+#define uv__req_register(loop, req)                                           \
+  do {                                                                        \
+    QUEUE_INSERT_TAIL(&(loop)->active_reqs, &(req)->active_queue);            \
+  }                                                                           \
+  while (0)
+
+#define uv__req_unregister(loop, req)                                         \
+  do {                                                                        \
+    assert(uv__has_active_reqs(loop));                                        \
+    QUEUE_REMOVE(&(req)->active_queue);                                       \
+  }                                                                           \
+  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_CLOSING |  UV_CLOSED)) != 0)
+
+#define uv__handle_start(h)                                                   \
+  do {                                                                        \
+    assert(((h)->flags & UV__HANDLE_CLOSING) == 0);                           \
+    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 {                                                                        \
+    assert(((h)->flags & UV__HANDLE_CLOSING) == 0);                           \
+    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)
+
+#define tuv__handle_deinit(h)                                                 \
+  do {                                                                        \
+    QUEUE_REMOVE(&(h)->handle_queue);                                         \
+    QUEUE_INIT(&(h)->handle_queue);                                           \
+  }                                                                           \
+  while (0)
+
+/* Allocator prototypes */
+void *uv__calloc(size_t count, size_t size);
+#if defined(__APPLE__) || defined(_WIN32) || defined(TUV_FEATURE_PIPE)
+char *uv__strdup(const char* s);
+#endif
+void* uv__malloc(size_t size);
+void uv__free(void* ptr);
+void* uv__realloc(void* ptr, size_t size);
+
+#endif /* UV_COMMON_H_ */
diff --git a/src/version.c b/src/version.c
new file mode 100644 (file)
index 0000000..686dedd
--- /dev/null
@@ -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/src/win/async.c b/src/win/async.c
new file mode 100644 (file)
index 0000000..424f410
--- /dev/null
@@ -0,0 +1,103 @@
+/* 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(loop, req);
+  req->type = UV_WAKEUP;
+  req->data = handle;
+
+  uv__handle_start(handle);
+
+  return 0;
+}
+
+int tuv_async_deinit(uv_loop_t* loop, uv_async_t* handle) {
+  /* No operation required */
+  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/src/win/atomicops-inl.h b/src/win/atomicops-inl.h
new file mode 100644 (file)
index 0000000..61e0060
--- /dev/null
@@ -0,0 +1,56 @@
+/* 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"
+
+
+/* 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 __declspec(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/src/win/core.c b/src/win/core.c
new file mode 100644 (file)
index 0000000..e84186d
--- /dev/null
@@ -0,0 +1,598 @@
+/* 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 "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() {
+  uv_mutex_init(&uv__loops_lock);
+  uv__loops = uv__calloc(UV__LOOPS_CHUNK_SIZE, sizeof(uv_loop_t*));
+  if (!uv__loops)
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+  uv__loops_size = 0;
+  uv__loops_capacity = UV__LOOPS_CHUNK_SIZE;
+}
+
+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 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() {
+  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) {
+  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());
+
+  /* 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);
+  QUEUE_INIT(&loop->active_reqs);
+  loop->active_handles = 0;
+
+  loop->pending_reqs_tail = NULL;
+
+  loop->endgame_handles = NULL;
+
+  RB_INIT(&loop->timers);
+
+  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:
+  CloseHandle(loop->iocp);
+  loop->iocp = INVALID_HANDLE_VALUE;
+
+  return err;
+}
+
+
+void uv__once_init(void) {
+  uv_once(&uv_init_guard_, uv_init);
+}
+
+
+void uv__loop_close(uv_loop_t* loop) {
+  size_t i;
+
+  uv__loops_remove(loop);
+
+  /* close the async handle without needing an extra loop iteration */
+  assert(!loop->wq_async.async_sent);
+  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);
+
+  CloseHandle(loop->iocp);
+}
+
+
+int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
+  return UV_ENOSYS;
+}
+
+
+int uv_backend_fd(const uv_loop_t* loop) {
+  return -1;
+}
+
+
+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(uv_loop_t* loop, DWORD timeout) {
+  DWORD bytes;
+  ULONG_PTR key;
+  OVERLAPPED* overlapped;
+  uv_req_t* req;
+  int repeat;
+  uint64_t timeout_time;
+
+  timeout_time = loop->time + timeout;
+
+  for (repeat = 0; ; repeat++) {
+    GetQueuedCompletionStatus(loop->iocp,
+                              &bytes,
+                              &key,
+                              &overlapped,
+                              timeout);
+
+    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_ex(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;
+
+  timeout_time = loop->time + timeout;
+
+  for (repeat = 0; ; repeat++) {
+    success = pGetQueuedCompletionStatusEx(loop->iocp,
+                                           overlappeds,
+                                           ARRAY_SIZE(overlappeds),
+                                           &count,
+                                           timeout,
+                                           FALSE);
+
+    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 loop->active_handles > 0 ||
+         !QUEUE_EMPTY(&loop->active_reqs) ||
+         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;
+  void (*poll)(uv_loop_t* loop, DWORD timeout);
+
+  if (pGetQueuedCompletionStatusEx)
+    poll = &uv_poll_ex;
+  else
+    poll = &uv_poll;
+
+  r = uv__loop_alive(loop);
+  if (!r)
+    uv_update_time(loop);
+
+  while (r != 0 && loop->stop_flag == 0) {
+    uv_update_time(loop);
+    uv_process_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);
+
+    (*poll)(loop, timeout);
+
+    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_process_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;
+}
diff --git a/src/win/detect-wakeup.c b/src/win/detect-wakeup.c
new file mode 100644 (file)
index 0000000..a12179f
--- /dev/null
@@ -0,0 +1,35 @@
+#include "uv.h"
+#include "internal.h"
+#include "winapi.h"
+
+static void uv__register_system_resume_callback();
+
+void uv__init_detect_system_wakeup() {
+  /* 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() {
+  _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/src/win/dl.c b/src/win/dl.c
new file mode 100644 (file)
index 0000000..39e400a
--- /dev/null
@@ -0,0 +1,118 @@
+/* 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, int 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, GetLastError());
+  }
+
+  lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+  if (lib->handle == NULL) {
+    return uv__dlerror(lib, 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) {
+  *ptr = (void*) 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){
+  DWORD_PTR args[1] = { (DWORD_PTR) errorno };
+  LPSTR fallback_error = "error: %1!d!";
+
+  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, int errorno) {
+  DWORD res;
+
+  if (lib->errmsg) {
+    LocalFree((void*)lib->errmsg);
+    lib->errmsg = NULL;
+  }
+
+  if (errorno) {
+    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) {
+      res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                           FORMAT_MESSAGE_FROM_SYSTEM |
+                           FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
+                           0, (LPSTR) &lib->errmsg, 0, NULL);
+    }
+
+    if (!res) {
+      uv__format_fallback_error(lib, errorno);
+    }
+  }
+
+  return errorno ? -1 : 0;
+}
diff --git a/src/win/error.c b/src/win/error.c
new file mode 100644 (file)
index 0000000..642d111
--- /dev/null
@@ -0,0 +1,171 @@
+/* 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);
+  }
+
+  *((char*)NULL) = 0xff; /* Force debug break */
+  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_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_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/src/win/fs-event.c b/src/win/fs-event.c
new file mode 100644 (file)
index 0000000..03e4adc
--- /dev/null
@@ -0,0 +1,545 @@
+/* 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);
+  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) {
+  int len = wcslen(filename);
+  int i = len;
+  while (i > 0 && filename[--i] != '\\' && filename[i] != '/');
+
+  if (i == 0) {
+    if (dir) {
+      *dir = (WCHAR*)uv__malloc((MAX_PATH + 1) * sizeof(WCHAR));
+      if (!*dir) {
+        uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+      }
+
+      if (!GetCurrentDirectoryW(MAX_PATH, *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(loop, (uv_req_t*)&handle->req);
+  handle->req.type = 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;
+  DWORD attr, last_error;
+  WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL;
+  WCHAR short_path[MAX_PATH];
+
+  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. */
+    handle->dirw = pathw;
+    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. */
+    if (!GetShortPathNameW(pathw, short_path, ARRAY_SIZE(short_path))) {
+      last_error = GetLastError();
+      goto error;
+    }
+
+    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;
+  }
+
+  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;
+  }
+
+  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, int file_name_len) {
+  int str_len;
+
+  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/src/win/fs.c b/src/win/fs.c
new file mode 100644 (file)
index 0000000..6902d4f
--- /dev/null
@@ -0,0 +1,2494 @@
+/* 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 <wincrypt.h>
+
+
+#define UV_FS_FREE_PATHS         0x0002
+#define UV_FS_FREE_PTR           0x0008
+#define UV_FS_CLEANEDUP          0x0010
+
+
+#define QUEUE_FS_TP_JOB(loop, req)                                          \
+  do {                                                                      \
+    uv__req_register(loop, req);                                            \
+    uv__work_submit((loop), &(req)->work_req, uv__fs_work, uv__fs_done);    \
+  } while (0)
+
+#define SET_REQ_RESULT(req, result_value)                                   \
+  do {                                                                      \
+    req->result = (result_value);                                           \
+    if (req->result == -1) {                                                \
+      req->sys_errno_ = _doserrno;                                          \
+      req->result = uv_translate_sys_error(req->sys_errno_);                \
+    }                                                                       \
+  } 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 FILETIME_TO_UINT(filetime)                                          \
+   (*((uint64_t*) &(filetime)) - 116444736000000000ULL)
+
+#define FILETIME_TO_TIME_T(filetime)                                        \
+   (FILETIME_TO_UINT(filetime) / 10000000ULL)
+
+#define FILETIME_TO_TIME_NS(filetime, secs)                                 \
+   ((FILETIME_TO_UINT(filetime) - (secs * 10000000ULL)) * 100)
+
+#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) * 10000000ULL)) +                \
+                                  116444736000000000ULL;                    \
+    (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'))
+
+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;
+
+
+void uv_fs_init() {
+  _fmode = _O_BINARY;
+}
+
+
+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_req_init(loop, (uv_req_t*) req);
+
+  req->type = UV_FS;
+  req->loop = loop;
+  req->flags = 0;
+  req->fs_type = fs_type;
+  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;
+
+  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 {
+    /* 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;
+
+  /* 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 & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
+  case _O_RDONLY:
+    access = FILE_GENERIC_READ;
+    break;
+  case _O_WRONLY:
+    access = FILE_GENERIC_WRITE;
+    break;
+  case _O_RDWR:
+    access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+    break;
+  default:
+    goto einval;
+  }
+
+  if (flags & _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 #1449.
+   */
+  share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+
+  switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
+  case 0:
+  case _O_EXCL:
+    disposition = OPEN_EXISTING;
+    break;
+  case _O_CREAT:
+    disposition = OPEN_ALWAYS;
+    break;
+  case _O_CREAT | _O_EXCL:
+  case _O_CREAT | _O_TRUNC | _O_EXCL:
+    disposition = CREATE_NEW;
+    break;
+  case _O_TRUNC:
+  case _O_TRUNC | _O_EXCL:
+    disposition = TRUNCATE_EXISTING;
+    break;
+  case _O_CREAT | _O_TRUNC:
+    disposition = CREATE_ALWAYS;
+    break;
+  default:
+    goto einval;
+  }
+
+  attributes |= FILE_ATTRIBUTE_NORMAL;
+  if (flags & _O_CREAT) {
+    if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) {
+      attributes |= FILE_ATTRIBUTE_READONLY;
+    }
+  }
+
+  if (flags & _O_TEMPORARY ) {
+    attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
+    access |= DELETE;
+  }
+
+  if (flags & _O_SHORT_LIVED) {
+    attributes |= FILE_ATTRIBUTE_TEMPORARY;
+  }
+
+  switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) {
+  case 0:
+    break;
+  case _O_SEQUENTIAL:
+    attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+    break;
+  case _O_RANDOM:
+    attributes |= FILE_FLAG_RANDOM_ACCESS;
+    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 & _O_CREAT) &&
+        !(flags & _O_EXCL)) {
+      /* Special case: when ERROR_FILE_EXISTS happens and 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, UV_UNKNOWN);
+    CloseHandle(file);
+    return;
+  }
+
+  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;
+
+  VERIFY_FD(fd, req);
+
+  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 {
+    req->result = 0;
+  }
+}
+
+
+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;
+
+  VERIFY_FD(fd, req);
+
+  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;
+  } 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 (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(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;
+
+  VERIFY_FD(fd, req);
+
+  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;
+  } 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 (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);
+  SET_REQ_RESULT(req, result);
+}
+
+
+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);
+
+    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. */
+  int result = _wmkdir(req->file.pathw);
+  SET_REQ_RESULT(req, result);
+}
+
+
+/* OpenBSD original: lib/libc/stdio/mktemp.c */
+void fs__mkdtemp(uv_fs_t* req) {
+  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;
+  HCRYPTPROV h_crypt_prov;
+  uint64_t v;
+  BOOL released;
+
+  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);
+    return;
+  }
+
+  if (!CryptAcquireContext(&h_crypt_prov, NULL, NULL, PROV_RSA_FULL,
+                           CRYPT_VERIFYCONTEXT)) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  tries = TMP_MAX;
+  do {
+    if (!CryptGenRandom(h_crypt_prov, sizeof(v), (BYTE*) &v)) {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+      break;
+    }
+
+    cp = ep - num_x;
+    for (i = 0; i < num_x; i++) {
+      *cp++ = tempchars[v % num_chars];
+      v /= num_chars;
+    }
+
+    if (_wmkdir(req->file.pathw) == 0) {
+      len = strlen(req->path);
+      wcstombs((char*) req->path + len - num_x, ep - num_x, num_x);
+      SET_REQ_RESULT(req, 0);
+      break;
+    } else if (errno != EEXIST) {
+      SET_REQ_RESULT(req, -1);
+      break;
+    }
+  } while (--tries);
+
+  released = CryptReleaseContext(h_crypt_prov, 0);
+  assert(released);
+  if (tries == 0) {
+    SET_REQ_RESULT(req, -1);
+  }
+}
+
+
+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 == 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);
+}
+
+
+INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) {
+  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;
+
+  if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+    /*
+     * It is possible for a file to have FILE_ATTRIBUTE_REPARSE_POINT but not have
+     * any link data. In that case DeviceIoControl() in fs__readlink_handle() sets
+     * the last error to ERROR_NOT_A_REPARSE_POINT. Then the stat result mode
+     * calculated below will indicate a normal directory or file, as if
+     * FILE_ATTRIBUTE_REPARSE_POINT was not present.
+     */
+    if (fs__readlink_handle(handle, NULL, &statbuf->st_size) == 0) {
+      statbuf->st_mode |= S_IFLNK;
+    } else if (GetLastError() != ERROR_NOT_A_REPARSE_POINT) {
+      return -1;
+    }
+  }
+
+  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 =
+      file_info.StandardInformation.AllocationSize.QuadPart >> 9ULL;
+
+  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.
+   */
+  statbuf->st_blksize = 2048;
+
+  /* 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 void fs__stat_impl(uv_fs_t* req, int do_lstat) {
+  HANDLE handle;
+  DWORD flags;
+
+  flags = FILE_FLAG_BACKUP_SEMANTICS;
+  if (do_lstat) {
+    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+  }
+
+  handle = CreateFileW(req->file.pathw,
+                       FILE_READ_ATTRIBUTES,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       flags,
+                       NULL);
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (fs__stat_handle(handle, &req->statbuf) != 0) {
+    DWORD error = GetLastError();
+    if (do_lstat && error == ERROR_SYMLINK_NOT_SUPPORTED) {
+      /* 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, GetLastError());
+    }
+
+    CloseHandle(handle);
+    return;
+  }
+
+  req->ptr = &req->statbuf;
+  req->result = 0;
+  CloseHandle(handle);
+}
+
+
+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) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  req->ptr = &req->statbuf;
+  req->result = 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;
+  NTSTATUS status;
+  IO_STATUS_BLOCK io_status;
+  FILE_END_OF_FILE_INFORMATION eof_info;
+
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  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));
+  }
+}
+
+
+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->flags & 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);
+  SET_REQ_RESULT(req, result);
+}
+
+
+static void fs__fchmod(uv_fs_t* req) {
+  int fd = req->file.fd;
+  HANDLE handle;
+  NTSTATUS nt_status;
+  IO_STATUS_BLOCK io_status;
+  FILE_BASIC_INFORMATION file_info;
+
+  VERIFY_FD(fd, req);
+
+  handle = uv__get_osfhandle(fd);
+
+  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));
+    return;
+  }
+
+  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));
+    return;
+  }
+
+  SET_REQ_SUCCESS(req);
+}
+
+
+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;
+}
+
+
+static void fs__utime(uv_fs_t* req) {
+  HANDLE handle;
+
+  handle = CreateFileW(req->file.pathw,
+                       FILE_WRITE_ATTRIBUTES,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL,
+                       OPEN_EXISTING,
+                       FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+
+  if (handle == INVALID_HANDLE_VALUE) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    return;
+  }
+
+  if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    CloseHandle(handle);
+    return;
+  }
+
+  CloseHandle(handle);
+
+  req->result = 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;
+  }
+
+  req->result = 0;
+}
+
+
+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 {
+    req->result = 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 = req->file.pathw;
+  WCHAR* new_pathw = req->fs.info.new_pathw;
+  int flags = req->fs.info.file_flags;
+  int result;
+
+
+  if (flags & UV_FS_SYMLINK_JUNCTION) {
+    fs__create_junction(req, pathw, new_pathw);
+  } else if (pCreateSymbolicLinkW) {
+    result = pCreateSymbolicLinkW(new_pathw,
+                                  pathw,
+                                  flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1;
+    if (result == -1) {
+      SET_REQ_WIN32_ERROR(req, GetLastError());
+    } else {
+      SET_REQ_RESULT(req, result);
+    }
+  } else {
+    SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
+  }
+}
+
+
+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 size_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 = pGetFinalPathNameByHandleW(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 (pGetFinalPathNameByHandleW(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;
+
+  if (!pGetFinalPathNameByHandleW) {
+    SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
+    return;
+  }
+
+  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) {
+  req->result = 0;
+}
+
+
+static void fs__fchown(uv_fs_t* req) {
+  req->result = 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(SENDFILE, sendfile)
+    XX(STAT, stat)
+    XX(LSTAT, lstat)
+    XX(FSTAT, fstat)
+    XX(FTRUNCATE, ftruncate)
+    XX(UTIME, utime)
+    XX(FUTIME, futime)
+    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(RENAME, rename)
+    XX(SCANDIR, scandir)
+    XX(LINK, link)
+    XX(SYMLINK, symlink)
+    XX(READLINK, readlink)
+    XX(REALPATH, realpath)
+    XX(CHOWN, chown)
+    XX(FCHOWN, fchown);
+    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);
+    req->result = UV_ECANCELED;
+  }
+
+  req->cb(req);
+}
+
+
+void uv_fs_req_cleanup(uv_fs_t* req) {
+  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
+      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;
+
+  uv_fs_req_init(loop, req, UV_FS_OPEN, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  req->fs.info.file_flags = flags;
+  req->fs.info.mode = mode;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__open(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  uv_fs_req_init(loop, req, UV_FS_CLOSE, cb);
+  req->file.fd = fd;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__close(req);
+    return req->result;
+  }
+}
+
+
+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) {
+  if (bufs == NULL || nbufs == 0)
+    return UV_EINVAL;
+
+  uv_fs_req_init(loop, req, UV_FS_READ, cb);
+
+  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)
+    return UV_ENOMEM;
+
+  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->fs.info.offset = offset;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__read(req);
+    return req->result;
+  }
+}
+
+
+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) {
+  if (bufs == NULL || nbufs == 0)
+    return UV_EINVAL;
+
+  uv_fs_req_init(loop, req, UV_FS_WRITE, cb);
+
+  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)
+    return UV_ENOMEM;
+
+  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
+
+  req->fs.info.offset = offset;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__write(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_UNLINK, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__unlink(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+    uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_MKDIR, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  req->fs.info.mode = mode;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__mkdir(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl,
+    uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_MKDTEMP, cb);
+
+  err = fs__capture_path(req, tpl, NULL, TRUE);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__mkdtemp(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_RMDIR, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__rmdir(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
+    uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_SCANDIR, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  req->fs.info.file_flags = flags;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__scandir(req);
+    return req->result;
+  }
+}
+
+
+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;
+
+  uv_fs_req_init(loop, req, UV_FS_LINK, cb);
+
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__link(req);
+    return req->result;
+  }
+}
+
+
+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;
+
+  uv_fs_req_init(loop, req, UV_FS_SYMLINK, cb);
+
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  req->fs.info.file_flags = flags;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__symlink(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_READLINK, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__readlink(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path,
+    uv_fs_cb cb) {
+  int err;
+
+  if (!req || !path) {
+    return UV_EINVAL;
+  }
+
+  uv_fs_req_init(loop, req, UV_FS_REALPATH, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__realpath(req);
+    return req->result;
+  }
+}
+
+
+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;
+
+  uv_fs_req_init(loop, req, UV_FS_CHOWN, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__chown(req);
+    return req->result;
+  }
+}
+
+
+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) {
+  uv_fs_req_init(loop, req, UV_FS_FCHOWN, cb);
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__fchown(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_STAT, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__stat(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_LSTAT, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__lstat(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  uv_fs_req_init(loop, req, UV_FS_FSTAT, cb);
+  req->file.fd = fd;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__fstat(req);
+    return req->result;
+  }
+}
+
+
+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;
+
+  uv_fs_req_init(loop, req, UV_FS_RENAME, cb);
+
+  err = fs__capture_path(req, path, new_path, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__rename(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  uv_fs_req_init(loop, req, UV_FS_FSYNC, cb);
+  req->file.fd = fd;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__fsync(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
+  uv_fs_req_init(loop, req, UV_FS_FDATASYNC, cb);
+  req->file.fd = fd;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__fdatasync(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd,
+    int64_t offset, uv_fs_cb cb) {
+  uv_fs_req_init(loop, req, UV_FS_FTRUNCATE, cb);
+
+  req->file.fd = fd;
+  req->fs.info.offset = offset;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__ftruncate(req);
+    return req->result;
+  }
+}
+
+
+
+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) {
+  uv_fs_req_init(loop, req, UV_FS_SENDFILE, cb);
+
+  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;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__sendfile(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_access(uv_loop_t* loop,
+                 uv_fs_t* req,
+                 const char* path,
+                 int flags,
+                 uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_ACCESS, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  req->flags = flags;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  }
+
+  fs__access(req);
+  return req->result;
+}
+
+
+int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
+    uv_fs_cb cb) {
+  int err;
+
+  uv_fs_req_init(loop, req, UV_FS_CHMOD, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  req->fs.info.mode = mode;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__chmod(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode,
+    uv_fs_cb cb) {
+  uv_fs_req_init(loop, req, UV_FS_FCHMOD, cb);
+
+  req->file.fd = fd;
+  req->fs.info.mode = mode;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__fchmod(req);
+    return req->result;
+  }
+}
+
+
+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;
+
+  uv_fs_req_init(loop, req, UV_FS_UTIME, cb);
+
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__utime(req);
+    return req->result;
+  }
+}
+
+
+int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
+    double mtime, uv_fs_cb cb) {
+  uv_fs_req_init(loop, req, UV_FS_FUTIME, cb);
+
+  req->file.fd = fd;
+  req->fs.time.atime = atime;
+  req->fs.time.mtime = mtime;
+
+  if (cb) {
+    QUEUE_FS_TP_JOB(loop, req);
+    return 0;
+  } else {
+    fs__futime(req);
+    return req->result;
+  }
+}
diff --git a/src/win/getaddrinfo.c b/src/win/getaddrinfo.c
new file mode 100644 (file)
index 0000000..c13bfec
--- /dev/null
@@ -0,0 +1,384 @@
+/* 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"
+
+/* EAI_* constants. */
+#include <winsock2.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)
+
+
+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) {
+  int nodesize = 0;
+  int servicesize = 0;
+  int hintssize = 0;
+  char* alloc_ptr = NULL;
+  int err;
+
+  if (req == NULL || (node == NULL && service == NULL)) {
+    return UV_EINVAL;
+  }
+
+  uv_req_init(loop, (uv_req_t*)req);
+
+  req->getaddrinfo_cb = getaddrinfo_cb;
+  req->addrinfo = NULL;
+  req->type = UV_GETADDRINFO;
+  req->loop = loop;
+  req->retcode = 0;
+
+  /* calculate required memory size for all input values */
+  if (node != NULL) {
+    nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, node, -1, NULL, 0) *
+                            sizeof(WCHAR));
+    if (nodesize == 0) {
+      err = GetLastError();
+      goto error;
+    }
+  }
+
+  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__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);
+}
diff --git a/src/win/getnameinfo.c b/src/win/getnameinfo.c
new file mode 100644 (file)
index 0000000..66b64b8
--- /dev/null
@@ -0,0 +1,150 @@
+/* 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 = 0;
+
+  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);
+
+  /* convert results to UTF-8 */
+  WideCharToMultiByte(CP_UTF8,
+                      0,
+                      host,
+                      -1,
+                      req->host,
+                      sizeof(req->host),
+                      NULL,
+                      NULL);
+
+  WideCharToMultiByte(CP_UTF8,
+                      0,
+                      service,
+                      -1,
+                      req->service,
+                      sizeof(req->service),
+                      NULL,
+                      NULL);
+}
+
+
+/*
+* 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(loop, (uv_req_t*)req);
+  uv__req_register(loop, req);
+
+  req->getnameinfo_cb = getnameinfo_cb;
+  req->flags = flags;
+  req->type = UV_GETNAMEINFO;
+  req->loop = loop;
+  req->retcode = 0;
+
+  if (getnameinfo_cb) {
+    uv__work_submit(loop,
+                    &req->work_req,
+                    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/src/win/handle-inl.h b/src/win/handle-inl.h
new file mode 100644 (file)
index 0000000..8d0334c
--- /dev/null
@@ -0,0 +1,179 @@
+/* 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_endgame(loop, (uv_timer_t*) 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/src/win/handle.c b/src/win/handle.c
new file mode 100644 (file)
index 0000000..72b49d9
--- /dev/null
@@ -0,0 +1,154 @@
+/* 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);
+      uv_want_endgame(loop, handle);
+      return;
+
+    default:
+      /* Not supported */
+      abort();
+  }
+}
+
+
+int uv_is_closing(const uv_handle_t* handle) {
+  return !!(handle->flags & (UV__HANDLE_CLOSING | UV_HANDLE_CLOSED));
+}
diff --git a/src/win/internal.h b/src/win/internal.h
new file mode 100644 (file)
index 0000000..b8cfde9
--- /dev/null
@@ -0,0 +1,394 @@
+/* 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 "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
+
+/*
+ * Handles
+ * (also see handle-inl.h)
+ */
+
+/* Used by all handles. */
+#define UV_HANDLE_CLOSED                        0x00000002
+#define UV_HANDLE_ENDGAME_QUEUED                0x00000008
+
+/* uv-common.h: #define UV__HANDLE_CLOSING      0x00000001 */
+/* uv-common.h: #define UV__HANDLE_ACTIVE       0x00000040 */
+/* uv-common.h: #define UV__HANDLE_REF          0x00000020 */
+/* uv-common.h: #define UV_HANDLE_INTERNAL      0x00000080 */
+
+/* Used by streams and UDP handles. */
+#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
+#define UV_HANDLE_READ_PENDING                  0x00020000
+#define UV_HANDLE_SYNC_BYPASS_IOCP              0x00040000
+#define UV_HANDLE_ZERO_READ                     0x00080000
+#define UV_HANDLE_EMULATE_IOCP                  0x00100000
+#define UV_HANDLE_BLOCKING_WRITES               0x00200000
+#define UV_HANDLE_CANCELLATION_PENDING          0x00400000
+
+/* Used by uv_tcp_t and uv_udp_t handles */
+#define UV_HANDLE_IPV6                          0x01000000
+
+/* Only used by uv_tcp_t handles. */
+#define UV_HANDLE_TCP_NODELAY                   0x02000000
+#define UV_HANDLE_TCP_KEEPALIVE                 0x04000000
+#define UV_HANDLE_TCP_SINGLE_ACCEPT             0x08000000
+#define UV_HANDLE_TCP_ACCEPT_STATE_CHANGING     0x10000000
+#define UV_HANDLE_TCP_SOCKET_CLOSED             0x20000000
+#define UV_HANDLE_SHARED_TCP_SOCKET             0x40000000
+
+/* Only used by uv_pipe_t handles. */
+#define UV_HANDLE_NON_OVERLAPPED_PIPE           0x01000000
+#define UV_HANDLE_PIPESERVER                    0x02000000
+#define UV_HANDLE_PIPE_READ_CANCELABLE          0x04000000
+
+/* Only used by uv_tty_t handles. */
+#define UV_HANDLE_TTY_READABLE                  0x01000000
+#define UV_HANDLE_TTY_RAW                       0x02000000
+#define UV_HANDLE_TTY_SAVED_POSITION            0x04000000
+#define UV_HANDLE_TTY_SAVED_ATTRIBUTES          0x08000000
+
+/* Only used by uv_poll_t handles. */
+#define UV_HANDLE_POLL_SLOW                     0x02000000
+
+
+/*
+ * Requests: see req-inl.h
+ */
+
+
+/*
+ * Streams: see stream-inl.h
+ */
+
+
+/*
+ * TCP
+ */
+
+typedef struct {
+  WSAPROTOCOL_INFOW socket_info;
+  int delayed_error;
+} uv__ipc_socket_info_ex;
+
+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_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex,
+    int tcp_connection);
+
+int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid,
+    LPWSAPROTOCOL_INFOW protocol_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);
+int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle,
+    const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
+int uv_pipe_write2(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle,
+    const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle,
+    uv_write_cb cb);
+void uv__pipe_pause_read(uv_pipe_t* handle);
+void uv__pipe_unpause_read(uv_pipe_t* handle);
+void uv__pipe_stop_read(uv_pipe_t* handle);
+
+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();
+
+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);
+/* TODO: remove me */
+void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* raw_req);
+/* TODO: remove me */
+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);
+
+
+/*
+ * Timers
+ */
+void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle);
+
+DWORD uv__next_timeout(const uv_loop_t* loop);
+void uv_process_timers(uv_loop_t* loop);
+
+
+/*
+ * 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();
+
+
+/*
+ * 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();
+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();
+
+
+/*
+ * 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();
+
+uint64_t uv__hrtime(double scale);
+int uv_parent_pid();
+int uv_current_pid();
+__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);
+
+
+/*
+ * 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();
+
+
+/*
+ * Winsock utility functions
+ */
+void uv_winsock_init();
+
+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();
+
+/*
+ * Init system wake-up detection
+ */
+void uv__init_detect_system_wakeup();
+
+#endif /* UV_WIN_INTERNAL_H_ */
diff --git a/src/win/loop-watcher.c b/src/win/loop-watcher.c
new file mode 100644 (file)
index 0000000..20e4509
--- /dev/null
@@ -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/src/win/pipe.c b/src/win/pipe.c
new file mode 100644 (file)
index 0000000..2442be7
--- /dev/null
@@ -0,0 +1,2130 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "internal.h"
+#include "handle-inl.h"
+#include "stream-inl.h"
+#include "req-inl.h"
+
+typedef struct uv__ipc_queue_item_s uv__ipc_queue_item_t;
+
+struct uv__ipc_queue_item_s {
+  /*
+   * NOTE: It is important for socket_info_ex to be the first field,
+   * because we will we assigning it to the pending_ipc_info.socket_info
+   */
+  uv__ipc_socket_info_ex socket_info_ex;
+  QUEUE member;
+  int tcp_connection;
+};
+
+/* 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 protocol flags. */
+#define UV_IPC_RAW_DATA       0x0001
+#define UV_IPC_TCP_SERVER     0x0002
+#define UV_IPC_TCP_CONNECTION 0x0004
+
+/* IPC frame header. */
+typedef struct {
+  int flags;
+  uint64_t raw_data_length;
+} uv_ipc_frame_header_t;
+
+/* IPC frame, which contains an imported TCP socket stream. */
+typedef struct {
+  uv_ipc_frame_header_t header;
+  uv__ipc_socket_info_ex socket_info_ex;
+} uv_ipc_frame_uv_stream;
+
+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_pid = 0;
+  handle->pipe.conn.remaining_ipc_rawdata_bytes = 0;
+  QUEUE_INIT(&handle->pipe.conn.pending_ipc_info.queue);
+  handle->pipe.conn.pending_ipc_info.queue_len = 0;
+  handle->ipc = ipc;
+  handle->pipe.conn.non_overlapped_writes_tail = NULL;
+  handle->pipe.conn.readfile_thread = NULL;
+
+  uv_req_init(loop, (uv_req_t*) &handle->pipe.conn.ipc_header_write_req);
+
+  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 (pCancelSynchronousIo &&
+      handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
+      uv_mutex_init(&handle->pipe.conn.readfile_mutex);
+      handle->flags |= UV_HANDLE_PIPE_READ_CANCELABLE;
+  }
+}
+
+
+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,
+      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) &&
+      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 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_queue_item_t* item;
+
+  if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) {
+    handle->flags &= ~UV_HANDLE_PIPE_READ_CANCELABLE;
+    uv_mutex_destroy(&handle->pipe.conn.readfile_mutex);
+  }
+
+  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.pending_ipc_info.queue)) {
+        QUEUE* q;
+        SOCKET socket;
+
+        q = QUEUE_HEAD(&handle->pipe.conn.pending_ipc_info.queue);
+        QUEUE_REMOVE(q);
+        item = QUEUE_DATA(q, uv__ipc_queue_item_t, member);
+
+        /* Materialize socket and close it */
+        socket = WSASocketW(FROM_PROTOCOL_INFO,
+                            FROM_PROTOCOL_INFO,
+                            FROM_PROTOCOL_INFO,
+                            &item->socket_info_ex.socket_info,
+                            0,
+                            WSA_FLAG_OVERLAPPED);
+        uv__free(item);
+
+        if (socket != INVALID_SOCKET)
+          closesocket(socket);
+      }
+      handle->pipe.conn.pending_ipc_info.queue_len = 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) {
+          CloseHandle(handle->read_req.event_handle);
+          handle->read_req.event_handle = NULL;
+        }
+      }
+    }
+
+    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(loop, (uv_req_t*) req);
+    req->type = 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 = (WCHAR*)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.
+   */
+  handle->pipe.serv.accept_reqs[0].pipeHandle = CreateNamedPipeW(handle->name,
+      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+      FILE_FLAG_FIRST_PIPE_INSTANCE,
+      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+      PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL);
+
+  if (handle->pipe.serv.accept_reqs[0].pipeHandle == INVALID_HANDLE_VALUE) {
+    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;
+  }
+
+  if (uv_set_pipe_handle(loop,
+                         handle,
+                         handle->pipe.serv.accept_reqs[0].pipeHandle,
+                         -1,
+                         0)) {
+    err = GetLastError();
+    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;
+  }
+
+  if (handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE) {
+    CloseHandle(handle->pipe.serv.accept_reqs[0].pipeHandle);
+    handle->pipe.serv.accept_reqs[0].pipeHandle = INVALID_HANDLE_VALUE;
+  }
+
+  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(loop, (uv_req_t*) req);
+  req->type = 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 = (WCHAR*)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_pause_read(uv_pipe_t* handle) {
+  if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) {
+      /* Pause the ReadFile task briefly, to work
+         around the Windows kernel bug that causes
+         any access to a NamedPipe to deadlock if
+         any process has called ReadFile */
+      HANDLE h;
+      uv_mutex_lock(&handle->pipe.conn.readfile_mutex);
+      h = handle->pipe.conn.readfile_thread;
+      while (h) {
+        /* spinlock: we expect this to finish quickly,
+           or we are probably about to deadlock anyways
+           (in the kernel), so it doesn't matter */
+        pCancelSynchronousIo(h);
+        SwitchToThread(); /* yield thread control briefly */
+        h = handle->pipe.conn.readfile_thread;
+      }
+  }
+}
+
+
+void uv__pipe_unpause_read(uv_pipe_t* handle) {
+  if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) {
+    uv_mutex_unlock(&handle->pipe.conn.readfile_mutex);
+  }
+}
+
+
+void uv__pipe_stop_read(uv_pipe_t* handle) {
+  handle->flags &= ~UV_HANDLE_READING;
+  uv__pipe_pause_read((uv_pipe_t*)handle);
+  uv__pipe_unpause_read((uv_pipe_t*)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_stop_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);
+}
+
+
+static 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) {
+    assert(req->pipeHandle == INVALID_HANDLE_VALUE);
+
+    req->pipeHandle = CreateNamedPipeW(handle->name,
+        PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+        PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL);
+
+    if (req->pipeHandle == INVALID_HANDLE_VALUE) {
+      SET_REQ_ERROR(req, GetLastError());
+      uv_insert_pending_req(loop, (uv_req_t*) req);
+      handle->reqs_pending++;
+      return;
+    }
+
+    if (uv_set_pipe_handle(loop, handle, req->pipeHandle, -1, 0)) {
+      CloseHandle(req->pipeHandle);
+      req->pipeHandle = INVALID_HANDLE_VALUE;
+      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;
+  }
+
+  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_queue_item_t* item;
+  int err;
+
+  if (server->ipc) {
+    if (QUEUE_EMPTY(&server->pipe.conn.pending_ipc_info.queue)) {
+      /* No valid pending sockets. */
+      return WSAEWOULDBLOCK;
+    }
+
+    q = QUEUE_HEAD(&server->pipe.conn.pending_ipc_info.queue);
+    QUEUE_REMOVE(q);
+    server->pipe.conn.pending_ipc_info.queue_len--;
+    item = QUEUE_DATA(q, uv__ipc_queue_item_t, member);
+
+    err = uv_tcp_import((uv_tcp_t*)client,
+                        &item->socket_info_ex,
+                        item->tcp_connection);
+    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;
+
+    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;
+  }
+
+  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* parameter) {
+  int result;
+  DWORD bytes;
+  uv_read_t* req = (uv_read_t*) parameter;
+  uv_pipe_t* handle = (uv_pipe_t*) req->data;
+  uv_loop_t* loop = handle->loop;
+  HANDLE hThread = NULL;
+  DWORD err;
+  uv_mutex_t *m = &handle->pipe.conn.readfile_mutex;
+
+  assert(req != NULL);
+  assert(req->type == UV_READ);
+  assert(handle->type == UV_NAMED_PIPE);
+
+  if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) {
+    uv_mutex_lock(m); /* mutex controls *setting* of readfile_thread */
+    if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                        GetCurrentProcess(), &hThread,
+                        0, TRUE, DUPLICATE_SAME_ACCESS)) {
+      handle->pipe.conn.readfile_thread = hThread;
+    } else {
+      hThread = NULL;
+    }
+    uv_mutex_unlock(m);
+  }
+restart_readfile:
+  result = ReadFile(handle->handle,
+                    &uv_zero_,
+                    0,
+                    &bytes,
+                    NULL);
+  if (!result) {
+    err = GetLastError();
+    if (err == ERROR_OPERATION_ABORTED &&
+        handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) {
+      if (handle->flags & UV_HANDLE_READING) {
+        /* just a brief break to do something else */
+        handle->pipe.conn.readfile_thread = NULL;
+        /* resume after it is finished */
+        uv_mutex_lock(m);
+        handle->pipe.conn.readfile_thread = hThread;
+        uv_mutex_unlock(m);
+        goto restart_readfile;
+      } else {
+        result = 1; /* successfully stopped reading */
+      }
+    }
+  }
+  if (hThread) {
+    assert(hThread == handle->pipe.conn.readfile_thread);
+    /* mutex does not control clearing readfile_thread */
+    handle->pipe.conn.readfile_thread = NULL;
+    uv_mutex_lock(m);
+    /* only when we hold the mutex lock is it safe to
+       open or close the handle */
+    CloseHandle(hThread);
+    uv_mutex_unlock(m);
+  }
+
+  if (!result) {
+    SET_REQ_ERROR(req, err);
+  }
+
+  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) {
+    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) {
+      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->event_handle) {
+        req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+        if (!req->event_handle) {
+          uv_fatal_error(GetLastError(), "CreateEvent");
+        }
+      }
+      if (req->wait_handle == INVALID_HANDLE_VALUE) {
+        if (!RegisterWaitForSingleObject(&req->wait_handle,
+            req->u.io.overlapped.hEvent, 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))
+    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_pipe_write_impl(uv_loop_t* loop,
+                              uv_write_t* req,
+                              uv_pipe_t* handle,
+                              const uv_buf_t bufs[],
+                              unsigned int nbufs,
+                              uv_stream_t* send_handle,
+                              uv_write_cb cb) {
+  int err;
+  int result;
+  uv_tcp_t* tcp_send_handle;
+  uv_write_t* ipc_header_req = NULL;
+  uv_ipc_frame_uv_stream ipc_frame;
+
+  if (nbufs != 1 && (nbufs != 0 || !send_handle)) {
+    return ERROR_NOT_SUPPORTED;
+  }
+
+  /* Only TCP handles are supported for sharing. */
+  if (send_handle && ((send_handle->type != UV_TCP) ||
+      (!(send_handle->flags & UV_HANDLE_BOUND) &&
+       !(send_handle->flags & UV_HANDLE_CONNECTION)))) {
+    return ERROR_NOT_SUPPORTED;
+  }
+
+  assert(handle->handle != INVALID_HANDLE_VALUE);
+
+  uv_req_init(loop, (uv_req_t*) req);
+  req->type = UV_WRITE;
+  req->handle = (uv_stream_t*) handle;
+  req->cb = cb;
+  req->ipc_header = 0;
+  req->event_handle = NULL;
+  req->wait_handle = INVALID_HANDLE_VALUE;
+  memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
+
+  if (handle->ipc) {
+    assert(!(handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
+    ipc_frame.header.flags = 0;
+
+    /* Use the IPC framing protocol. */
+    if (send_handle) {
+      tcp_send_handle = (uv_tcp_t*)send_handle;
+
+      if (handle->pipe.conn.ipc_pid == 0) {
+          handle->pipe.conn.ipc_pid = uv_current_pid();
+      }
+
+      err = uv_tcp_duplicate_socket(tcp_send_handle, handle->pipe.conn.ipc_pid,
+          &ipc_frame.socket_info_ex.socket_info);
+      if (err) {
+        return err;
+      }
+
+      ipc_frame.socket_info_ex.delayed_error = tcp_send_handle->delayed_error;
+
+      ipc_frame.header.flags |= UV_IPC_TCP_SERVER;
+
+      if (tcp_send_handle->flags & UV_HANDLE_CONNECTION) {
+        ipc_frame.header.flags |= UV_IPC_TCP_CONNECTION;
+      }
+    }
+
+    if (nbufs == 1) {
+      ipc_frame.header.flags |= UV_IPC_RAW_DATA;
+      ipc_frame.header.raw_data_length = bufs[0].len;
+    }
+
+    /*
+     * Use the provided req if we're only doing a single write.
+     * If we're doing multiple writes, use ipc_header_write_req to do
+     * the first write, and then use the provided req for the second write.
+     */
+    if (!(ipc_frame.header.flags & UV_IPC_RAW_DATA)) {
+      ipc_header_req = req;
+    } else {
+      /*
+       * Try to use the preallocated write req if it's available.
+       * Otherwise allocate a new one.
+       */
+      if (handle->pipe.conn.ipc_header_write_req.type != UV_WRITE) {
+        ipc_header_req = (uv_write_t*)&handle->pipe.conn.ipc_header_write_req;
+      } else {
+        ipc_header_req = (uv_write_t*)uv__malloc(sizeof(uv_write_t));
+        if (!ipc_header_req) {
+          uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+        }
+      }
+
+      uv_req_init(loop, (uv_req_t*) ipc_header_req);
+      ipc_header_req->type = UV_WRITE;
+      ipc_header_req->handle = (uv_stream_t*) handle;
+      ipc_header_req->cb = NULL;
+      ipc_header_req->ipc_header = 1;
+    }
+
+    /* Write the header or the whole frame. */
+    memset(&ipc_header_req->u.io.overlapped, 0,
+           sizeof(ipc_header_req->u.io.overlapped));
+
+    /* Using overlapped IO, but wait for completion before returning.
+       This write is blocking because ipc_frame is on stack. */
+    ipc_header_req->u.io.overlapped.hEvent = CreateEvent(NULL, 1, 0, NULL);
+    if (!ipc_header_req->u.io.overlapped.hEvent) {
+      uv_fatal_error(GetLastError(), "CreateEvent");
+    }
+
+    result = WriteFile(handle->handle,
+                        &ipc_frame,
+                        ipc_frame.header.flags & UV_IPC_TCP_SERVER ?
+                          sizeof(ipc_frame) : sizeof(ipc_frame.header),
+                        NULL,
+                        &ipc_header_req->u.io.overlapped);
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      err = GetLastError();
+      CloseHandle(ipc_header_req->u.io.overlapped.hEvent);
+      return err;
+    }
+
+    if (!result) {
+      /* Request not completed immediately. Wait for it.*/
+      if (WaitForSingleObject(ipc_header_req->u.io.overlapped.hEvent, INFINITE) !=
+          WAIT_OBJECT_0) {
+        err = GetLastError();
+        CloseHandle(ipc_header_req->u.io.overlapped.hEvent);
+        return err;
+      }
+    }
+    ipc_header_req->u.io.queued_bytes = 0;
+    CloseHandle(ipc_header_req->u.io.overlapped.hEvent);
+    ipc_header_req->u.io.overlapped.hEvent = NULL;
+
+    REGISTER_HANDLE_REQ(loop, handle, ipc_header_req);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+
+    /* If we don't have any raw data to write - we're done. */
+    if (!(ipc_frame.header.flags & UV_IPC_RAW_DATA)) {
+      return 0;
+    }
+  }
+
+  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,
+                       bufs[0].base,
+                       bufs[0].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 = bufs[0];
+    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 = bufs[0].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 */
+    req->u.io.overlapped.hEvent = CreateEvent(NULL, 1, 0, NULL);
+    if (!req->u.io.overlapped.hEvent) {
+      uv_fatal_error(GetLastError(), "CreateEvent");
+    }
+
+    result = WriteFile(handle->handle,
+                       bufs[0].base,
+                       bufs[0].len,
+                       NULL,
+                       &req->u.io.overlapped);
+
+    if (!result && GetLastError() != ERROR_IO_PENDING) {
+      err = GetLastError();
+      CloseHandle(req->u.io.overlapped.hEvent);
+      return err;
+    }
+
+    if (result) {
+      /* Request completed immediately. */
+      req->u.io.queued_bytes = 0;
+    } else {
+      /* Request queued by the kernel. */
+      req->u.io.queued_bytes = bufs[0].len;
+      handle->write_queue_size += req->u.io.queued_bytes;
+      if (WaitForSingleObject(req->u.io.overlapped.hEvent, INFINITE) !=
+          WAIT_OBJECT_0) {
+        err = GetLastError();
+        CloseHandle(req->u.io.overlapped.hEvent);
+        return uv_translate_sys_error(err);
+      }
+    }
+    CloseHandle(req->u.io.overlapped.hEvent);
+
+    REGISTER_HANDLE_REQ(loop, handle, req);
+    handle->reqs_pending++;
+    handle->stream.conn.write_reqs_pending++;
+    return 0;
+  } else {
+    result = WriteFile(handle->handle,
+                       bufs[0].base,
+                       bufs[0].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 = bufs[0].len;
+      handle->write_queue_size += req->u.io.queued_bytes;
+    }
+
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      req->event_handle = CreateEvent(NULL, 0, 0, NULL);
+      if (!req->event_handle) {
+        uv_fatal_error(GetLastError(), "CreateEvent");
+      }
+      if (!RegisterWaitForSingleObject(&req->wait_handle,
+          req->u.io.overlapped.hEvent, 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;
+}
+
+
+int uv_pipe_write(uv_loop_t* loop,
+                  uv_write_t* req,
+                  uv_pipe_t* handle,
+                  const uv_buf_t bufs[],
+                  unsigned int nbufs,
+                  uv_write_cb cb) {
+  return uv_pipe_write_impl(loop, req, handle, bufs, nbufs, NULL, cb);
+}
+
+
+int uv_pipe_write2(uv_loop_t* loop,
+                   uv_write_t* req,
+                   uv_pipe_t* handle,
+                   const uv_buf_t bufs[],
+                   unsigned int nbufs,
+                   uv_stream_t* send_handle,
+                   uv_write_cb cb) {
+  if (!handle->ipc) {
+    return WSAEINVAL;
+  }
+
+  return uv_pipe_write_impl(loop, req, handle, bufs, nbufs, send_handle, cb);
+}
+
+
+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);
+  }
+}
+
+
+void uv__pipe_insert_pending_socket(uv_pipe_t* handle,
+                                    uv__ipc_socket_info_ex* info,
+                                    int tcp_connection) {
+  uv__ipc_queue_item_t* item;
+
+  item = (uv__ipc_queue_item_t*) uv__malloc(sizeof(*item));
+  if (item == NULL)
+    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
+
+  memcpy(&item->socket_info_ex, info, sizeof(item->socket_info_ex));
+  item->tcp_connection = tcp_connection;
+  QUEUE_INSERT_TAIL(&handle->pipe.conn.pending_ipc_info.queue, &item->member);
+  handle->pipe.conn.pending_ipc_info.queue_len++;
+}
+
+
+void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle,
+    uv_req_t* req) {
+  DWORD bytes, avail;
+  uv_buf_t buf;
+  uv_ipc_frame_uv_stream ipc_frame;
+
+  assert(handle->type == UV_NAMED_PIPE);
+
+  handle->flags &= ~UV_HANDLE_READ_PENDING;
+  eof_timer_stop(handle);
+
+  if (!REQ_SUCCESS(req)) {
+    /* An error occurred doing the 0-read. */
+    if (handle->flags & UV_HANDLE_READING) {
+      uv_pipe_read_error_or_eof(loop,
+                                handle,
+                                GET_REQ_ERROR(req),
+                                uv_null_buf_);
+    }
+  } else {
+    /* Do non-blocking reads until the buffer is empty */
+    while (handle->flags & UV_HANDLE_READING) {
+      if (!PeekNamedPipe(handle->handle,
+                          NULL,
+                          0,
+                          NULL,
+                          &avail,
+                          NULL)) {
+        uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
+        break;
+      }
+
+      if (avail == 0) {
+        /* There is nothing to read after all. */
+        break;
+      }
+
+      if (handle->ipc) {
+        /* Use the IPC framing protocol to read the incoming data. */
+        if (handle->pipe.conn.remaining_ipc_rawdata_bytes == 0) {
+          /* We're reading a new frame.  First, read the header. */
+          assert(avail >= sizeof(ipc_frame.header));
+
+          if (!ReadFile(handle->handle,
+                        &ipc_frame.header,
+                        sizeof(ipc_frame.header),
+                        &bytes,
+                        NULL)) {
+            uv_pipe_read_error_or_eof(loop, handle, GetLastError(),
+              uv_null_buf_);
+            break;
+          }
+
+          assert(bytes == sizeof(ipc_frame.header));
+          assert(ipc_frame.header.flags <= (UV_IPC_TCP_SERVER | UV_IPC_RAW_DATA |
+            UV_IPC_TCP_CONNECTION));
+
+          if (ipc_frame.header.flags & UV_IPC_TCP_SERVER) {
+            assert(avail - sizeof(ipc_frame.header) >=
+              sizeof(ipc_frame.socket_info_ex));
+
+            /* Read the TCP socket info. */
+            if (!ReadFile(handle->handle,
+                          &ipc_frame.socket_info_ex,
+                          sizeof(ipc_frame) - sizeof(ipc_frame.header),
+                          &bytes,
+                          NULL)) {
+              uv_pipe_read_error_or_eof(loop, handle, GetLastError(),
+                uv_null_buf_);
+              break;
+            }
+
+            assert(bytes == sizeof(ipc_frame) - sizeof(ipc_frame.header));
+
+            /* Store the pending socket info. */
+            uv__pipe_insert_pending_socket(
+                handle,
+                &ipc_frame.socket_info_ex,
+                ipc_frame.header.flags & UV_IPC_TCP_CONNECTION);
+          }
+
+          if (ipc_frame.header.flags & UV_IPC_RAW_DATA) {
+            handle->pipe.conn.remaining_ipc_rawdata_bytes =
+              ipc_frame.header.raw_data_length;
+            continue;
+          }
+        } else {
+          avail = min(avail, (DWORD)handle->pipe.conn.remaining_ipc_rawdata_bytes);
+        }
+      }
+
+      buf = uv_buf_init(NULL, 0);
+      handle->alloc_cb((uv_handle_t*) handle, avail, &buf);
+      if (buf.base == NULL || buf.len == 0) {
+        handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
+        break;
+      }
+      assert(buf.base != NULL);
+
+      if (ReadFile(handle->handle,
+                   buf.base,
+                   min(buf.len, avail),
+                   &bytes,
+                   NULL)) {
+        /* Successful read */
+        if (handle->ipc) {
+          assert(handle->pipe.conn.remaining_ipc_rawdata_bytes >= bytes);
+          handle->pipe.conn.remaining_ipc_rawdata_bytes =
+            handle->pipe.conn.remaining_ipc_rawdata_bytes - bytes;
+        }
+        handle->read_cb((uv_stream_t*)handle, bytes, &buf);
+
+        /* Read again only if bytes == buf.len */
+        if (bytes <= buf.len) {
+          break;
+        }
+      } else {
+        uv_pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
+        break;
+      }
+    }
+
+    /* Post another 0-read if still reading and not closing. */
+    if ((handle->flags & UV_HANDLE_READING) &&
+        !(handle->flags & UV_HANDLE_READ_PENDING)) {
+      uv_pipe_queue_read(loop, handle);
+    }
+  }
+
+  DECREASE_PENDING_REQ_COUNT(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;
+    }
+  }
+
+  if (req->ipc_header) {
+    if (req == &handle->pipe.conn.ipc_header_write_req) {
+      req->type = UV_UNKNOWN_REQ;
+    } else {
+      uv__free(req);
+    }
+  } else {
+    if (req->cb) {
+      err = GET_REQ_ERROR(req);
+      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;
+
+  /* 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_pid = uv_parent_pid();
+    assert(pipe->pipe.conn.ipc_pid != -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;
+
+  name_info = NULL;
+
+  if (handle->handle == INVALID_HANDLE_VALUE) {
+    *size = 0;
+    return UV_EINVAL;
+  }
+
+  uv__pipe_pause_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;
+  goto cleanup;
+
+error:
+  uv__free(name_info);
+
+cleanup:
+  uv__pipe_unpause_read((uv_pipe_t*)handle); /* cast away const warning */
+  return err;
+}
+
+
+int uv_pipe_pending_count(uv_pipe_t* handle) {
+  if (!handle->ipc)
+    return 0;
+  return handle->pipe.conn.pending_ipc_info.queue_len;
+}
+
+
+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.pending_ipc_info.queue_len == 0)
+    return UV_UNKNOWN_HANDLE;
+  else
+    return UV_TCP;
+}
diff --git a/src/win/poll.c b/src/win/poll.c
new file mode 100644 (file)
index 0000000..d479e52
--- /dev/null
@@ -0,0 +1,646 @@
+/* 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() {
+  uv_once(&overlapped_dummy_init_guard_, uv__init_overlapped_dummy);
+  return &overlapped_dummy_;
+}
+
+
+static AFD_POLL_INFO* uv__get_afd_poll_info_dummy() {
+  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;
+  DWORD 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 int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
+  AFD_POLL_INFO afd_poll_info;
+  DWORD result;
+
+  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) {
+    DWORD error = WSAGetLastError();
+    if (error != WSA_IO_PENDING)
+      return error;
+  }
+
+  return 0;
+}
+
+
+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 int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) {
+  assert(handle->type == UV_POLL);
+  assert(!(handle->flags & UV__HANDLE_CLOSING));
+  assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0);
+
+  handle->events = events;
+
+  if (handle->events != 0) {
+    uv__handle_start(handle);
+  } else {
+    uv__handle_stop(handle);
+  }
+
+  if ((handle->events & ~(handle->submitted_events_1 |
+      handle->submitted_events_2)) != 0) {
+    uv__fast_poll_submit_poll_req(handle->loop, handle);
+  }
+
+  return 0;
+}
+
+
+static int uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
+  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;
+  } else {
+    /* Cancel outstanding poll requests by executing another, unique poll */
+    /* request that forces the outstanding ones to return. */
+    return uv__fast_poll_cancel_poll_req(loop, 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);
+  }
+}
+
+
+static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) {
+  assert(handle->type == UV_POLL);
+  assert(!(handle->flags & UV__HANDLE_CLOSING));
+  assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0);
+
+  handle->events = events;
+
+  if (handle->events != 0) {
+    uv__handle_start(handle);
+  } else {
+    uv__handle_stop(handle);
+  }
+
+  if ((handle->events &
+      ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) {
+    uv__slow_poll_submit_poll_req(handle->loop, handle);
+  }
+
+  return 0;
+}
+
+
+static int uv__slow_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
+  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;
+}
+
+
+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(loop, (uv_req_t*) &(handle->poll_req_1));
+  handle->poll_req_1.type = UV_POLL_REQ;
+  handle->poll_req_1.data = handle;
+
+  handle->submitted_events_2 = 0;
+  uv_req_init(loop, (uv_req_t*) &(handle->poll_req_2));
+  handle->poll_req_2.type = UV_POLL_REQ;
+  handle->poll_req_2.data = handle;
+
+  return 0;
+}
+
+
+int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) {
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
+    err = uv__fast_poll_set(handle->loop, handle, events);
+  } else {
+    err = uv__slow_poll_set(handle->loop, handle, events);
+  }
+
+  if (err) {
+    return uv_translate_sys_error(err);
+  }
+
+  handle->poll_cb = cb;
+
+  return 0;
+}
+
+
+int uv_poll_stop(uv_poll_t* handle) {
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
+    err = uv__fast_poll_set(handle->loop, handle, 0);
+  } else {
+    err = uv__slow_poll_set(handle->loop, handle, 0);
+  }
+
+  return uv_translate_sys_error(err);
+}
+
+
+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) {
+  if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
+    return uv__fast_poll_close(loop, handle);
+  } else {
+    return uv__slow_poll_close(loop, handle);
+  }
+}
+
+
+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/src/win/process-stdio.c b/src/win/process-stdio.c
new file mode 100644 (file)
index 0000000..032e309
--- /dev/null
@@ -0,0 +1,511 @@
+/* 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;
+
+  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;
+
+  child_pipe = CreateFileA(pipe_name,
+                           client_access,
+                           0,
+                           &sa,
+                           OPEN_EXISTING,
+                           server_pipe->ipc ? 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 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/src/win/process.c b/src/win/process.c
new file mode 100644 (file)
index 0000000..bdf88d2
--- /dev/null
@@ -0,0 +1,1247 @@
+/* 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 size_t n_required_vars = ARRAY_SIZE(required_vars);
+
+
+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(loop, (uv_req_t*)&handle->exit_req);
+  handle->exit_req.type = 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;
+
+      /* Slice until the next ; or \0 is found */
+      dir_end = wcschr(dir_start, 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 = alloca(n_required_vars * sizeof(DWORD*));
+
+  /* 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) {
+    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 == 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 < n_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 < n_required_vars;
+       ptr += len) {
+    int cmp;
+    if (i >= n_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 != len-1) { /* 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 == (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 (wcsncmp(env, L"PATH=", 5) == 0)
+      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 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_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);
+
+  if (options->flags & UV_PROCESS_WINDOWS_HIDE) {
+    /* Use SW_HIDE to avoid any potential process window. */
+    startup.wShowWindow = SW_HIDE;
+  } else {
+    startup.wShowWindow = SW_SHOWDEFAULT;
+  }
+
+  process_flags = CREATE_UNICODE_ENVIRONMENT;
+
+  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_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) {
+  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 = 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/src/win/req-inl.h b/src/win/req-inl.h
new file mode 100644 (file)
index 0000000..b5e502e
--- /dev/null
@@ -0,0 +1,224 @@
+/* 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)))
+
+#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 void uv_req_init(uv_loop_t* loop, uv_req_t* req) {
+  req->type = UV_UNKNOWN_REQ;
+  SET_REQ_SUCCESS(req);
+}
+
+
+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/src/win/req.c b/src/win/req.c
new file mode 100644 (file)
index 0000000..111cc5e
--- /dev/null
@@ -0,0 +1,25 @@
+/* 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"
diff --git a/src/win/signal.c b/src/win/signal.c
new file mode 100644 (file)
index 0000000..2c64a55
--- /dev/null
@@ -0,0 +1,356 @@
+/* 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 ssize_t volatile uv__signal_control_handler_refs = 0;
+static CRITICAL_SECTION uv__signal_lock;
+
+
+void uv_signals_init() {
+  InitializeCriticalSection(&uv__signal_lock);
+}
+
+
+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 = 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 (!previous) {
+      POST_COMPLETION_FOR_REQ(handle->loop, &handle->signal_req);
+    }
+
+    dispatched = 1;
+  }
+
+  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;
+  }
+}
+
+
+static int uv__signal_register_control_handler() {
+  /* When this function is called, the uv__signal_lock must be held. */
+
+  /* If the console control handler has already been hooked, just add a */
+  /* reference. */
+  if (uv__signal_control_handler_refs > 0) {
+    uv__signal_control_handler_refs++;
+    return 0;
+  }
+
+  if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE))
+    return GetLastError();
+
+  uv__signal_control_handler_refs++;
+
+  return 0;
+}
+
+
+static void uv__signal_unregister_control_handler() {
+  /* When this function is called, the uv__signal_lock must be held. */
+  BOOL r;
+
+  /* Don't unregister if the number of console control handlers exceeds one. */
+  /* Just remove a reference in that case. */
+  if (uv__signal_control_handler_refs > 1) {
+    uv__signal_control_handler_refs--;
+    return;
+  }
+
+  assert(uv__signal_control_handler_refs == 1);
+
+  r = SetConsoleCtrlHandler(uv__signal_control_handler, FALSE);
+  /* This should never fail; if it does it is probably a bug in libuv. */
+  assert(r);
+
+  uv__signal_control_handler_refs--;
+}
+
+
+static int uv__signal_register(int signum) {
+  switch (signum) {
+    case SIGINT:
+    case SIGBREAK:
+    case SIGHUP:
+      return uv__signal_register_control_handler();
+
+    case SIGWINCH:
+      /* SIGWINCH is generated in tty.c. No need to register anything. */
+      return 0;
+
+    case SIGILL:
+    case SIGABRT_COMPAT:
+    case SIGFPE:
+    case SIGSEGV:
+    case SIGTERM:
+    case SIGABRT:
+      /* Signal is never raised. */
+      return 0;
+
+    default:
+      /* Invalid signal. */
+      return ERROR_INVALID_PARAMETER;
+  }
+}
+
+
+static void uv__signal_unregister(int signum) {
+  switch (signum) {
+    case SIGINT:
+    case SIGBREAK:
+    case SIGHUP:
+      uv__signal_unregister_control_handler();
+      return;
+
+    case SIGWINCH:
+      /* SIGWINCH is generated in tty.c. No need to unregister anything. */
+      return;
+
+    case SIGILL:
+    case SIGABRT_COMPAT:
+    case SIGFPE:
+    case SIGSEGV:
+    case SIGTERM:
+    case SIGABRT:
+      /* Nothing is registered for this signal. */
+      return;
+
+    default:
+      /* Libuv bug. */
+      assert(0 && "Invalid signum");
+      return;
+  }
+}
+
+
+int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+  uv_req_t* req;
+
+  uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
+  handle->pending_signum = 0;
+  handle->signum = 0;
+  handle->signal_cb = NULL;
+
+  req = &handle->signal_req;
+  uv_req_init(loop, req);
+  req->type = UV_SIGNAL_REQ;
+  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);
+
+  uv__signal_unregister(handle->signum);
+
+  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) {
+  int err;
+
+  /* If the user supplies signum == 0, then return an error already. If the */
+  /* signum is otherwise invalid then uv__signal_register will find out */
+  /* eventually. */
+  if (signum == 0) {
+    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);
+
+  err = uv__signal_register(signum);
+  if (err) {
+    /* Uh-oh, didn't work. */
+    LeaveCriticalSection(&uv__signal_lock);
+    return uv_translate_sys_error(err);
+  }
+
+  handle->signum = signum;
+  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__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/src/win/snprintf.c b/src/win/snprintf.c
new file mode 100644 (file)
index 0000000..776c0e3
--- /dev/null
@@ -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/src/win/stream-inl.h b/src/win/stream-inl.h
new file mode 100644 (file)
index 0000000..b7a3c11
--- /dev/null
@@ -0,0 +1,56 @@
+/* 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;
+}
+
+
+INLINE static void uv_connection_init(uv_stream_t* handle) {
+  handle->flags |= UV_HANDLE_CONNECTION;
+  handle->stream.conn.write_reqs_pending = 0;
+
+  uv_req_init(handle->loop, (uv_req_t*) &(handle->read_req));
+  handle->read_req.event_handle = NULL;
+  handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+  handle->read_req.type = UV_READ;
+  handle->read_req.data = handle;
+
+  handle->stream.conn.shutdown_req = NULL;
+}
+
+
+#endif /* UV_WIN_STREAM_INL_H_ */
diff --git a/src/win/stream.c b/src/win/stream.c
new file mode 100644 (file)
index 0000000..a2466e5
--- /dev/null
@@ -0,0 +1,249 @@
+/* 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_stop_read((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, 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 (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    return UV_EPIPE;
+  }
+
+  err = ERROR_INVALID_PARAMETER;
+  switch (handle->type) {
+    case UV_NAMED_PIPE:
+      err = uv_pipe_write2(loop,
+                           req,
+                           (uv_pipe_t*) handle,
+                           bufs,
+                           nbufs,
+                           send_handle,
+                           cb);
+      break;
+    default:
+      assert(0);
+  }
+
+  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)) {
+    return UV_EPIPE;
+  }
+
+  uv_req_init(loop, (uv_req_t*) req);
+  req->type = UV_SHUTDOWN;
+  req->handle = handle;
+  req->cb = cb;
+
+  handle->flags &= ~UV_HANDLE_WRITABLE;
+  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/src/win/tcp.c b/src/win/tcp.c
new file mode 100644 (file)
index 0000000..0709696
--- /dev/null
@@ -0,0 +1,1510 @@
+/* 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 (pSetFileCompletionNotificationModes &&
+      !(handle->flags & UV_HANDLE_EMULATE_IOCP) && !non_ifs_lsp) {
+    if (pSetFileCompletionNotificationModes((HANDLE) socket,
+        FILE_SKIP_SET_EVENT_ON_HANDLE |
+        FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
+      handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
+    } else if (GetLastError() != ERROR_INVALID_FUNCTION) {
+      return GetLastError();
+    }
+  }
+
+  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) {
+            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) {
+        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");
+  }
+}
+
+
+static 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) {
+    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);
+      handle->reqs_pending++;
+      return;
+    }
+  } 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->u.io.overlapped.hEvent);
+      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);
+    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);
+
+  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, (uv_req_t*)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++;
+    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++;
+  }
+}
+
+
+int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
+  uv_loop_t* loop = handle->loop;
+  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) {
+    handle->tcp.serv.accept_reqs = (uv_tcp_accept_t*)
+      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(loop, (uv_req_t*)req);
+      req->type = 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) {
+          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 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(loop, (uv_req_t*) req);
+      req->type = 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) {
+      handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
+      if (!handle->read_req.event_handle) {
+        uv_fatal_error(GetLastError(), "CreateEvent");
+      }
+    }
+    uv_tcp_queue_read(loop, handle);
+  }
+
+  return 0;
+}
+
+
+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;
+  const struct sockaddr* bind_addr;
+  BOOL success;
+  DWORD bytes;
+  int 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;
+    }
+  }
+
+  uv_req_init(loop, (uv_req_t*) req);
+  req->type = 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,
+                                           addr,
+                                           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) {
+  int result;
+
+  if (handle->socket == INVALID_SOCKET) {
+    return UV_EINVAL;
+  }
+
+  if (handle->delayed_error) {
+    return uv_translate_sys_error(handle->delayed_error);
+  }
+
+  result = getsockname(handle->socket, name, namelen);
+  if (result != 0) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+int uv_tcp_getpeername(const uv_tcp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+  int result;
+
+  if (handle->socket == INVALID_SOCKET) {
+    return UV_EINVAL;
+  }
+
+  if (handle->delayed_error) {
+    return uv_translate_sys_error(handle->delayed_error);
+  }
+
+  result = getpeername(handle->socket, name, namelen);
+  if (result != 0) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 0;
+}
+
+
+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(loop, (uv_req_t*) req);
+  req->type = 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) {
+      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;
+
+  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 */
+    while (handle->flags & UV_HANDLE_READING) {
+      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) {
+      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 (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_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex,
+    int tcp_connection) {
+  int err;
+  SOCKET socket = WSASocketW(FROM_PROTOCOL_INFO,
+                             FROM_PROTOCOL_INFO,
+                             FROM_PROTOCOL_INFO,
+                             &socket_info_ex->socket_info,
+                             0,
+                             WSA_FLAG_OVERLAPPED);
+
+  if (socket == INVALID_SOCKET) {
+    return WSAGetLastError();
+  }
+
+  err = uv_tcp_set_socket(tcp->loop,
+                          tcp,
+                          socket,
+                          socket_info_ex->socket_info.iAddressFamily,
+                          1);
+  if (err) {
+    closesocket(socket);
+    return err;
+  }
+
+  if (tcp_connection) {
+    uv_connection_init((uv_stream_t*)tcp);
+    tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+  }
+
+  tcp->flags |= UV_HANDLE_BOUND;
+  tcp->flags |= UV_HANDLE_SHARED_TCP_SOCKET;
+
+  tcp->delayed_error = socket_info_ex->delayed_error;
+
+  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_duplicate_socket(uv_tcp_t* handle, int pid,
+    LPWSAPROTOCOL_INFOW protocol_info) {
+  if (!(handle->flags & UV_HANDLE_CONNECTION)) {
+    /*
+     * 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.  So, 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_INVALID_PARAMETER;
+      }
+
+      if (!(handle->delayed_error)) {
+        if (listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) {
+          handle->delayed_error = WSAGetLastError();
+        }
+      }
+    }
+  }
+
+  if (WSADuplicateSocketW(handle->socket, pid, protocol_info)) {
+    return WSAGetLastError();
+  }
+
+  handle->flags |= UV_HANDLE_SHARED_TCP_SOCKET;
+
+  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;
+
+  /* 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);
+  }
+
+  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/src/win/thread.c b/src/win/thread.c
new file mode 100644 (file)
index 0000000..91684e9
--- /dev/null
@@ -0,0 +1,697 @@
+/* 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>
+
+#include "uv.h"
+#include "internal.h"
+
+
+#define HAVE_CONDVAR_API() (pInitializeConditionVariable != NULL)
+
+static int uv_cond_fallback_init(uv_cond_t* cond);
+static void uv_cond_fallback_destroy(uv_cond_t* cond);
+static void uv_cond_fallback_signal(uv_cond_t* cond);
+static void uv_cond_fallback_broadcast(uv_cond_t* cond);
+static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex);
+static int uv_cond_fallback_timedwait(uv_cond_t* cond,
+    uv_mutex_t* mutex, uint64_t timeout);
+
+static int uv_cond_condvar_init(uv_cond_t* cond);
+static void uv_cond_condvar_destroy(uv_cond_t* cond);
+static void uv_cond_condvar_signal(uv_cond_t* cond);
+static void uv_cond_condvar_broadcast(uv_cond_t* cond);
+static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex);
+static int uv_cond_condvar_timedwait(uv_cond_t* cond,
+    uv_mutex_t* mutex, uint64_t timeout);
+
+
+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) {
+  struct thread_ctx* ctx;
+  int err;
+  HANDLE thread;
+
+  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,
+                                   0,
+                                   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;
+    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;
+}
+
+
+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. */
+}
+
+
+/* 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.
+ */
+
+static int uv_cond_fallback_init(uv_cond_t* cond) {
+  int err;
+
+  /* Initialize the count to 0. */
+  cond->fallback.waiters_count = 0;
+
+  InitializeCriticalSection(&cond->fallback.waiters_count_lock);
+
+  /* Create an auto-reset event. */
+  cond->fallback.signal_event = CreateEvent(NULL,  /* no security */
+                                            FALSE, /* auto-reset event */
+                                            FALSE, /* non-signaled initially */
+                                            NULL); /* unnamed */
+  if (!cond->fallback.signal_event) {
+    err = GetLastError();
+    goto error2;
+  }
+
+  /* Create a manual-reset event. */
+  cond->fallback.broadcast_event = CreateEvent(NULL,  /* no security */
+                                               TRUE,  /* manual-reset */
+                                               FALSE, /* non-signaled */
+                                               NULL); /* unnamed */
+  if (!cond->fallback.broadcast_event) {
+    err = GetLastError();
+    goto error;
+  }
+
+  return 0;
+
+error:
+  CloseHandle(cond->fallback.signal_event);
+error2:
+  DeleteCriticalSection(&cond->fallback.waiters_count_lock);
+  return uv_translate_sys_error(err);
+}
+
+
+static int uv_cond_condvar_init(uv_cond_t* cond) {
+  pInitializeConditionVariable(&cond->cond_var);
+  return 0;
+}
+
+
+int uv_cond_init(uv_cond_t* cond) {
+  uv__once_init();
+
+  if (HAVE_CONDVAR_API())
+    return uv_cond_condvar_init(cond);
+  else
+    return uv_cond_fallback_init(cond);
+}
+
+
+static void uv_cond_fallback_destroy(uv_cond_t* cond) {
+  if (!CloseHandle(cond->fallback.broadcast_event))
+    abort();
+  if (!CloseHandle(cond->fallback.signal_event))
+    abort();
+  DeleteCriticalSection(&cond->fallback.waiters_count_lock);
+}
+
+
+static void uv_cond_condvar_destroy(uv_cond_t* cond) {
+  /* nothing to do */
+}
+
+
+void uv_cond_destroy(uv_cond_t* cond) {
+  if (HAVE_CONDVAR_API())
+    uv_cond_condvar_destroy(cond);
+  else
+    uv_cond_fallback_destroy(cond);
+}
+
+
+static void uv_cond_fallback_signal(uv_cond_t* cond) {
+  int have_waiters;
+
+  /* Avoid race conditions. */
+  EnterCriticalSection(&cond->fallback.waiters_count_lock);
+  have_waiters = cond->fallback.waiters_count > 0;
+  LeaveCriticalSection(&cond->fallback.waiters_count_lock);
+
+  if (have_waiters)
+    SetEvent(cond->fallback.signal_event);
+}
+
+
+static void uv_cond_condvar_signal(uv_cond_t* cond) {
+  pWakeConditionVariable(&cond->cond_var);
+}
+
+
+void uv_cond_signal(uv_cond_t* cond) {
+  if (HAVE_CONDVAR_API())
+    uv_cond_condvar_signal(cond);
+  else
+    uv_cond_fallback_signal(cond);
+}
+
+
+static void uv_cond_fallback_broadcast(uv_cond_t* cond) {
+  int have_waiters;
+
+  /* Avoid race conditions. */
+  EnterCriticalSection(&cond->fallback.waiters_count_lock);
+  have_waiters = cond->fallback.waiters_count > 0;
+  LeaveCriticalSection(&cond->fallback.waiters_count_lock);
+
+  if (have_waiters)
+    SetEvent(cond->fallback.broadcast_event);
+}
+
+
+static void uv_cond_condvar_broadcast(uv_cond_t* cond) {
+  pWakeAllConditionVariable(&cond->cond_var);
+}
+
+
+void uv_cond_broadcast(uv_cond_t* cond) {
+  if (HAVE_CONDVAR_API())
+    uv_cond_condvar_broadcast(cond);
+  else
+    uv_cond_fallback_broadcast(cond);
+}
+
+
+static int uv_cond_wait_helper(uv_cond_t* cond, uv_mutex_t* mutex,
+    DWORD dwMilliseconds) {
+  DWORD result;
+  int last_waiter;
+  HANDLE handles[2] = {
+    cond->fallback.signal_event,
+    cond->fallback.broadcast_event
+  };
+
+  /* Avoid race conditions. */
+  EnterCriticalSection(&cond->fallback.waiters_count_lock);
+  cond->fallback.waiters_count++;
+  LeaveCriticalSection(&cond->fallback.waiters_count_lock);
+
+  /* It's ok to release the <mutex> here since Win32 manual-reset events */
+  /* maintain state when used with <SetEvent>. This avoids the "lost wakeup" */
+  /* bug. */
+  uv_mutex_unlock(mutex);
+
+  /* Wait for either event to become signaled due to <uv_cond_signal> being */
+  /* called or <uv_cond_broadcast> being called. */
+  result = WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds);
+
+  EnterCriticalSection(&cond->fallback.waiters_count_lock);
+  cond->fallback.waiters_count--;
+  last_waiter = result == WAIT_OBJECT_0 + 1
+      && cond->fallback.waiters_count == 0;
+  LeaveCriticalSection(&cond->fallback.waiters_count_lock);
+
+  /* Some thread called <pthread_cond_broadcast>. */
+  if (last_waiter) {
+    /* We're the last waiter to be notified or to stop waiting, so reset the */
+    /* the manual-reset event. */
+    ResetEvent(cond->fallback.broadcast_event);
+  }
+
+  /* Reacquire the <mutex>. */
+  uv_mutex_lock(mutex);
+
+  if (result == WAIT_OBJECT_0 || result == WAIT_OBJECT_0 + 1)
+    return 0;
+
+  if (result == WAIT_TIMEOUT)
+    return UV_ETIMEDOUT;
+
+  abort();
+  return -1; /* Satisfy the compiler. */
+}
+
+
+static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
+  if (uv_cond_wait_helper(cond, mutex, INFINITE))
+    abort();
+}
+
+
+static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
+  if (!pSleepConditionVariableCS(&cond->cond_var, mutex, INFINITE))
+    abort();
+}
+
+
+void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) {
+  if (HAVE_CONDVAR_API())
+    uv_cond_condvar_wait(cond, mutex);
+  else
+    uv_cond_fallback_wait(cond, mutex);
+}
+
+
+static int uv_cond_fallback_timedwait(uv_cond_t* cond,
+    uv_mutex_t* mutex, uint64_t timeout) {
+  return uv_cond_wait_helper(cond, mutex, (DWORD)(timeout / 1e6));
+}
+
+
+static int uv_cond_condvar_timedwait(uv_cond_t* cond,
+    uv_mutex_t* mutex, uint64_t timeout) {
+  if (pSleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6)))
+    return 0;
+  if (GetLastError() != ERROR_TIMEOUT)
+    abort();
+  return UV_ETIMEDOUT;
+}
+
+
+int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex,
+    uint64_t timeout) {
+  if (HAVE_CONDVAR_API())
+    return uv_cond_condvar_timedwait(cond, mutex, timeout);
+  else
+    return uv_cond_fallback_timedwait(cond, mutex, timeout);
+}
+
+
+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/src/win/timer.c b/src/win/timer.c
new file mode 100644 (file)
index 0000000..27ca771
--- /dev/null
@@ -0,0 +1,195 @@
+/* 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 "uv.h"
+#include "internal.h"
+#include "tree.h"
+#include "handle-inl.h"
+
+
+/* The number of milliseconds in one second. */
+#define UV__MILLISEC 1000
+
+
+void uv_update_time(uv_loop_t* loop) {
+  uint64_t new_time = uv__hrtime(UV__MILLISEC);
+  assert(new_time >= loop->time);
+  loop->time = new_time;
+}
+
+
+static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) {
+  if (a->due < b->due)
+    return -1;
+  if (a->due > b->due)
+    return 1;
+  /*
+   *  compare start_id when both has the same due. start_id is
+   *  allocated with loop->timer_counter in uv_timer_start().
+   */
+  if (a->start_id < b->start_id)
+    return -1;
+  if (a->start_id > b->start_id)
+    return 1;
+  return 0;
+}
+
+
+RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare);
+
+
+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;
+}
+
+
+void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) {
+  if (handle->flags & UV__HANDLE_CLOSING) {
+    assert(!(handle->flags & UV_HANDLE_CLOSED));
+    uv__handle_close(handle);
+  }
+}
+
+
+static uint64_t get_clamped_due_time(uint64_t loop_time, uint64_t timeout) {
+  uint64_t clamped_timeout;
+
+  clamped_timeout = loop_time + timeout;
+  if (clamped_timeout < timeout)
+    clamped_timeout = (uint64_t) -1;
+
+  return clamped_timeout;
+}
+
+
+int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, uint64_t timeout,
+    uint64_t repeat) {
+  uv_loop_t* loop = handle->loop;
+  uv_timer_t* old;
+
+  if (timer_cb == NULL)
+    return UV_EINVAL;
+
+  if (uv__is_active(handle))
+    uv_timer_stop(handle);
+
+  handle->timer_cb = timer_cb;
+  handle->due = get_clamped_due_time(loop->time, timeout);
+  handle->repeat = repeat;
+  uv__handle_start(handle);
+
+  /* start_id is the second index to be compared in uv__timer_cmp() */
+  handle->start_id = handle->loop->timer_counter++;
+
+  old = RB_INSERT(uv_timer_tree_s, &loop->timers, handle);
+  assert(old == NULL);
+
+  return 0;
+}
+
+
+int uv_timer_stop(uv_timer_t* handle) {
+  uv_loop_t* loop = handle->loop;
+
+  if (!uv__is_active(handle))
+    return 0;
+
+  RB_REMOVE(uv_timer_tree_s, &loop->timers, handle);
+  uv__handle_stop(handle);
+
+  return 0;
+}
+
+
+int uv_timer_again(uv_timer_t* handle) {
+  /* If timer_cb is NULL that means that the timer was never started. */
+  if (!handle->timer_cb) {
+    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) {
+  assert(handle->type == UV_TIMER);
+  handle->repeat = repeat;
+}
+
+
+uint64_t uv_timer_get_repeat(const uv_timer_t* handle) {
+  assert(handle->type == UV_TIMER);
+  return handle->repeat;
+}
+
+
+DWORD uv__next_timeout(const uv_loop_t* loop) {
+  uv_timer_t* timer;
+  int64_t delta;
+
+  /* Check if there are any running timers
+   * Need to cast away const first, since RB_MIN doesn't know what we are
+   * going to do with this return value, it can't be marked const
+   */
+  timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)loop)->timers);
+  if (timer) {
+    delta = timer->due - loop->time;
+    if (delta >= UINT_MAX - 1) {
+      /* A timeout value of UINT_MAX means infinite, so that's no good. */
+      return UINT_MAX - 1;
+    } else if (delta < 0) {
+      /* Negative timeout values are not allowed */
+      return 0;
+    } else {
+      return (DWORD)delta;
+    }
+  } else {
+    /* No timers */
+    return INFINITE;
+  }
+}
+
+
+void uv_process_timers(uv_loop_t* loop) {
+  uv_timer_t* timer;
+
+  /* Call timer callbacks */
+  for (timer = RB_MIN(uv_timer_tree_s, &loop->timers);
+       timer != NULL && timer->due <= loop->time;
+       timer = RB_MIN(uv_timer_tree_s, &loop->timers)) {
+
+    uv_timer_stop(timer);
+    uv_timer_again(timer);
+    timer->timer_cb((uv_timer_t*) timer);
+  }
+}
diff --git a/src/win/tty.c b/src/win/tty.c
new file mode 100644 (file)
index 0000000..1b7adf6
--- /dev/null
@@ -0,0 +1,2282 @@
+/* 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 "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           0x00
+#define ANSI_ESCAPE_SEEN      0x02
+#define ANSI_CSI              0x04
+#define ANSI_ST_CONTROL       0x08
+#define ANSI_IGNORE           0x10
+#define ANSI_IN_ARG           0x20
+#define ANSI_IN_STRING        0x40
+#define ANSI_BACKSLASH_SEEN   0x80
+
+#define MAX_INPUT_BUFFER_LENGTH 8192
+#define MAX_CONSOLE_CHAR 8192
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* 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;
+
+/* 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 HANDLE uv_tty_output_handle = INVALID_HANDLE_VALUE;
+
+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;
+
+typedef enum {
+  UV_SUPPORTED,
+  UV_UNCHECKED,
+  UV_UNSUPPORTED
+} uv_vtermstate_t;
+/* Determine whether or not ANSI support is enabled. */
+static uv_vtermstate_t uv__vterm_state = UV_UNCHECKED;
+static void uv__determine_vterm_state(HANDLE handle);
+
+void uv_console_init() {
+  if (uv_sem_init(&uv_tty_output_lock, 1))
+    abort();
+}
+
+
+int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) {
+  HANDLE handle;
+  CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
+
+  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;
+  }
+
+  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 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__vterm_state == UV_UNCHECKED)
+      uv__determine_vterm_state(handle);
+
+    /* Store the global tty output handle. This handle is used by TTY read */
+    /* streams to update the virtual window when a CONSOLE_BUFFER_SIZE_EVENT */
+    /* is received. */
+    uv_tty_output_handle = handle;
+
+    /* Remember the original console text attributes. */
+    uv_tty_capture_initial_style(&screen_buffer_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* 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 = 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;
+
+  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;
+  }
+
+  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_is_tty(uv_file file) {
+  DWORD result;
+  return GetConsoleMode((HANDLE) _get_osfhandle(file), &result) != 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);
+    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--;
+
+      /* If the window was resized, recompute the virtual window size. This */
+      /* will trigger a SIGWINCH signal if the window size changed in an */
+      /* way that matters to libuv. */
+      if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
+        CONSOLE_SCREEN_BUFFER_INFO info;
+
+        uv_sem_wait(&uv_tty_output_lock);
+
+        if (uv_tty_output_handle != INVALID_HANDLE_VALUE &&
+            GetConsoleScreenBufferInfo(uv_tty_output_handle, &info)) {
+          uv_tty_update_virtual_window(&info);
+        }
+
+        uv_sem_post(&uv_tty_output_lock);
+
+        continue;
+      }
+
+      /* Ignore other events that are not key or resize 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.dwControlKeyState & LEFT_ALT_PRESSED) ||
+          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] = { handle->tty.rd.last_utf16_high_surrogate,
+                                    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 {
+      /* The read was cancelled, or whatever we don't care */
+      handle->read_cb((uv_stream_t*) handle, 0, &buf);
+    }
+
+  } else {
+    if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
+      /* 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);
+    } else {
+      handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING;
+      handle->read_cb((uv_stream_t*) handle, 0, &buf);
+    }
+  }
+
+  /* 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);
+    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) {
+  int old_virtual_width = uv_tty_virtual_width;
+  int old_virtual_height = uv_tty_virtual_height;
+
+  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;
+  }
+
+  /* If the virtual window size changed, emit a SIGWINCH signal. Don't emit */
+  /* if this was the first time the virtual window size was computed. */
+  if (old_virtual_width != -1 && old_virtual_height != -1 &&
+      (uv_tty_virtual_width != old_virtual_width ||
+       uv_tty_virtual_height != old_virtual_height)) {
+    uv__signal_dispatch(SIGWINCH);
+  }
+}
+
+
+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 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, &info)) {
+    *error = GetLastError();
+    return -1;
+  }
+
+  count = info.dwSize.X * 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(&info);
+
+  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_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];
+  WCHAR* utf16_buffer;
+  DWORD utf16_buf_used = 0;
+  unsigned int i, len, max_len, pos;
+  int allocate = 0;
+
+#define FLUSH_TEXT()                                                 \
+  do {                                                               \
+    pos = 0;                                                         \
+    do {                                                             \
+      len = utf16_buf_used - pos;                                    \
+      if (len > MAX_CONSOLE_CHAR)                                    \
+        len = MAX_CONSOLE_CHAR;                                      \
+      uv_tty_emit_text(handle, &utf16_buffer[pos], len, error);      \
+      pos += len;                                                    \
+    } while (pos < utf16_buf_used);                                  \
+    if (allocate) {                                                  \
+      uv__free(utf16_buffer);                                        \
+      allocate = 0;                                                  \
+      utf16_buffer = utf16_buf;                                      \
+    }                                                                \
+    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 char 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;
+
+  utf16_buffer = utf16_buf;
+
+  uv_sem_wait(&uv_tty_output_lock);
+
+  for (i = 0; i < nbufs; i++) {
+    uv_buf_t buf = bufs[i];
+    unsigned int j;
+
+    if (uv__vterm_state == UV_SUPPORTED && buf.len > 0) {
+      utf16_buf_used = MultiByteToWideChar(CP_UTF8,
+                                           0,
+                                           buf.base,
+                                           buf.len,
+                                           NULL,
+                                           0);
+
+      if (utf16_buf_used == 0) {
+        *error = GetLastError();
+        break;
+      }
+
+      max_len = (utf16_buf_used + 1) * sizeof(WCHAR);
+      allocate = max_len > MAX_CONSOLE_CHAR;
+      if (allocate)
+        utf16_buffer = uv__malloc(max_len);
+      if (!MultiByteToWideChar(CP_UTF8,
+                               0,
+                               buf.base,
+                               buf.len,
+                               utf16_buffer,
+                               utf16_buf_used)) {
+        if (allocate)
+          uv__free(utf16_buffer);
+        *error = GetLastError();
+        break;
+      }
+
+      FLUSH_TEXT();
+
+      continue;
+    }
+
+    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 (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_CSI) {
+        if (!(ansi_parser_state & ANSI_IGNORE)) {
+          if (utf8_codepoint >= '0' && utf8_codepoint <= '9') {
+            /* Parsing a numerical argument */
+
+            if (!(ansi_parser_state & ANSI_IN_ARG)) {
+              /* We were not currently parsing a number */
+
+              /* 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;
+              }
+
+              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) &&
+                     handle->tty.wr.ansi_csi_argc == 0) {
+            /* Ignores '?' 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. */
+            continue;
+
+          } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~' &&
+                     (handle->tty.wr.ansi_csi_argc > 0 || utf8_codepoint != '[')) {
+            int x, y, d;
+
+            /* Command byte */
+            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;
+
+              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;
+            }
+
+            /* 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 {
+          /* 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_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(loop, (uv_req_t*) req);
+  req->type = 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->u.fd == -1)
+    CloseHandle(handle->handle);
+  else
+    close(handle->u.fd);
+
+  if (handle->flags & UV_HANDLE_READING)
+    uv_tty_read_stop(handle);
+
+  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);
+  }
+}
+
+
+/* TODO: remove me */
+void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+    uv_req_t* raw_req) {
+  abort();
+}
+
+
+/* TODO: remove me */
+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;
+
+  if (!GetConsoleMode(handle, &dwMode)) {
+    uv__vterm_state = UV_UNSUPPORTED;
+    return;
+  }
+
+  dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+  if (!SetConsoleMode(handle, dwMode)) {
+    uv__vterm_state = UV_UNSUPPORTED;
+    return;
+  }
+
+  uv__vterm_state = UV_SUPPORTED;
+}
diff --git a/src/win/udp.c b/src/win/udp.c
new file mode 100644 (file)
index 0000000..9bf1453
--- /dev/null
@@ -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 <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_getsockname(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+  int result;
+
+  if (handle->socket == INVALID_SOCKET) {
+    return UV_EINVAL;
+  }
+
+  result = getsockname(handle->socket, name, namelen);
+  if (result != 0) {
+    return uv_translate_sys_error(WSAGetLastError());
+  }
+
+  return 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();
+  }
+
+  if (pSetFileCompletionNotificationModes) {
+    /* 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 (pSetFileCompletionNotificationModes((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 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__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(loop, (uv_req_t*) &(handle->recv_req));
+  handle->recv_req.type = 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;
+}
+
+
+int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) {
+  return uv_udp_init_ex(loop, handle, AF_UNSPEC);
+}
+
+
+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);
+  }
+}
+
+
+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 WSAEALREADY;
+  }
+
+  err = uv_udp_maybe_bind(handle,
+                          (const struct sockaddr*) &uv_addr_ip4_any_,
+                          sizeof(uv_addr_ip4_any_),
+                          0);
+  if (err)
+    return 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(loop, (uv_req_t*) req);
+  req->type = 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;
+}
+
+
+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_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->flags & UV_HANDLE_BOUND))
+    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->flags & UV_HANDLE_BOUND))
+    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_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);
+  return uv_translate_sys_error(err);
+}
+
+
+#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->flags & UV_HANDLE_BOUND))                                   \
+      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;
+}
+
+
+/* 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 {
+      abort();
+    }
+    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) {
+  return UV_ENOSYS;
+}
diff --git a/src/win/util.c b/src/win/util.c
new file mode 100644 (file)
index 0000000..050058a
--- /dev/null
@@ -0,0 +1,1389 @@
+/* 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"
+
+#include <winsock2.h>
+#include <winperf.h>
+#include <iphlpapi.h>
+#include <psapi.h>
+#include <tlhelp32.h>
+#include <windows.h>
+#include <userenv.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
+
+/* Cached copy of the process title, plus a mutex guarding it. */
+static char *process_title;
+static CRITICAL_SECTION process_title_lock;
+
+/* Cached copy of the process id, written once. */
+static DWORD current_pid = 0;
+
+
+/* Interval (in seconds) of the high-resolution clock. */
+static double hrtime_interval_ = 0;
+
+
+/*
+ * One-time initialization code for functionality defined in util.c.
+ */
+void uv__util_init() {
+  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_interval_ = 1.0 / perf_frequency.QuadPart;
+  } else {
+    hrtime_interval_= 0;
+  }
+}
+
+
+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[MAX_PATH];
+  int r;
+
+  if (buffer == NULL || size == NULL) {
+    return UV_EINVAL;
+  }
+
+  utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer);
+  if (utf16_len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  } else if (utf16_len > MAX_PATH) {
+    /* This should be impossible;  however the CRT has a code path to deal */
+    /* with this scenario, so I added a check anyway. */
+    return UV_EIO;
+  }
+
+  /* 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) {
+    return uv_translate_sys_error(GetLastError());
+  } else if (r > (int) *size) {
+    *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);
+  if (r == 0) {
+    return uv_translate_sys_error(GetLastError());
+  }
+
+  *size = r - 1;
+  return 0;
+}
+
+
+int uv_chdir(const char* dir) {
+  WCHAR utf16_buffer[MAX_PATH];
+  size_t utf16_len;
+  WCHAR drive_letter, env_var[4];
+
+  if (dir == NULL) {
+    return UV_EINVAL;
+  }
+
+  if (MultiByteToWideChar(CP_UTF8,
+                          0,
+                          dir,
+                          -1,
+                          utf16_buffer,
+                          MAX_PATH) == 0) {
+    DWORD error = GetLastError();
+    /* The maximum length of the current working directory is 260 chars, */
+    /* including terminating null. If it doesn't fit, the path name must be */
+    /* too long. */
+    if (error == ERROR_INSUFFICIENT_BUFFER) {
+      return UV_ENAMETOOLONG;
+    } else {
+      return uv_translate_sys_error(error);
+    }
+  }
+
+  if (!SetCurrentDirectoryW(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. */
+  utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer);
+  if (utf16_len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  } else if (utf16_len > MAX_PATH) {
+    return UV_EIO;
+  }
+
+  /* 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';
+
+    if (!SetEnvironmentVariableW(env_var, utf16_buffer)) {
+      return uv_translate_sys_error(GetLastError());
+    }
+  }
+
+  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;
+}
+
+
+int uv_parent_pid() {
+  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;
+}
+
+
+int uv_current_pid() {
+  if (current_pid == 0) {
+    current_pid = GetCurrentProcessId();
+  }
+  return current_pid;
+}
+
+
+char** uv_setup_args(int argc, char** argv) {
+  return argv;
+}
+
+
+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() {
+  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(double scale) {
+  LARGE_INTEGER counter;
+
+  /* If the performance interval is zero, there's no support. */
+  if (hrtime_interval_ == 0) {
+    return 0;
+  }
+
+  if (!QueryPerformanceCounter(&counter)) {
+    return 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.
+   */
+  return (uint64_t) ((double) counter.QuadPart * hrtime_interval_ * scale);
+}
+
+
+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 = (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, r, 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));
+
+    r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                      key_name,
+                      0,
+                      KEY_QUERY_VALUE,
+                      &processor_key);
+    if (r != ERROR_SUCCESS) {
+      err = GetLastError();
+      goto error;
+    }
+
+    if (RegQueryValueExW(processor_key,
+                         L"~MHz",
+                         NULL,
+                         NULL,
+                         (BYTE*) &cpu_speed,
+                         &cpu_speed_size) != ERROR_SUCCESS) {
+      err = GetLastError();
+      RegCloseKey(processor_key);
+      goto error;
+    }
+
+    if (RegQueryValueExW(processor_key,
+                         L"ProcessorNameString",
+                         NULL,
+                         NULL,
+                         (BYTE*) &cpu_brand,
+                         &cpu_brand_size) != ERROR_SUCCESS) {
+      err = GetLastError();
+      RegCloseKey(processor_key);
+      goto error;
+    }
+
+    RegCloseKey(processor_key);
+
+    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:
+  /* 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);
+}
+
+
+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);
+}
+
+
+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;
+
+  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;
+  wchar_t path[MAX_PATH];
+  DWORD bufsize;
+  size_t len;
+  int r;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  /* Check if the USERPROFILE environment variable is set first */
+  len = GetEnvironmentVariableW(L"USERPROFILE", path, MAX_PATH);
+
+  if (len == 0) {
+    r = GetLastError();
+
+    /* Don't return an error if USERPROFILE was not found */
+    if (r != ERROR_ENVVAR_NOT_FOUND)
+      return uv_translate_sys_error(r);
+  } else if (len > MAX_PATH) {
+    /* This should not be possible */
+    return UV_EIO;
+  } else {
+    /* Check how much space we need */
+    bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -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,
+                                  path,
+                                  -1,
+                                  buffer,
+                                  *size,
+                                  NULL,
+                                  NULL);
+
+    if (bufsize == 0)
+      return uv_translate_sys_error(GetLastError());
+
+    *size = bufsize - 1;
+    return 0;
+  }
+
+  /* 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[MAX_PATH + 1];
+  DWORD bufsize;
+  size_t len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  len = GetTempPathW(MAX_PATH + 1, path);
+
+  if (len == 0) {
+    return uv_translate_sys_error(GetLastError());
+  } else if (len > MAX_PATH + 1) {
+    /* This should not be possible */
+    return UV_EIO;
+  }
+
+  /* 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) {
+    return uv_translate_sys_error(GetLastError());
+  } else if (bufsize > *size) {
+    *size = bufsize;
+    return UV_ENOBUFS;
+  }
+
+  /* Convert to UTF-8 */
+  bufsize = WideCharToMultiByte(CP_UTF8,
+                                0,
+                                path,
+                                -1,
+                                buffer,
+                                *size,
+                                NULL,
+                                NULL);
+
+  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;
+}
+
+
+int uv__getpwuid_r(uv_passwd_t* pwd) {
+  HANDLE token;
+  wchar_t username[UNLEN + 1];
+  wchar_t path[MAX_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 = sizeof(path);
+  if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
+    r = GetLastError();
+    CloseHandle(token);
+
+    /* This should not be possible */
+    if (r == ERROR_INSUFFICIENT_BUFFER)
+      return UV_ENOMEM;
+
+    return uv_translate_sys_error(r);
+  }
+
+  CloseHandle(token);
+
+  /* Get the username using GetUserNameW() */
+  bufsize = sizeof(username);
+  if (!GetUserNameW(username, &bufsize)) {
+    r = GetLastError();
+
+    /* 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);
+
+  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);
+}
diff --git a/src/win/winapi.c b/src/win/winapi.c
new file mode 100644 (file)
index 0000000..1fa179b
--- /dev/null
@@ -0,0 +1,159 @@
+/* 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 */
+sRtlNtStatusToDosError pRtlNtStatusToDosError;
+sNtDeviceIoControlFile pNtDeviceIoControlFile;
+sNtQueryInformationFile pNtQueryInformationFile;
+sNtSetInformationFile pNtSetInformationFile;
+sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
+sNtQueryDirectoryFile pNtQueryDirectoryFile;
+sNtQuerySystemInformation pNtQuerySystemInformation;
+
+
+/* Kernel32 function pointers */
+sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
+sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
+sCreateSymbolicLinkW pCreateSymbolicLinkW;
+sCancelIoEx pCancelIoEx;
+sInitializeConditionVariable pInitializeConditionVariable;
+sSleepConditionVariableCS pSleepConditionVariableCS;
+sSleepConditionVariableSRW pSleepConditionVariableSRW;
+sWakeAllConditionVariable pWakeAllConditionVariable;
+sWakeConditionVariable pWakeConditionVariable;
+sCancelSynchronousIo pCancelSynchronousIo;
+sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW;
+
+
+/* Powrprof.dll function pointer */
+sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
+
+
+void uv_winapi_init() {
+  HMODULE ntdll_module;
+  HMODULE kernel32_module;
+  HMODULE powrprof_module;
+
+  ntdll_module = GetModuleHandleA("ntdll.dll");
+  if (ntdll_module == NULL) {
+    uv_fatal_error(GetLastError(), "GetModuleHandleA");
+  }
+
+  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");
+  }
+
+  kernel32_module = GetModuleHandleA("kernel32.dll");
+  if (kernel32_module == NULL) {
+    uv_fatal_error(GetLastError(), "GetModuleHandleA");
+  }
+
+  pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress(
+      kernel32_module,
+      "GetQueuedCompletionStatusEx");
+
+  pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes)
+    GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes");
+
+  pCreateSymbolicLinkW = (sCreateSymbolicLinkW)
+    GetProcAddress(kernel32_module, "CreateSymbolicLinkW");
+
+  pCancelIoEx = (sCancelIoEx)
+    GetProcAddress(kernel32_module, "CancelIoEx");
+
+  pInitializeConditionVariable = (sInitializeConditionVariable)
+    GetProcAddress(kernel32_module, "InitializeConditionVariable");
+
+  pSleepConditionVariableCS = (sSleepConditionVariableCS)
+    GetProcAddress(kernel32_module, "SleepConditionVariableCS");
+
+  pSleepConditionVariableSRW = (sSleepConditionVariableSRW)
+    GetProcAddress(kernel32_module, "SleepConditionVariableSRW");
+
+  pWakeAllConditionVariable = (sWakeAllConditionVariable)
+    GetProcAddress(kernel32_module, "WakeAllConditionVariable");
+
+  pWakeConditionVariable = (sWakeConditionVariable)
+    GetProcAddress(kernel32_module, "WakeConditionVariable");
+
+  pCancelSynchronousIo = (sCancelSynchronousIo)
+    GetProcAddress(kernel32_module, "CancelSynchronousIo");
+
+  pGetFinalPathNameByHandleW = (sGetFinalPathNameByHandleW)
+    GetProcAddress(kernel32_module, "GetFinalPathNameByHandleW");
+
+
+  powrprof_module = LoadLibraryA("powrprof.dll");
+  if (powrprof_module != NULL) {
+    pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
+      GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification");
+  }
+
+}
diff --git a/src/win/winapi.h b/src/win/winapi.h
new file mode 100644 (file)
index 0000000..341fcd0
--- /dev/null
@@ -0,0 +1,4748 @@
+/* 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
+
+/* from winternl.h */
+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;
+    };
+  } 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 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
+
+typedef VOID (NTAPI *PIO_APC_ROUTINE)
+             (PVOID ApcContext,
+              PIO_STATUS_BLOCK IoStatusBlock,
+              ULONG Reserved);
+
+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
+                );
+
+/*
+ * 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_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);
+
+typedef BOOL (WINAPI* sSetFileCompletionNotificationModes)
+             (HANDLE FileHandle,
+              UCHAR Flags);
+
+typedef BOOLEAN (WINAPI* sCreateSymbolicLinkW)
+                (LPCWSTR lpSymlinkFileName,
+                 LPCWSTR lpTargetFileName,
+                 DWORD dwFlags);
+
+typedef BOOL (WINAPI* sCancelIoEx)
+             (HANDLE hFile,
+              LPOVERLAPPED lpOverlapped);
+
+typedef VOID (WINAPI* sInitializeConditionVariable)
+             (PCONDITION_VARIABLE ConditionVariable);
+
+typedef BOOL (WINAPI* sSleepConditionVariableCS)
+             (PCONDITION_VARIABLE ConditionVariable,
+              PCRITICAL_SECTION CriticalSection,
+              DWORD dwMilliseconds);
+
+typedef BOOL (WINAPI* sSleepConditionVariableSRW)
+             (PCONDITION_VARIABLE ConditionVariable,
+              PSRWLOCK SRWLock,
+              DWORD dwMilliseconds,
+              ULONG Flags);
+
+typedef VOID (WINAPI* sWakeAllConditionVariable)
+             (PCONDITION_VARIABLE ConditionVariable);
+
+typedef VOID (WINAPI* sWakeConditionVariable)
+             (PCONDITION_VARIABLE ConditionVariable);
+
+typedef BOOL (WINAPI* sCancelSynchronousIo)
+             (HANDLE hThread);
+
+typedef DWORD (WINAPI* sGetFinalPathNameByHandleW)
+             (HANDLE hFile,
+              LPWSTR lpszFilePath,
+              DWORD cchFilePath,
+              DWORD dwFlags);
+
+/* 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);
+
+
+/* Ntdll function pointers */
+extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
+extern sNtDeviceIoControlFile pNtDeviceIoControlFile;
+extern sNtQueryInformationFile pNtQueryInformationFile;
+extern sNtSetInformationFile pNtSetInformationFile;
+extern sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
+extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
+extern sNtQuerySystemInformation pNtQuerySystemInformation;
+
+
+/* Kernel32 function pointers */
+extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
+extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
+extern sCreateSymbolicLinkW pCreateSymbolicLinkW;
+extern sCancelIoEx pCancelIoEx;
+extern sInitializeConditionVariable pInitializeConditionVariable;
+extern sSleepConditionVariableCS pSleepConditionVariableCS;
+extern sSleepConditionVariableSRW pSleepConditionVariableSRW;
+extern sWakeAllConditionVariable pWakeAllConditionVariable;
+extern sWakeConditionVariable pWakeConditionVariable;
+extern sCancelSynchronousIo pCancelSynchronousIo;
+extern sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW;
+
+
+/* Powrprof.dll function pointer */
+extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
+
+#endif /* UV_WIN_WINAPI_H_ */
diff --git a/src/win/winsock.c b/src/win/winsock.c
new file mode 100644 (file)
index 0000000..32c2b0d
--- /dev/null
@@ -0,0 +1,563 @@
+/* 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);
+}
+
+
+static int error_means_no_support(DWORD error) {
+  return error == WSAEPROTONOSUPPORT || error == WSAESOCKTNOSUPPORT ||
+         error == WSAEPFNOSUPPORT || error == WSAEAFNOSUPPORT;
+}
+
+
+void uv_winsock_init() {
+  WSADATA wsa_data;
+  int errorno;
+  SOCKET dummy;
+  WSAPROTOCOL_INFOW protocol_info;
+  int opt_len;
+
+  /* Initialize winsock */
+  errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+  if (errorno != 0) {
+    uv_fatal_error(errorno, "WSAStartup");
+  }
+
+  /* Set implicit binding address used by connectEx */
+  if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {
+    abort();
+  }
+
+  /* TODO: IPv6 support in libtuv is incomplete, disable this for now.
+   if (uv_ip6_addr("::", 0, &uv_addr_ip6_any_)) {
+    abort();
+  }
+  */
+
+  /* Detect non-IFS LSPs */
+  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) == SOCKET_ERROR)
+      uv_fatal_error(WSAGetLastError(), "getsockopt");
+
+    if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES))
+      uv_tcp_non_ifs_lsp_ipv4 = 1;
+
+    if (closesocket(dummy) == SOCKET_ERROR)
+      uv_fatal_error(WSAGetLastError(), "closesocket");
+
+  } else if (!error_means_no_support(WSAGetLastError())) {
+    /* Any error other than "socket type not supported" is fatal. */
+    uv_fatal_error(WSAGetLastError(), "socket");
+  }
+
+  /* Detect IPV6 support and non-IFS LSPs */
+  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) == SOCKET_ERROR)
+      uv_fatal_error(WSAGetLastError(), "getsockopt");
+
+    if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES))
+      uv_tcp_non_ifs_lsp_ipv6 = 1;
+
+    if (closesocket(dummy) == SOCKET_ERROR)
+      uv_fatal_error(WSAGetLastError(), "closesocket");
+
+  } else if (!error_means_no_support(WSAGetLastError())) {
+    /* Any error other than "socket type not supported" is fatal. */
+    uv_fatal_error(WSAGetLastError(), "socket");
+  }
+}
+
+
+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;
+  }
+}
diff --git a/src/win/winsock.h b/src/win/winsock.h
new file mode 100644 (file)
index 0000000..7c007ab
--- /dev/null
@@ -0,0 +1,190 @@
+/* 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
+
+/*
+ * 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
+
+#endif /* UV_WIN_WINSOCK_H_ */
diff --git a/test/echo_server.c b/test/echo_server.c
new file mode 100644 (file)
index 0000000..f0ce6d1
--- /dev/null
@@ -0,0 +1,268 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <uv.h>
+#include "runner.h"
+
+static uv_loop_t loop;
+
+static int server_closed;
+static stream_type serverType;
+static uv_tcp_t tcpServer;
+static uv_handle_t* serverHandle;
+
+
+//-----------------------------------------------------------------------------
+
+static void on_close(uv_handle_t* peer) {
+  free(peer);
+}
+
+static void on_server_close(uv_handle_t* handle) {
+  TUV_ASSERT(handle == serverHandle);
+}
+
+
+//-----------------------------------------------------------------------------
+
+static void after_write(uv_write_t* req, int status) {
+  write_req_t* wr;
+
+  /* Free the read/write buffer and the request */
+  wr = (write_req_t*) req;
+  free(wr->buf.base);
+  free(wr);
+
+  if (status == 0)
+    return;
+
+  TDLOG("uv_write error: %s - %s", uv_err_name(status), uv_strerror(status));
+}
+
+
+static void after_shutdown(uv_shutdown_t* req, int status) {
+  uv_close((uv_handle_t*) req->handle, on_close);
+  free(req);
+}
+
+
+static void after_read(uv_stream_t* handle,
+                       ssize_t nread,
+                       const uv_buf_t* buf) {
+  int i;
+  write_req_t *wr;
+  uv_shutdown_t* sreq;
+
+  if (nread < 0) {
+    /* Error or EOF */
+    TUV_ASSERT(nread == UV_EOF);
+
+    free(buf->base);
+    sreq = (uv_shutdown_t*)malloc(sizeof(uv_shutdown_t));
+    memset(sreq, 0, sizeof(uv_shutdown_t));
+    TUV_ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown));
+    return;
+  }
+
+  if (nread == 0) {
+    /* Everything OK, but nothing read. */
+    free(buf->base);
+    return;
+  }
+
+  /*
+   * Scan for the letter Q which signals that we should quit the server.
+   * If we get QS it means close the stream.
+   */
+  if (!server_closed) {
+    for (i = 0; i < nread; i++) {
+      if (buf->base[i] == 'Q') {
+        if (i + 1 < nread && buf->base[i + 1] == 'S') {
+          free(buf->base);
+          uv_close((uv_handle_t*)handle, on_close);
+          return;
+        } else {
+          free(buf->base);
+          uv_close((uv_handle_t*)handle, on_close);
+          uv_close(serverHandle, on_server_close);
+          server_closed = 1;
+          //break;
+          return;
+        }
+      }
+    }
+  }
+
+  wr = (write_req_t*)malloc(sizeof(write_req_t));
+  TUV_ASSERT(wr != NULL);
+  wr->buf = uv_buf_init(buf->base, nread);
+
+  if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) {
+    TUV_FATAL("uv_write failed");
+  }
+}
+
+
+static void echo_alloc(uv_handle_t* handle,
+                       size_t suggested_size,
+                       uv_buf_t* buf) {
+  buf->base = (char*)malloc(suggested_size);
+  buf->len = suggested_size;
+}
+
+
+static void on_connection(uv_stream_t* server, int status) {
+  uv_stream_t* stream;
+  int r;
+
+  if (status != 0) {
+    TDLOG("echo server on_connection, Connect error %d, %s",
+          status, uv_err_name(status));
+    return;
+  }
+  TUV_ASSERT(status == 0);
+
+  switch (serverType) {
+  case TEST_TCP:
+    stream = (uv_stream_t*)malloc(sizeof(uv_tcp_t));
+    TUV_ASSERT(stream != NULL);
+    r = uv_tcp_init(&loop, (uv_tcp_t*)stream);
+    TUV_ASSERT(r == 0);
+    break;
+
+#ifdef TUV_FEATURE_PIPE
+  case TEST_PIPE:
+    stream = (uv_stream_t*)malloc(sizeof(uv_pipe_t));
+    TUV_ASSERT(stream != NULL);
+    r = uv_pipe_init(&loop, (uv_pipe_t*)stream, 0);
+    TUV_ASSERT(r == 0);
+    break;
+#endif
+
+  default:
+    TUV_ASSERT(0 && "Bad serverType");
+    ABORT();
+  }
+
+  /* associate server with stream */
+  stream->data = server;
+
+  r = uv_accept(server, stream);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start(stream, echo_alloc, after_read);
+  TUV_ASSERT(r == 0);
+
+#if defined(__HOST_HELPER__)
+  struct sockaddr_in tcpname;
+  int namelen;
+  namelen = sizeof(struct sockaddr_in);
+  uv_tcp_getpeername(stream, &tcpname, &namelen);
+  union {
+    uint8_t addr8[4];
+    uint32_t addr32;
+  } c4;
+  c4.addr32 = tcpname.sin_addr.s_addr;
+  TDDDLOG("Client has connected: %d.%d.%d.%d",
+          c4.addr8[0], c4.addr8[1], c4.addr8[2], c4.addr8[3]);
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+
+static int tcp4_echo_start(int port) {
+  struct sockaddr_in addr;
+  int r;
+
+  TUV_ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr));
+
+  serverHandle = (uv_handle_t*)&tcpServer;
+  serverType = TEST_TCP;
+
+  r = uv_tcp_init(&loop, &tcpServer);
+  if (r) {
+    /* TODO: Error codes */
+    TDLOG("svr Socket creation error");
+    return 1;
+  }
+
+  r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0);
+  if (r) {
+    /* TODO: Error codes */
+    TDLOG("svr Bind error");
+    return 1;
+  }
+
+  r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection);
+  if (r) {
+    /* TODO: Error codes */
+    TDLOG("svr Listen error %s", uv_err_name(r));
+    return 1;
+  }
+
+  return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+HELPER_IMPL(tcp4_echo_server) {
+  int r;
+
+  server_closed = 0;
+  serverHandle = NULL;
+
+  r = uv_loop_init(&loop);
+  TUV_ASSERT(r == 0);
+
+  if (tcp4_echo_start(TEST_PORT))
+    return 1;
+
+  r = uv_run(&loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  assert(server_closed != 0);
+
+  TUV_ASSERT(uv_loop_close(&loop) == 0);
+
+  return 0;
+}
+
diff --git a/test/raw/apiemul.h b/test/raw/apiemul.h
new file mode 100644 (file)
index 0000000..2ba6941
--- /dev/null
@@ -0,0 +1,37 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __apiemul_header__
+#define __apiemul_header__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define APIEMUL_DUMP_HEAP_ADDRESS 1
+
+
+int apiemultester_entry(void);
+
+void apiemul_main(void);
+void apiemul_socket(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/raw/apiemul_main.c b/test/raw/apiemul_main.c
new file mode 100644 (file)
index 0000000..cc0807f
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <uv.h>
+
+#include "apiemul.h"
+
+
+void apiemul_main(void) {
+  apiemul_socket();
+}
diff --git a/test/raw/apiemul_socket.c b/test/raw/apiemul_socket.c
new file mode 100644 (file)
index 0000000..fc366a9
--- /dev/null
@@ -0,0 +1,377 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <uv.h>
+
+#include "apiemul.h"
+
+
+#if defined(__TUV_HOST_IPEXIST__)
+#include "tuv_host_ipaddress.h"
+#else
+#pragma message("Please create tuv_host_ipaddress.h for tcp_open test.")
+#endif
+
+// Please create a file with your host IP address like below.
+// This information is use to connect to host that should be running
+// "tuvhelper" prgoram.
+/*
+#ifndef __tuv_host_ipaddress_header__
+#define __tuv_host_ipaddress_header__
+
+#define HOST_IP_ADDRESS   "192.168.1.2"
+
+#endif
+*/
+
+//----------------------------------------------------------------------------
+
+#define HOST_ECHO_PORT 9123
+
+#define SOCKET_TEST_PORT 7777
+#define SOCKET_TEST_CLIENT_MAX 16
+#define SOCKET_TEST_READBUFFER 1024
+
+//----------------------------------------------------------------------------
+
+// task for network poll
+static uv_thread_t _thread;
+static uv_thread_t _thread_cli;
+
+static struct sockaddr_in _addr_in;
+static int _sock_server;
+static int _sock_client;
+
+static int sock_clients[SOCKET_TEST_CLIENT_MAX];
+static int sock_client_cnt;
+
+static unsigned char read_buffer[SOCKET_TEST_READBUFFER];
+static unsigned char read_client[SOCKET_TEST_READBUFFER];
+
+
+//----------------------------------------------------------------------------
+
+static void test_init_clients(void) {
+  int i;
+  sock_client_cnt = 0;
+  for (i=0;i<SOCKET_TEST_CLIENT_MAX; i++) {
+    sock_clients[i] = -1;
+  }
+}
+
+static int test_client_count(void) {
+  return sock_client_cnt;
+}
+
+static int test_client_add(int fd) {
+  int i;
+  for (i=0;i<SOCKET_TEST_CLIENT_MAX; i++) {
+    if (sock_clients[i] < 0) {
+      sock_clients[i] = fd;
+      sock_client_cnt++;
+      return fd;
+    }
+  }
+  return -1;
+}
+
+static int test_client_sub(int fd) {
+  int i;
+  for (i=0;i<SOCKET_TEST_CLIENT_MAX; i++) {
+    if (sock_clients[i] == fd) {
+      sock_clients[i] = -1;
+      sock_client_cnt--;
+      return fd;
+    }
+  }
+  return -1;
+}
+
+static int test_client_fillfds(struct pollfd* fds) {
+  int i, j;
+  for (i=0, j=0;i<SOCKET_TEST_CLIENT_MAX; i++) {
+    if (sock_clients[i] > 0) {
+      fds[j].fd = sock_clients[i];
+      fds[j].events = POLLIN | POLLHUP;
+      j++;
+    }
+  }
+  return j;
+}
+
+//----------------------------------------------------------------------------
+
+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 ip4_addr(const char* ip, int port, struct sockaddr_in* addr) {
+  memset(addr, 0, sizeof(*addr));
+  addr->sin_family = AF_INET;
+  addr->sin_port = port;
+  return inet_pton4(ip, (unsigned char *)&(addr->sin_addr.s_addr));
+}
+
+static void dump_socket_info(int sockfd) {
+  struct sockaddr_in addrthis;
+  socklen_t len = sizeof(addrthis);
+  union {
+    uint8_t addr8[4];
+    uint32_t addr32;
+  } c4;
+
+  int ret = tuvp_getsockname(sockfd, (struct sockaddr*)&addrthis, &len);
+  c4.addr32 = addrthis.sin_addr.s_addr;
+  if (ret == 0) {
+    printf("%d.%d.%d.%d:%d",
+           c4.addr8[0], c4.addr8[1], c4.addr8[2], c4.addr8[3],
+           addrthis.sin_port);
+  }
+  else {
+    printf("(invalid socket)");
+  }
+}
+
+static void test_new_connection(int sockserver) {
+  struct sockaddr addr_client;
+  int clisockfd;
+  socklen_t socklen;
+
+  memset(&addr_client, 0, sizeof(addr_client));
+  socklen = (socklen_t)sizeof(addr_client);
+  clisockfd = tuvp_accept(sockserver, &addr_client, &socklen);
+  assert(addr_client.sa_family == AF_INET);
+
+  printf(".. new connection to ");
+  dump_socket_info(clisockfd);
+
+  struct sockaddr_in* addrin = (struct sockaddr_in*)&addr_client;
+  union {
+    uint8_t addr8[4];
+    uint32_t addr32;
+  } c4;
+  c4.addr32 = addrin->sin_addr.s_addr;
+  printf(" fd(%d) from %d.%d.%d.%d:%d\r\n",
+         clisockfd,
+         c4.addr8[0], c4.addr8[1], c4.addr8[2], c4.addr8[3],
+         addrin->sin_port);
+
+  test_client_add(clisockfd);
+
+  const char* msg;
+  msg = "Welcome to libtuv api test echo server!\r\nSend 'Q' to end.\r\n";
+  tuvp_write(clisockfd, msg, strlen(msg));
+}
+
+static void test_handle_read(int sockfd) {
+  ssize_t result;
+  result = tuvp_read(sockfd, read_buffer, SOCKET_TEST_READBUFFER);
+  if (result > 0) {
+    read_buffer[result] = 0;
+    if (read_buffer[0] == 'Q') {
+      tuvp_close(sockfd);
+      test_client_sub(sockfd);
+      printf(".. RECV with 'Q', socket closed\r\n");
+      return;
+    }
+    tuvp_write(sockfd, read_buffer, strlen((const char*)read_buffer));
+  }
+}
+
+static void do_tcp_server_start(void) {
+  int result;
+  tuvp_tcp_init();
+
+  ip4_addr("0.0.0.0", tuvp_htons(SOCKET_TEST_PORT), &_addr_in);
+  _sock_server = tuvp_socket(AF_INET, SOCK_STREAM, 0);
+
+  result = tuvp_bind(_sock_server,
+                     (const struct sockaddr*)&_addr_in,
+                     sizeof(_addr_in));
+  if (result != 0) {
+    printf("!! tuvp_bind failed (%d)\r\n", result);
+    return;
+  }
+  result = tuvp_listen(_sock_server, SOMAXCONN);
+  if (result != 0) {
+    printf("!! tuvp_listen failed (%d)\r\n", result);
+    return;
+  }
+
+  printf(".. echo server started at ");
+  dump_socket_info(_sock_server);
+  printf("\r\n");
+
+  printf(".. use 'telnet' to test, manually\r\n");
+}
+
+static void do_poll_test(void) {
+  struct pollfd* fds;
+  int fds_cnt;
+
+  // prepare fds
+  fds_cnt = 1 + test_client_count();
+  fds = (struct pollfd*)malloc(sizeof(struct pollfd) * fds_cnt);
+  memset(fds, 0, sizeof(struct pollfd) * fds_cnt);
+
+  // add listener
+  fds[0].fd = _sock_server;
+  fds[0].events = POLLIN;
+  // add others
+  test_client_fillfds(&fds[1]);
+
+  // call poll
+  if (tuvp_net_poll(fds, fds_cnt) > 0) {
+    if (fds[0].revents & POLLIN) {
+      // server has new connection
+      test_new_connection(_sock_server);
+    }
+    else {
+      int i;
+      for (i=1; i<fds_cnt; i++) {
+        if (fds[i].revents & POLLHUP) {
+          // client hang-up
+          tuvp_close(fds[i].fd);
+          test_client_sub(fds[i].fd);
+        }
+        else if (fds[i].revents & POLLIN) {
+          // client has sent something
+          test_handle_read(fds[i].fd);
+        }
+      }
+    }
+  }
+  free(fds);
+}
+
+#if defined(__TUV_HOST_IPEXIST__)
+static void clitest_entry(void* arg) {
+}
+
+static int clitest_loop(void* arg) {
+  int rsend;
+  static int send_count = 0;
+  static int ok_to_send = 1;
+  struct pollfd fds;
+
+  fds.fd = _sock_client;
+  fds.events = POLLIN | POLLOUT;
+  if (tuvp_net_poll(&fds, 1) > 0) {
+    if (fds.revents & POLLOUT) {
+      rsend = rand() % 100;
+      if (rsend == 0 && ok_to_send) {
+        const char* msg = "Hello api emul tester!";
+        tuvp_write(_sock_client, msg, strlen(msg));
+        ok_to_send = 0;
+      }
+    }
+    if (fds.revents & POLLIN) {
+      tuvp_read(_sock_client, read_client, SOCKET_TEST_READBUFFER);
+      printf(":: read [%s]\r\n", read_client);
+      send_count++;
+      ok_to_send = 1;
+    }
+  }
+  if (send_count > 10) {
+    tuvp_close(_sock_client);
+    _sock_client = -1;
+    ok_to_send = 0;
+    printf(":: connect/send ok. close client\r\n");
+    return 0;
+  }
+  return 1;
+}
+
+static void do_tcp_client_connect(void) {
+  _sock_client = tuvp_socket(AF_INET, SOCK_STREAM, 0);
+  if (_sock_client == -1) {
+    printf("!! tuvp_socket failed, err(%d)\r\n", get_errno());
+    return;
+  }
+
+  struct sockaddr_in server_addr;
+  uv_ip4_addr(HOST_IP_ADDRESS, HOST_ECHO_PORT, &server_addr);
+
+  tuvp_connect(_sock_client, (const sockaddr*)&server_addr,
+               sizeof(server_addr));
+
+  tuv_task_create(&_thread_cli, clitest_entry, clitest_loop, NULL);
+}
+#endif
+
+static void task_entry(void* arg) {
+  test_init_clients();
+  do_tcp_server_start();
+}
+
+static int task_loop(void* arg) {
+#if defined(__TUV_HOST_IPEXIST__)
+  if (_sock_client == -2) {
+    do_tcp_client_connect();
+  }
+#endif
+  do_poll_test();
+  return 1; /* continue task */
+}
+
+/*
+ * socket api testing with echo server
+ *  - start server and wait for connections
+ *  - use 'telnet (server_ip) 7777'
+ *  - to close connection, send "Q"
+ */
+void apiemul_socket(void) {
+  _sock_client = -2;
+  _sock_server = -1;
+  tuv_task_create(&_thread, task_entry, task_loop, NULL);
+}
diff --git a/test/raw/echo_server_raw.c b/test/raw/echo_server_raw.c
new file mode 100644 (file)
index 0000000..08ce727
--- /dev/null
@@ -0,0 +1,264 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <uv.h>
+#include "runner.h"
+
+static uv_loop_t loop;
+
+static int server_closed;
+static stream_type serverType;
+static uv_tcp_t tcpServer;
+static uv_handle_t* serverHandle;
+
+
+//-----------------------------------------------------------------------------
+
+static void on_close(uv_handle_t* peer) {
+  free(peer);
+}
+
+static void on_server_close(uv_handle_t* handle) {
+  TUV_ASSERT(handle == serverHandle);
+}
+
+
+//-----------------------------------------------------------------------------
+
+static void after_write(uv_write_t* req, int status) {
+  write_req_t* wr;
+
+  /* Free the read/write buffer and the request */
+  wr = (write_req_t*) req;
+  free(wr->buf.base);
+  free(wr);
+
+  if (status == 0)
+    return;
+
+  TDLOG("uv_write error: %s - %s", uv_err_name(status), uv_strerror(status));
+}
+
+
+static void after_shutdown(uv_shutdown_t* req, int status) {
+  uv_close((uv_handle_t*) req->handle, on_close);
+  free(req);
+}
+
+
+static void after_read(uv_stream_t* handle,
+                       ssize_t nread,
+                       const uv_buf_t* buf) {
+  int i;
+  write_req_t *wr;
+  uv_shutdown_t* sreq;
+
+  if (nread < 0) {
+    /* Error or EOF */
+    TUV_ASSERT(nread == UV_EOF);
+
+    free(buf->base);
+    sreq = (uv_shutdown_t*)malloc(sizeof(uv_shutdown_t));
+    memset(sreq, 0, sizeof(uv_shutdown_t));
+    TUV_ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown));
+    return;
+  }
+
+  if (nread == 0) {
+    /* Everything OK, but nothing read. */
+    free(buf->base);
+    return;
+  }
+
+  /*
+   * Scan for the letter Q which signals that we should quit the server.
+   * If we get QS it means close the stream.
+   */
+  if (!server_closed) {
+    for (i = 0; i < nread; i++) {
+      if (buf->base[i] == 'Q') {
+        if (i + 1 < nread && buf->base[i + 1] == 'S') {
+          free(buf->base);
+          uv_close((uv_handle_t*)handle, on_close);
+          return;
+        } else {
+          free(buf->base);
+          uv_close((uv_handle_t*)handle, on_close);
+          uv_close(serverHandle, on_server_close);
+          server_closed = 1;
+          //break;
+          return;
+        }
+      }
+    }
+  }
+
+  wr = (write_req_t*)malloc(sizeof(write_req_t));
+  TUV_ASSERT(wr != NULL);
+  wr->buf = uv_buf_init(buf->base, nread);
+
+  if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) {
+    TUV_FATAL("uv_write failed");
+  }
+}
+
+
+static void echo_alloc(uv_handle_t* handle,
+                       size_t suggested_size,
+                       uv_buf_t* buf) {
+  buf->base = (char*)malloc(suggested_size);
+  buf->len = suggested_size;
+}
+
+
+static void on_connection(uv_stream_t* server, int status) {
+  uv_stream_t* stream;
+  int r;
+
+  if (status != 0) {
+    TDLOG("echo server on_connection, Connect error %d, %s",
+          status, uv_err_name(status));
+    return;
+  }
+  TUV_ASSERT(status == 0);
+  TDDDLOG("echo server test on_connection server(%p)", server);
+
+  switch (serverType) {
+  case TEST_TCP:
+    stream = (uv_stream_t*)malloc(sizeof(uv_tcp_t));
+    TUV_ASSERT(stream != NULL);
+    r = uv_tcp_init(&loop, (uv_tcp_t*)stream);
+    TUV_ASSERT(r == 0);
+    break;
+
+/*
+  case PIPE:
+    stream = (uv_stream_t*)malloc(sizeof(uv_pipe_t));
+    TUV_ASSERT(stream != NULL);
+    r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0);
+    TUV_ASSERT(r == 0);
+    break;
+*/
+
+  default:
+    TUV_ASSERT(0 && "Bad serverType");
+    ABORT();
+  }
+
+  /* associate server with stream */
+  stream->data = server;
+
+  r = uv_accept(server, stream);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start(stream, echo_alloc, after_read);
+  TUV_ASSERT(r == 0);
+}
+
+
+//-----------------------------------------------------------------------------
+
+static int tcp4_echo_start(int port) {
+  struct sockaddr_in addr;
+  int r;
+
+  TUV_ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr));
+
+  serverHandle = (uv_handle_t*)&tcpServer;
+  serverType = TEST_TCP;
+
+  r = uv_tcp_init(&loop, &tcpServer);
+  if (r) {
+    /* TODO: Error codes */
+    TDLOG("svr Socket creation error");
+    return 1;
+  }
+
+  r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0);
+  if (r) {
+    /* TODO: Error codes */
+    TDLOG("svr Bind error");
+    return 1;
+  }
+
+  r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection);
+  if (r) {
+    /* TODO: Error codes */
+    TDLOG("svr Listen error %s", uv_err_name(r));
+    return 1;
+  }
+
+  return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+static int echo_server_loop(void* vparam) {
+  uv_run(&loop, UV_RUN_ONCE);
+#pragma message "Make it stop running when signaled"
+  return 1;
+}
+
+static int echo_server_final(void* vparam) {
+  assert(server_closed != 0);
+
+  TUV_ASSERT(uv_loop_close(&loop) == 0);
+}
+
+HELPER_IMPL(tcp4_echo_server) {
+  int r;
+
+  server_closed = 0;
+  serverHandle = NULL;
+
+  r = uv_loop_init(&loop);
+  TUV_ASSERT(r == 0);
+
+  if (tcp4_echo_start(TEST_PORT))
+    return 1;
+
+  tuv_run(&loop, echo_server_loop, echo_server_final, NULL);
+
+  run_tests_continue();
+
+  return 0;
+}
+
diff --git a/test/raw/raw_main.c b/test/raw/raw_main.c
new file mode 100644 (file)
index 0000000..61bb92e
--- /dev/null
@@ -0,0 +1,28 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <uv.h>
+
+#include "raw_main.h"
+#include "apiemul.h"
+
+
+void call_tuv_tester(void) {
+#if defined(__TUV_APIEMUL__)
+  apiemultester_entry();
+#else
+  tuvtester_entry();
+#endif
+}
diff --git a/test/raw/raw_main.h b/test/raw/raw_main.h
new file mode 100644 (file)
index 0000000..020c9a5
--- /dev/null
@@ -0,0 +1,38 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __raw_main_header__
+#define __raw_main_header__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+void tuvtester_entry(void);
+
+// to make tuv to control which tester to begin
+void call_tuv_tester(void);
+  // can call regular unit tester, tuvtester_entry()
+  // can call api emulator for raw system, apiemultester_entry()
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/test/raw/runner_main_raw.c b/test/raw/runner_main_raw.c
new file mode 100644 (file)
index 0000000..6eb6cd2
--- /dev/null
@@ -0,0 +1,203 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <unistd.h>
+#include <uv.h>
+
+#include "runner.h"
+
+
+//-----------------------------------------------------------------------------
+// test function list
+
+#define TEST_ENTRY(name, timeout) \
+  { #name, #name, &run_test_##name, 0, 0, timeout },
+
+task_entry_t TASKS[] = {
+
+  TEST_LIST_ALL(TEST_ENTRY)
+  TEST_LIST_EXT(TEST_ENTRY)
+
+  { NULL, NULL, NULL, 0, 0, 0 }
+};
+
+#undef TEST_ENTRY
+
+//-----------------------------------------------------------------------------
+// test function list
+
+#define HELPER_ENTRY(task_name, name)               \
+    { #task_name, #name, &run_helper_##name, 1, 0, 0 },
+
+task_entry_t HELPERS[] = {
+
+  HELPER_LIST_ALL(HELPER_ENTRY)
+
+  { NULL, NULL, NULL, 0, 0, 0 }
+};
+
+#undef HELPER_ENTRY
+
+//-----------------------------------------------------------------------------
+
+int run_test_part(const char* test, const char* part) {
+  int r;
+  task_entry_t* task;
+
+  for (task = TASKS; task->main; task++) {
+    if (strcmp(test, task->task_name) == 0 &&
+        strcmp(part, task->process_name) == 0) {
+      r = task->main();
+      return r;
+    }
+  }
+
+  fprintf(stderr, "No test part with that name: %s:%s\n", test, part);
+  return 255;
+}
+
+
+task_entry_t* get_helper(const char* test) {
+  int r;
+  task_entry_t* task;
+
+  for (task = HELPERS; task->main; task++) {
+    if (strcmp(test, task->task_name) == 0) {
+      return task;
+    }
+  }
+  return NULL;
+}
+
+static int cur_task = 0;
+static int cur_entry = 0;
+
+void run_tests_init(void) {
+  cur_task = 0;
+  cur_entry = 0;
+}
+
+
+void run_tests_one(void) {
+  task_entry_t* task_group = NULL;
+  task_entry_t* task = NULL;
+
+  while (1) {
+    switch (cur_task) {
+    case 0 :
+      if (!cur_entry)
+        fprintf(stderr, "Run Helpers...\r\n");
+      task_group = HELPERS;
+      break;
+    case 2 :
+      if (!cur_entry)
+        fprintf(stderr, "Stopping Helpers...\r\n");
+      task_group = HELPERS;
+      break;
+    case 1 :
+      task_group = TASKS;
+      break;
+    default:
+      run_test_one(NULL);
+      return;
+    }
+    task = &task_group[cur_entry];
+    if (task && task->task_name) {
+      fprintf(stderr, "[%-30s]\r\n", task->task_name);
+
+      switch (cur_task) {
+      case 0 : run_helper(task); break;
+      case 1 : run_test_one(task); break;
+      case 2 : wait_helper(task); break;
+      }
+      cur_entry++;
+      return;
+    }
+    cur_entry = 0;
+    cur_task++;
+  }
+}
+
+
+// dummy for api emulator
+void apiemul_main(void) {
+}
+
+#if 0
+int run_tests() {
+  int entry;
+  int result;
+  task_entry_t* task;
+
+  fprintf(stderr, "Run Helpers...\r\n");
+  entry = 0;
+  while (HELPERS[entry].task_name) {
+    task = &HELPERS[entry];
+
+    fprintf(stderr, "[%-30s]...", task->task_name);
+    result = run_helper(task);
+    fprintf(stderr, "%s\r\n", result ? "failed" : "OK");
+    entry++;
+  }
+
+  entry = 0;
+  while (TASKS[entry].task_name) {
+    task = &TASKS[entry];
+
+    fprintf(stderr, "[%-30s]...", task->task_name);
+    result = run_test_one(task);
+    fprintf(stderr, "%s\r\n", result ? "failed" : "OK");
+    entry++;
+  }
+
+  fprintf(stderr, "Waiting Helpers to end...\r\n");
+  entry = 0;
+  while (HELPERS[entry].task_name) {
+    task = &HELPERS[entry];
+
+    fprintf(stderr, "[%-30s]...", task->task_name);
+    result = wait_helper(task);
+    fprintf(stderr, "%s\r\n", result ? "failed" : "OK");
+    entry++;
+  }
+
+  fprintf(stderr, "run_tests end\r\n");
+
+  return 0;
+}
+#endif
+
diff --git a/test/raw/test_active_raw.c b/test/raw/test_active_raw.c
new file mode 100644 (file)
index 0000000..76bc0df
--- /dev/null
@@ -0,0 +1,138 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+static int close_cb_called = 0;
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  close_cb_called++;
+}
+
+
+static void timer_cb(uv_timer_t* handle) {
+  TUV_ASSERT(0 && "timer_cb should not have been called");
+}
+
+
+typedef struct {
+
+  uv_loop_t* loop;
+  uv_timer_t timer;
+
+} active_param_t;
+
+
+static int active_loop(void* vparam) {
+  active_param_t* param = (active_param_t*)vparam;
+  uv_loop_t* loop = param->loop;
+  int r;
+
+  r = uv_run(loop, UV_RUN_ONCE);
+
+  return r; // continue while r == 1
+}
+
+static int active_final(void* vparam) {
+  active_param_t* param = (active_param_t*)vparam;
+  uv_loop_t* loop = param->loop;
+
+  TUV_ASSERT(close_cb_called == 1);
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+
+TEST_IMPL(active) {
+  uv_loop_t* loop;
+  active_param_t* param;
+
+  param = (active_param_t*)malloc(sizeof(active_param_t));
+  loop = uv_default_loop();
+  param->loop = loop;
+
+  int r;
+
+  close_cb_called = 0;
+
+  r = uv_timer_init(loop, &param->timer);
+  TUV_ASSERT(r == 0);
+
+  /* uv_is_active() and uv_is_closing() should always return either 0 or 1. */
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &param->timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &param->timer));
+
+  r = uv_timer_start(&param->timer, timer_cb, 1000, 0);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(1 == uv_is_active((uv_handle_t*) &param->timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &param->timer));
+
+  r = uv_timer_stop(&param->timer);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &param->timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &param->timer));
+
+  r = uv_timer_start(&param->timer, timer_cb, 1000, 0);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(1 == uv_is_active((uv_handle_t*) &param->timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &param->timer));
+
+  uv_close((uv_handle_t*) &param->timer, close_cb);
+
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &param->timer));
+  TUV_ASSERT(1 == uv_is_closing((uv_handle_t*) &param->timer));
+
+  tuv_run(loop, active_loop, active_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_async_raw.c b/test/raw/test_async_raw.c
new file mode 100644 (file)
index 0000000..ff630f9
--- /dev/null
@@ -0,0 +1,181 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+static uv_thread_t _thread;
+static uv_mutex_t _mutex;
+
+static uv_timer_t _timer;
+static uv_async_t _async;
+
+static int _async_cb_called;
+static int _timer_cb_called;
+static int _close_cb_called;
+
+
+static void task_entry(void* arg) {
+}
+
+static int task_loop(void* arg) {
+  int n;
+  int r;
+
+  uv_mutex_lock(&_mutex);
+  n = _async_cb_called;
+  uv_mutex_unlock(&_mutex);
+
+  if (n == 3) {
+    // stop task
+    return 0;
+  }
+
+  r = uv_async_send(&_async);
+  TUV_ASSERT(r == 0);
+
+  // continue task
+  return 1;
+}
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  _close_cb_called++;
+}
+
+
+static void async_cb(uv_async_t* handle) {
+  int n;
+
+  TUV_ASSERT(handle == &_async);
+
+  uv_mutex_lock(&_mutex);
+  n = ++_async_cb_called;
+  uv_mutex_unlock(&_mutex);
+
+  if (n == 3) {
+    uv_close((uv_handle_t*)&_async, close_cb);
+  }
+}
+
+
+static void timer_cb(uv_timer_t* timer) {
+  int r;
+
+  TUV_ASSERT(timer == &_timer);
+  _timer_cb_called++;
+
+  r = tuv_task_create(&_thread, task_entry, task_loop, NULL);
+  TUV_ASSERT(r == 0);
+  uv_mutex_unlock(&_mutex);
+
+  uv_close((uv_handle_t*)timer, NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+} async_param_t;
+
+
+static int async_loop(void* vparam) {
+  async_param_t* param = (async_param_t*)vparam;
+  int r;
+  r = uv_run(param->loop, UV_RUN_ONCE);
+  if (r == 0) {
+    if (tuv_task_running(&_thread))
+      return 1;
+  }
+  return r;
+}
+
+
+static int async_final(void* vparam) {
+  async_param_t* param = (async_param_t*)vparam;
+
+  TUV_ASSERT(_async_cb_called == 3);
+  TUV_ASSERT(_close_cb_called == 1);
+  TUV_ASSERT(_timer_cb_called == 1);
+
+  //TUV_ASSERT(0 == uv_thread_join(&_thread));
+
+  uv_mutex_destroy(&_mutex);
+  tuv_task_close(&_thread);
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+
+TEST_IMPL(async) {
+  async_param_t* param = (async_param_t*)malloc(sizeof(async_param_t));
+  param->loop = uv_default_loop();
+
+  int r;
+
+  _async_cb_called = 0;
+  _timer_cb_called = 0;
+  _close_cb_called = 0;
+
+  r = uv_mutex_init(&_mutex);
+  TUV_ASSERT(r == 0);
+  uv_mutex_lock(&_mutex);
+
+  r = uv_async_init(param->loop, &_async, async_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_init(param->loop, &_timer);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&_timer, timer_cb, 250, 0);
+  TUV_ASSERT(r == 0);
+
+  tuv_run(param->loop, async_loop, async_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_condvar_raw.c b/test/raw/test_condvar_raw.c
new file mode 100644 (file)
index 0000000..bb1b115
--- /dev/null
@@ -0,0 +1,143 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <errno.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+typedef struct {
+  uv_mutex_t mutex;
+  uv_cond_t cond;
+  int delay;
+  int use_broadcast;
+  volatile int posted;
+  void* data;
+} worker_config;
+
+
+static void worker_entry(void* arg) {
+  worker_config* c = (worker_config*)arg;
+
+  if (c->delay)
+    run_sleep(c->delay);
+
+  uv_mutex_lock(&c->mutex);
+  TUV_ASSERT(c->posted == 0);
+  c->posted = 1;
+  if (c->use_broadcast)
+    uv_cond_broadcast(&c->cond);
+  else
+    uv_cond_signal(&c->cond);
+  uv_mutex_unlock(&c->mutex);
+}
+
+static int worker_loop(void* arg) {
+  // stop the loop
+  return 0;
+}
+
+
+typedef struct {
+  uv_loop_t* loop;
+  uv_thread_t thread;
+  worker_config wc;
+  int waitcond;
+} condvar_1_param_t;
+
+
+static int condvar_1_loop(void* vparam) {
+  condvar_1_param_t* param = (condvar_1_param_t*)vparam;
+  int r;
+
+  if (param->waitcond) {
+    uv_mutex_lock(&param->wc.mutex);
+    run_sleep(100);
+    r = tuv_cond_wait(&param->wc.cond, &param->wc.mutex);
+    uv_mutex_unlock(&param->wc.mutex);
+    if (r == 0) {
+      // keep the loop going
+      return 1;
+    }
+    param->waitcond = 0;
+  }
+  // check task has closed(something like thread joined)
+  r = tuv_task_running(&param->thread);
+  // r=1 for task needs to run, return 1 so that loop keeps going
+  // r=0 for task has ended, return 0 so that loop ends
+  return r > 0 ? 1 : 0;
+}
+
+static int condvar_1_final(void* vparam) {
+  condvar_1_param_t* param = (condvar_1_param_t*)vparam;
+
+  TUV_ASSERT(param->wc.posted == 1);
+
+  tuv_task_close(&param->thread);
+  uv_mutex_destroy(&param->wc.mutex);
+  uv_cond_destroy(&param->wc.cond);
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+
+TEST_IMPL(condvar_1) {
+  condvar_1_param_t* param;
+  param = (condvar_1_param_t*)malloc(sizeof(condvar_1_param_t));
+  param->loop = uv_default_loop();
+
+  memset(&param->wc, 0, sizeof(param->wc));
+  param->wc.data = param;
+  param->waitcond = 1;
+
+  TUV_ASSERT(0 == uv_cond_init(&param->wc.cond));
+  TUV_ASSERT(0 == uv_mutex_init(&param->wc.mutex));
+  TUV_ASSERT(0 == tuv_task_create(&param->thread,
+                                 worker_entry, worker_loop,
+                                 &param->wc));
+
+  tuv_run(param->loop, condvar_1_loop, condvar_1_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_idle_raw.c b/test/raw/test_idle_raw.c
new file mode 100644 (file)
index 0000000..0b36c7c
--- /dev/null
@@ -0,0 +1,73 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <uv.h>
+#include "runner.h"
+
+
+static uv_idle_t idle_handle;
+static int idle_cb_called = 0;
+
+void idle_callback(uv_idle_t* handle) {
+  idle_cb_called++;
+}
+
+
+typedef struct {
+  uv_loop_t* loop;
+} idle_param_t;
+
+
+static int idle_base_loop(void* vparam) {
+  idle_param_t* param = (idle_param_t*)vparam;
+  uv_run(param->loop, UV_RUN_ONCE);
+  return 0; // stop the loop
+}
+
+static int idle_base_final(void* vparam) {
+  idle_param_t* param = (idle_param_t*)vparam;
+
+  uv_close((uv_handle_t*) &idle_handle, NULL);
+  uv_run(param->loop, UV_RUN_ONCE);
+
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+  TUV_WARN(idle_cb_called == 1);
+
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+TEST_IMPL(idle_basic) {
+  idle_param_t* param = (idle_param_t*)malloc(sizeof(idle_param_t));
+  param->loop = uv_default_loop();
+
+  idle_cb_called = 0;
+
+  uv_idle_init(param->loop, &idle_handle);
+  uv_idle_start(&idle_handle, idle_callback);
+
+  tuv_run(param->loop, idle_base_loop, idle_base_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_shutdown_eof_raw.c b/test/raw/test_shutdown_eof_raw.c
new file mode 100644 (file)
index 0000000..2840987
--- /dev/null
@@ -0,0 +1,206 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+#include "runner.h"
+
+
+#if defined(__TUV_HOST_IPEXIST__)
+#include "tuv_host_ipaddress.h"
+#else
+#pragma message("Please create tuv_host_ipaddress.h for shutdown_eof test.")
+#endif
+
+// Please create a file with your host IP address like below.
+// This information is use to connect to host that should be running
+// "tuvhelper" prgoram.
+/*
+#ifndef __tuv_host_ipaddress_header__
+#define __tuv_host_ipaddress_header__
+
+#define HOST_IP_ADDRESS   "192.168.1.2"
+
+#endif
+*/
+
+//-----------------------------------------------------------------------------
+
+#if defined(__TUV_HOST_IPEXIST__)
+static uv_tcp_t tcp;
+static uv_connect_t connect_req;
+static uv_write_t write_req;
+static uv_shutdown_t shutdown_req;
+static int got_q;
+static int got_eof;
+
+static int called_connect_cb;
+static int called_shutdown_cb;
+
+
+static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
+  buf->base = (char*)malloc(size);
+  buf->len = size;
+}
+
+
+static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) {
+  TUV_ASSERT((uv_tcp_t*)t == &tcp);
+
+
+  if (buf->base)
+    free(buf->base);
+
+  if (nread == 0) {
+    return;
+  }
+
+  if (nread > 0) {
+    TUV_ASSERT(0);
+    //TUV_ASSERT(!got_eof);
+    //TUV_ASSERT(buf->base[0] == 'Q');
+    //uv_buf_t qbuf = uv_buf_init((char*)"Q", 1);
+    //int r = uv_write(&write_req, t, &qbuf, 1, NULL);
+    //TUV_ASSERT(r == 0);
+    //got_q = 1;
+  } else {
+    TUV_ASSERT(nread == UV_EOF);
+    uv_close((uv_handle_t*)t, NULL);
+    got_eof = 1;
+  }
+}
+
+
+static void shutdown_cb(uv_shutdown_t *req, int status) {
+  TUV_ASSERT(req == &shutdown_req);
+  TUV_ASSERT(called_connect_cb == 1);
+  TUV_ASSERT(!got_eof);
+
+  called_shutdown_cb++;
+}
+
+
+static void connect_cb(uv_connect_t *req, int status) {
+  TUV_ASSERT(status == 0);
+  TUV_ASSERT(req == &connect_req);
+  int r;
+
+  /* Start reading from our connection so we can receive the EOF.  */
+  r = uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb);
+  TUV_ASSERT(r == 0);
+
+  /*
+   * Write the letter 'Q' to gracefully kill the echo-server. This will not
+   * effect our connection.
+   */
+  uv_buf_t qsbuf = uv_buf_init((char*)"Q", 1);
+  r = uv_write(&write_req, (uv_stream_t*) &tcp, &qsbuf, 1, NULL);
+  TUV_ASSERT(r == 0);
+
+  /* Shutdown our end of the connection.  */
+  //r = uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb);
+  //TUV_ASSERT(r == 0);
+
+  called_connect_cb++;
+  TUV_ASSERT(called_shutdown_cb == 0);
+}
+
+
+typedef struct {
+  uv_loop_t* loop;
+} shutdown_eof_param_t;
+
+static int shutdown_eof_loop(void* vparam) {
+  shutdown_eof_param_t* param = (shutdown_eof_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int shutdown_eof_final(void* vparam) {
+  shutdown_eof_param_t* param = (shutdown_eof_param_t*)vparam;
+
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  TUV_ASSERT(1 == called_connect_cb);
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+#endif
+
+/*
+ * This test has a client which connects to the echo_server and immediately
+ * issues a shutdown. The echo-server, in response, will also shutdown their
+ * connection. We check, with a timer, that libuv is not automatically
+ * calling uv_close when the client receives the EOF from echo-server.
+ */
+TEST_IMPL(shutdown_eof) {
+#if defined(__TUV_HOST_IPEXIST__)
+  shutdown_eof_param_t* param;
+  param = (shutdown_eof_param_t*)malloc(sizeof(shutdown_eof_param_t));
+  param->loop = uv_default_loop();
+
+  struct sockaddr_in server_addr;
+  int r;
+
+  got_q = 0;
+  got_eof = 0;
+  called_connect_cb = 0;
+  called_shutdown_cb = 0;
+
+  r = uv_ip4_addr(HOST_IP_ADDRESS, TEST_PORT, &server_addr);
+  TUV_ASSERT(r == 0);
+  r = uv_tcp_init(param->loop, &tcp);
+  TUV_ASSERT(r == 0);
+  r = uv_tcp_connect(&connect_req,
+                     &tcp,
+                     (const struct sockaddr*) &server_addr,
+                     connect_cb);
+  TUV_ASSERT(r == 0);
+
+  tuv_run(param->loop, shutdown_eof_loop, shutdown_eof_final, param);
+#else
+  TDDLOG("'HOST_IP_ADDRESS' not defined. skip test.\r\n");
+  run_tests_continue();
+#endif
+  return 0;
+}
diff --git a/test/raw/test_tcp_open_raw.c b/test/raw/test_tcp_open_raw.c
new file mode 100644 (file)
index 0000000..a3ce370
--- /dev/null
@@ -0,0 +1,267 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+#if defined(__TUV_HOST_IPEXIST__)
+#include "tuv_host_ipaddress.h"
+#else
+#pragma message("Please create tuv_host_ipaddress.h for tcp_open test.")
+#endif
+
+// Please create a file with your host IP address like below.
+// This information is use to connect to host that should be running
+// "tuvhelper" prgoram.
+/*
+#ifndef __tuv_host_ipaddress_header__
+#define __tuv_host_ipaddress_header__
+
+#define HOST_IP_ADDRESS   "192.168.1.2"
+
+#endif
+*/
+
+
+#if defined(__TUV_HOST_IPEXIST__)
+
+static int shutdown_cb_called = 0;
+static int connect_cb_called = 0;
+static int write_cb_called = 0;
+static int close_cb_called = 0;
+
+static uv_connect_t connect_req;
+static uv_shutdown_t shutdown_req;
+static uv_write_t write_req;
+
+
+static void startup(void) {
+  uv_sleep(1);
+}
+
+
+static uv_os_sock_t create_tcp_socket(void) {
+  uv_os_sock_t sock;
+
+  sock = tuvp_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+  TUV_ASSERT(sock >= 0);
+
+  {
+    /* Allow reuse of the port. */
+    int yes = 1;
+    int r = tuvp_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
+    TUV_ASSERT(r == 0);
+  }
+
+  return sock;
+}
+
+
+static void alloc_cb(uv_handle_t* handle,
+                     size_t suggested_size,
+                     uv_buf_t* buf) {
+#ifdef EMBED_LOW_MEMORY
+  static char slab[2048];
+#else
+  static char slab[65536];
+#endif
+  TUV_ASSERT(suggested_size <= sizeof(slab));
+  buf->base = slab;
+  buf->len = sizeof(slab);
+}
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  close_cb_called++;
+}
+
+
+static void shutdown_cb(uv_shutdown_t* req, int status) {
+  TUV_ASSERT(req == &shutdown_req);
+  TUV_ASSERT(status == 0);
+
+  /* Now we wait for the EOF */
+  shutdown_cb_called++;
+}
+
+
+static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) {
+  TUV_ASSERT(tcp != NULL);
+
+  if (nread > 0) {
+    TUV_ASSERT(nread == 4);
+    TUV_ASSERT(memcmp("PING", buf->base, nread) == 0);
+
+    /* tell server to close connection */
+    {
+      //TDDDLOG("read_cb, sending QS to close this");
+      uv_buf_t buf = uv_buf_init((char*)"QS", 2);
+      int r = uv_write(&write_req, tcp, &buf, 1, NULL);
+      TUV_ASSERT(r == 0);
+    }
+  }
+  else if (nread == 0) {
+
+  }
+  else if (nread < 0) {
+    TUV_ASSERT(nread == UV_EOF);
+    uv_close((uv_handle_t*)tcp, close_cb);
+  }
+}
+
+
+static void write_cb(uv_write_t* req, int status) {
+  TUV_ASSERT(req != NULL);
+
+  if (status) {
+    TDLOG("tcp_open uv_write error: %s", uv_strerror(status));
+    TUV_ASSERT(0);
+  }
+
+  write_cb_called++;
+}
+
+
+static void connect_cb(uv_connect_t* req, int status) {
+  uv_buf_t buf = uv_buf_init((char*)"PING", 4);
+  uv_stream_t* stream;
+  int r;
+
+  if (status == UV__ECONNREFUSED) {
+    TDLOG("tcp_open Connection refused. Run server first.");
+    TUV_ASSERT(0);
+  }
+
+  printf(">> connect_cb status(%d)", status);
+
+  TUV_ASSERT(req == &connect_req);
+  TUV_ASSERT(status == 0);
+
+  stream = req->handle;
+  connect_cb_called++;
+
+  r = uv_write(&write_req, stream, &buf, 1, write_cb);
+  TUV_ASSERT(r == 0);
+
+  /* Shutdown on drain. */
+  // r = uv_shutdown(&shutdown_req, stream, shutdown_cb);
+  //TUV_ASSERT(r == 0);
+
+  /* Start reading */
+  r = uv_read_start(stream, alloc_cb, read_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+typedef struct {
+  uv_loop_t* loop;
+  uv_tcp_t client;
+  uv_os_sock_t sock;
+} tcp_open_param_t;
+
+
+static int tcp_open_loop(void* vparam) {
+  tcp_open_param_t* param = (tcp_open_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+
+static int tcp_open_final(void* vparam) {
+  tcp_open_param_t* param = (tcp_open_param_t*)vparam;
+
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  //TUV_ASSERT(shutdown_cb_called == 1);
+  TUV_ASSERT(connect_cb_called == 1);
+  TUV_ASSERT(write_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+#endif
+
+
+TEST_IMPL(tcp_open) {
+#if defined(__TUV_HOST_IPEXIST__)
+  tcp_open_param_t* param;
+  param = (tcp_open_param_t*)malloc(sizeof(tcp_open_param_t));
+  param->loop = uv_default_loop();
+
+  struct sockaddr_in addr;
+  int r;
+
+  shutdown_cb_called = 0;
+  connect_cb_called = 0;
+  write_cb_called = 0;
+  close_cb_called = 0;
+
+  TUV_ASSERT(0 == uv_ip4_addr(HOST_IP_ADDRESS, TEST_PORT, &addr));
+
+  startup();
+  param->sock = create_tcp_socket();
+
+  r = uv_tcp_init(param->loop, &param->client);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_open(&param->client, param->sock);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_connect(&connect_req,
+                     &param->client,
+                     (const struct sockaddr*)&addr,
+                     connect_cb);
+  TUV_ASSERT(r == 0);
+
+  tuv_run(param->loop, tcp_open_loop, tcp_open_final, param);
+#else
+  TDDLOG("'HOST_IP_ADDRESS' not defined. skip test.\r\n");
+  run_tests_continue();
+#endif
+  return 0;
+}
diff --git a/test/raw/test_threadpool_raw_queue_work_simple.c b/test/raw/test_threadpool_raw_queue_work_simple.c
new file mode 100644 (file)
index 0000000..e550306
--- /dev/null
@@ -0,0 +1,114 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+static int work_cb_count;
+static int after_work_cb_count;
+static uv_work_t work_req;
+static char data;
+
+
+static void work_cb(uv_work_t* req) {
+  TUV_ASSERT(req == &work_req);
+  TUV_ASSERT(req->data == &data);
+  work_cb_count++;
+}
+
+
+static void after_work_cb(uv_work_t* req, int status) {
+  TUV_ASSERT(status == 0);
+  TUV_ASSERT(req == &work_req);
+  TUV_ASSERT(req->data == &data);
+  after_work_cb_count++;
+}
+
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+} queuework_param_t;
+
+
+static int queuework_loop(void* vparam) {
+  queuework_param_t* param = (queuework_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+
+static int queuework_final(void* vparam) {
+  queuework_param_t* param = (queuework_param_t*)vparam;
+
+  TUV_ASSERT(work_cb_count == 1);
+  TUV_ASSERT(after_work_cb_count == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+
+TEST_IMPL(threadpool_queue_work_simple) {
+  queuework_param_t* param =
+                        (queuework_param_t*)malloc(sizeof(queuework_param_t));
+  param->loop = uv_default_loop();
+
+  int r;
+
+  work_cb_count = 0;
+  after_work_cb_count = 0;
+
+  work_req.data = &data;
+  r = uv_queue_work(param->loop, &work_req, work_cb, after_work_cb);
+  TUV_ASSERT(r == 0);
+
+  tuv_run(param->loop, queuework_loop, queuework_final, param);
+
+  return 0;
+}
+
diff --git a/test/raw/test_timer_raw_again.c b/test/raw/test_timer_raw_again.c
new file mode 100644 (file)
index 0000000..2e7da24
--- /dev/null
@@ -0,0 +1,175 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+static int close_cb_called = 0;
+static int repeat_1_cb_called = 0;
+static int repeat_2_cb_called = 0;
+static int repeat_2_cb_allowed = 0;
+
+static uv_timer_t dummy, repeat_1, repeat_2;
+
+static uint64_t start_time;
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+
+  close_cb_called++;
+}
+
+
+static void repeat_1_cb(uv_timer_t* handle) {
+  int r;
+
+  TUV_ASSERT(handle == &repeat_1);
+  TUV_ASSERT(uv_timer_get_repeat((uv_timer_t*)handle) == 50);
+
+  repeat_1_cb_called++;
+
+  r = uv_timer_again(&repeat_2);
+  TUV_ASSERT(r == 0);
+
+  if (repeat_1_cb_called == 10) {
+    uv_close((uv_handle_t*)handle, close_cb);
+    /* We're not calling uv_timer_again on repeat_2 any more, so after this */
+    /* timer_2_cb is expected. */
+    repeat_2_cb_allowed = 1;
+    return;
+  }
+}
+
+
+static void repeat_2_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle == &repeat_2);
+  TUV_ASSERT(repeat_2_cb_allowed);
+
+  repeat_2_cb_called++;
+
+  if (uv_timer_get_repeat(&repeat_2) == 0) {
+    TUV_ASSERT(0 == uv_is_active((uv_handle_t*) handle));
+    uv_close((uv_handle_t*)handle, close_cb);
+    return;
+  }
+
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_2) == 100);
+
+  /* This shouldn't take effect immediately. */
+  uv_timer_set_repeat(&repeat_2, 0);
+}
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+} timer_param_t;
+
+
+static int timer_loop(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int timer_final(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+
+  TUV_ASSERT(repeat_1_cb_called == 10);
+  TUV_ASSERT(repeat_2_cb_called == 2);
+  TUV_ASSERT(close_cb_called == 2);
+
+  // for platform that needs closing
+  tuv_timer_deinit(param->loop, (uv_handle_t*)&dummy);
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+TEST_IMPL(timer_again) {
+  timer_param_t* param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  param->loop = uv_default_loop();
+
+  int r;
+
+  close_cb_called = 0;
+  repeat_1_cb_called = 0;
+  repeat_2_cb_called = 0;
+  repeat_2_cb_allowed = 0;
+
+  start_time = uv_now(param->loop);
+  TUV_ASSERT(0 < start_time);
+
+  /* Verify that it is not possible to uv_timer_again a never-started timer. */
+  r = uv_timer_init(param->loop, &dummy);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_again(&dummy);
+  TUV_ASSERT(r == UV_EINVAL);
+  uv_unref((uv_handle_t*)&dummy);
+
+  /* Start timer repeat_1. */
+  r = uv_timer_init(param->loop, &repeat_1);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&repeat_1, repeat_1_cb, 50, 0);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_1) == 0);
+
+  /* Actually make repeat_1 repeating. */
+  uv_timer_set_repeat(&repeat_1, 50);
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_1) == 50);
+
+  /*
+   * Start another repeating timer. It'll be again()ed by the repeat_1 so
+   * it should not time out until repeat_1 stops.
+   */
+  r = uv_timer_init(param->loop, &repeat_2);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&repeat_2, repeat_2_cb, 100, 100);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_2) == 100);
+
+  tuv_run(param->loop, timer_loop, timer_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_timer_raw_huge_repeat.c b/test/raw/test_timer_raw_huge_repeat.c
new file mode 100644 (file)
index 0000000..19039b6
--- /dev/null
@@ -0,0 +1,105 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+//-----------------------------------------------------------------------------
+
+static uv_timer_t tiny_timer;
+static uv_timer_t huge_timer1;
+static uv_timer_t huge_timer2;
+
+
+static int huge_repeat_cb_ncalls;
+
+static void huge_repeat_cb(uv_timer_t* handle) {
+  if (huge_repeat_cb_ncalls == 0)
+    TUV_ASSERT(handle == &huge_timer1);
+  else
+    TUV_ASSERT(handle == &tiny_timer);
+
+  if (++huge_repeat_cb_ncalls == 10) {
+    uv_close((uv_handle_t*) &tiny_timer, NULL);
+    uv_close((uv_handle_t*) &huge_timer1, NULL);
+  }
+}
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+} timer_param_t;
+
+
+static int timer_loop(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int timer_final(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+
+TEST_IMPL(timer_huge_repeat) {
+  timer_param_t* param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  param->loop = uv_default_loop();
+
+  huge_repeat_cb_ncalls = 0;
+
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &tiny_timer));
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &huge_timer1));
+  TUV_ASSERT(0 == uv_timer_start(&tiny_timer, huge_repeat_cb, 2, 2));
+  TUV_ASSERT(0 == uv_timer_start(&huge_timer1, huge_repeat_cb, 1, (uint64_t) -1));
+
+  tuv_run(param->loop, timer_loop, timer_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_timer_raw_huge_timeout.c b/test/raw/test_timer_raw_huge_timeout.c
new file mode 100644 (file)
index 0000000..50f2796
--- /dev/null
@@ -0,0 +1,96 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+static uv_timer_t tiny_timer;
+static uv_timer_t huge_timer1;
+static uv_timer_t huge_timer2;
+
+
+static void tiny_timer_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle == &tiny_timer);
+  uv_close((uv_handle_t*) &tiny_timer, NULL);
+  uv_close((uv_handle_t*) &huge_timer1, NULL);
+  uv_close((uv_handle_t*) &huge_timer2, NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+} timer_param_t;
+
+
+static int timer_loop(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int timer_final(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+TEST_IMPL(timer_huge_timeout) {
+  timer_param_t* param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  param->loop = uv_default_loop();
+
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &tiny_timer));
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &huge_timer1));
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &huge_timer2));
+  TUV_ASSERT(0 == uv_timer_start(&tiny_timer, tiny_timer_cb, 1, 0));
+  TUV_ASSERT(0 == uv_timer_start(&huge_timer1, tiny_timer_cb, 0xffffffffffffLL, 0));
+  TUV_ASSERT(0 == uv_timer_start(&huge_timer2, tiny_timer_cb, (uint64_t) -1, 0));
+
+  tuv_run(param->loop, timer_loop, timer_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_timer_raw_init.c b/test/raw/test_timer_raw_init.c
new file mode 100644 (file)
index 0000000..95761bc
--- /dev/null
@@ -0,0 +1,88 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+  uv_timer_t handle;
+} timer_param_t;
+
+
+static int timer_loop(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  uv_loop_t* loop = param->loop;
+
+  uv_run(loop, UV_RUN_ONCE);
+  // make this loop run once
+  return 0;
+}
+
+static int timer_final(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  uv_loop_t* loop = param->loop;
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+TEST_IMPL(timer_init) {
+  timer_param_t* param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  param->loop = uv_default_loop();
+
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &param->handle));
+  TUV_ASSERT(0 == uv_timer_get_repeat(&param->handle));
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &param->handle));
+
+  // for platforms that needs cleaning
+  uv_close((uv_handle_t*) &param->handle, NULL);
+
+  tuv_run(param->loop, timer_loop, timer_final, param);
+
+  return 0;
+}
+
diff --git a/test/raw/test_timer_raw_norm.c b/test/raw/test_timer_raw_norm.c
new file mode 100644 (file)
index 0000000..c235522
--- /dev/null
@@ -0,0 +1,186 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+static int once_cb_called = 0;
+static int once_close_cb_called = 0;
+static int repeat_cb_called = 0;
+static int repeat_close_cb_called = 0;
+
+static uint64_t start_time = 0ll;
+
+
+static void once_close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(0 == uv_is_active(handle));
+
+  once_close_cb_called++;
+}
+
+static void once_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) handle));
+
+  once_cb_called++;
+
+  uv_close((uv_handle_t*)handle, once_close_cb);
+
+  /* Just call this randomly for the code coverage. */
+  uv_update_time(uv_default_loop());
+}
+
+
+static void repeat_close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+
+  repeat_close_cb_called++;
+}
+
+
+static void repeat_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(1 == uv_is_active((uv_handle_t*) handle));
+
+  repeat_cb_called++;
+
+  if (repeat_cb_called == 5) {
+    uv_close((uv_handle_t*)handle, repeat_close_cb);
+  }
+}
+
+
+static void never_cb(uv_timer_t* handle) {
+  TUV_FATAL("never_cb should never be called");
+}
+
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+  uv_timer_t once_timers[10];
+  uv_timer_t repeat, never;
+} timer_param_t;
+
+
+static int timer_loop(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int timer_final(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+
+  TUV_ASSERT(once_cb_called == 10);
+  TUV_ASSERT(once_close_cb_called == 10);
+  TUV_ASSERT(repeat_cb_called == 5);
+  TUV_ASSERT(repeat_close_cb_called == 1);
+
+  TUV_ASSERT(500 <= uv_now(param->loop) - start_time);
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+TEST_IMPL(timer) {
+  timer_param_t* param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  param->loop = uv_default_loop();
+
+  uv_timer_t *once;
+  unsigned int i;
+  int r;
+
+  once_cb_called = 0;
+  once_close_cb_called = 0;
+  repeat_cb_called = 0;
+  repeat_close_cb_called = 0;
+
+  start_time = uv_now(param->loop);
+  TUV_ASSERT(0 < start_time);
+
+  /* Let 10 timers time out in 500 ms total. */
+  for (i = 0; i < ARRAY_SIZE(param->once_timers); i++) {
+    once = param->once_timers + i;
+    r = uv_timer_init(param->loop, once);
+    TUV_ASSERT(r == 0);
+    r = uv_timer_start(once, once_cb, i * 50, 0);
+    TUV_ASSERT(r == 0);
+  }
+
+  /* The 11th timer is a repeating timer that runs 4 times */
+  r = uv_timer_init(param->loop, &param->repeat);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&param->repeat, repeat_cb, 100, 100);
+  TUV_ASSERT(r == 0);
+
+  /* The 12th timer should not do anything. */
+  r = uv_timer_init(param->loop, &param->never);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&param->never, never_cb, 100, 100);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_stop(&param->never);
+  TUV_ASSERT(r == 0);
+  uv_unref((uv_handle_t*)&param->never);
+  // for platforms that needs cleaning
+  uv_close((uv_handle_t*) &param->never, NULL);
+
+#if 1
+  tuv_run(param->loop, timer_loop, timer_final, param);
+#else
+  loop->raw_loopcb = timer_loop;
+  loop->raw_finalcb = timer_final;
+  loop->raw_param = param;
+  while(true) {
+    int r = timer_loop(param);
+    if (!r) break;
+  }
+  timer_final(param);
+#endif
+
+  return 0;
+}
diff --git a/test/raw/test_timer_raw_order.c b/test/raw/test_timer_raw_order.c
new file mode 100644 (file)
index 0000000..a587031
--- /dev/null
@@ -0,0 +1,140 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <unistd.h> // usleep
+#include <uv.h>
+
+
+#include "runner.h"
+
+
+
+//-----------------------------------------------------------------------------
+
+static int order_cb_called = 0;
+
+
+static void order_cb_a(uv_timer_t *handle) {
+  TUV_ASSERT(order_cb_called++ == *(int*)handle->data);
+}
+
+
+static void order_cb_b(uv_timer_t *handle) {
+  TUV_ASSERT(order_cb_called++ == *(int*)handle->data);
+}
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+  uv_timer_t handle_a;
+  uv_timer_t handle_b;
+  int first;
+  int second;
+} timer_param_t;
+
+
+static int timer_loop(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int timer_final1(void* vparam);
+static int timer_final2(void* vparam);
+
+static int timer_final1(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+
+  TUV_ASSERT(order_cb_called == 2);
+
+  TUV_ASSERT(0 == uv_timer_stop(&param->handle_a));
+  TUV_ASSERT(0 == uv_timer_stop(&param->handle_b));
+
+  /* Test for starting handle_b then handle_a */
+  order_cb_called = 0;
+  param->handle_b.data = &param->first;
+  TUV_ASSERT(0 == uv_timer_start(&param->handle_b, order_cb_b, 0, 0));
+
+  param->handle_a.data = &param->second;
+  TUV_ASSERT(0 == uv_timer_start(&param->handle_a, order_cb_a, 0, 0));
+
+  tuv_run(param->loop, timer_loop, timer_final2, param);
+
+  return 1; // don't clear loop param info
+}
+
+static int timer_final2(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+
+  TUV_ASSERT(order_cb_called == 2);
+
+  // for platforms that needs cleaning
+  uv_close((uv_handle_t*) &param->handle_a, NULL);
+  uv_close((uv_handle_t*) &param->handle_b, NULL);
+  uv_run(param->loop, UV_RUN_ONCE);
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+TEST_IMPL(timer_order) {
+  timer_param_t* param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  param->loop = uv_default_loop();
+
+  order_cb_called = 0;
+
+  param->first = 0;
+  param->second = 1;
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &param->handle_a));
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &param->handle_b));
+
+  /* Test for starting handle_a then handle_b */
+  param->handle_a.data = &param->first;
+  TUV_ASSERT(0 == uv_timer_start(&param->handle_a, order_cb_a, 0, 0));
+  param->handle_b.data = &param->second;
+  TUV_ASSERT(0 == uv_timer_start(&param->handle_b, order_cb_b, 0, 0));
+
+  tuv_run(param->loop, timer_loop, timer_final1, param);
+
+  return 0;
+}
diff --git a/test/raw/test_timer_raw_run_null_callback.c b/test/raw/test_timer_raw_run_null_callback.c
new file mode 100644 (file)
index 0000000..0cfa154
--- /dev/null
@@ -0,0 +1,57 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+TEST_IMPL(timer_null_callback) {
+  uv_timer_t handle;
+
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &handle));
+  TUV_ASSERT(UV_EINVAL == uv_timer_start(&handle, NULL, 100, 100));
+
+  // for platform that needs closing
+  tuv_timer_deinit(uv_default_loop(), (uv_handle_t*) &handle);
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
diff --git a/test/raw/test_timer_raw_run_once.c b/test/raw/test_timer_raw_run_once.c
new file mode 100644 (file)
index 0000000..79c1991
--- /dev/null
@@ -0,0 +1,94 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+//-----------------------------------------------------------------------------
+
+static unsigned int timer_run_once_timer_cb_called = 0;
+
+static void timer_run_once_timer_cb(uv_timer_t* handle) {
+  timer_run_once_timer_cb_called++;
+}
+
+
+typedef struct {
+  uv_loop_t* loop;
+  uv_timer_t timer_handle;
+} timer_param_t;
+
+
+TEST_IMPL(timer_run_once) {
+  timer_param_t* param;
+  uv_loop_t* loop;
+
+  param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  loop = uv_default_loop();
+  param->loop = loop;
+
+  timer_run_once_timer_cb_called = 0;
+
+  TUV_ASSERT(0 == uv_timer_init(param->loop, &param->timer_handle));
+  TUV_ASSERT(0 == uv_timer_start(&param->timer_handle,
+                                 timer_run_once_timer_cb, 0, 0));
+  TUV_ASSERT(0 == uv_run(param->loop, UV_RUN_ONCE));
+  TUV_ASSERT(1 == timer_run_once_timer_cb_called);
+
+  TUV_ASSERT(0 == uv_timer_start(&param->timer_handle,
+                                timer_run_once_timer_cb, 1, 0));
+  // slow systems may have nano second resolution
+  // give some time to sleep so that time tick is changed
+  uv_sleep(1);
+  TUV_ASSERT(0 == uv_run(param->loop, UV_RUN_ONCE));
+  TUV_ASSERT(2 == timer_run_once_timer_cb_called);
+
+  uv_close((uv_handle_t*)&param->timer_handle, NULL);
+  TUV_ASSERT(0 == uv_run(param->loop, UV_RUN_ONCE));
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
diff --git a/test/raw/test_timer_raw_start_twice.c b/test/raw/test_timer_raw_start_twice.c
new file mode 100644 (file)
index 0000000..f1e6bbf
--- /dev/null
@@ -0,0 +1,123 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+
+static int once_cb_called = 0;
+static int once_close_cb_called = 0;
+
+
+static void once_close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(0 == uv_is_active(handle));
+
+  once_close_cb_called++;
+}
+
+
+static void once_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) handle));
+
+  once_cb_called++;
+
+  uv_close((uv_handle_t*)handle, once_close_cb);
+
+  /* Just call this randomly for the code coverage. */
+  uv_update_time(uv_default_loop());
+}
+
+
+static void never_cb(uv_timer_t* handle) {
+  TUV_FATAL("never_cb should never be called");
+}
+
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+  uv_timer_t once;
+} timer_param_t;
+
+static int timer_loop(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int timer_final(void* vparam) {
+  timer_param_t* param = (timer_param_t*)vparam;
+
+  TUV_ASSERT(once_cb_called == 1);
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+TEST_IMPL(timer_start_twice) {
+  timer_param_t* param = (timer_param_t*)malloc(sizeof(timer_param_t));
+  param->loop = uv_default_loop();
+
+  int r;
+
+  once_cb_called = 0;
+  once_close_cb_called = 0;
+
+  r = uv_timer_init(param->loop, &param->once);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&param->once, never_cb, 86400 * 1000, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&param->once, once_cb, 10, 0);
+  TUV_ASSERT(r == 0);
+
+  tuv_run(param->loop, timer_loop, timer_final, param);
+
+  return 0;
+}
diff --git a/test/raw/test_walk_handles_raw.c b/test/raw/test_walk_handles_raw.c
new file mode 100644 (file)
index 0000000..67a320e
--- /dev/null
@@ -0,0 +1,120 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+static char magic_cookie[] = "magic cookie";
+static int seen_timer_handle;
+static uv_timer_t timer;
+
+
+static void walk_cb(uv_handle_t* handle, void* arg) {
+  TUV_ASSERT(arg == (void*)magic_cookie);
+
+  if (handle == (uv_handle_t*)&timer) {
+    seen_timer_handle++;
+  } else {
+    TUV_ASSERT(0 && "unexpected handle");
+  }
+}
+
+
+static void timer_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle == &timer);
+
+  uv_walk(handle->loop, walk_cb, magic_cookie);
+  uv_close((uv_handle_t*)handle, NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+
+typedef struct {
+  uv_loop_t* loop;
+} walk_param_t;
+
+static int walk_loop(void* vparam) {
+  walk_param_t* param = (walk_param_t*)vparam;
+  return uv_run(param->loop, UV_RUN_ONCE);
+}
+
+static int walk_final(void* vparam) {
+  walk_param_t* param = (walk_param_t*)vparam;
+
+  TUV_ASSERT(seen_timer_handle == 1);
+  /* Loop is finished, walk_cb should not see our timer handle. */
+  seen_timer_handle = 0;
+  uv_walk(param->loop, walk_cb, magic_cookie);
+  TUV_ASSERT(seen_timer_handle == 0);
+  TUV_ASSERT(0 == uv_loop_close(param->loop));
+
+  // cleanup tuv param
+  free(param);
+
+  // jump to next test
+  run_tests_continue();
+
+  return 0;
+}
+
+
+TEST_IMPL(walk_handles) {
+  walk_param_t* param = (walk_param_t*)malloc(sizeof(walk_param_t));
+  param->loop = uv_default_loop();
+
+  int r;
+
+  seen_timer_handle = 0;
+
+  r = uv_timer_init(param->loop, &timer);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_start(&timer, timer_cb, 1, 0);
+  TUV_ASSERT(r == 0);
+
+  /* Start event loop, expect to see the timer handle in walk_cb. */
+  TUV_ASSERT(seen_timer_handle == 0);
+
+  tuv_run(param->loop, walk_loop, walk_final, param);
+
+  return 0;
+}
diff --git a/test/resources/empty_file b/test/resources/empty_file
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/runner.h b/test/runner.h
new file mode 100644 (file)
index 0000000..b779a25
--- /dev/null
@@ -0,0 +1,220 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 __tuv_test_runner_header__
+#define __tuv_test_runner_header__
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#if defined(__NUTTX__) || defined(__TUV_RAW__)
+#define EMBED_LOW_MEMORY
+#endif
+
+// for tcp_test
+#define TEST_PORT 9123
+#define TEST_PORT_2 9124
+
+
+typedef struct {
+  const char *task_name;
+  const char *process_name;
+  int (*main)(void);
+  int is_helper;
+  int show_output;
+
+  /*
+   * The time in milliseconds after which a single test or benchmark times out.
+   */
+  int timeout;
+  uv_sem_t semsync;
+} task_entry_t;
+
+
+#define TEST_IMPL(name)                                                       \
+  int run_test_##name(void);                                                  \
+  int run_test_##name(void)
+
+#define HELPER_IMPL(name)                                                     \
+  int run_helper_##name(void);                                                \
+  int run_helper_##name(void)
+
+#ifdef _WIN32
+# define TEST_PIPENAME "\\\\?\\pipe\\uv-test"
+# define TEST_PIPENAME_2 "\\\\?\\pipe\\uv-test2"
+# define TEST_PIPENAME_3 "\\\\?\\pipe\\uv-test3"
+#else
+# define TEST_PIPENAME "/tmp/uv-test-sock"
+# define TEST_PIPENAME_2 "/tmp/uv-test-sock2"
+# define TEST_PIPENAME_3 "/tmp/uv-test-sock3"
+#endif
+
+/* Have our own assert, so we are sure it does not get optimized away in
+ * a release build.
+ */
+#define TUV_ASSERT(expr)                                  \
+ do {                                                     \
+  if (!(expr)) {                                          \
+    fprintf(stderr,                                       \
+            "Assertion failed in %s on line %d: %s\r\n",  \
+            __FILE__,                                     \
+            __LINE__,                                     \
+            #expr);                                       \
+    ABORT();                                              \
+  }                                                       \
+ } while (0)
+
+#define TUV_WARN(expr)                                    \
+ do {                                                     \
+  if (!(expr)) {                                          \
+    fprintf(stderr,                                       \
+            "Failed in %s on line %d: %s\r\n",            \
+            __FILE__,                                     \
+            __LINE__,                                     \
+            #expr);                                       \
+    return -1;                                            \
+  }                                                       \
+ } while (0)
+
+#define TUV_FATAL(msg)                                    \
+  do {                                                    \
+    fprintf(stderr,                                       \
+            "Fatal error in %s on line %d: %s\r\n",       \
+            __FILE__,                                     \
+            __LINE__,                                     \
+            msg);                                         \
+    fflush(stderr);                                       \
+    ABORT();                                              \
+  } while (0)
+
+/* Reserved test exit codes. */
+enum test_status {
+  TEST_OK = 0,
+  TEST_TODO,
+  TEST_SKIP
+};
+
+typedef struct {
+  FILE* stdout_file;
+  pid_t pid;
+  char* name;
+  int status;
+  int terminated;
+} process_info_t;
+
+typedef struct {
+  int pipe[2];
+  process_info_t* vec;
+  int n;
+} dowait_args;
+
+/* tcp server */
+typedef struct {
+  uv_write_t req;
+  uv_buf_t buf;
+} write_req_t;
+
+typedef enum {
+  TEST_TCP = 0,
+  TEST_UDP,
+  TEST_PIPE
+} stream_type;
+
+#ifdef PATH_MAX
+#define EXEC_PATH_LENGTH PATH_MAX
+#else
+#define EXEC_PATH_LENGTH 4096
+#endif
+
+
+int process_start(const char* name, const char* part, process_info_t* p,
+                  int is_helper);
+int process_wait(process_info_t* vec, int n, int timeout);
+int process_reap(process_info_t *p);
+int process_read_last_line(process_info_t *p, char* buffer, size_t buffer_len);
+void process_cleanup(process_info_t *p);
+
+int run_test_one(task_entry_t* task);
+int run_test_part(const char* test, const char* part);
+int run_tests(void);
+void run_sleep(int msec);
+
+task_entry_t* get_helper(const char* test);
+int run_helper(task_entry_t* task);
+int wait_helper(task_entry_t* task);
+
+
+// for raw systems
+void run_tests_init(void);
+void run_tests_one(void);
+void run_tests_continue(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// test function declaration
+
+#include "runner_list.h"
+
+
+#define TEST_DECLARE(name,x)                                                  \
+  int run_test_##name(void);
+
+#define HELPER_DECLARE(task_name,name)                                        \
+  int run_helper_##name(void);
+
+
+TEST_LIST_ALL(TEST_DECLARE)
+TEST_LIST_EXT(TEST_DECLARE)
+
+HELPER_LIST_ALL(HELPER_DECLARE)
+
+#undef TEST_DECLARE
+#undef HELPER_DECLARE
+
+
+#endif // __tuv_test_runner_header__
diff --git a/test/runner_linux.c b/test/runner_linux.c
new file mode 100644 (file)
index 0000000..1b4a42e
--- /dev/null
@@ -0,0 +1,546 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+void run_sleep(int msec) {
+  usleep(msec * 1000);
+}
+
+
+#if 0
+
+char executable_path[EXEC_PATH_LENGTH];
+
+
+/* Do platform-specific initialization. */
+int platform_init(int argc, char **argv) {
+
+  /* Disable stdio output buffering. */
+  setvbuf(stdout, NULL, _IONBF, 0);
+  setvbuf(stderr, NULL, _IONBF, 0);
+  signal(SIGPIPE, SIG_IGN);
+
+  if (realpath(argv[0], executable_path) == NULL) {
+    perror("realpath");
+    return -1;
+  }
+
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+/* Invoke "argv[0] test-name [test-part]". Store process info in *p. */
+/* Make sure that all stdio output of the processes is buffered up. */
+int process_start(const char* name, const char* part, process_info_t* p,
+                  int is_helper) {
+  FILE* stdout_file;
+  const char* arg;
+  const char* args[16];
+  int n;
+  pid_t pid;
+
+  stdout_file = tmpfile();
+  if (!stdout_file) {
+    perror("tmpfile");
+    return -1;
+  }
+
+  p->terminated = 0;
+  p->status = 0;
+
+  pid = fork();
+
+  if (pid < 0) {
+    perror("fork");
+    return -1;
+  }
+
+  if (pid == 0) {
+    /* child */
+    n = 0;
+
+    args[n++] = executable_path;
+    args[n++] = name;
+    args[n++] = part;
+    args[n++] = NULL;
+
+    dup2(fileno(stdout_file), STDOUT_FILENO);
+    dup2(fileno(stdout_file), STDERR_FILENO);
+    execvp(args[0], (char* const*)args);
+    perror("execvp()");
+    _exit(127);
+  }
+
+  /* parent */
+  p->pid = pid;
+  p->name = strdup(name);
+  p->stdout_file = stdout_file;
+
+  return 0;
+}
+
+/* This function is run inside a pthread. We do this so that we can possibly
+ * timeout.
+ */
+static void* dowait(void* data) {
+  dowait_args* args = (dowait_args*)data;
+
+  int i, r;
+  process_info_t* p;
+
+  for (i = 0; i < args->n; i++) {
+    p = (process_info_t*)(args->vec + i * sizeof(process_info_t));
+    if (p->terminated) continue;
+    r = waitpid(p->pid, &p->status, 0);
+    if (r < 0) {
+      perror("waitpid");
+      return NULL;
+    }
+    p->terminated = 1;
+  }
+
+  if (args->pipe[1] >= 0) {
+    /* Write a character to the main thread to notify it about this. */
+    ssize_t r;
+
+    do
+      r = write(args->pipe[1], "", 1);
+    while (r == -1 && errno == EINTR);
+  }
+
+  return NULL;
+}
+
+
+/* Wait for all `n` processes in `vec` to terminate. */
+/* Time out after `timeout` msec, or never if timeout == -1 */
+/* Return 0 if all processes are terminated, -1 on error, -2 on timeout. */
+int process_wait(process_info_t* vec, int n, int timeout) {
+  int i;
+  int r;
+  int retval;
+  process_info_t* p;
+  dowait_args args;
+  pthread_t tid;
+  struct timeval tv;
+  fd_set fds;
+
+  args.vec = vec;
+  args.n = n;
+  args.pipe[0] = -1;
+  args.pipe[1] = -1;
+
+  /* The simple case is where there is no timeout */
+  if (timeout == -1) {
+    dowait(&args);
+    return 0;
+  }
+
+  /* Hard case. Do the wait with a timeout.
+   *
+   * Assumption: we are the only ones making this call right now. Otherwise
+   * we'd need to lock vec.
+   */
+
+  r = pipe((int*)&(args.pipe));
+  if (r) {
+    perror("pipe()");
+    return -1;
+  }
+
+  r = pthread_create(&tid, NULL, dowait, &args);
+  if (r) {
+    perror("pthread_create()");
+    retval = -1;
+    goto terminate;
+  }
+
+  tv.tv_sec = timeout / 1000;
+  tv.tv_usec = 0;
+
+  FD_ZERO(&fds);
+  FD_SET(args.pipe[0], &fds);
+
+  r = select(args.pipe[0] + 1, &fds, NULL, NULL, &tv);
+
+  if (r == -1) {
+    perror("select()");
+    retval = -1;
+
+  } else if (r) {
+    /* The thread completed successfully. */
+    retval = 0;
+
+  } else {
+    /* Timeout. Kill all the children. */
+    for (i = 0; i < n; i++) {
+      p = (process_info_t*)(vec + i * sizeof(process_info_t));
+      kill(p->pid, SIGTERM);
+    }
+    retval = -2;
+
+    /* Wait for thread to finish. */
+    r = pthread_join(tid, NULL);
+    if (r) {
+      perror("pthread_join");
+      retval = -1;
+    }
+  }
+
+terminate:
+  close(args.pipe[0]);
+  close(args.pipe[1]);
+  return retval;
+}
+
+/* Return the exit code of process p. */
+/* On error, return -1. */
+int process_reap(process_info_t *p) {
+  if (WIFEXITED(p->status)) {
+    return WEXITSTATUS(p->status);
+  } else  {
+    return p->status; /* ? */
+  }
+}
+
+int process_read_last_line(process_info_t *p, char* buffer, size_t buffer_len) {
+  char* ptr;
+
+  int r = fseek(p->stdout_file, 0, SEEK_SET);
+  if (r < 0) {
+    perror("fseek");
+    return -1;
+  }
+
+  buffer[0] = '\0';
+
+  while (fgets(buffer, buffer_len, p->stdout_file) != NULL) {
+    for (ptr = buffer; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++);
+    *ptr = '\0';
+  }
+
+  if (ferror(p->stdout_file)) {
+    perror("read");
+    buffer[0] = '\0';
+    return -1;
+  }
+  return 0;
+}
+
+void process_cleanup(process_info_t *p) {
+  fclose(p->stdout_file);
+  free(p->name);
+}
+
+int run_test_one(task_entry_t* task) {
+  process_info_t processes;
+  char errmsg[1024] = "no error";
+  int result = 0;
+  int status;
+
+  status = 255;
+  if (process_start(task->task_name,
+                    task->process_name,
+                    &processes, 0) == -1) {
+    fprintf(stderr,
+            "Process `%s` failed to start.",
+            task->process_name);
+    return -1;
+  }
+
+  usleep(1000);
+
+  if (task->is_helper)
+    return 0;
+
+  result = process_wait(&processes, 1, task->timeout);
+  if (result == -1) {
+    TUV_FATAL("process_wait failed ");
+  } else if (result == -2) {
+    /* Don't have to clean up the process, process_wait() has killed it. */
+    fprintf(stderr, "timeout ");
+    goto out;
+  }
+  status = process_reap(&processes);
+  if (status != TEST_OK) {
+    fprintf(stderr, "exit code %d ", status);
+    result = -1;
+    goto out;
+  }
+
+out:
+  if (result == 0) {
+    result = process_read_last_line(&processes, errmsg, sizeof(errmsg));
+    if (strlen(errmsg))
+      fprintf(stderr, "lastmsg:[%s] ", errmsg);
+  }
+  process_cleanup(&processes);
+
+  return result;
+}
+
+
+int main(int argc, char *argv[]) {
+  int ret = -1;
+  TuvUseDebug usedebug;
+
+  platform_init(argc, argv);
+
+  switch (argc) {
+  case 1:
+    ret = run_tests();
+    break;
+  case 3:
+    ret = run_test_part(argv[1], argv[2]);
+    break;
+  default :
+    TUV_FATAL("System Error");
+    break;
+  }
+  return ret;
+}
+
+#else
+
+int ipc_helper(int listen_after_write);
+int ipc_helper_tcp_connection(void);
+// int ipc_send_recv_helper(void);
+int ipc_helper_bind_twice(void);
+// int stdio_over_pipes_helper(void);
+int spawn_stdin_stdout(void);
+
+static int maybe_run_test(int argc, char **argv);
+static pthread_t tid = 0;
+
+
+/* Do platform-specific initialization. */
+int platform_init(int argc, char **argv) {
+  return 0;
+}
+
+static void* helper_proc(void* data) {
+  task_entry_t* task = (task_entry_t*)data;
+  int ret;
+
+  sem_post(&task->semsync);
+  ret = task->main();
+  TUV_ASSERT(ret == 0);
+
+  sem_post(&task->semsync);
+  pthread_exit(0);
+  return NULL;
+}
+
+int run_helper(task_entry_t* task) {
+  int r;
+  sem_init(&task->semsync, 0, 0);
+  r = pthread_create(&tid, NULL, helper_proc, task);
+  sem_wait(&task->semsync);
+  return r;
+}
+
+int wait_helper(task_entry_t* task) {
+  sem_wait(&task->semsync);
+  pthread_join(tid, NULL);
+  sem_destroy(&task->semsync);
+  return 0;
+}
+
+int run_test_one(task_entry_t* task) {
+  return run_test_part(task->task_name, task->process_name);
+}
+
+
+int main(int argc, char **argv) {
+  if (platform_init(argc, argv))
+    return 1;
+
+  InitDebugSettings();
+
+  switch (argc) {
+  case 1: return run_tests();
+  case 2: return maybe_run_test(argc, argv);
+  case 3: return run_test_part(argv[1], argv[2]);
+  default:
+    ReleaseDebugSettings();
+
+    fprintf(stderr, "Too many arguments.\n");
+    fflush(stderr);
+    return 1;
+  }
+
+  ReleaseDebugSettings();
+
+  return 0;
+}
+
+static int maybe_run_test(int argc, char **argv) {
+#if TUV_FEATURE_PROCESS
+  if (strcmp(argv[1], "ipc_helper_listen_before_write") == 0) {
+    return ipc_helper(0);
+  }
+
+  if (strcmp(argv[1], "ipc_helper_listen_after_write") == 0) {
+    return ipc_helper(1);
+  }
+
+  // if (strcmp(argv[1], "ipc_send_recv_helper") == 0) {
+  //   return ipc_send_recv_helper();
+  // }
+
+  if (strcmp(argv[1], "ipc_helper_tcp_connection") == 0) {
+    return ipc_helper_tcp_connection();
+  }
+
+  if (strcmp(argv[1], "ipc_helper_bind_twice") == 0) {
+    return ipc_helper_bind_twice();
+  }
+
+  // if (strcmp(argv[1], "stdio_over_pipes_helper") == 0) {
+  //   return stdio_over_pipes_helper();
+  // }
+
+  if (strcmp(argv[1], "spawn_helper1") == 0) {
+    return 1;
+  }
+
+  if (strcmp(argv[1], "spawn_helper2") == 0) {
+    printf("hello world\n");
+    return 1;
+  }
+
+  if (strcmp(argv[1], "spawn_helper3") == 0) {
+    char buffer[256];
+    TUV_ASSERT(buffer == fgets(buffer, sizeof(buffer) - 1, stdin));
+    buffer[sizeof(buffer) - 1] = '\0';
+    fputs(buffer, stdout);
+    return 1;
+  }
+
+  if (strcmp(argv[1], "spawn_helper4") == 0) {
+    /* Never surrender, never return! */
+    while (1) uv_sleep(10000);
+  }
+
+  if (strcmp(argv[1], "spawn_helper5") == 0) {
+    const char out[] = "fourth stdio!\n";
+#ifdef _WIN32
+    DWORD bytes;
+    WriteFile((HANDLE) _get_osfhandle(3), out, sizeof(out) - 1, &bytes, NULL);
+#else
+    {
+      ssize_t r;
+
+      do
+        r = write(3, out, sizeof(out) - 1);
+      while (r == -1 && errno == EINTR);
+
+      fsync(3);
+    }
+#endif
+    return 1;
+  }
+
+  if (strcmp(argv[1], "spawn_helper6") == 0) {
+    int r;
+
+    r = fprintf(stdout, "hello world\n");
+    TUV_ASSERT(r > 0);
+
+    r = fprintf(stderr, "hello errworld\n");
+    TUV_ASSERT(r > 0);
+
+    return 1;
+  }
+
+  if (strcmp(argv[1], "spawn_helper7") == 0) {
+    int r;
+    char *test;
+    /* Test if the test value from the parent is still set */
+    test = getenv("ENV_TEST");
+    TUV_ASSERT(test != NULL);
+
+    r = fprintf(stdout, "%s", test);
+    TUV_ASSERT(r > 0);
+
+    return 1;
+  }
+
+#ifndef _WIN32
+  if (strcmp(argv[1], "spawn_helper8") == 0) {
+    int fd;
+    TUV_ASSERT(sizeof(fd) == read(0, &fd, sizeof(fd)));
+    TUV_ASSERT(fd > 2);
+    TUV_ASSERT(-1 == write(fd, "x", 1));
+
+    return 1;
+  }
+#endif  /* !_WIN32 */
+
+  if (strcmp(argv[1], "spawn_helper9") == 0) {
+    return spawn_stdin_stdout();
+  }
+
+#ifndef _WIN32
+  if (strcmp(argv[1], "spawn_helper_setuid_setgid") == 0) {
+    uv_uid_t uid = atoi(argv[2]);
+    uv_gid_t gid = atoi(argv[3]);
+
+    TUV_ASSERT(uid == getuid());
+    TUV_ASSERT(gid == getgid());
+
+    return 1;
+  }
+#endif  /* !_WIN32 */
+
+#endif /* TUV_FEATURE_PROCESS */
+  // return run_test(argv[1], 0, 1);
+  return 1;
+}
+
+#endif
diff --git a/test/runner_linux_raw.c b/test/runner_linux_raw.c
new file mode 100644 (file)
index 0000000..5994673
--- /dev/null
@@ -0,0 +1,120 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+/*
+ * About this file.
+ * "runner_linux_raw.c" is used as to create only for host executable,
+ * for unit test running inside target boards which can have limited
+ * capabilites.
+ * Executable is (and should be) generated only for host system, and
+ * the file is placed at same place as tuvtester program with
+ * the name 'tuvhelper'.
+ *
+ * current usage: helper for echo server for mbed
+ *    mbed cannot run echo server and at the same time a client connecting
+ *    to the echo server.
+ */
+
+
+void run_sleep(int msec) {
+  usleep(msec * 1000);
+}
+
+
+static pthread_t tid = 0;
+
+int platform_init(int argc, char **argv) {
+  return 0;
+}
+
+static void* helper_proc(void* data) {
+  task_entry_t* task = (task_entry_t*)data;
+  int ret;
+
+  sem_post(&task->semsync);
+  ret = task->main();
+  TUV_ASSERT(ret == 0);
+
+  sem_post(&task->semsync);
+  pthread_exit(0);
+  return NULL;
+}
+
+int run_helper(task_entry_t* task) {
+  int r;
+  sem_init(&task->semsync, 0, 0);
+  r = pthread_create(&tid, NULL, helper_proc, task);
+  sem_wait(&task->semsync);
+  return r;
+}
+
+int wait_helper(task_entry_t* task) {
+  sem_wait(&task->semsync);
+  pthread_join(tid, NULL);
+  sem_destroy(&task->semsync);
+  return 0;
+}
+
+int run_test_one(task_entry_t* task) {
+  return run_test_part(task->task_name, task->process_name);
+}
+
+int main(int argc, char *argv[]) {
+  InitDebugSettings();
+  platform_init(argc, argv);
+
+  task_entry_t* task;
+  task = get_helper("tcp4_echo_server");
+  if (task) {
+    TDDDLOG("Running network helper for target boards.");
+    run_helper(task);
+    TDDDLOG("Waiting for Test client and quit command...");
+    wait_helper(task);
+  }
+  ReleaseDebugSettings();
+  return 0;
+}
diff --git a/test/runner_list.h b/test/runner_list.h
new file mode 100644 (file)
index 0000000..1b2e06e
--- /dev/null
@@ -0,0 +1,204 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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.
+ */
+
+
+#if !defined(__TUV_RAW__)
+#define TEST_LIST_ALL(TE)                                                     \
+  TE(idle_basic, 5000)                                                        \
+  TE(active, 5000)                                                            \
+  TE(timer_init, 5000)                                                        \
+  TE(timer, 5000)                                                             \
+  TE(timer_start_twice, 5000)                                                 \
+  TE(timer_order, 5000)                                                       \
+  TE(timer_run_once, 5000)                                                    \
+  TE(timer_null_callback, 5000)                                               \
+  TE(timer_again, 5000)                                                       \
+  TE(timer_huge_timeout, 5000)                                                \
+  TE(timer_huge_repeat, 5000)                                                 \
+  \
+  TE(async, 5000)                                                             \
+  TE(condvar_2, 5000)                                                         \
+  TE(condvar_3, 5000)                                                         \
+  TE(cwd, 5000)                                                               \
+  \
+  TE(fs_file_noent, 5000)                                                     \
+  TE(fs_file_sync, 5000)                                                      \
+  TE(fs_file_async, 5000)                                                     \
+  TE(fs_file_write_null_buffer, 5000)                                         \
+  TE(fs_stat_missing_path, 5000)                                              \
+  TE(fs_scandir_empty_dir, 5000)                                              \
+  TE(fs_scandir_file, 5000)                                                   \
+  TE(fs_open_dir, 5000)                                                       \
+  TE(fs_file_open_append, 5000)                                               \
+  TE(fs_read_file_eof, 5000)                                                  \
+  TE(fs_async_dir, 5000)                                                      \
+  \
+  TE(error_message, 5000)                                                     \
+  \
+  TE(threadpool_queue_work_simple, 5000)                                      \
+  TE(walk_handles, 5000)                                                      \
+  \
+  TE(tcp_open,5000)                                                           \
+  TE(shutdown_eof,5000)                                                       \
+
+#else
+#define TEST_LIST_ALL(TE)                                                     \
+  TE(idle_basic, 5000)                                                        \
+  TE(active, 5000)                                                            \
+  TE(timer_init, 5000)                                                        \
+  TE(timer, 5000)                                                             \
+  TE(timer_start_twice, 5000)                                                 \
+  TE(timer_order, 5000)                                                       \
+  TE(timer_run_once, 5000)                                                    \
+  TE(timer_null_callback, 5000)                                               \
+  TE(timer_again, 5000)                                                       \
+  TE(timer_huge_timeout, 5000)                                                \
+  TE(timer_huge_repeat, 5000)                                                 \
+  \
+  TE(async, 5000)                                                             \
+  TE(condvar_1, 5000)                                                         \
+  \
+  TE(threadpool_queue_work_simple, 5000)                                      \
+  TE(walk_handles, 5000)                                                      \
+  TE(tcp_open,5000)                                                           \
+  TE(shutdown_eof,5000)                                                       \
+
+
+#endif
+
+// shutdown_eof should be last of tcp test, it'll stop "echo_sevrer"
+
+#if defined(__linux__) && defined(TUV_FEATURE_PIPE)
+#define TEST_LIST_EXT_PIPE(TE)                                                \
+  TE(pipe_bind_error_addrinuse, 5000)                                         \
+  TE(pipe_bind_error_addrnotavail, 5000)                                      \
+  TE(pipe_bind_error_inval, 5000)                                             \
+  TE(pipe_listen_without_bind, 5000)                                          \
+/*TE(pipe_close_stdout_read_stdin, 5000)*/                                    \
+  TE(pipe_connect_bad_name, 5000)                                             \
+/*TE(pipe_connect_to_file, 5000)*/                                            \
+/*TE(pipe_connect_multiple, 5000)*/                                           \
+/*TE(pipe_connect_on_prepare, 5000)*/                                         \
+  TE(pipe_getsockname, 5000)                                                  \
+  TE(pipe_getsockname_abstract, 5000)                                         \
+  TE(pipe_getsockname_blocking, 5000)                                         \
+  TE(pipe_pending_instances, 5000)                                            \
+  TE(pipe_sendmsg, 5000)                                                      \
+  TE(pipe_server_close, 5000)                                                 \
+  TE(pipe_set_non_blocking, 5000)
+#else
+#define TEST_LIST_EXT_PIPE(TE)
+#endif
+
+#if defined(__linux__) && defined(TUV_FEATURE_SIGNAL)
+#define TEST_LIST_EXT_SIGNAL(TE)                                              \
+  TE(we_get_signal, 5000)                                                     \
+  TE(we_get_signals, 5000)
+#else
+#define TEST_LIST_EXT_SIGNAL(TE)
+#endif
+
+#if defined(__linux__) && defined(TUV_FEATURE_PROCESS)
+#define TEST_LIST_EXT_PROCESS(TE)                                             \
+/*TE(ipc_listen_before_write, 5000)*/                                         \
+/*TE(ipc_listen_after_write, 5000)*/                                          \
+/*TE(ipc_tcp_connection, 5000)*/                                              \
+/*TE(spawn_fails_check_for_waitpid_cleanup, 5000) */                          \
+/*TE(spawn_exit_code, 5000) */                                                \
+  TE(spawn_stdout, 5000)                                                      \
+/*TE(spawn_stdout_to_file, 5000) */                                           \
+/*TE(spawn_stdout_and_stderr_to_file, 5000) */                                \
+/*TE(spawn_stdout_and_stderr_to_file2, 5000) */                               \
+/*TE(spawn_stdout_and_stderr_to_file_swap, 5000) */                           \
+/*TE(spawn_stdin, 5000) */                                                    \
+/*TE(spawn_stdio_greater_than_3, 5000) */                                     \
+/*TE(spawn_ignored_stdio, 5000) */                                            \
+/*TE(spawn_and_kill, 5000) */                                                 \
+/*TE(spawn_preserve_env, 5000) */                                             \
+/*TE(spawn_detached, 5000) */                                                 \
+/*TE(spawn_and_kill_with_std, 5000) */                                        \
+/*TE(spawn_and_ping, 5000) */                                                 \
+/*TE(spawn_same_stdout_stderr, 5000) */                                       \
+/*TE(spawn_closed_process_io, 5000) */                                        \
+/*TE(spawn_fails, 5000) */                                                    \
+/*TE(kill, 5000) */                                                           \
+/*TE(spawn_setuid_fails, 5000) */                                             \
+/*TE(spawn_setgid_fails, 5000) */                                             \
+/*TE(spawn_setuid_fails, 5000) */                                             \
+/*TE(spawn_setgid_fails, 5000) */                                             \
+/*TE(spawn_auto_unref, 5000) */                                               \
+/*TE(spawn_fs_open, 5000) */                                                  \
+/*TE(closed_fd_events, 5000) */                                               \
+/*TE(spawn_reads_child_path, 5000) */                                         \
+/*TE(spawn_inherit_streams, 5000) */
+#else
+#define TEST_LIST_EXT_PROCESS(TE)
+#endif
+
+#if defined(__linux__)
+#define TEST_LIST_EXT(TE)                                                     \
+  TE(condvar_1, 5000)                                                         \
+  TE(condvar_4, 5000)                                                         \
+  \
+  TE(fs_file_nametoolong, 5000)                                               \
+  TE(fs_fstat, 5000)                                                          \
+  TE(fs_utime, 5000)                                                          \
+  TE(fs_futime, 5000)                                                         \
+  \
+  TE(getaddrinfo_basic, 5000)                                                 \
+  TE(getaddrinfo_basic_sync, 5000)                                            \
+  TE(getaddrinfo_concurrent, 5000)                                            \
+  \
+  TEST_LIST_EXT_PIPE(TE) \
+  TEST_LIST_EXT_PROCESS(TE) \
+  TEST_LIST_EXT_SIGNAL(TE)
+
+#elif defined(__TUV_RAW__)
+#define TEST_LIST_EXT(TE)                                                     \
+
+#else
+#define TEST_LIST_EXT(TE)                                                     \
+
+#endif
+
+#if !defined(__TUV_RAW__)
+#define HELPER_LIST_ALL(TE)                                                   \
+  TE(tcp4_echo_server, tcp4_echo_server)                                      \
+
+#else
+#define HELPER_LIST_ALL(TE)                                                   \
+
+#endif
diff --git a/test/runner_main.c b/test/runner_main.c
new file mode 100644 (file)
index 0000000..b0292f0
--- /dev/null
@@ -0,0 +1,147 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <unistd.h>
+#include <uv.h>
+
+#include "runner.h"
+
+
+//-----------------------------------------------------------------------------
+// test function list
+
+#define TEST_ENTRY(name, timeout) \
+  { #name, #name, &run_test_##name, 0, 0, timeout },
+
+task_entry_t TASKS[] = {
+
+  TEST_LIST_ALL(TEST_ENTRY)
+  TEST_LIST_EXT(TEST_ENTRY)
+
+  { NULL, NULL, NULL, 0, 0, 0 }
+};
+
+#undef TEST_ENTRY
+
+//-----------------------------------------------------------------------------
+// test function list
+
+#define HELPER_ENTRY(task_name, name)               \
+    { #task_name, #name, &run_helper_##name, 1, 0, 0 },
+
+task_entry_t HELPERS[] = {
+
+  HELPER_LIST_ALL(HELPER_ENTRY)
+
+  { NULL, NULL, NULL, 0, 0, 0 }
+};
+
+#undef HELPER_ENTRY
+
+//-----------------------------------------------------------------------------
+
+int run_test_part(const char* test, const char* part) {
+  int r;
+  task_entry_t* task;
+
+  for (task = TASKS; task->main; task++) {
+    if (strcmp(test, task->task_name) == 0 &&
+        strcmp(part, task->process_name) == 0) {
+      r = task->main();
+      return r;
+    }
+  }
+
+  fprintf(stderr, "No test part with that name: %s:%s\n", test, part);
+  return 255;
+}
+
+
+task_entry_t* get_helper(const char* test) {
+  int r;
+  task_entry_t* task;
+
+  for (task = HELPERS; task->main; task++) {
+    if (strcmp(test, task->task_name) == 0) {
+      return task;
+    }
+  }
+  return NULL;
+}
+
+
+int run_tests() {
+  int entry;
+  int result;
+  task_entry_t* task;
+
+  fprintf(stderr, "Run Helpers...\r\n");
+  entry = 0;
+  while (HELPERS[entry].task_name) {
+    task = &HELPERS[entry];
+
+    fprintf(stderr, "[%-30s]...", task->task_name);
+    result = run_helper(task);
+    fprintf(stderr, "%s\r\n", result ? "failed" : "OK");
+    entry++;
+  }
+
+  entry = 0;
+  while (TASKS[entry].task_name) {
+    task = &TASKS[entry];
+
+    fprintf(stderr, "[%-30s]...", task->task_name);
+    result = run_test_one(task);
+    fprintf(stderr, "%s\r\n", result ? "failed" : "OK");
+    entry++;
+  }
+
+  fprintf(stderr, "Waiting Helpers to end...\r\n");
+  entry = 0;
+  while (HELPERS[entry].task_name) {
+    task = &HELPERS[entry];
+
+    fprintf(stderr, "[%-30s]...", task->task_name);
+    result = wait_helper(task);
+    fprintf(stderr, "%s\r\n", result ? "failed" : "OK");
+    entry++;
+  }
+
+  fprintf(stderr, "run_tests end\r\n");
+
+  return 0;
+}
diff --git a/test/runner_nuttx.c b/test/runner_nuttx.c
new file mode 100644 (file)
index 0000000..ee6eddc
--- /dev/null
@@ -0,0 +1,128 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+/* Do platform-specific initialization. */
+int platform_init(int argc, char **argv) {
+  return 0;
+}
+
+void run_sleep(int msec) {
+  usleep(msec * 1000);
+}
+
+//-----------------------------------------------------------------------------
+
+#define HELPER_PRIORITY     100
+#define HELPER_STACKSIZE    2048
+
+static pid_t pid_helper;
+static sem_t startsem;
+
+
+static int helper_procedure(int argc, char *argv[]) {
+  task_entry_t* helper;
+  int ret;
+  helper = get_helper(argv[1]);
+  TUV_ASSERT(helper);
+
+  sem_post(&helper->semsync);
+  ret = helper->main();
+  TUV_ASSERT(ret == 0);
+
+  sem_post(&helper->semsync);
+
+  return ret;
+}
+
+int run_helper(task_entry_t* task) {
+
+  const char* argv[3] = {
+    task->task_name,
+    task->process_name,
+    0
+  };
+
+  sem_init(&task->semsync, 0, 0);
+
+  pid_helper = task_create(task->process_name,
+                           HELPER_PRIORITY, HELPER_STACKSIZE,
+                           helper_procedure,
+                           (char * const *)argv);
+
+  sem_wait(&task->semsync);
+
+  usleep(1000);
+
+  return 0;
+}
+
+int wait_helper(task_entry_t* task) {
+  sem_wait(&task->semsync);
+  task_delete(pid_helper);
+  sem_destroy(&task->semsync);
+  return 0;
+}
+
+
+int run_test_one(task_entry_t* task) {
+  return run_test_part(task->task_name, task->process_name);
+}
+
+
+//
+// this is called from nuttx apps system tuvtester
+//
+int tuvtester_entry(int argc, char *argv[]) {
+  fprintf(stderr, ">> tuvtester_entry call ok\n");
+  fflush(stderr);
+  platform_init(argc, argv);
+  if (argc>2) {
+    return run_test_part(argv[1], argv[2]);
+  }
+  return run_tests();
+}
diff --git a/test/runner_tizenrt.c b/test/runner_tizenrt.c
new file mode 100644 (file)
index 0000000..5611231
--- /dev/null
@@ -0,0 +1,128 @@
+/* Copyright 2017-present Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+/* Do platform-specific initialization. */
+int platform_init(int argc, char **argv) {
+  return 0;
+}
+
+void run_sleep(int msec) {
+  usleep(msec * 1000);
+}
+
+//-----------------------------------------------------------------------------
+
+#define HELPER_PRIORITY     100
+#define HELPER_STACKSIZE    2048
+
+static pid_t pid_helper;
+static sem_t startsem;
+
+
+static int helper_procedure(int argc, char *argv[]) {
+  task_entry_t* helper;
+  int ret;
+  helper = get_helper(argv[1]);
+  TUV_ASSERT(helper);
+
+  sem_post(&helper->semsync);
+  ret = helper->main();
+  TUV_ASSERT(ret == 0);
+
+  sem_post(&helper->semsync);
+
+  return ret;
+}
+
+int run_helper(task_entry_t* task) {
+
+  const char* argv[3] = {
+    task->task_name,
+    task->process_name,
+    0
+  };
+
+  sem_init(&task->semsync, 0, 0);
+
+  pid_helper = task_create(task->process_name,
+                           HELPER_PRIORITY, HELPER_STACKSIZE,
+                           helper_procedure,
+                           (char * const *)argv);
+
+  sem_wait(&task->semsync);
+
+  usleep(1000);
+
+  return 0;
+}
+
+int wait_helper(task_entry_t* task) {
+  sem_wait(&task->semsync);
+  task_delete(pid_helper);
+  sem_destroy(&task->semsync);
+  return 0;
+}
+
+
+int run_test_one(task_entry_t* task) {
+  return run_test_part(task->task_name, task->process_name);
+}
+
+
+//
+// this is called from nuttx apps system tuvtester
+//
+int tuvtester_entry(int argc, char *argv[]) {
+  fprintf(stderr, ">> tuvtester_entry call ok\n");
+  fflush(stderr);
+  platform_init(argc, argv);
+  if (argc>2) {
+    return run_test_part(argv[1], argv[2]);
+  }
+  return run_tests();
+}
diff --git a/test/test_active.c b/test/test_active.c
new file mode 100644 (file)
index 0000000..3f6a739
--- /dev/null
@@ -0,0 +1,103 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+static int close_cb_called = 0;
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  close_cb_called++;
+}
+
+
+static void timer_cb(uv_timer_t* handle) {
+  TUV_ASSERT(0 && "timer_cb should not have been called");
+}
+
+
+TEST_IMPL(active) {
+  int r;
+  uv_timer_t timer;
+
+  close_cb_called = 0;
+
+  r = uv_timer_init(uv_default_loop(), &timer);
+  TUV_ASSERT(r == 0);
+
+  /* uv_is_active() and uv_is_closing() should always return either 0 or 1. */
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &timer));
+
+  r = uv_timer_start(&timer, timer_cb, 1000, 0);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(1 == uv_is_active((uv_handle_t*) &timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &timer));
+
+  r = uv_timer_stop(&timer);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &timer));
+
+  r = uv_timer_start(&timer, timer_cb, 1000, 0);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(1 == uv_is_active((uv_handle_t*) &timer));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &timer));
+
+  uv_close((uv_handle_t*) &timer, close_cb);
+
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &timer));
+  TUV_ASSERT(1 == uv_is_closing((uv_handle_t*) &timer));
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(close_cb_called == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
diff --git a/test/test_async.c b/test/test_async.c
new file mode 100644 (file)
index 0000000..6eba056
--- /dev/null
@@ -0,0 +1,144 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+static uv_thread_t _thread;
+static uv_mutex_t _mutex;
+
+static uv_timer_t _timer;
+static uv_async_t _async;
+
+static int _async_cb_called;
+static int _timer_cb_called;
+static int _close_cb_called;
+
+
+static void thread_cb(void *arg) {
+  int n;
+  int r;
+
+  for (;;) {
+    uv_mutex_lock(&_mutex);
+    n = _async_cb_called;
+    uv_mutex_unlock(&_mutex);
+
+    if (n == 3) {
+      break;
+    }
+
+    r = uv_async_send(&_async);
+    TUV_ASSERT(r == 0);
+
+    uv_sleep(1);
+  }
+}
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  _close_cb_called++;
+}
+
+
+static void async_cb(uv_async_t* handle) {
+  int n;
+
+  TUV_ASSERT(handle == &_async);
+
+  uv_mutex_lock(&_mutex);
+  n = ++_async_cb_called;
+  uv_mutex_unlock(&_mutex);
+
+  if (n == 3) {
+    uv_close((uv_handle_t*)&_async, close_cb);
+  }
+}
+
+
+static void timer_cb(uv_timer_t* timer) {
+  int r;
+
+  TUV_ASSERT(timer == &_timer);
+  _timer_cb_called++;
+
+  r = uv_thread_create(&_thread, thread_cb, NULL);
+  TUV_ASSERT(r == 0);
+  uv_mutex_unlock(&_mutex);
+
+  uv_close((uv_handle_t*)timer, NULL);
+}
+
+
+TEST_IMPL(async) {
+  int r;
+  uv_loop_t* loop = uv_default_loop();
+
+  _async_cb_called = 0;
+  _timer_cb_called = 0;
+  _close_cb_called = 0;
+
+  r = uv_mutex_init(&_mutex);
+  TUV_ASSERT(r == 0);
+  uv_mutex_lock(&_mutex);
+
+  r = uv_async_init(loop, &_async, async_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_init(loop, &_timer);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&_timer, timer_cb, 250, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(_async_cb_called == 3);
+  TUV_ASSERT(_close_cb_called == 1);
+  TUV_ASSERT(_timer_cb_called == 1);
+
+  TUV_ASSERT(0 == uv_thread_join(&_thread));
+
+  uv_mutex_destroy(&_mutex);
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  return 0;
+}
diff --git a/test/test_condvar.c b/test/test_condvar.c
new file mode 100644 (file)
index 0000000..af612c0
--- /dev/null
@@ -0,0 +1,190 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <errno.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+typedef struct {
+  uv_mutex_t mutex;
+  uv_cond_t cond;
+  int delay;
+  int use_broadcast;
+  volatile int posted;
+} worker_config;
+
+
+static void worker(void* arg) {
+  worker_config* c = (worker_config*)arg;
+
+  if (c->delay)
+    run_sleep(c->delay);
+
+  uv_mutex_lock(&c->mutex);
+  TUV_ASSERT(c->posted == 0);
+  c->posted = 1;
+  if (c->use_broadcast)
+    uv_cond_broadcast(&c->cond);
+  else
+    uv_cond_signal(&c->cond);
+  uv_mutex_unlock(&c->mutex);
+}
+
+
+TEST_IMPL(condvar_1) {
+  uv_thread_t thread;
+  worker_config wc;
+
+  memset(&wc, 0, sizeof(wc));
+
+  TUV_ASSERT(0 == uv_cond_init(&wc.cond));
+  TUV_ASSERT(0 == uv_mutex_init(&wc.mutex));
+  TUV_ASSERT(0 == uv_thread_create(&thread, worker, &wc));
+
+  uv_mutex_lock(&wc.mutex);
+  run_sleep(100);
+  uv_cond_wait(&wc.cond, &wc.mutex);
+  TUV_ASSERT(wc.posted == 1);
+  uv_mutex_unlock(&wc.mutex);
+
+  TUV_ASSERT(0 == uv_thread_join(&thread));
+  uv_mutex_destroy(&wc.mutex);
+  uv_cond_destroy(&wc.cond);
+
+  return 0;
+}
+
+
+TEST_IMPL(condvar_2) {
+  uv_thread_t thread;
+  worker_config wc;
+
+  memset(&wc, 0, sizeof(wc));
+  wc.delay = 100;
+
+  TUV_ASSERT(0 == uv_cond_init(&wc.cond));
+  TUV_ASSERT(0 == uv_mutex_init(&wc.mutex));
+  TUV_ASSERT(0 == uv_thread_create(&thread, worker, &wc));
+
+  uv_mutex_lock(&wc.mutex);
+  uv_cond_wait(&wc.cond, &wc.mutex);
+  uv_mutex_unlock(&wc.mutex);
+
+  TUV_ASSERT(0 == uv_thread_join(&thread));
+  uv_mutex_destroy(&wc.mutex);
+  uv_cond_destroy(&wc.cond);
+
+  return 0;
+}
+
+
+TEST_IMPL(condvar_3) {
+  uv_thread_t thread;
+  worker_config wc;
+  int r;
+
+  memset(&wc, 0, sizeof(wc));
+  wc.delay = 100;
+
+  TUV_ASSERT(0 == uv_cond_init(&wc.cond));
+  TUV_ASSERT(0 == uv_mutex_init(&wc.mutex));
+  TUV_ASSERT(0 == uv_thread_create(&thread, worker, &wc));
+
+  uv_mutex_lock(&wc.mutex);
+  r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(50 * 1e6));
+  TUV_ASSERT(r == UV_ETIMEDOUT);
+  uv_mutex_unlock(&wc.mutex);
+
+  TUV_ASSERT(0 == uv_thread_join(&thread));
+  uv_mutex_destroy(&wc.mutex);
+  uv_cond_destroy(&wc.cond);
+
+  return 0;
+}
+
+
+TEST_IMPL(condvar_4) {
+  uv_thread_t thread;
+  worker_config wc;
+  int r;
+
+  memset(&wc, 0, sizeof(wc));
+  wc.delay = 100;
+
+  TUV_ASSERT(0 == uv_cond_init(&wc.cond));
+  TUV_ASSERT(0 == uv_mutex_init(&wc.mutex));
+  TUV_ASSERT(0 == uv_thread_create(&thread, worker, &wc));
+
+  uv_mutex_lock(&wc.mutex);
+  r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(250 * 1e6));
+  TUV_ASSERT(r == 0);
+  uv_mutex_unlock(&wc.mutex);
+
+  TUV_ASSERT(0 == uv_thread_join(&thread));
+  uv_mutex_destroy(&wc.mutex);
+  uv_cond_destroy(&wc.cond);
+
+  return 0;
+}
+
+
+TEST_IMPL(condvar_5) {
+  uv_thread_t thread;
+  worker_config wc;
+
+  memset(&wc, 0, sizeof(wc));
+  wc.use_broadcast = 1;
+  wc.delay = 100;
+
+  TUV_ASSERT(0 == uv_cond_init(&wc.cond));
+  TUV_ASSERT(0 == uv_mutex_init(&wc.mutex));
+  TUV_ASSERT(0 == uv_thread_create(&thread, worker, &wc));
+
+  uv_mutex_lock(&wc.mutex);
+  run_sleep(100);
+  uv_cond_wait(&wc.cond, &wc.mutex);
+  TUV_ASSERT(wc.posted == 1);
+  uv_mutex_unlock(&wc.mutex);
+
+  TUV_ASSERT(0 == uv_thread_join(&thread));
+  uv_mutex_destroy(&wc.mutex);
+  uv_cond_destroy(&wc.cond);
+
+  return 0;
+}
diff --git a/test/test_cwd.c b/test/test_cwd.c
new file mode 100644 (file)
index 0000000..5d25248
--- /dev/null
@@ -0,0 +1,37 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <uv.h>
+#include "runner.h"
+
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+# define DIR_BUFF_LENGTH 64
+#else
+# define DIR_BUFF_LENGTH 512
+#endif
+
+static char buffer[DIR_BUFF_LENGTH];
+
+TEST_IMPL(cwd) {
+  size_t size;
+  int err;
+
+  size = sizeof(buffer);
+  err = uv_cwd(buffer, &size);
+  TUV_ASSERT(err == 0);
+  TDDDLOG("uv_cwd: %s\n", buffer);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/test/test_error.c b/test/test_error.c
new file mode 100644 (file)
index 0000000..d73a68e
--- /dev/null
@@ -0,0 +1,67 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <uv.h>
+#include "runner.h"
+
+
+/*
+ * Synthetic errors (errors that originate from within libuv, not the system)
+ * should produce sensible error messages when run through uv_strerror().
+ *
+ * See https://github.com/joyent/libuv/issues/210
+ */
+TEST_IMPL(error_message) {
+  /* Cop out. Can't do proper checks on systems with
+   * i18n-ized error messages...
+   */
+  /*
+  if (strcmp(uv_strerror(0), "Success") != 0) {
+    printf("i18n error messages detected, skipping test.\n");
+    return 0;
+  }
+  */
+
+  TUV_ASSERT(strstr(uv_strerror(UV_EINVAL), "Success") == NULL);
+  TUV_ASSERT(strcmp(uv_strerror(1337), "Unknown error") == 0);
+  TUV_ASSERT(strcmp(uv_strerror(-1337), "Unknown error") == 0);
+
+  return 0;
+}
diff --git a/test/test_fs.c b/test/test_fs.c
new file mode 100644 (file)
index 0000000..4cdf9d6
--- /dev/null
@@ -0,0 +1,1444 @@
+/* Copyright 2015-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h> /* unlink, rmdir, etc. */
+#include <dirent.h> /* scandir */
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+//-----------------------------------------------------------------------------
+
+#if defined(__linux__) || defined(__APPLE__)
+static const char* filename1 = "test_file";
+static const char* filename2 = "test_file2";
+#elif defined(__NUTTX__) || defined(__TIZENRT__)
+static const char* filename1 = "/mnt/sdcard/test_file";
+static const char* filename2 = "/mnt/sdcard/test_file2";
+#endif
+
+
+typedef struct {
+  const char* path;
+  double atime;
+  double mtime;
+} utime_check_t;
+
+
+static int dummy_cb_count;
+static int close_cb_count;
+static int create_cb_count;
+static int open_cb_count;
+static int read_cb_count;
+static int write_cb_count;
+static int unlink_cb_count;
+static int mkdir_cb_count;
+static int mkdtemp_cb_count;
+static int rmdir_cb_count;
+static int scandir_cb_count;
+static int stat_cb_count;
+static int rename_cb_count;
+static int fsync_cb_count;
+static int fdatasync_cb_count;
+static int ftruncate_cb_count;
+static int sendfile_cb_count;
+static int fstat_cb_count;
+static int access_cb_count;
+static int chmod_cb_count;
+static int fchmod_cb_count;
+static int chown_cb_count;
+static int fchown_cb_count;
+static int link_cb_count;
+static int symlink_cb_count;
+static int readlink_cb_count;
+static int utime_cb_count;
+static int futime_cb_count;
+
+static uv_loop_t* loop;
+
+static uv_fs_t open_req1;
+static uv_fs_t open_req2;
+static uv_fs_t read_req;
+static uv_fs_t write_req;
+static uv_fs_t unlink_req;
+static uv_fs_t close_req;
+static uv_fs_t mkdir_req;
+static uv_fs_t mkdtemp_req1;
+static uv_fs_t mkdtemp_req2;
+static uv_fs_t rmdir_req;
+static uv_fs_t scandir_req;
+static uv_fs_t stat_req;
+static uv_fs_t rename_req;
+static uv_fs_t fsync_req;
+static uv_fs_t fdatasync_req;
+static uv_fs_t ftruncate_req;
+static uv_fs_t sendfile_req;
+static uv_fs_t utime_req;
+static uv_fs_t futime_req;
+
+static char buf[32];
+static char buf2[32];
+static char test_buf[] = "test-buffer\n";
+static char test_buf2[] = "second-buffer\n";
+static uv_buf_t iov;
+
+static void check_permission(const char* filename, unsigned int mode) {
+  int r;
+  uv_fs_t req;
+  uv_stat_t* s;
+
+  r = uv_fs_stat(uv_default_loop(), &req, filename, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+
+  s = &req.statbuf;
+  TUV_ASSERT((s->st_mode & 0777) == mode);
+
+  uv_fs_req_cleanup(&req);
+}
+
+
+static void dummy_cb(uv_fs_t* req) {
+  (void) req;
+  dummy_cb_count++;
+}
+
+
+static void link_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_LINK);
+  TUV_ASSERT(req->result == 0);
+  link_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+
+static void symlink_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_SYMLINK);
+  TUV_ASSERT(req->result == 0);
+  symlink_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+static void readlink_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_READLINK);
+  TUV_ASSERT(req->result == 0);
+  TUV_ASSERT(strcmp((const char*)req->ptr, "test_file_symlink2") == 0);
+  readlink_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+
+static void access_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_ACCESS);
+  access_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+
+static void fchmod_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_FCHMOD);
+  TUV_ASSERT(req->result == 0);
+  fchmod_cb_count++;
+  uv_fs_req_cleanup(req);
+  check_permission(filename1, *(int*)req->data);
+}
+
+
+static void chmod_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_CHMOD);
+  TUV_ASSERT(req->result == 0);
+  chmod_cb_count++;
+  uv_fs_req_cleanup(req);
+  check_permission(filename1, *(int*)req->data);
+}
+
+
+static void fchown_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_FCHOWN);
+  TUV_ASSERT(req->result == 0);
+  fchown_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+
+static void chown_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_CHOWN);
+  TUV_ASSERT(req->result == 0);
+  chown_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+static void chown_root_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_CHOWN);
+#if defined(_WIN32) || defined(__NUTTX__) || defined(__TIZENRT__)
+  /* On windows, chown is a no-op and always succeeds. */
+  TUV_ASSERT(req->result == 0);
+#else
+  /* On unix, chown'ing the root directory is not allowed -
+   * unless you're root, of course.
+   */
+  if (geteuid() == 0)
+    TUV_ASSERT(req->result == 0);
+  else
+    TUV_ASSERT(req->result == UV_EPERM);
+#endif
+  chown_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+static void unlink_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &unlink_req);
+  TUV_ASSERT(req->fs_type == UV_FS_UNLINK);
+  TUV_ASSERT(req->result == 0);
+  unlink_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+static void fstat_cb(uv_fs_t* req) {
+  uv_stat_t* s = (uv_stat_t*)req->ptr;
+  TUV_ASSERT(req->fs_type == UV_FS_FSTAT);
+  TUV_ASSERT(req->result == 0);
+  TUV_ASSERT(s->st_size == sizeof(test_buf));
+  uv_fs_req_cleanup(req);
+  fstat_cb_count++;
+}
+
+
+static void close_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &close_req);
+  TUV_ASSERT(req->fs_type == UV_FS_CLOSE);
+  TUV_ASSERT(req->result == 0);
+  close_cb_count++;
+  uv_fs_req_cleanup(req);
+  if (close_cb_count == 3) {
+    r = uv_fs_unlink(loop, &unlink_req, filename2, unlink_cb);
+    TUV_ASSERT(r == 0);
+  }
+}
+
+
+static void ftruncate_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &ftruncate_req);
+  TUV_ASSERT(req->fs_type == UV_FS_FTRUNCATE);
+  TUV_ASSERT(req->result == 0);
+  ftruncate_cb_count++;
+  uv_fs_req_cleanup(req);
+  r = uv_fs_close(loop, &close_req, open_req1.result, close_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void read_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &read_req);
+  TUV_ASSERT(req->fs_type == UV_FS_READ);
+  TUV_ASSERT(req->result >= 0);  /* FIXME(bnoordhuis) Check if requested size? */
+  read_cb_count++;
+  uv_fs_req_cleanup(req);
+  if (read_cb_count == 1) {
+    TUV_ASSERT(strcmp(buf, test_buf) == 0);
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+    r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7,
+                        ftruncate_cb);
+#else
+    /* cannot truncate on nuttx so just close the file */
+    r = uv_fs_close(loop, &close_req, open_req1.result, close_cb);
+#endif
+  } else {
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+    TUV_ASSERT(strcmp(buf, "test-bu") == 0);
+#else
+    TUV_ASSERT(strcmp(buf, test_buf) == 0);
+#endif
+    r = uv_fs_close(loop, &close_req, open_req1.result, close_cb);
+  }
+  TUV_ASSERT(r == 0);
+}
+
+
+static void open_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &open_req1);
+  TUV_ASSERT(req->fs_type == UV_FS_OPEN);
+  if (req->result < 0) {
+    TDLOG("async open error: %d\n", (int)req->result);
+    TUV_ASSERT(0);
+  }
+  open_cb_count++;
+
+  TUV_ASSERT(req->path);
+  TUV_ASSERT(memcmp(req->path, filename2, strlen(filename2)) == 0);
+  uv_fs_req_cleanup(req);
+  memset(buf, 0, sizeof(buf));
+  iov = uv_buf_init(buf, sizeof(buf));
+  r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, read_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void open_cb_simple(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_OPEN);
+  if (req->result < 0) {
+    TDLOG("async open error: %d\n", (int)req->result);
+    TUV_ASSERT(0);
+  }
+  open_cb_count++;
+
+  TUV_ASSERT(req->path);
+  uv_fs_req_cleanup(req);
+}
+
+
+static void fsync_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &fsync_req);
+  TUV_ASSERT(req->fs_type == UV_FS_FSYNC);
+  TUV_ASSERT(req->result == 0);
+  fsync_cb_count++;
+
+  uv_fs_req_cleanup(req);
+  r = uv_fs_close(loop, &close_req, open_req1.result, close_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void fdatasync_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &fdatasync_req);
+  TUV_ASSERT(req->fs_type == UV_FS_FDATASYNC);
+  TUV_ASSERT(req->result == 0);
+  fdatasync_cb_count++;
+
+  uv_fs_req_cleanup(req);
+  r = uv_fs_fsync(loop, &fsync_req, open_req1.result, fsync_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void write_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &write_req);
+  TUV_ASSERT(req->fs_type == UV_FS_WRITE);
+  TUV_ASSERT(req->result >= 0);  /* FIXME(bnoordhuis) Check if requested size? */
+  write_cb_count++;
+
+  uv_fs_req_cleanup(req);
+  r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void create_cb(uv_fs_t* req) {
+  int r;
+  TUV_ASSERT(req == &open_req1);
+  TUV_ASSERT(req->fs_type == UV_FS_OPEN);
+  TUV_ASSERT(req->result >= 0);
+  create_cb_count++;
+  uv_fs_req_cleanup(req);
+  iov = uv_buf_init(test_buf, sizeof(test_buf));
+
+  r = uv_fs_write(loop, &write_req, req->result, &iov, 1, -1, write_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void rename_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &rename_req);
+  TUV_ASSERT(req->fs_type == UV_FS_RENAME);
+  TUV_ASSERT(req->result == 0);
+  rename_cb_count++;
+
+  uv_fs_req_cleanup(req);
+}
+
+
+static void mkdir_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &mkdir_req);
+  TUV_ASSERT(req->fs_type == UV_FS_MKDIR);
+  TUV_ASSERT(req->result == 0);
+  mkdir_cb_count++;
+
+  TUV_ASSERT(req->path);
+  TUV_ASSERT(memcmp(req->path, "test_dir\0", 9) == 0);
+  uv_fs_req_cleanup(req);
+}
+
+
+static void check_mkdtemp_result(uv_fs_t* req) {
+  int r;
+
+  TUV_ASSERT(req->fs_type == UV_FS_MKDTEMP);
+  TUV_ASSERT(req->result == 0);
+  TUV_ASSERT(req->path);
+  TUV_ASSERT(strlen(req->path) == 15);
+  TUV_ASSERT(memcmp(req->path, "test_dir_", 9) == 0);
+  TUV_ASSERT(memcmp(req->path + 9, "XXXXXX", 6) != 0);
+  check_permission(req->path, 0700);
+
+  /* Check if req->path is actually a directory */
+  r = uv_fs_stat(uv_default_loop(), &stat_req, req->path, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(((uv_stat_t*)stat_req.ptr)->st_mode & S_IFDIR);
+  uv_fs_req_cleanup(&stat_req);
+}
+
+
+static void mkdtemp_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &mkdtemp_req1);
+  check_mkdtemp_result(req);
+  mkdtemp_cb_count++;
+}
+
+
+static void rmdir_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &rmdir_req);
+  TUV_ASSERT(req->fs_type == UV_FS_RMDIR);
+  TUV_ASSERT(req->result == 0);
+  rmdir_cb_count++;
+  TUV_ASSERT(req->path);
+  TUV_ASSERT(memcmp(req->path, "test_dir\0", 9) == 0);
+  uv_fs_req_cleanup(req);
+}
+
+static void assert_is_file_type(uv_dirent_t dent) {
+#ifdef HAVE_DIRENT_TYPES
+  /*
+   * For Apple and Windows, we know getdents is expected to work but for other
+   * environments, the filesystem dictates whether or not getdents supports
+   * returning the file type.
+   *
+   *   See:
+   *     http://man7.org/linux/man-pages/man2/getdents.2.html
+   *     https://github.com/libuv/libuv/issues/501
+   */
+  #if defined(__APPLE__) || defined(_WIN32)
+    TUV_ASSERT(dent.type == UV_DIRENT_FILE);
+  #else
+    TUV_ASSERT(dent.type == UV_DIRENT_FILE || dent.type == UV_DIRENT_UNKNOWN);
+  #endif
+#else
+  TUV_ASSERT(dent.type == UV_DIRENT_UNKNOWN);
+#endif
+}
+
+static void scandir_cb(uv_fs_t* req) {
+  uv_dirent_t dent;
+  TUV_ASSERT(req == &scandir_req);
+  TUV_ASSERT(req->fs_type == UV_FS_SCANDIR);
+  TUV_ASSERT(req->result == 2);
+  TUV_ASSERT(req->ptr);
+
+  while (UV_EOF != uv_fs_scandir_next(req, &dent)) {
+    TUV_ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0);
+    TUV_ASSERT(dent.type == UV_DIRENT_FILE || dent.type == UV_DIRENT_UNKNOWN);
+  }
+  scandir_cb_count++;
+  TUV_ASSERT(req->path);
+  TUV_ASSERT(memcmp(req->path, "test_dir\0", 9) == 0);
+  uv_fs_req_cleanup(req);
+  TUV_ASSERT(!req->ptr);
+}
+
+
+static void empty_scandir_cb(uv_fs_t* req) {
+  uv_dirent_t dent;
+
+  TUV_ASSERT(req == &scandir_req);
+  TUV_ASSERT(req->fs_type == UV_FS_SCANDIR);
+  TUV_ASSERT(req->result == 0);
+  TUV_ASSERT(req->ptr == NULL);
+  TUV_ASSERT(UV_EOF == uv_fs_scandir_next(req, &dent));
+  uv_fs_req_cleanup(req);
+  scandir_cb_count++;
+}
+
+
+static void file_scandir_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &scandir_req);
+  TUV_ASSERT(req->fs_type == UV_FS_SCANDIR);
+  TUV_ASSERT(req->result == UV_ENOTDIR);
+  TUV_ASSERT(req->ptr == NULL);
+  uv_fs_req_cleanup(req);
+  scandir_cb_count++;
+}
+
+
+static void stat_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &stat_req);
+  TUV_ASSERT(req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT);
+  TUV_ASSERT(req->result == 0);
+  TUV_ASSERT(req->ptr);
+  stat_cb_count++;
+  uv_fs_req_cleanup(req);
+  TUV_ASSERT(!req->ptr);
+}
+
+
+static void sendfile_cb(uv_fs_t* req) {
+  TUV_ASSERT(req == &sendfile_req);
+  TUV_ASSERT(req->fs_type == UV_FS_SENDFILE);
+  TUV_ASSERT(req->result == 65546);
+  sendfile_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+
+static void open_noent_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_OPEN);
+  TUV_ASSERT(req->result == UV_ENOENT);
+  open_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+static void open_nametoolong_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_OPEN);
+  TUV_ASSERT(req->result == UV_ENAMETOOLONG);
+  open_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+static void open_loop_cb(uv_fs_t* req) {
+  TUV_ASSERT(req->fs_type == UV_FS_OPEN);
+  TUV_ASSERT(req->result == UV_ELOOP);
+  open_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+
+static void check_utime(const char* path, double atime, double mtime) {
+  uv_stat_t* s;
+  uv_fs_t req;
+  int r;
+
+  r = uv_fs_stat(loop, &req, path, NULL);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(req.result == 0);
+  s = &req.statbuf;
+
+  TUV_ASSERT(s->st_atim.tv_sec == atime);
+  TUV_ASSERT(s->st_mtim.tv_sec == mtime);
+
+  uv_fs_req_cleanup(&req);
+}
+
+
+static void utime_cb(uv_fs_t* req) {
+  utime_check_t* c;
+
+  TUV_ASSERT(req == &utime_req);
+  TUV_ASSERT(req->result == 0);
+  TUV_ASSERT(req->fs_type == UV_FS_UTIME);
+
+  c = (utime_check_t*)req->data;
+  check_utime(c->path, c->atime, c->mtime);
+
+  uv_fs_req_cleanup(req);
+  utime_cb_count++;
+}
+
+
+static void futime_cb(uv_fs_t* req) {
+  utime_check_t* c;
+
+  TUV_ASSERT(req == &futime_req);
+  TUV_ASSERT(req->result == 0);
+  TUV_ASSERT(req->fs_type == UV_FS_FUTIME);
+
+  c = (utime_check_t*)req->data;
+  check_utime(c->path, c->atime, c->mtime);
+
+  uv_fs_req_cleanup(req);
+  futime_cb_count++;
+}
+
+
+TEST_IMPL(fs_file_noent) {
+  uv_fs_t req;
+  int r;
+
+  open_cb_count = 0;
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(NULL, &req, "/does_not_exist", O_RDONLY, 0, NULL);
+  TUV_ASSERT(r == UV_ENOENT);
+  TUV_ASSERT(req.result == UV_ENOENT);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_open(loop, &req, "/does_not_exist", O_RDONLY, 0, open_noent_cb);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(open_cb_count == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(open_cb_count == 1);
+
+  /* TODO add EACCES test */
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  return 0;
+}
+
+
+#if defined(__linux__) || defined(__APPLE__)
+#define TOO_LONG_NAME_LENGTH 65536
+#endif
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+#define TOO_LONG_NAME_LENGTH (256+16)
+#endif
+
+static char name[TOO_LONG_NAME_LENGTH + 1];
+
+
+TEST_IMPL(fs_file_nametoolong) {
+  uv_fs_t req;
+  int r;
+
+  open_cb_count = 0;
+
+  loop = uv_default_loop();
+
+  memset(name, 'a', TOO_LONG_NAME_LENGTH);
+  name[TOO_LONG_NAME_LENGTH] = 0;
+
+  r = uv_fs_open(loop, &req, name, O_RDONLY, 0, NULL);
+  TUV_ASSERT(r == UV_ENAMETOOLONG);
+  TUV_ASSERT(req.result == UV_ENAMETOOLONG);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_open(loop, &req, name, O_RDONLY, 0, open_nametoolong_cb);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(open_cb_count == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(open_cb_count == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  return 0;
+}
+
+/*
+TEST_IMPL(fs_file_loop) {
+  uv_fs_t req;
+  int r;
+
+  loop = uv_default_loop();
+
+  unlink("test_symlink");
+  r = uv_fs_symlink(loop, &req, "test_symlink", "test_symlink", 0, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_open(loop, &req, "test_symlink", O_RDONLY, 0, NULL);
+  TUV_ASSERT(r == UV_ELOOP);
+  TUV_ASSERT(req.result == UV_ELOOP);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_open(loop, &req, "test_symlink", O_RDONLY, 0, open_loop_cb);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(open_cb_count == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(open_cb_count == 1);
+
+  unlink("test_symlink");
+
+  return 0;
+}
+*/
+
+
+TEST_IMPL(fs_file_async) {
+  int r;
+
+  /* Setup. */
+  unlink(filename1);
+  unlink(filename2);
+
+  open_cb_count = 0;
+  read_cb_count = 0;
+  close_cb_count = 0;
+  rename_cb_count = 0;
+  create_cb_count = 0;
+  write_cb_count = 0;
+  fsync_cb_count = 0;
+  fdatasync_cb_count = 0;
+  unlink_cb_count = 0;
+
+  memset(&open_req1, 0, sizeof(open_req1));
+  memset(&rename_req, 0, sizeof(rename_req));
+
+  loop = uv_default_loop();
+
+  //---------------------------------------------------------------------------
+  r = uv_fs_open(loop, &open_req1, filename1, O_WRONLY | O_CREAT,
+                 S_IRUSR | S_IWUSR, create_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(create_cb_count == 1);
+  TUV_ASSERT(write_cb_count == 1);
+  TUV_ASSERT(fsync_cb_count == 1);
+  TUV_ASSERT(fdatasync_cb_count == 1);
+  TUV_ASSERT(close_cb_count == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  //---------------------------------------------------------------------------
+  r = uv_loop_init(loop);
+  TUV_ASSERT(r == 0);
+
+  r = uv_fs_rename(loop, &rename_req, filename1, filename2, rename_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(create_cb_count == 1);
+  TUV_ASSERT(write_cb_count == 1);
+  TUV_ASSERT(close_cb_count == 1);
+  TUV_ASSERT(rename_cb_count == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  //---------------------------------------------------------------------------
+  r = uv_loop_init(loop);
+  TUV_ASSERT(r == 0);
+  r = uv_fs_open(loop, &open_req1, filename2, O_RDWR, 0, open_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(open_cb_count == 1);
+  TUV_ASSERT(read_cb_count == 1);
+  TUV_ASSERT(close_cb_count == 2);
+  TUV_ASSERT(rename_cb_count == 1);
+  TUV_ASSERT(create_cb_count == 1);
+  TUV_ASSERT(write_cb_count == 1);
+  //TUV_ASSERT(ftruncate_cb_count == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  //---------------------------------------------------------------------------
+  r = uv_loop_init(loop);
+  TUV_ASSERT(r == 0);
+  r = uv_fs_open(loop, &open_req1, filename2, O_RDONLY, 0, open_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(open_cb_count == 2);
+  TUV_ASSERT(read_cb_count == 2);
+  TUV_ASSERT(close_cb_count == 3);
+  TUV_ASSERT(rename_cb_count == 1);
+  TUV_ASSERT(unlink_cb_count == 1);
+  TUV_ASSERT(create_cb_count == 1);
+  TUV_ASSERT(write_cb_count == 1);
+  //TUV_ASSERT(ftruncate_cb_count == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  //---------------------------------------------------------------------------
+  /* Cleanup. */
+  unlink(filename1);
+  unlink(filename2);
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_file_sync) {
+  int r;
+
+  /* Setup. */
+  unlink(filename1);
+  unlink(filename2);
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_WRONLY | O_CREAT,
+      S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  iov = uv_buf_init(test_buf, sizeof(test_buf));
+  r = uv_fs_write(loop, &write_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(write_req.result >= 0);
+  uv_fs_req_cleanup(&write_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_RDWR, 0, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  iov = uv_buf_init(buf, sizeof(buf));
+  r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(read_req.result >= 0);
+  TUV_ASSERT(strcmp(buf, test_buf) == 0);
+  uv_fs_req_cleanup(&read_req);
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(ftruncate_req.result == 0);
+  uv_fs_req_cleanup(&ftruncate_req);
+#endif
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_rename(loop, &rename_req, filename1, filename2, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(rename_req.result == 0);
+  uv_fs_req_cleanup(&rename_req);
+
+  r = uv_fs_open(loop, &open_req1, filename2, O_RDONLY, 0, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  memset(buf, 0, sizeof(buf));
+  iov = uv_buf_init(buf, sizeof(buf));
+  r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(read_req.result >= 0);
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+  TUV_ASSERT(strcmp(buf, "test-bu") == 0);
+#else
+  TUV_ASSERT(strcmp(buf, test_buf) == 0);
+#endif
+  uv_fs_req_cleanup(&read_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_unlink(loop, &unlink_req, filename2, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(unlink_req.result == 0);
+  uv_fs_req_cleanup(&unlink_req);
+
+  /* Cleanup */
+  unlink(filename1);
+  unlink(filename2);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  return 0;
+}
+
+TEST_IMPL(fs_file_write_null_buffer) {
+  int r;
+
+  /* Setup. */
+  unlink(filename1);
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_WRONLY | O_CREAT,
+                  S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  iov = uv_buf_init(NULL, 0);
+  r = uv_fs_write(loop, &write_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(write_req.result == 0);
+  uv_fs_req_cleanup(&write_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+  unlink(filename1);
+
+  return 0;
+}
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+TEST_IMPL(fs_fstat) {
+  int r;
+  uv_fs_t req;
+  uv_file file;
+  uv_stat_t* s;
+  struct stat t;
+
+  /* Setup. */
+  unlink(filename1);
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &req, filename1, O_RDWR | O_CREAT,
+                 S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(req.result >= 0);
+  file = req.result;
+  uv_fs_req_cleanup(&req);
+
+  iov = uv_buf_init(test_buf, sizeof(test_buf));
+  r = uv_fs_write(loop, &req, file, &iov, 1, -1, NULL);
+  TUV_ASSERT(r == sizeof(test_buf));
+  TUV_ASSERT(req.result == sizeof(test_buf));
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_fstat(loop, &req, file, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+  s = (uv_stat_t*)req.ptr;
+  TUV_ASSERT(s->st_size == sizeof(test_buf));
+
+  r = fstat(file, &t);
+  TUV_ASSERT(r == 0);
+
+#if defined(__linux__)
+  TUV_ASSERT(s->st_dev == (uint64_t) t.st_dev);
+  TUV_ASSERT(s->st_nlink == (uint64_t) t.st_nlink);
+  TUV_ASSERT(s->st_uid == (uint64_t) t.st_uid);
+  TUV_ASSERT(s->st_gid == (uint64_t) t.st_gid);
+  TUV_ASSERT(s->st_rdev == (uint64_t) t.st_rdev);
+  TUV_ASSERT(s->st_ino == (uint64_t) t.st_ino);
+#endif
+  TUV_ASSERT(s->st_mode == (uint64_t) t.st_mode);
+  TUV_ASSERT(s->st_size == (uint64_t) t.st_size);
+  TUV_ASSERT(s->st_blksize == (uint64_t) t.st_blksize);
+  TUV_ASSERT(s->st_blocks == (uint64_t) t.st_blocks);
+
+#if defined (__linux__)
+  TUV_ASSERT(s->st_atim.tv_sec == t.st_atim.tv_sec);
+  TUV_ASSERT(s->st_atim.tv_nsec == t.st_atim.tv_nsec);
+  TUV_ASSERT(s->st_mtim.tv_sec == t.st_mtim.tv_sec);
+  TUV_ASSERT(s->st_mtim.tv_nsec == t.st_mtim.tv_nsec);
+  TUV_ASSERT(s->st_ctim.tv_sec == t.st_ctim.tv_sec);
+  TUV_ASSERT(s->st_ctim.tv_nsec == t.st_ctim.tv_nsec);
+#else
+  TUV_ASSERT(s->st_atim.tv_sec == t.st_atime);
+  TUV_ASSERT(s->st_atim.tv_nsec == 0);
+  TUV_ASSERT(s->st_mtim.tv_sec == t.st_mtime);
+  TUV_ASSERT(s->st_mtim.tv_nsec == 0);
+  TUV_ASSERT(s->st_ctim.tv_sec == t.st_ctime);
+  TUV_ASSERT(s->st_ctim.tv_nsec == 0);
+#endif
+
+  uv_fs_req_cleanup(&req);
+
+  /* Now do the uv_fs_fstat call asynchronously */
+  r = uv_fs_fstat(loop, &req, file, fstat_cb);
+  TUV_ASSERT(r == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(fstat_cb_count == 1);
+
+  r = uv_fs_close(loop, &req, file, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+  uv_fs_req_cleanup(&req);
+
+  /*
+   * Run the loop just to check we don't have make any extraneous uv_ref()
+   * calls. This should drop out immediately.
+   */
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  /* Cleanup. */
+  unlink(filename1);
+
+  return 0;
+}
+#endif // __NUTTX__
+
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+TEST_IMPL(fs_utime) {
+  utime_check_t checkme;
+  const char* path = filename1;
+  double atime;
+  double mtime;
+  uv_fs_t req;
+  int r;
+
+  /* Setup. */
+  loop = uv_default_loop();
+  unlink(path);
+  r = uv_fs_open(loop, &req, path, O_RDWR | O_CREAT,
+                 S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(req.result >= 0);
+  uv_fs_req_cleanup(&req);
+  close(r);
+
+  atime = mtime = 400497753; /* 1982-09-10 11:22:33 */
+  r = uv_fs_utime(loop, &req, path, atime, mtime, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_stat(loop, &req, path, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+  check_utime(path, atime, mtime);
+  uv_fs_req_cleanup(&req);
+
+
+  atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */
+  checkme.path = path;
+  checkme.atime = atime;
+  checkme.mtime = mtime;
+
+  /* async utime */
+  utime_req.data = &checkme;
+  r = uv_fs_utime(loop, &utime_req, path, atime, mtime, utime_cb);
+  TUV_ASSERT(r == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(utime_cb_count == 1);
+
+  /* Cleanup. */
+  TUV_ASSERT(0 == uv_loop_close(loop));
+  unlink(path);
+
+  return 0;
+}
+#endif
+
+#if !defined(__NUTTX__) && !defined(__TIZENRT__)
+TEST_IMPL(fs_futime) {
+  utime_check_t checkme;
+  const char* path = filename1;
+  double atime;
+  double mtime;
+  uv_file file;
+  uv_fs_t req;
+  int r;
+
+  /* Setup. */
+  loop = uv_default_loop();
+  unlink(path);
+  r = uv_fs_open(loop, &req, path, O_RDWR | O_CREAT,
+      S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(req.result >= 0);
+  uv_fs_req_cleanup(&req);
+  close(r);
+
+  atime = mtime = 400497753; /* 1982-09-10 11:22:33 */
+
+  r = uv_fs_open(loop, &req, path, O_RDWR, 0, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(req.result >= 0);
+  file = req.result; /* FIXME probably not how it's supposed to be used */
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_futime(loop, &req, file, atime, mtime, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_stat(loop, &req, path, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+  check_utime(path, atime, mtime);
+  uv_fs_req_cleanup(&req);
+
+  atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */
+
+  checkme.atime = atime;
+  checkme.mtime = mtime;
+  checkme.path = path;
+
+  /* async futime */
+  futime_req.data = &checkme;
+  r = uv_fs_futime(loop, &futime_req, file, atime, mtime, futime_cb);
+  TUV_ASSERT(r == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(futime_cb_count == 1);
+
+  /* Cleanup. */
+  unlink(path);
+
+  return 0;
+}
+#endif
+
+
+TEST_IMPL(fs_stat_missing_path) {
+  uv_fs_t req;
+  int r;
+
+  loop = uv_default_loop();
+
+  r = uv_fs_stat(loop, &req, "non_existent_file", NULL);
+  TUV_ASSERT(r == UV_ENOENT);
+  TUV_ASSERT(req.result == UV_ENOENT);
+  uv_fs_req_cleanup(&req);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_scandir_empty_dir) {
+  const char* path;
+  uv_fs_t req;
+  uv_dirent_t dent;
+  int r;
+
+  path = "./empty_dir/";
+  loop = uv_default_loop();
+
+  uv_fs_mkdir(NULL, &req, path, 0777, NULL);
+  uv_fs_req_cleanup(&req);
+
+  /* Fill the req to ensure that required fields are cleaned up */
+  memset(&req, 0xdb, sizeof(req));
+
+  scandir_cb_count = 0;
+  r = uv_fs_scandir(NULL, &req, path, 0, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(req.result == 0);
+  TUV_ASSERT(req.ptr == NULL);
+  TUV_ASSERT(UV_EOF == uv_fs_scandir_next(&req, &dent));
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_scandir(loop, &scandir_req, path, 0, empty_scandir_cb);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(scandir_cb_count == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(scandir_cb_count == 1);
+
+  uv_fs_rmdir(NULL, &req, path, NULL);
+  uv_fs_req_cleanup(&req);
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_scandir_file) {
+  const char* path;
+  int r;
+
+  path = "test/resources/empty_file";
+  loop = uv_default_loop();
+
+  scandir_cb_count = 0;
+  r = uv_fs_scandir(NULL, &scandir_req, path, 0, NULL);
+  TUV_ASSERT(r == UV_ENOTDIR);
+  uv_fs_req_cleanup(&scandir_req);
+
+  r = uv_fs_scandir(loop, &scandir_req, path, 0, file_scandir_cb);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(scandir_cb_count == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(scandir_cb_count == 1);
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_open_dir) {
+  const char* path;
+  uv_fs_t req;
+  int r, file;
+
+  open_cb_count = 0;
+
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+  path = "/mnt/sdcard";
+#else
+  path = ".";
+#endif
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &req, path, O_RDONLY, 0, NULL);
+#if defined(__NUTTX__) || defined(__TIZENRT__)
+  TUV_ASSERT(r == -EISDIR);
+  uv_fs_req_cleanup(&req);
+#else
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(req.result >= 0);
+  TUV_ASSERT(req.ptr == NULL);
+  file = r;
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_close(loop, &req, file, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&req);
+#endif
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_file_open_append) {
+  int r;
+
+  /* Setup. */
+  unlink(filename1);
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_WRONLY | O_CREAT,
+                 S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  iov = uv_buf_init(test_buf, sizeof(test_buf));
+  r = uv_fs_write(loop, &write_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(write_req.result >= 0);
+  uv_fs_req_cleanup(&write_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_RDWR | O_APPEND, 0, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  iov = uv_buf_init(test_buf, sizeof(test_buf));
+  r = uv_fs_write(loop, &write_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(write_req.result >= 0);
+  uv_fs_req_cleanup(&write_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_RDONLY, S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  iov = uv_buf_init(buf, sizeof(buf));
+  r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r == 26);
+  TUV_ASSERT(read_req.result == 26);
+  TUV_ASSERT(memcmp(buf,
+                    "test-buffer\n\0test-buffer\n\0",
+                    sizeof("test-buffer\n\0test-buffer\n\0") - 1) == 0);
+  uv_fs_req_cleanup(&read_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  /* Cleanup */
+  TUV_ASSERT(0 == uv_loop_close(loop));
+  unlink(filename1);
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_read_file_eof) {
+  int r;
+
+  /* Setup. */
+  unlink(filename1);
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_WRONLY | O_CREAT,
+                 S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  iov = uv_buf_init(test_buf, sizeof(test_buf));
+  r = uv_fs_write(loop, &write_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(write_req.result >= 0);
+  uv_fs_req_cleanup(&write_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_open(loop, &open_req1, filename1, O_RDONLY, 0, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(open_req1.result >= 0);
+  uv_fs_req_cleanup(&open_req1);
+
+  memset(buf, 0, sizeof(buf));
+  iov = uv_buf_init(buf, sizeof(buf));
+  r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, NULL);
+  TUV_ASSERT(r >= 0);
+  TUV_ASSERT(read_req.result >= 0);
+  TUV_ASSERT(strcmp(buf, test_buf) == 0);
+  uv_fs_req_cleanup(&read_req);
+
+  iov = uv_buf_init(buf, sizeof(buf));
+  r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1,
+                 read_req.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(read_req.result == 0);
+  uv_fs_req_cleanup(&read_req);
+
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(close_req.result == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  /* Cleanup */
+  TUV_ASSERT(0 == uv_loop_close(loop));
+  unlink(filename1);
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_async_dir) {
+  int r;
+  uv_dirent_t dent;
+
+  /* Setup */
+  scandir_cb_count = 0;
+  unlink("test_dir/file1");
+  unlink("test_dir/file2");
+  rmdir("test_dir");
+
+  loop = uv_default_loop();
+
+  r = uv_fs_mkdir(loop, &mkdir_req, "test_dir", 0755, mkdir_cb);
+  TUV_ASSERT(r == 0);
+
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(mkdir_cb_count == 1);
+
+  /* Create 2 files synchronously. */
+  r = uv_fs_open(loop, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT,
+      S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  uv_fs_req_cleanup(&open_req1);
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_open(loop, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT,
+      S_IWUSR | S_IRUSR, NULL);
+  TUV_ASSERT(r >= 0);
+  uv_fs_req_cleanup(&open_req1);
+  r = uv_fs_close(loop, &close_req, open_req1.result, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&close_req);
+
+  r = uv_fs_scandir(loop, &scandir_req, "test_dir", 0, scandir_cb);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(scandir_cb_count == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(scandir_cb_count == 1);
+
+  /* sync uv_fs_scandir */
+  r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL);
+  TUV_ASSERT(r == 2);
+  TUV_ASSERT(scandir_req.result == 2);
+  TUV_ASSERT(scandir_req.ptr);
+  while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) {
+    TUV_ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0);
+    assert_is_file_type(dent);
+  }
+  uv_fs_req_cleanup(&scandir_req);
+  TUV_ASSERT(!scandir_req.ptr);
+
+  r = uv_fs_stat(loop, &stat_req, "test_dir", stat_cb);
+  TUV_ASSERT(r == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  r = uv_fs_stat(loop, &stat_req, "test_dir/", stat_cb);
+  TUV_ASSERT(r == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  TUV_ASSERT(stat_cb_count == 2);
+
+  r = uv_fs_unlink(loop, &unlink_req, "test_dir/file1", NULL);
+  TUV_ASSERT(r == 0);
+
+  r = uv_fs_unlink(loop, &unlink_req, "test_dir/file2", NULL);
+  TUV_ASSERT(r == 0);
+
+  r = uv_fs_rmdir(loop, &rmdir_req, "test_dir", rmdir_cb);
+  TUV_ASSERT(r == 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(rmdir_cb_count == 1);
+
+  /* Cleanup */
+  unlink("test_dir/file1");
+  unlink("test_dir/file2");
+  rmdir("test_dir");
+  return 0;
+}
diff --git a/test/test_getaddrinfo.c b/test/test_getaddrinfo.c
new file mode 100644 (file)
index 0000000..2175c01
--- /dev/null
@@ -0,0 +1,158 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+#define CONCURRENT_COUNT    10
+
+static const char* name = "localhost";
+
+static int getaddrinfo_cbs = 0;
+
+static uv_getaddrinfo_t* getaddrinfo_handle;
+static uv_getaddrinfo_t getaddrinfo_handles[CONCURRENT_COUNT];
+static int callback_counts[CONCURRENT_COUNT];
+
+
+static void getaddrinfo_basic_cb(uv_getaddrinfo_t* handle,
+                                 int status,
+                                 struct addrinfo* res) {
+  TUV_ASSERT(handle == getaddrinfo_handle);
+  getaddrinfo_cbs++;
+  free(handle);
+  uv_freeaddrinfo(res);
+}
+
+
+TEST_IMPL(getaddrinfo_basic) {
+  int r;
+
+  getaddrinfo_cbs = 0;
+
+  getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t));
+
+  r = uv_getaddrinfo(uv_default_loop(),
+                     getaddrinfo_handle,
+                     &getaddrinfo_basic_cb,
+                     name,
+                     NULL,
+                     NULL);
+  TUV_ASSERT(r == 0);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(getaddrinfo_cbs == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+TEST_IMPL(getaddrinfo_basic_sync) {
+  uv_getaddrinfo_t req;
+
+  TUV_ASSERT(0 == uv_getaddrinfo(uv_default_loop(),
+                                 &req,
+                                 NULL,
+                                 name,
+                                 NULL,
+                                 NULL));
+  uv_freeaddrinfo(req.addrinfo);
+
+  return 0;
+}
+
+
+
+static void getaddrinfo_cuncurrent_cb(uv_getaddrinfo_t* handle,
+                                      int status,
+                                      struct addrinfo* res) {
+  int i;
+  int* data = (int*)handle->data;
+
+  for (i = 0; i < CONCURRENT_COUNT; i++) {
+    if (&getaddrinfo_handles[i] == handle) {
+      TUV_ASSERT(i == *data);
+
+      callback_counts[i]++;
+      break;
+    }
+  }
+  TUV_ASSERT(i < CONCURRENT_COUNT);
+
+  free(data);
+  uv_freeaddrinfo(res);
+
+  getaddrinfo_cbs++;
+}
+
+
+TEST_IMPL(getaddrinfo_concurrent) {
+  int i, r;
+  int* data;
+
+  for (i = 0; i < CONCURRENT_COUNT; i++) {
+    callback_counts[i] = 0;
+
+    data = (int*)malloc(sizeof(int));
+    TUV_ASSERT(data != NULL);
+    *data = i;
+    getaddrinfo_handles[i].data = data;
+
+    r = uv_getaddrinfo(uv_default_loop(),
+                       &getaddrinfo_handles[i],
+                       &getaddrinfo_cuncurrent_cb,
+                       name,
+                       NULL,
+                       NULL);
+    TUV_ASSERT(r == 0);
+  }
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  for (i = 0; i < CONCURRENT_COUNT; i++) {
+    TUV_ASSERT(callback_counts[i] == 1);
+  }
+
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
diff --git a/test/test_idle.c b/test/test_idle.c
new file mode 100644 (file)
index 0000000..c9b7c54
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <uv.h>
+#include "runner.h"
+
+
+static uv_idle_t idle_handle;
+static int idle_cb_called = 0;
+
+void idle_callback(uv_idle_t* handle) {
+  TDDDLOG("idle_callback handle flag %p, %x, %d",
+          handle, handle->flags, idle_cb_called);
+  idle_cb_called++;
+}
+
+
+TEST_IMPL(idle_basic) {
+  uv_loop_t* loop;
+
+  idle_cb_called = 0;
+
+  loop = uv_default_loop();
+  uv_idle_init(loop, &idle_handle);
+  uv_idle_start(&idle_handle, idle_callback);
+  uv_run(loop, UV_RUN_ONCE);
+  uv_close((uv_handle_t*) &idle_handle, NULL);
+  uv_run(loop, UV_RUN_ONCE);
+
+  TUV_ASSERT(0 == uv_loop_close(loop));
+
+  TUV_WARN(idle_cb_called==1);
+
+  return 0;
+}
diff --git a/test/test_ipc.c b/test/test_ipc.c
new file mode 100644 (file)
index 0000000..4972f2b
--- /dev/null
@@ -0,0 +1,773 @@
+/* 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 "runner.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static uv_pipe_t channel;
+static uv_tcp_t tcp_server;
+static uv_tcp_t tcp_server2;
+static uv_tcp_t tcp_connection;
+
+static int exit_cb_called;
+static int read_cb_called;
+static int tcp_write_cb_called;
+static int tcp_read_cb_called;
+static int on_pipe_read_called;
+static int local_conn_accepted;
+static int remote_conn_accepted;
+static int tcp_server_listening;
+static uv_write_t write_req;
+static uv_write_t conn_notify_req;
+static int close_cb_called;
+static int connection_accepted;
+static int tcp_conn_read_cb_called;
+static int tcp_conn_write_cb_called;
+
+typedef struct {
+  uv_connect_t conn_req;
+  uv_write_t tcp_write_req;
+  uv_tcp_t conn;
+} tcp_conn;
+
+#define CONN_COUNT 100
+#define BACKLOG 128
+
+
+static void close_server_conn_cb(uv_handle_t* handle) {
+  free(handle);
+}
+
+
+static void on_connection(uv_stream_t* server, int status) {
+  uv_tcp_t* conn;
+  int r;
+
+  if (!local_conn_accepted) {
+    /* Accept the connection and close it.  Also and close the server. */
+    TUV_ASSERT(status == 0);
+    TUV_ASSERT((uv_stream_t*)&tcp_server == server);
+
+    conn = malloc(sizeof(*conn));
+    TUV_ASSERT(conn);
+    r = uv_tcp_init(server->loop, conn);
+    TUV_ASSERT(r == 0);
+
+    r = uv_accept(server, (uv_stream_t*)conn);
+    TUV_ASSERT(r == 0);
+
+    uv_close((uv_handle_t*)conn, close_server_conn_cb);
+    uv_close((uv_handle_t*)server, NULL);
+    local_conn_accepted = 1;
+  }
+}
+
+
+static void exit_cb(uv_process_t* process,
+                    int64_t exit_status,
+                    int term_signal) {
+  printf("exit_cb\n");
+  exit_cb_called++;
+  TUV_ASSERT(exit_status == 0);
+  uv_close((uv_handle_t*)process, NULL);
+}
+
+
+static void on_alloc(uv_handle_t* handle,
+                     size_t suggested_size,
+                     uv_buf_t* buf) {
+  buf->base = malloc(suggested_size);
+  buf->len = suggested_size;
+}
+
+
+static void close_client_conn_cb(uv_handle_t* handle) {
+  tcp_conn* p = (tcp_conn*)handle->data;
+  free(p);
+}
+
+
+static void connect_cb(uv_connect_t* req, int status) {
+  uv_close((uv_handle_t*)req->handle, close_client_conn_cb);
+}
+
+
+static void make_many_connections(void) {
+  tcp_conn* conn;
+  struct sockaddr_in addr;
+  int r, i;
+
+  for (i = 0; i < CONN_COUNT; i++) {
+    conn = malloc(sizeof(*conn));
+    TUV_ASSERT(conn);
+
+    r = uv_tcp_init(uv_default_loop(), &conn->conn);
+    TUV_ASSERT(r == 0);
+
+    TUV_ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
+
+    r = uv_tcp_connect(&conn->conn_req,
+                       (uv_tcp_t*) &conn->conn,
+                       (const struct sockaddr*) &addr,
+                       connect_cb);
+    TUV_ASSERT(r == 0);
+
+    conn->conn.data = conn;
+  }
+}
+
+
+static void on_read(uv_stream_t* handle,
+                    ssize_t nread,
+                    const uv_buf_t* buf) {
+  int r;
+  uv_pipe_t* pipe;
+  uv_handle_type pending;
+  uv_buf_t outbuf;
+
+  pipe = (uv_pipe_t*) handle;
+
+  if (nread == 0) {
+    /* Everything OK, but nothing read. */
+    free(buf->base);
+    return;
+  }
+
+  if (nread < 0) {
+    if (nread == UV_EOF) {
+      free(buf->base);
+      return;
+    }
+
+    printf("error recving on channel: %s\n", uv_strerror(nread));
+    abort();
+  }
+
+  fprintf(stderr, "got %d bytes\n", (int)nread);
+
+  pending = uv_pipe_pending_type(pipe);
+  if (!tcp_server_listening) {
+    TUV_ASSERT(1 == uv_pipe_pending_count(pipe));
+    TUV_ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE);
+    read_cb_called++;
+
+    /* Accept the pending TCP server, and start listening on it. */
+    TUV_ASSERT(pending == UV_TCP);
+    r = uv_tcp_init(uv_default_loop(), &tcp_server);
+    TUV_ASSERT(r == 0);
+
+    r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server);
+    TUV_ASSERT(r == 0);
+
+    r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection);
+    TUV_ASSERT(r == 0);
+
+    tcp_server_listening = 1;
+
+    /* Make sure that the expected data is correctly multiplexed. */
+    TUV_ASSERT(memcmp("hello\n", buf->base, nread) == 0);
+
+    outbuf = uv_buf_init("world\n", 6);
+    r = uv_write(&write_req, (uv_stream_t*)pipe, &outbuf, 1, NULL);
+    TUV_ASSERT(r == 0);
+
+    /* Create a bunch of connections to get both servers to accept. */
+    make_many_connections();
+  } else if (memcmp("accepted_connection\n", buf->base, nread) == 0) {
+    /* Remote server has accepted a connection.  Close the channel. */
+    TUV_ASSERT(0 == uv_pipe_pending_count(pipe));
+    TUV_ASSERT(pending == UV_UNKNOWN_HANDLE);
+    remote_conn_accepted = 1;
+    uv_close((uv_handle_t*)&channel, NULL);
+  }
+
+  free(buf->base);
+}
+
+#ifdef _WIN32
+static void on_read_listen_after_bound_twice(uv_stream_t* handle,
+                                             ssize_t nread,
+                                             const uv_buf_t* buf) {
+  int r;
+  uv_pipe_t* pipe;
+  uv_handle_type pending;
+
+  pipe = (uv_pipe_t*) handle;
+
+  if (nread == 0) {
+    /* Everything OK, but nothing read. */
+    free(buf->base);
+    return;
+  }
+
+  if (nread < 0) {
+    if (nread == UV_EOF) {
+      free(buf->base);
+      return;
+    }
+
+    printf("error recving on channel: %s\n", uv_strerror(nread));
+    abort();
+  }
+
+  fprintf(stderr, "got %d bytes\n", (int)nread);
+
+  TUV_ASSERT(uv_pipe_pending_count(pipe) > 0);
+  pending = uv_pipe_pending_type(pipe);
+  TUV_ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE);
+  read_cb_called++;
+
+  if (read_cb_called == 1) {
+    /* Accept the first TCP server, and start listening on it. */
+    TUV_ASSERT(pending == UV_TCP);
+    r = uv_tcp_init(uv_default_loop(), &tcp_server);
+    TUV_ASSERT(r == 0);
+
+    r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server);
+    TUV_ASSERT(r == 0);
+
+    r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection);
+    TUV_ASSERT(r == 0);
+  } else if (read_cb_called == 2) {
+    /* Accept the second TCP server, and start listening on it. */
+    TUV_ASSERT(pending == UV_TCP);
+    r = uv_tcp_init(uv_default_loop(), &tcp_server2);
+    TUV_ASSERT(r == 0);
+
+    r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server2);
+    TUV_ASSERT(r == 0);
+
+    r = uv_listen((uv_stream_t*)&tcp_server2, BACKLOG, on_connection);
+    TUV_ASSERT(r == UV_EADDRINUSE);
+
+    uv_close((uv_handle_t*)&tcp_server, NULL);
+    uv_close((uv_handle_t*)&tcp_server2, NULL);
+    TUV_ASSERT(0 == uv_pipe_pending_count(pipe));
+    uv_close((uv_handle_t*)&channel, NULL);
+  }
+
+  free(buf->base);
+}
+#endif
+
+void spawn_helper(uv_pipe_t* channel,
+                  uv_process_t* process,
+                  const char* helper) {
+  uv_process_options_t options;
+  size_t exepath_size;
+  char exepath[1024];
+  char* args[3];
+  int r;
+  uv_stdio_container_t stdio[1];
+
+  r = uv_pipe_init(uv_default_loop(), channel, 1);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(channel->ipc);
+
+  exepath_size = sizeof(exepath);
+  r = uv_exepath(exepath, &exepath_size);
+  TUV_ASSERT(r == 0);
+
+  exepath[exepath_size] = '\0';
+  args[0] = exepath;
+  args[1] = (char*)helper;
+  args[2] = NULL;
+
+  memset(&options, 0, sizeof(options));
+  options.file = exepath;
+  options.args = args;
+  options.exit_cb = exit_cb;
+
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_CREATE_PIPE |
+    UV_READABLE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[0].data.stream = (uv_stream_t*)channel;
+  options.stdio_count = 1;
+
+  r = uv_spawn(uv_default_loop(), process, &options);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void on_tcp_write(uv_write_t* req, int status) {
+  TUV_ASSERT(status == 0);
+  TUV_ASSERT(req->handle == (uv_stream_t*)&tcp_connection);
+  tcp_write_cb_called++;
+}
+
+
+static void on_read_alloc(uv_handle_t* handle,
+                          size_t suggested_size,
+                          uv_buf_t* buf) {
+  buf->base = malloc(suggested_size);
+  buf->len = suggested_size;
+}
+
+
+static void on_tcp_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) {
+  TUV_ASSERT(nread > 0);
+  TUV_ASSERT(memcmp("hello again\n", buf->base, nread) == 0);
+  TUV_ASSERT(tcp == (uv_stream_t*)&tcp_connection);
+  free(buf->base);
+
+  tcp_read_cb_called++;
+
+  uv_close((uv_handle_t*)tcp, NULL);
+  uv_close((uv_handle_t*)&channel, NULL);
+}
+
+
+static void on_read_connection(uv_stream_t* handle,
+                               ssize_t nread,
+                               const uv_buf_t* buf) {
+  int r;
+  uv_buf_t outbuf;
+  uv_pipe_t* pipe;
+  uv_handle_type pending;
+
+  pipe = (uv_pipe_t*) handle;
+  if (nread == 0) {
+    /* Everything OK, but nothing read. */
+    free(buf->base);
+    return;
+  }
+
+  if (nread < 0) {
+    if (nread == UV_EOF) {
+      free(buf->base);
+      return;
+    }
+
+    printf("error recving on channel: %s\n", uv_strerror(nread));
+    abort();
+  }
+
+  fprintf(stderr, "got %d bytes\n", (int)nread);
+
+  TUV_ASSERT(1 == uv_pipe_pending_count(pipe));
+  pending = uv_pipe_pending_type(pipe);
+
+  TUV_ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE);
+  read_cb_called++;
+
+  /* Accept the pending TCP connection */
+  TUV_ASSERT(pending == UV_TCP);
+  r = uv_tcp_init(uv_default_loop(), &tcp_connection);
+  TUV_ASSERT(r == 0);
+
+  r = uv_accept(handle, (uv_stream_t*)&tcp_connection);
+  TUV_ASSERT(r == 0);
+
+  /* Make sure that the expected data is correctly multiplexed. */
+  TUV_ASSERT(memcmp("hello\n", buf->base, nread) == 0);
+
+  /* Write/read to/from the connection */
+  outbuf = uv_buf_init("world\n", 6);
+  r = uv_write(&write_req, (uv_stream_t*)&tcp_connection, &outbuf, 1,
+    on_tcp_write);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*)&tcp_connection, on_read_alloc, on_tcp_read);
+  TUV_ASSERT(r == 0);
+
+  free(buf->base);
+}
+
+
+static int run_ipc_test(const char* helper, uv_read_cb read_cb) {
+  uv_process_t process;
+  int r;
+
+  spawn_helper(&channel, &process, helper);
+  uv_read_start((uv_stream_t*)&channel, on_alloc, read_cb);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(ipc_listen_before_write) {
+  int r = run_ipc_test("ipc_helper_listen_before_write", on_read);
+  TUV_ASSERT(local_conn_accepted == 1);
+  TUV_ASSERT(remote_conn_accepted == 1);
+  TUV_ASSERT(read_cb_called == 1);
+  TUV_ASSERT(exit_cb_called == 1);
+  return r;
+}
+
+
+TEST_IMPL(ipc_listen_after_write) {
+  int r = run_ipc_test("ipc_helper_listen_after_write", on_read);
+  TUV_ASSERT(local_conn_accepted == 1);
+  TUV_ASSERT(remote_conn_accepted == 1);
+  TUV_ASSERT(read_cb_called == 1);
+  TUV_ASSERT(exit_cb_called == 1);
+  return r;
+}
+
+
+TEST_IMPL(ipc_tcp_connection) {
+  int r = run_ipc_test("ipc_helper_tcp_connection", on_read_connection);
+  TUV_ASSERT(read_cb_called == 1);
+  TUV_ASSERT(tcp_write_cb_called == 1);
+  TUV_ASSERT(tcp_read_cb_called == 1);
+  TUV_ASSERT(exit_cb_called == 1);
+  return r;
+}
+
+
+#ifdef _WIN32
+TEST_IMPL(listen_with_simultaneous_accepts) {
+  uv_tcp_t server;
+  int r;
+  struct sockaddr_in addr;
+
+  TUV_ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
+
+  r = uv_tcp_init(uv_default_loop(), &server);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_simultaneous_accepts(&server, 1);
+  TUV_ASSERT(r == 0);
+
+  r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(server.reqs_pending == 32);
+
+  return 0;
+}
+
+
+TEST_IMPL(listen_no_simultaneous_accepts) {
+  uv_tcp_t server;
+  int r;
+  struct sockaddr_in addr;
+
+  TUV_ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
+
+  r = uv_tcp_init(uv_default_loop(), &server);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_simultaneous_accepts(&server, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(server.reqs_pending == 1);
+
+  return 0;
+}
+
+TEST_IMPL(ipc_listen_after_bind_twice) {
+  int r = run_ipc_test("ipc_helper_bind_twice", on_read_listen_after_bound_twice);
+  TUV_ASSERT(read_cb_called == 2);
+  TUV_ASSERT(exit_cb_called == 1);
+  return r;
+}
+#endif
+
+
+/* Everything here runs in a child process. */
+
+static tcp_conn conn;
+
+
+static void close_cb(uv_handle_t* handle) {
+  close_cb_called++;
+}
+
+
+static void conn_notify_write_cb(uv_write_t* req, int status) {
+  uv_close((uv_handle_t*)&tcp_server, close_cb);
+  uv_close((uv_handle_t*)&channel, close_cb);
+}
+
+
+static void tcp_connection_write_cb(uv_write_t* req, int status) {
+  TUV_ASSERT((uv_handle_t*)&conn.conn == (uv_handle_t*)req->handle);
+  uv_close((uv_handle_t*)req->handle, close_cb);
+  uv_close((uv_handle_t*)&channel, close_cb);
+  uv_close((uv_handle_t*)&tcp_server, close_cb);
+  tcp_conn_write_cb_called++;
+}
+
+
+static void on_tcp_child_process_read(uv_stream_t* tcp,
+                                      ssize_t nread,
+                                      const uv_buf_t* buf) {
+  uv_buf_t outbuf;
+  int r;
+
+  if (nread < 0) {
+    if (nread == UV_EOF) {
+      free(buf->base);
+      return;
+    }
+
+    printf("error recving on tcp connection: %s\n", uv_strerror(nread));
+    abort();
+  }
+
+  TUV_ASSERT(nread > 0);
+  TUV_ASSERT(memcmp("world\n", buf->base, nread) == 0);
+  on_pipe_read_called++;
+  free(buf->base);
+
+  /* Write to the socket */
+  outbuf = uv_buf_init("hello again\n", 12);
+  r = uv_write(&conn.tcp_write_req, tcp, &outbuf, 1, tcp_connection_write_cb);
+  TUV_ASSERT(r == 0);
+
+  tcp_conn_read_cb_called++;
+}
+
+
+static void connect_child_process_cb(uv_connect_t* req, int status) {
+  int r;
+
+  TUV_ASSERT(status == 0);
+  r = uv_read_start(req->handle, on_read_alloc, on_tcp_child_process_read);
+  TUV_ASSERT(r == 0);
+}
+
+
+static void ipc_on_connection(uv_stream_t* server, int status) {
+  int r;
+  uv_buf_t buf;
+
+  if (!connection_accepted) {
+    /*
+     * Accept the connection and close it.  Also let the other
+     * side know.
+     */
+    TUV_ASSERT(status == 0);
+    TUV_ASSERT((uv_stream_t*)&tcp_server == server);
+
+    r = uv_tcp_init(server->loop, &conn.conn);
+    TUV_ASSERT(r == 0);
+
+    r = uv_accept(server, (uv_stream_t*)&conn.conn);
+    TUV_ASSERT(r == 0);
+
+    uv_close((uv_handle_t*)&conn.conn, close_cb);
+
+    buf = uv_buf_init("accepted_connection\n", 20);
+    r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1,
+      NULL, conn_notify_write_cb);
+    TUV_ASSERT(r == 0);
+
+    connection_accepted = 1;
+  }
+}
+
+
+static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) {
+  int r;
+  uv_buf_t buf;
+  uv_tcp_t* conn;
+
+  TUV_ASSERT(status == 0);
+  TUV_ASSERT((uv_stream_t*)&tcp_server == server);
+
+  conn = malloc(sizeof(*conn));
+  TUV_ASSERT(conn);
+
+  r = uv_tcp_init(server->loop, conn);
+  TUV_ASSERT(r == 0);
+
+  r = uv_accept(server, (uv_stream_t*)conn);
+  TUV_ASSERT(r == 0);
+
+  /* Send the accepted connection to the other process */
+  buf = uv_buf_init("hello\n", 6);
+  r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1,
+    (uv_stream_t*)conn, NULL);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) conn,
+                    on_read_alloc,
+                    on_tcp_child_process_read);
+  TUV_ASSERT(r == 0);
+
+  uv_close((uv_handle_t*)conn, close_cb);
+}
+
+
+int ipc_helper(int listen_after_write) {
+  /*
+   * This is launched from test-ipc.c. stdin is a duplex channel that we
+   * over which a handle will be transmitted.
+   */
+  struct sockaddr_in addr;
+  uv_write_t write_req;
+  int r;
+  uv_buf_t buf;
+
+  TUV_ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
+
+  r = uv_pipe_init(uv_default_loop(), &channel, 1);
+  TUV_ASSERT(r == 0);
+
+  uv_pipe_open(&channel, 0);
+
+  TUV_ASSERT(1 == uv_is_readable((uv_stream_t*) &channel));
+  TUV_ASSERT(1 == uv_is_writable((uv_stream_t*) &channel));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &channel));
+
+  r = uv_tcp_init(uv_default_loop(), &tcp_server);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0);
+  TUV_ASSERT(r == 0);
+
+  if (!listen_after_write) {
+    r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection);
+    TUV_ASSERT(r == 0);
+  }
+
+  buf = uv_buf_init("hello\n", 6);
+  r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1,
+      (uv_stream_t*)&tcp_server, NULL);
+  TUV_ASSERT(r == 0);
+
+  if (listen_after_write) {
+    r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection);
+    TUV_ASSERT(r == 0);
+  }
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(connection_accepted == 1);
+  TUV_ASSERT(close_cb_called == 3);
+
+  return 0;
+}
+
+
+int ipc_helper_tcp_connection(void) {
+  /*
+   * This is launched from test-ipc.c. stdin is a duplex channel
+   * over which a handle will be transmitted.
+   */
+
+  int r;
+  struct sockaddr_in addr;
+
+  r = uv_pipe_init(uv_default_loop(), &channel, 1);
+  TUV_ASSERT(r == 0);
+
+  uv_pipe_open(&channel, 0);
+
+  TUV_ASSERT(1 == uv_is_readable((uv_stream_t*) &channel));
+  TUV_ASSERT(1 == uv_is_writable((uv_stream_t*) &channel));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &channel));
+
+  r = uv_tcp_init(uv_default_loop(), &tcp_server);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
+
+  r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection_tcp_conn);
+  TUV_ASSERT(r == 0);
+
+  /* Make a connection to the server */
+  r = uv_tcp_init(uv_default_loop(), &conn.conn);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
+
+  r = uv_tcp_connect(&conn.conn_req,
+                     (uv_tcp_t*) &conn.conn,
+                     (const struct sockaddr*) &addr,
+                     connect_child_process_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(tcp_conn_read_cb_called == 1);
+  TUV_ASSERT(tcp_conn_write_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 4);
+
+  return 0;
+}
+
+int ipc_helper_bind_twice(void) {
+  /*
+   * This is launched from test-ipc.c. stdin is a duplex channel
+   * over which two handles will be transmitted.
+   */
+  struct sockaddr_in addr;
+  uv_write_t write_req;
+  uv_write_t write_req2;
+  int r;
+  uv_buf_t buf;
+
+  TUV_ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr));
+
+  r = uv_pipe_init(uv_default_loop(), &channel, 1);
+  TUV_ASSERT(r == 0);
+
+  uv_pipe_open(&channel, 0);
+
+  TUV_ASSERT(1 == uv_is_readable((uv_stream_t*) &channel));
+  TUV_ASSERT(1 == uv_is_writable((uv_stream_t*) &channel));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &channel));
+
+  buf = uv_buf_init("hello\n", 6);
+
+  r = uv_tcp_init(uv_default_loop(), &tcp_server);
+  TUV_ASSERT(r == 0);
+  r = uv_tcp_init(uv_default_loop(), &tcp_server2);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_tcp_bind(&tcp_server2, (const struct sockaddr*) &addr, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1,
+                (uv_stream_t*)&tcp_server, NULL);
+  TUV_ASSERT(r == 0);
+  r = uv_write2(&write_req2, (uv_stream_t*)&channel, &buf, 1,
+                (uv_stream_t*)&tcp_server2, NULL);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  return 0;
+}
diff --git a/test/test_pipe_bind_error.c b/test/test_pipe_bind_error.c
new file mode 100644 (file)
index 0000000..ddc31aa
--- /dev/null
@@ -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 "uv.h"
+#include "runner.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#ifdef _WIN32
+# define BAD_PIPENAME "bad-pipe"
+#else
+# define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there"
+#endif
+
+
+static int close_cb_called = 0;
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  close_cb_called++;
+}
+
+
+TEST_IMPL(pipe_bind_error_addrinuse) {
+  uv_pipe_t server1, server2;
+  int r;
+  close_cb_called = 0;
+
+  r = uv_pipe_init(uv_default_loop(), &server1, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_pipe_bind(&server1, TEST_PIPENAME);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_init(uv_default_loop(), &server2, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_pipe_bind(&server2, TEST_PIPENAME);
+  TUV_ASSERT(r == UV_EADDRINUSE);
+
+  r = uv_listen((uv_stream_t*)&server1, SOMAXCONN, NULL);
+  TUV_ASSERT(r == 0);
+  r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL);
+  TUV_ASSERT(r == UV_EINVAL);
+
+  uv_close((uv_handle_t*)&server1, close_cb);
+  uv_close((uv_handle_t*)&server2, close_cb);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(close_cb_called == 2);
+
+  return 0;
+}
+
+
+TEST_IMPL(pipe_bind_error_addrnotavail) {
+  uv_pipe_t server;
+  int r;
+  close_cb_called = 0;
+
+  r = uv_pipe_init(uv_default_loop(), &server, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_bind(&server, BAD_PIPENAME);
+  TUV_ASSERT(r == UV_EACCES);
+
+  uv_close((uv_handle_t*)&server, close_cb);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
+
+
+TEST_IMPL(pipe_bind_error_inval) {
+  uv_pipe_t server;
+  int r;
+  close_cb_called = 0;
+
+  r = uv_pipe_init(uv_default_loop(), &server, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_pipe_bind(&server, TEST_PIPENAME);
+  TUV_ASSERT(r == 0);
+  r = uv_pipe_bind(&server, TEST_PIPENAME_2);
+  TUV_ASSERT(r == UV_EINVAL);
+
+  uv_close((uv_handle_t*)&server, close_cb);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
+
+
+TEST_IMPL(pipe_listen_without_bind) {
+  uv_pipe_t server;
+  int r;
+  close_cb_called = 0;
+
+  r = uv_pipe_init(uv_default_loop(), &server, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL);
+  TUV_ASSERT(r == UV_EINVAL);
+
+  uv_close((uv_handle_t*)&server, close_cb);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
diff --git a/test/test_pipe_close_stdout_read_stdin.c b/test/test_pipe_close_stdout_read_stdin.c
new file mode 100644 (file)
index 0000000..ae92dfa
--- /dev/null
@@ -0,0 +1,106 @@
+/* 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
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+
+#include "uv.h"
+#include "runner.h"
+
+void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t* buf)
+{
+  static char buffer[1024];
+
+  buf->base = buffer;
+  buf->len = sizeof(buffer);
+}
+
+void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t* buf)
+{
+  if (nread < 0) {
+    uv_close((uv_handle_t*)stream, NULL);
+    return;
+  }
+}
+
+/*
+ * This test is a reproduction of joyent/libuv#1419 .
+ */
+TEST_IMPL(pipe_close_stdout_read_stdin) {
+  int r = -1;
+  int pid;
+  int fd[2];
+  int status;
+  char buf;
+  uv_pipe_t stdin_pipe;
+
+  r = pipe(fd);
+  TUV_ASSERT(r == 0);
+
+  if ((pid = fork()) == 0) {
+    /*
+     * Make the read side of the pipe our stdin.
+     * The write side will be closed by the parent process.
+    */
+    close(fd[1]);
+    /* block until write end of pipe is closed */
+    read(fd[0], &buf, 1);
+    close(0);
+    r = dup(fd[0]);
+    TUV_ASSERT(r != -1);
+
+    /* Create a stream that reads from the pipe. */
+    r = uv_pipe_init(uv_default_loop(), (uv_pipe_t *)&stdin_pipe, 0);
+    TUV_ASSERT(r == 0);
+
+    r = uv_pipe_open((uv_pipe_t *)&stdin_pipe, 0);
+    TUV_ASSERT(r == 0);
+
+    r = uv_read_start((uv_stream_t *)&stdin_pipe, alloc_buffer, read_stdin);
+    TUV_ASSERT(r == 0);
+
+    /*
+     * Because the other end of the pipe was closed, there should
+     * be no event left to process after one run of the event loop.
+     * Otherwise, it means that events were not processed correctly.
+     */
+    TUV_ASSERT(uv_run(uv_default_loop(), UV_RUN_NOWAIT) == 0);
+  } else {
+    /*
+     * Close both ends of the pipe so that the child
+     * get a POLLHUP event when it tries to read from
+     * the other end.
+     */
+     close(fd[1]);
+     close(fd[0]);
+
+    waitpid(pid, &status, 0);
+    TUV_ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0);
+  }
+
+  return 0;
+}
+
+#endif /* ifndef _WIN32 */
diff --git a/test/test_pipe_connect_error.c b/test/test_pipe_connect_error.c
new file mode 100644 (file)
index 0000000..563902f
--- /dev/null
@@ -0,0 +1,94 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "runner.h"
+
+
+#ifdef _WIN32
+# define BAD_PIPENAME "bad-pipe"
+#else
+# define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there"
+#endif
+
+
+static int close_cb_called = 0;
+static int connect_cb_called = 0;
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  close_cb_called++;
+}
+
+
+static void connect_cb(uv_connect_t* connect_req, int status) {
+  TUV_ASSERT(status == UV_ENOENT);
+  uv_close((uv_handle_t*)connect_req->handle, close_cb);
+  connect_cb_called++;
+}
+
+
+static void connect_cb_file(uv_connect_t* connect_req, int status) {
+  TUV_ASSERT(status == UV_ENOTSOCK || status == UV_ECONNREFUSED);
+  uv_close((uv_handle_t*)connect_req->handle, close_cb);
+  connect_cb_called++;
+}
+
+
+TEST_IMPL(pipe_connect_bad_name) {
+  uv_pipe_t client;
+  uv_connect_t req;
+  int r;
+
+  r = uv_pipe_init(uv_default_loop(), &client, 0);
+  TUV_ASSERT(r == 0);
+  uv_pipe_connect(&req, &client, BAD_PIPENAME, connect_cb);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(close_cb_called == 1);
+  TUV_ASSERT(connect_cb_called == 1);
+
+  return 0;
+}
+
+
+TEST_IMPL(pipe_connect_to_file) {
+  const char* path = "test/fixtures/empty_file";
+  uv_pipe_t client;
+  uv_connect_t req;
+  int r;
+
+  r = uv_pipe_init(uv_default_loop(), &client, 0);
+  TUV_ASSERT(r == 0);
+  uv_pipe_connect(&req, &client, path, connect_cb_file);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(close_cb_called == 1);
+  TUV_ASSERT(connect_cb_called == 1);
+
+  return 0;
+}
diff --git a/test/test_pipe_connect_multiple.c b/test/test_pipe_connect_multiple.c
new file mode 100644 (file)
index 0000000..5a3599a
--- /dev/null
@@ -0,0 +1,104 @@
+/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>.
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "runner.h"
+
+
+static int connection_cb_called = 0;
+static int connect_cb_called = 0;
+
+#define NUM_CLIENTS 4
+
+typedef struct {
+  uv_pipe_t pipe_handle;
+  uv_connect_t conn_req;
+} client_t;
+
+static uv_pipe_t server_handle;
+static client_t clients[NUM_CLIENTS];
+static uv_pipe_t connections[NUM_CLIENTS];
+
+
+static void connection_cb(uv_stream_t* server, int status) {
+  int r;
+  uv_pipe_t* conn;
+  TUV_ASSERT(status == 0);
+
+  conn = &connections[connection_cb_called];
+  r = uv_pipe_init(server->loop, conn, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_accept(server, (uv_stream_t*)conn);
+  TUV_ASSERT(r == 0);
+
+  if (++connection_cb_called == NUM_CLIENTS &&
+      connect_cb_called == NUM_CLIENTS) {
+    uv_stop(server->loop);
+  }
+}
+
+
+static void connect_cb(uv_connect_t* connect_req, int status) {
+  TUV_ASSERT(status == 0);
+  if (++connect_cb_called == NUM_CLIENTS &&
+      connection_cb_called == NUM_CLIENTS) {
+    uv_stop(connect_req->handle->loop);
+  }
+}
+
+
+TEST_IMPL(pipe_connect_multiple) {
+  int i;
+  int r;
+  uv_loop_t* loop;
+
+  loop = uv_default_loop();
+
+  r = uv_pipe_init(loop, &server_handle, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_bind(&server_handle, TEST_PIPENAME);
+  TUV_ASSERT(r == 0);
+
+  r = uv_listen((uv_stream_t*)&server_handle, 128, connection_cb);
+  TUV_ASSERT(r == 0);
+
+  for (i = 0; i < NUM_CLIENTS; i++) {
+    r = uv_pipe_init(loop, &clients[i].pipe_handle, 0);
+    TUV_ASSERT(r == 0);
+    uv_pipe_connect(&clients[i].conn_req,
+                    &clients[i].pipe_handle,
+                    TEST_PIPENAME,
+                    connect_cb);
+  }
+
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  TUV_ASSERT(connection_cb_called == NUM_CLIENTS);
+  TUV_ASSERT(connect_cb_called == NUM_CLIENTS);
+
+  return 0;
+}
diff --git a/test/test_pipe_connect_prepare.c b/test/test_pipe_connect_prepare.c
new file mode 100644 (file)
index 0000000..efdcf21
--- /dev/null
@@ -0,0 +1,84 @@
+/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>.
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "uv.h"
+#include "runner.h"
+
+
+#ifdef _WIN32
+# define BAD_PIPENAME "bad-pipe"
+#else
+# define BAD_PIPENAME "/path/to/unix/socket/that/really/should/not/be/there"
+#endif
+
+
+static int close_cb_called = 0;
+static int connect_cb_called = 0;
+
+static uv_pipe_t pipe_handle;
+static uv_prepare_t prepare_handle;
+static uv_connect_t conn_req;
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  close_cb_called++;
+}
+
+
+static void connect_cb(uv_connect_t* connect_req, int status) {
+  TUV_ASSERT(status == UV_ENOENT);
+  connect_cb_called++;
+  uv_close((uv_handle_t*)&prepare_handle, close_cb);
+  uv_close((uv_handle_t*)&pipe_handle, close_cb);
+}
+
+
+static void prepare_cb(uv_prepare_t* handle) {
+  TUV_ASSERT(handle == &prepare_handle);
+  uv_pipe_connect(&conn_req, &pipe_handle, BAD_PIPENAME, connect_cb);
+}
+
+/*
+TEST_IMPL(pipe_connect_on_prepare) {
+  int r;
+
+  r = uv_pipe_init(uv_default_loop(), &pipe_handle, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_prepare_init(uv_default_loop(), &prepare_handle);
+  TUV_ASSERT(r == 0);
+  r = uv_prepare_start(&prepare_handle, prepare_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(close_cb_called == 2);
+  TUV_ASSERT(connect_cb_called == 1);
+
+  return 0;
+}
+*/
diff --git a/test/test_pipe_getsockname.c b/test/test_pipe_getsockname.c
new file mode 100644 (file)
index 0000000..808922a
--- /dev/null
@@ -0,0 +1,262 @@
+/* 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 "runner.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(__linux__)
+  #include <sys/socket.h>
+  #include <sys/un.h>
+#endif
+
+#ifndef _WIN32
+# include <unistd.h>  /* close */
+#else
+# include <fcntl.h>
+#endif
+
+static uv_pipe_t pipe_client;
+static uv_pipe_t pipe_server;
+static uv_connect_t connect_req;
+
+static int pipe_close_cb_called = 0;
+static int pipe_client_connect_cb_called = 0;
+
+
+static void pipe_close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle == (uv_handle_t*) &pipe_client ||
+         handle == (uv_handle_t*) &pipe_server);
+  pipe_close_cb_called++;
+}
+
+
+static void pipe_client_connect_cb(uv_connect_t* req, int status) {
+  char buf[1024];
+  size_t len;
+  int r;
+
+  TUV_ASSERT(req == &connect_req);
+  TUV_ASSERT(status == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getpeername(&pipe_client, buf, &len);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(buf[len - 1] != 0);
+  TUV_ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getsockname(&pipe_client, buf, &len);
+  TUV_ASSERT(r == 0 && len == 0);
+
+  pipe_client_connect_cb_called++;
+
+
+  uv_close((uv_handle_t*) &pipe_client, pipe_close_cb);
+  uv_close((uv_handle_t*) &pipe_server, pipe_close_cb);
+}
+
+
+static void pipe_server_connection_cb(uv_stream_t* handle, int status) {
+  /* This function *may* be called, depending on whether accept or the
+   * connection callback is called first.
+   */
+  TUV_ASSERT(status == 0);
+}
+
+
+TEST_IMPL(pipe_getsockname) {
+  uv_loop_t* loop;
+  char buf[1024];
+  size_t len;
+  int r;
+  pipe_close_cb_called = 0;
+
+  loop = uv_default_loop();
+  TUV_ASSERT(loop != NULL);
+
+  r = uv_pipe_init(loop, &pipe_server, 0);
+  TUV_ASSERT(r == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getsockname(&pipe_server, buf, &len);
+  TUV_ASSERT(r == UV_EBADF);
+
+  len = sizeof buf;
+  r = uv_pipe_getpeername(&pipe_server, buf, &len);
+  TUV_ASSERT(r == UV_EBADF);
+
+  r = uv_pipe_bind(&pipe_server, TEST_PIPENAME);
+  TUV_ASSERT(r == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getsockname(&pipe_server, buf, &len);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(buf[len - 1] != 0);
+  TUV_ASSERT(buf[len] == '\0');
+  TUV_ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getpeername(&pipe_server, buf, &len);
+  TUV_ASSERT(r == UV_ENOTCONN);
+
+  r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_init(loop, &pipe_client, 0);
+  TUV_ASSERT(r == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getsockname(&pipe_client, buf, &len);
+  TUV_ASSERT(r == UV_EBADF);
+
+  len = sizeof buf;
+  r = uv_pipe_getpeername(&pipe_client, buf, &len);
+  TUV_ASSERT(r == UV_EBADF);
+
+  uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb);
+
+  len = sizeof buf;
+  r = uv_pipe_getsockname(&pipe_client, buf, &len);
+  TUV_ASSERT(r == 0 && len == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getpeername(&pipe_client, buf, &len);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(buf[len - 1] != 0);
+  TUV_ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(pipe_client_connect_cb_called == 1);
+  TUV_ASSERT(pipe_close_cb_called == 2);
+
+  return 0;
+}
+
+
+TEST_IMPL(pipe_getsockname_abstract) {
+#if defined(__linux__)
+  char buf[1024];
+  size_t len;
+  int r;
+  int sock;
+  struct sockaddr_un sun;
+  socklen_t sun_len;
+  char abstract_pipe[] = "\0test-pipe";
+  pipe_close_cb_called = 0;
+
+  sock = socket(AF_LOCAL, SOCK_STREAM, 0);
+  TUV_ASSERT(sock != -1);
+
+  sun_len = sizeof sun;
+  memset(&sun, 0, sun_len);
+  sun.sun_family = AF_UNIX;
+  memcpy(sun.sun_path, abstract_pipe, sizeof abstract_pipe);
+
+  r = bind(sock, (struct sockaddr*)&sun, sun_len);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_init(uv_default_loop(), &pipe_server, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_pipe_open(&pipe_server, sock);
+  TUV_ASSERT(r == 0);
+
+  len = sizeof buf;
+  r = uv_pipe_getsockname(&pipe_server, buf, &len);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(memcmp(buf, abstract_pipe, sizeof abstract_pipe) == 0);
+
+  uv_close((uv_handle_t*)&pipe_server, pipe_close_cb);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  close(sock);
+
+  TUV_ASSERT(pipe_close_cb_called == 1);
+  return 0;
+#else
+  return 0;
+#endif
+}
+
+TEST_IMPL(pipe_getsockname_blocking) {
+#ifdef _WIN32
+  HANDLE readh, writeh;
+  int readfd;
+  char buf1[1024], buf2[1024];
+  size_t len1, len2;
+  int r;
+
+  r = CreatePipe(&readh, &writeh, NULL, 65536);
+  TUV_ASSERT(r != 0);
+
+  r = uv_pipe_init(uv_default_loop(), &pipe_client, 0);
+  TUV_ASSERT(r == 0);
+  readfd = _open_osfhandle((intptr_t)readh, _O_RDONLY);
+  TUV_ASSERT(r != -1);
+  r = uv_pipe_open(&pipe_client, readfd);
+  TUV_ASSERT(r == 0);
+  r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL);
+  TUV_ASSERT(r == 0);
+  Sleep(100);
+  r = uv_read_stop((uv_stream_t*)&pipe_client);
+  TUV_ASSERT(r == 0);
+
+  len1 = sizeof buf1;
+  r = uv_pipe_getsockname(&pipe_client, buf1, &len1);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(len1 == 0);  /* It's an annonymous pipe. */
+
+  r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL);
+  TUV_ASSERT(r == 0);
+  Sleep(100);
+
+  len2 = sizeof buf2;
+  r = uv_pipe_getsockname(&pipe_client, buf2, &len2);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(len2 == 0);  /* It's an annonymous pipe. */
+
+  r = uv_read_stop((uv_stream_t*)&pipe_client);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(len1 == len2);
+  TUV_ASSERT(memcmp(buf1, buf2, len1) == 0);
+
+  pipe_close_cb_called = 0;
+  uv_close((uv_handle_t*)&pipe_client, pipe_close_cb);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(pipe_close_cb_called == 1);
+
+  CloseHandle(writeh);
+#endif
+
+  return 0;
+}
diff --git a/test/test_pipe_pending_instances.c b/test/test_pipe_pending_instances.c
new file mode 100644 (file)
index 0000000..600e1fe
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright (c) 2015 Saúl Ibarra Corretgé <saghul@gmail.com>.
+ * 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 "runner.h"
+
+
+static void connection_cb(uv_stream_t* server, int status) {
+  TUV_ASSERT(0 && "this will never be called");
+}
+
+
+TEST_IMPL(pipe_pending_instances) {
+  int r;
+  uv_pipe_t pipe_handle;
+  uv_loop_t* loop;
+
+  loop = uv_default_loop();
+
+  r = uv_pipe_init(loop, &pipe_handle, 0);
+  TUV_ASSERT(r == 0);
+
+  uv_pipe_pending_instances(&pipe_handle, 8);
+
+  r = uv_pipe_bind(&pipe_handle, TEST_PIPENAME);
+  TUV_ASSERT(r == 0);
+
+  uv_pipe_pending_instances(&pipe_handle, 16);
+
+  r = uv_listen((uv_stream_t*)&pipe_handle, 128, connection_cb);
+  TUV_ASSERT(r == 0);
+
+  uv_close((uv_handle_t*)&pipe_handle, NULL);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  return 0;
+}
diff --git a/test/test_pipe_sendmsg.c b/test/test_pipe_sendmsg.c
new file mode 100644 (file)
index 0000000..e63e767
--- /dev/null
@@ -0,0 +1,167 @@
+/* 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 "runner.h"
+
+
+#ifndef _WIN32
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+
+/* NOTE: size should be divisible by 2 */
+static uv_pipe_t incoming[4];
+static unsigned int incoming_count;
+static unsigned int close_called;
+
+
+static void set_nonblocking(uv_os_sock_t sock) {
+  int r;
+#ifdef _WIN32
+  unsigned long on = 1;
+  r = ioctlsocket(sock, FIONBIO, &on);
+  TUV_ASSERT(r == 0);
+#else
+  int flags = fcntl(sock, F_GETFL, 0);
+  TUV_ASSERT(flags >= 0);
+  r = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+  TUV_ASSERT(r >= 0);
+#endif
+}
+
+
+
+
+static void close_cb(uv_handle_t* handle) {
+  close_called++;
+}
+
+
+static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
+  static char base[1];
+
+  buf->base = base;
+  buf->len = sizeof(base);
+}
+
+
+static void read_cb(uv_stream_t* handle,
+                    ssize_t nread,
+                    const uv_buf_t* buf) {
+  uv_pipe_t* p;
+  uv_pipe_t* inc;
+  uv_handle_type pending;
+  unsigned int i;
+
+  p = (uv_pipe_t*) handle;
+  TUV_ASSERT(nread >= 0);
+
+  while (uv_pipe_pending_count(p) != 0) {
+    pending = uv_pipe_pending_type(p);
+    TUV_ASSERT(pending == UV_NAMED_PIPE);
+
+    TUV_ASSERT(incoming_count < ARRAY_SIZE(incoming));
+    inc = &incoming[incoming_count++];
+    TUV_ASSERT(0 == uv_pipe_init(p->loop, inc, 0));
+    TUV_ASSERT(0 == uv_accept(handle, (uv_stream_t*) inc));
+  }
+
+  if (incoming_count != ARRAY_SIZE(incoming))
+    return;
+
+  TUV_ASSERT(0 == uv_read_stop((uv_stream_t*) p));
+  uv_close((uv_handle_t*) p, close_cb);
+  for (i = 0; i < ARRAY_SIZE(incoming); i++)
+    uv_close((uv_handle_t*) &incoming[i], close_cb);
+}
+
+
+TEST_IMPL(pipe_sendmsg) {
+  uv_pipe_t p;
+  int r;
+  int fds[2];
+  int send_fds[ARRAY_SIZE(incoming)];
+  struct msghdr msg;
+  char scratch[64];
+  struct cmsghdr *cmsg;
+  unsigned int i;
+  uv_buf_t buf;
+
+  TUV_ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
+  for (i = 0; i < ARRAY_SIZE(send_fds); i += 2)
+    TUV_ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, send_fds + i));
+  TUV_ASSERT(i == ARRAY_SIZE(send_fds));
+  TUV_ASSERT(0 == uv_pipe_init(uv_default_loop(), &p, 1));
+  TUV_ASSERT(0 == uv_pipe_open(&p, fds[1]));
+
+  buf = uv_buf_init("X", 1);
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = (struct iovec*) &buf;
+  msg.msg_iovlen = 1;
+  msg.msg_flags = 0;
+
+  msg.msg_control = (void*) scratch;
+  msg.msg_controllen = CMSG_LEN(sizeof(send_fds));
+  TUV_ASSERT(sizeof(scratch) >= msg.msg_controllen);
+
+  cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = msg.msg_controllen;
+
+  /* silence aliasing warning */
+  {
+    void* pv = CMSG_DATA(cmsg);
+    int* pi = pv;
+    for (i = 0; i < ARRAY_SIZE(send_fds); i++)
+      pi[i] = send_fds[i];
+  }
+
+  set_nonblocking(fds[1]);
+  TUV_ASSERT(0 == uv_read_start((uv_stream_t*) &p, alloc_cb, read_cb));
+
+  do
+    r = sendmsg(fds[0], &msg, 0);
+  while (r == -1 && errno == EINTR);
+  TUV_ASSERT(r == 1);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(ARRAY_SIZE(incoming) == incoming_count);
+  TUV_ASSERT(ARRAY_SIZE(incoming) + 1 == close_called);
+  close(fds[0]);
+
+  return 0;
+}
+
+#else  /* !_WIN32 */
+
+TEST_IMPL(pipe_sendmsg) {
+  return 0;
+}
+
+#endif  /* _WIN32 */
diff --git a/test/test_pipe_server_close.c b/test/test_pipe_server_close.c
new file mode 100644 (file)
index 0000000..7b7c42b
--- /dev/null
@@ -0,0 +1,90 @@
+/* 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 <string.h>
+#include <errno.h>
+
+#include "uv.h"
+#include "runner.h"
+
+
+static uv_pipe_t pipe_client;
+static uv_pipe_t pipe_server;
+static uv_connect_t connect_req;
+
+static int pipe_close_cb_called = 0;
+static int pipe_client_connect_cb_called = 0;
+
+
+static void pipe_close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle == (uv_handle_t*) &pipe_client ||
+         handle == (uv_handle_t*) &pipe_server);
+  pipe_close_cb_called++;
+}
+
+
+static void pipe_client_connect_cb(uv_connect_t* req, int status) {
+  TUV_ASSERT(req == &connect_req);
+  TUV_ASSERT(status == 0);
+
+  pipe_client_connect_cb_called++;
+
+  uv_close((uv_handle_t*) &pipe_client, pipe_close_cb);
+  uv_close((uv_handle_t*) &pipe_server, pipe_close_cb);
+}
+
+
+static void pipe_server_connection_cb(uv_stream_t* handle, int status) {
+  /* This function *may* be called, depending on whether accept or the
+   * connection callback is called first.
+   */
+  TUV_ASSERT(status == 0);
+}
+
+
+TEST_IMPL(pipe_server_close) {
+  uv_loop_t* loop;
+  int r;
+
+  loop = uv_default_loop();
+  TUV_ASSERT(loop != NULL);
+
+  r = uv_pipe_init(loop, &pipe_server, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_bind(&pipe_server, TEST_PIPENAME);
+  TUV_ASSERT(r == 0);
+
+  r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_init(loop, &pipe_client, 0);
+  TUV_ASSERT(r == 0);
+
+  uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(pipe_client_connect_cb_called == 1);
+  TUV_ASSERT(pipe_close_cb_called == 2);
+
+  return 0;
+}
diff --git a/test/test_pipe_set_non_blocking.c b/test/test_pipe_set_non_blocking.c
new file mode 100644 (file)
index 0000000..f767796
--- /dev/null
@@ -0,0 +1,98 @@
+/* Copyright (c) 2015, 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.
+ */
+
+#include "uv.h"
+#include "runner.h"
+
+#ifdef _WIN32
+
+TEST_IMPL(pipe_set_non_blocking) {
+  RETURN_SKIP("Test not implemented on Windows.");
+}
+
+#else  /* !_WIN32 */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+struct thread_ctx {
+  uv_barrier_t barrier;
+  int fd;
+};
+
+static void thread_main(void* arg) {
+  struct thread_ctx* ctx;
+  char buf[4096];
+  ssize_t n;
+
+  ctx = arg;
+  uv_barrier_wait(&ctx->barrier);
+
+  do
+    n = read(ctx->fd, buf, sizeof(buf));
+  while (n > 0 || (n == -1 && errno == EINTR));
+
+  TUV_ASSERT(n == 0);
+}
+
+TEST_IMPL(pipe_set_non_blocking) {
+  struct thread_ctx ctx;
+  uv_pipe_t pipe_handle;
+  uv_thread_t thread;
+  size_t nwritten;
+  char data[4096];
+  uv_buf_t buf;
+  int fd[2];
+  int n;
+
+  TUV_ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0));
+  TUV_ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fd));
+  TUV_ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0]));
+  TUV_ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1));
+
+  ctx.fd = fd[1];
+  TUV_ASSERT(0 == uv_barrier_init(&ctx.barrier, 2));
+  TUV_ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx));
+  uv_barrier_wait(&ctx.barrier);
+
+  buf.len = sizeof(data);
+  buf.base = data;
+  memset(data, '.', sizeof(data));
+
+  nwritten = 0;
+  while (nwritten < 10 << 20) {
+    /* The stream is in blocking mode so uv_try_write() should always succeed
+     * with the exact number of bytes that we wanted written.
+     */
+    n = uv_try_write((uv_stream_t*) &pipe_handle, &buf, 1);
+    TUV_ASSERT(n == sizeof(data));
+    nwritten += n;
+  }
+
+  uv_close((uv_handle_t*) &pipe_handle, NULL);
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  TUV_ASSERT(0 == uv_thread_join(&thread));
+  TUV_ASSERT(0 == close(fd[1]));  /* fd[0] is closed by uv_close(). */
+  uv_barrier_destroy(&ctx.barrier);
+
+  return 0;
+}
+
+#endif  /* !_WIN32 */
diff --git a/test/test_shutdown_eof.c b/test/test_shutdown_eof.c
new file mode 100644 (file)
index 0000000..f416794
--- /dev/null
@@ -0,0 +1,156 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+#include "runner.h"
+
+
+//-----------------------------------------------------------------------------
+
+static uv_tcp_t tcp;
+static uv_connect_t connect_req;
+static uv_write_t write_req;
+static uv_shutdown_t shutdown_req;
+static int got_q;
+static int got_eof;
+
+static int called_connect_cb;
+static int called_shutdown_cb;
+
+
+static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
+  buf->base = (char*)malloc(size);
+  buf->len = size;
+}
+
+
+static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) {
+  TUV_ASSERT((uv_tcp_t*)t == &tcp);
+
+
+  if (buf->base)
+    free(buf->base);
+
+  if (nread == 0) {
+    return;
+  }
+
+  if (nread > 0) {
+    TUV_ASSERT(0);
+    //TUV_ASSERT(!got_eof);
+    //TUV_ASSERT(buf->base[0] == 'Q');
+    //uv_buf_t qbuf = uv_buf_init((char*)"Q", 1);
+    //int r = uv_write(&write_req, t, &qbuf, 1, NULL);
+    //TUV_ASSERT(r == 0);
+    //got_q = 1;
+  } else {
+    TUV_ASSERT(nread == UV_EOF);
+    uv_close((uv_handle_t*)t, NULL);
+    got_eof = 1;
+  }
+}
+
+
+static void shutdown_cb(uv_shutdown_t *req, int status) {
+  TUV_ASSERT(req == &shutdown_req);
+  TUV_ASSERT(called_connect_cb == 1);
+  TUV_ASSERT(!got_eof);
+
+  called_shutdown_cb++;
+}
+
+
+static void connect_cb(uv_connect_t *req, int status) {
+  TUV_ASSERT(status == 0);
+  TUV_ASSERT(req == &connect_req);
+  int r;
+
+  /* Start reading from our connection so we can receive the EOF.  */
+  r = uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb);
+  TUV_ASSERT(r == 0);
+
+  /*
+   * Write the letter 'Q' to gracefully kill the echo-server. This will not
+   * effect our connection.
+   */
+  uv_buf_t qsbuf = uv_buf_init((char*)"Q", 1);
+  r = uv_write(&write_req, (uv_stream_t*) &tcp, &qsbuf, 1, NULL);
+  TUV_ASSERT(r == 0);
+
+  /* Shutdown our end of the connection.  */
+  //r = uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb);
+  //TUV_ASSERT(r == 0);
+
+  called_connect_cb++;
+  TUV_ASSERT(called_shutdown_cb == 0);
+}
+
+
+/*
+ * This test has a client which connects to the echo_server and immediately
+ * issues a shutdown. The echo-server, in response, will also shutdown their
+ * connection. We check, with a timer, that libuv is not automatically
+ * calling uv_close when the client receives the EOF from echo-server.
+ */
+TEST_IMPL(shutdown_eof) {
+  struct sockaddr_in server_addr;
+  int r;
+
+  got_q = 0;
+  got_eof = 0;
+  called_connect_cb = 0;
+  called_shutdown_cb = 0;
+
+
+  TUV_ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr));
+  r = uv_tcp_init(uv_default_loop(), &tcp);
+  TUV_ASSERT(!r);
+
+  r = uv_tcp_connect(&connect_req,
+                     &tcp,
+                     (const struct sockaddr*) &server_addr,
+                     connect_cb);
+  TUV_ASSERT(!r);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
diff --git a/test/test_signal.c b/test/test_signal.c
new file mode 100644 (file)
index 0000000..843e20d
--- /dev/null
@@ -0,0 +1,153 @@
+/* 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 test does not pretend to be cross-platform. */
+#ifndef _WIN32
+
+#include "uv.h"
+#include "runner.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define NSIGNALS  10
+
+#define container_of(ptr, type, member) \
+  ((type *) ((char *) (ptr) - offsetof(type, member)))
+
+struct timer_ctx {
+  unsigned int ncalls;
+  uv_timer_t handle;
+  int signum;
+};
+
+struct signal_ctx {
+  enum { CLOSE, STOP } stop_or_close;
+  unsigned int ncalls;
+  uv_signal_t handle;
+  int signum;
+};
+
+
+static void signal_cb(uv_signal_t* handle, int signum) {
+  struct signal_ctx* ctx = container_of(handle, struct signal_ctx, handle);
+  TUV_ASSERT(signum == ctx->signum);
+
+  if (++ctx->ncalls == NSIGNALS) {
+    if (ctx->stop_or_close == STOP)
+      uv_signal_stop(handle);
+    else if (ctx->stop_or_close == CLOSE)
+      uv_close((uv_handle_t*)handle, NULL);
+    else
+      TUV_ASSERT(0);
+  }
+}
+
+
+static void timer_cb(uv_timer_t* handle) {
+  struct timer_ctx* ctx = container_of(handle, struct timer_ctx, handle);
+
+  raise(ctx->signum);
+
+  if (++ctx->ncalls == NSIGNALS)
+    uv_close((uv_handle_t*)handle, NULL);
+}
+
+
+static void start_watcher(uv_loop_t* loop, int signum, struct signal_ctx* ctx) {
+  ctx->ncalls = 0;
+  ctx->signum = signum;
+  ctx->stop_or_close = CLOSE;
+  TUV_ASSERT(0 == uv_signal_init(loop, &ctx->handle));
+  TUV_ASSERT(0 == uv_signal_start(&ctx->handle, signal_cb, signum));
+}
+
+
+static void start_timer(uv_loop_t* loop, int signum, struct timer_ctx* ctx) {
+  ctx->ncalls = 0;
+  ctx->signum = signum;
+  TUV_ASSERT(0 == uv_timer_init(loop, &ctx->handle));
+  TUV_ASSERT(0 == uv_timer_start(&ctx->handle, timer_cb, 5, 5));
+}
+
+
+TEST_IMPL(we_get_signal) {
+  struct signal_ctx sc;
+  struct timer_ctx tc;
+  uv_loop_t* loop;
+
+  loop = uv_default_loop();
+  start_timer(loop, SIGCHLD, &tc);
+  start_watcher(loop, SIGCHLD, &sc);
+  sc.stop_or_close = STOP; /* stop, don't close the signal handle */
+  TUV_ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
+  TUV_ASSERT(tc.ncalls == NSIGNALS);
+  TUV_ASSERT(sc.ncalls == NSIGNALS);
+
+  start_timer(loop, SIGCHLD, &tc);
+  TUV_ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
+  TUV_ASSERT(tc.ncalls == NSIGNALS);
+  TUV_ASSERT(sc.ncalls == NSIGNALS);
+
+  sc.ncalls = 0;
+  sc.stop_or_close = CLOSE; /* now close it when it's done */
+  uv_signal_start(&sc.handle, signal_cb, SIGCHLD);
+
+  start_timer(loop, SIGCHLD, &tc);
+  TUV_ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
+  TUV_ASSERT(tc.ncalls == NSIGNALS);
+  TUV_ASSERT(sc.ncalls == NSIGNALS);
+
+  return 0;
+}
+
+
+TEST_IMPL(we_get_signals) {
+  struct signal_ctx sc[4];
+  struct timer_ctx tc[2];
+  uv_loop_t* loop;
+  unsigned int i;
+
+  loop = uv_default_loop();
+  start_watcher(loop, SIGUSR1, sc + 0);
+  start_watcher(loop, SIGUSR1, sc + 1);
+  start_watcher(loop, SIGUSR2, sc + 2);
+  start_watcher(loop, SIGUSR2, sc + 3);
+  start_timer(loop, SIGUSR1, tc + 0);
+  start_timer(loop, SIGUSR2, tc + 1);
+  TUV_ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
+
+  for (i = 0; i < ARRAY_SIZE(sc); i++)
+    TUV_ASSERT(sc[i].ncalls == NSIGNALS);
+
+  for (i = 0; i < ARRAY_SIZE(tc); i++)
+    TUV_ASSERT(tc[i].ncalls == NSIGNALS);
+
+  return 0;
+}
+
+#endif /* _WIN32 */
diff --git a/test/test_spawn.c b/test/test_spawn.c
new file mode 100644 (file)
index 0000000..1c48122
--- /dev/null
@@ -0,0 +1,1638 @@
+/* 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 "runner.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+# if defined(__MINGW32__)
+#  include <basetyps.h>
+# endif
+# include <shellapi.h>
+# include <wchar.h>
+#else
+# include <unistd.h>
+# include <sys/wait.h>
+#endif
+
+
+static int close_cb_called;
+static int exit_cb_called;
+static uv_process_t process;
+static uv_timer_t timer;
+static uv_process_options_t options;
+static char exepath[1024];
+static size_t exepath_size = 1024;
+static char* args[5];
+static int no_term_signal;
+static int timer_counter;
+
+#define OUTPUT_SIZE 1024
+static char output[OUTPUT_SIZE];
+static int output_used;
+
+
+static void close_cb(uv_handle_t* handle) {
+  printf("close_cb\n");
+  close_cb_called++;
+}
+
+static void exit_cb(uv_process_t* process,
+                    int64_t exit_status,
+                    int term_signal) {
+  printf("exit_cb\n");
+  exit_cb_called++;
+  TUV_ASSERT(exit_status == 1);
+  TUV_ASSERT(term_signal == 0);
+  uv_close((uv_handle_t*)process, close_cb);
+}
+
+
+static void fail_cb(uv_process_t* process,
+                    int64_t exit_status,
+                    int term_signal) {
+  TUV_ASSERT(0 && "fail_cb called");
+}
+
+
+static void kill_cb(uv_process_t* process,
+                    int64_t exit_status,
+                    int term_signal) {
+  int err;
+
+  printf("exit_cb\n");
+  exit_cb_called++;
+#ifdef _WIN32
+  TUV_ASSERT(exit_status == 1);
+#else
+  TUV_ASSERT(exit_status == 0);
+#endif
+  TUV_ASSERT(no_term_signal || term_signal == 15);
+  uv_close((uv_handle_t*)process, close_cb);
+
+  /*
+   * Sending signum == 0 should check if the
+   * child process is still alive, not kill it.
+   * This process should be dead.
+   */
+  err = uv_kill(process->pid, 0);
+  TUV_ASSERT(err == UV_ESRCH);
+}
+
+static void detach_failure_cb(uv_process_t* process,
+                              int64_t exit_status,
+                              int term_signal) {
+  printf("detach_cb\n");
+  exit_cb_called++;
+}
+
+static void on_alloc(uv_handle_t* handle,
+                     size_t suggested_size,
+                     uv_buf_t* buf) {
+  buf->base = output + output_used;
+  buf->len = OUTPUT_SIZE - output_used;
+}
+
+
+static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) {
+  if (nread > 0) {
+    output_used += nread;
+  } else if (nread < 0) {
+    TUV_ASSERT(nread == UV_EOF);
+    uv_close((uv_handle_t*)tcp, close_cb);
+  }
+}
+
+
+static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) {
+  uv_read_stop(tcp);
+  on_read(tcp, nread, buf);
+}
+
+
+static void write_cb(uv_write_t* req, int status) {
+  TUV_ASSERT(status == 0);
+  uv_close((uv_handle_t*)req->handle, close_cb);
+}
+
+
+static void init_process_options(char* test, uv_exit_cb exit_cb) {
+  /* Note spawn_helper1 defined in test/run-tests.c */
+  int r = uv_exepath(exepath, &exepath_size);
+  TUV_ASSERT(r == 0);
+  exepath[exepath_size] = '\0';
+  args[0] = exepath;
+  args[1] = test;
+  args[2] = NULL;
+  args[3] = NULL;
+  args[4] = NULL;
+  options.file = exepath;
+  options.args = args;
+  options.exit_cb = exit_cb;
+  options.flags = 0;
+}
+
+
+static void timer_cb(uv_timer_t* handle) {
+  uv_process_kill(&process, /* SIGTERM */ 15);
+  uv_close((uv_handle_t*)handle, close_cb);
+}
+
+
+static void timer_counter_cb(uv_timer_t* handle) {
+  ++timer_counter;
+}
+
+
+TEST_IMPL(spawn_fails) {
+  int r;
+
+  init_process_options("", fail_cb);
+  options.file = options.args[0] = "program-that-had-better-not-exist";
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == UV_ENOENT || r == UV_EACCES);
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &process));
+  uv_close((uv_handle_t*) &process, NULL);
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  return 0;
+}
+
+
+#ifndef _WIN32
+TEST_IMPL(spawn_fails_check_for_waitpid_cleanup) {
+  int r;
+  int status;
+  int err;
+
+  init_process_options("", fail_cb);
+  options.file = options.args[0] = "program-that-had-better-not-exist";
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == UV_ENOENT || r == UV_EACCES);
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &process));
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  /* verify the child is successfully cleaned up within libuv */
+  do
+    err = waitpid(process.pid, &status, 0);
+  while (err == -1 && errno == EINTR);
+
+  TUV_ASSERT(err == -1);
+  TUV_ASSERT(errno == ECHILD);
+
+  uv_close((uv_handle_t*) &process, NULL);
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  return 0;
+}
+#endif
+
+
+TEST_IMPL(spawn_exit_code) {
+  int r;
+
+  init_process_options("spawn_helper1", exit_cb);
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_stdout) {
+  int r;
+  uv_pipe_t out;
+  uv_stdio_container_t stdio[2];
+
+  init_process_options("spawn_helper2", exit_cb);
+
+  uv_pipe_init(uv_default_loop(), &out, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[1].data.stream = (uv_stream_t*)&out;
+  options.stdio_count = 2;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
+  printf("output is: %s", output);
+  TUV_ASSERT(strcmp("hello world\n", output) == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_stdout_to_file) {
+  int r;
+  uv_file file;
+  uv_fs_t fs_req;
+  uv_stdio_container_t stdio[2];
+  uv_buf_t buf;
+
+  /* Setup. */
+  unlink("stdout_file");
+
+  init_process_options("spawn_helper2", exit_cb);
+
+  r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR,
+      S_IRUSR | S_IWUSR, NULL);
+  TUV_ASSERT(r != -1);
+  uv_fs_req_cleanup(&fs_req);
+
+  file = r;
+
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_INHERIT_FD;
+  options.stdio[1].data.fd = file;
+  options.stdio_count = 2;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  buf = uv_buf_init(output, sizeof(output));
+  r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL);
+  TUV_ASSERT(r == 12);
+  uv_fs_req_cleanup(&fs_req);
+
+  r = uv_fs_close(NULL, &fs_req, file, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&fs_req);
+
+  printf("output is: %s", output);
+  TUV_ASSERT(strcmp("hello world\n", output) == 0);
+
+  /* Cleanup. */
+  unlink("stdout_file");
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_stdout_and_stderr_to_file) {
+  int r;
+  uv_file file;
+  uv_fs_t fs_req;
+  uv_stdio_container_t stdio[3];
+  uv_buf_t buf;
+
+  /* Setup. */
+  unlink("stdout_file");
+
+  init_process_options("spawn_helper6", exit_cb);
+
+  r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR,
+      S_IRUSR | S_IWUSR, NULL);
+  TUV_ASSERT(r != -1);
+  uv_fs_req_cleanup(&fs_req);
+
+  file = r;
+
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_INHERIT_FD;
+  options.stdio[1].data.fd = file;
+  options.stdio[2].flags = UV_INHERIT_FD;
+  options.stdio[2].data.fd = file;
+  options.stdio_count = 3;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  buf = uv_buf_init(output, sizeof(output));
+  r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL);
+  TUV_ASSERT(r == 27);
+  uv_fs_req_cleanup(&fs_req);
+
+  r = uv_fs_close(NULL, &fs_req, file, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&fs_req);
+
+  printf("output is: %s", output);
+  TUV_ASSERT(strcmp("hello world\nhello errworld\n", output) == 0);
+
+  /* Cleanup. */
+  unlink("stdout_file");
+
+  return 0;
+}
+
+
+#ifndef _WIN32
+TEST_IMPL(spawn_stdout_and_stderr_to_file2) {
+  int r;
+  uv_file file;
+  uv_fs_t fs_req;
+  uv_stdio_container_t stdio[3];
+  uv_buf_t buf;
+
+  /* Setup. */
+  unlink("stdout_file");
+
+  init_process_options("spawn_helper6", exit_cb);
+
+  /* Replace stderr with our file */
+  r = uv_fs_open(NULL,
+                 &fs_req,
+                 "stdout_file",
+                 O_CREAT | O_RDWR,
+                 S_IRUSR | S_IWUSR,
+                 NULL);
+  TUV_ASSERT(r != -1);
+  uv_fs_req_cleanup(&fs_req);
+  file = dup2(r, STDERR_FILENO);
+  TUV_ASSERT(file != -1);
+
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_INHERIT_FD;
+  options.stdio[1].data.fd = file;
+  options.stdio[2].flags = UV_INHERIT_FD;
+  options.stdio[2].data.fd = file;
+  options.stdio_count = 3;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  buf = uv_buf_init(output, sizeof(output));
+  r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL);
+  TUV_ASSERT(r == 27);
+  uv_fs_req_cleanup(&fs_req);
+
+  r = uv_fs_close(NULL, &fs_req, file, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&fs_req);
+
+  printf("output is: %s", output);
+  TUV_ASSERT(strcmp("hello world\nhello errworld\n", output) == 0);
+
+  /* Cleanup. */
+  unlink("stdout_file");
+
+  return 0;
+}
+#endif
+
+
+#ifndef _WIN32
+TEST_IMPL(spawn_stdout_and_stderr_to_file_swap) {
+  int r;
+  uv_file stdout_file;
+  uv_file stderr_file;
+  uv_fs_t fs_req;
+  uv_stdio_container_t stdio[3];
+  uv_buf_t buf;
+
+  /* Setup. */
+  unlink("stdout_file");
+  unlink("stderr_file");
+
+  init_process_options("spawn_helper6", exit_cb);
+
+  /* open 'stdout_file' and replace STDOUT_FILENO with it */
+  r = uv_fs_open(NULL,
+                 &fs_req,
+                 "stdout_file",
+                 O_CREAT | O_RDWR,
+                 S_IRUSR | S_IWUSR,
+                 NULL);
+  TUV_ASSERT(r != -1);
+  uv_fs_req_cleanup(&fs_req);
+  stdout_file = dup2(r, STDOUT_FILENO);
+  TUV_ASSERT(stdout_file != -1);
+
+  /* open 'stderr_file' and replace STDERR_FILENO with it */
+  r = uv_fs_open(NULL, &fs_req, "stderr_file", O_CREAT | O_RDWR,
+      S_IRUSR | S_IWUSR, NULL);
+  TUV_ASSERT(r != -1);
+  uv_fs_req_cleanup(&fs_req);
+  stderr_file = dup2(r, STDERR_FILENO);
+  TUV_ASSERT(stderr_file != -1);
+
+  /* now we're going to swap them: the child process' stdout will be our
+   * stderr_file and vice versa */
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_INHERIT_FD;
+  options.stdio[1].data.fd = stderr_file;
+  options.stdio[2].flags = UV_INHERIT_FD;
+  options.stdio[2].data.fd = stdout_file;
+  options.stdio_count = 3;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  buf = uv_buf_init(output, sizeof(output));
+
+  /* check the content of stdout_file */
+  r = uv_fs_read(NULL, &fs_req, stdout_file, &buf, 1, 0, NULL);
+  TUV_ASSERT(r >= 15);
+  uv_fs_req_cleanup(&fs_req);
+
+  r = uv_fs_close(NULL, &fs_req, stdout_file, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&fs_req);
+
+  printf("output is: %s", output);
+  TUV_ASSERT(strncmp("hello errworld\n", output, 15) == 0);
+
+  /* check the content of stderr_file */
+  r = uv_fs_read(NULL, &fs_req, stderr_file, &buf, 1, 0, NULL);
+  TUV_ASSERT(r >= 12);
+  uv_fs_req_cleanup(&fs_req);
+
+  r = uv_fs_close(NULL, &fs_req, stderr_file, NULL);
+  TUV_ASSERT(r == 0);
+  uv_fs_req_cleanup(&fs_req);
+
+  printf("output is: %s", output);
+  TUV_ASSERT(strncmp("hello world\n", output, 12) == 0);
+
+  /* Cleanup. */
+  unlink("stdout_file");
+  unlink("stderr_file");
+
+  return 0;
+}
+#endif
+
+
+TEST_IMPL(spawn_stdin) {
+  int r;
+  uv_pipe_t out;
+  uv_pipe_t in;
+  uv_write_t write_req;
+  uv_buf_t buf;
+  uv_stdio_container_t stdio[2];
+  char buffer[] = "hello-from-spawn_stdin";
+
+  init_process_options("spawn_helper3", exit_cb);
+
+  uv_pipe_init(uv_default_loop(), &out, 0);
+  uv_pipe_init(uv_default_loop(), &in, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+  options.stdio[0].data.stream = (uv_stream_t*)&in;
+  options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[1].data.stream = (uv_stream_t*)&out;
+  options.stdio_count = 2;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  buf.base = buffer;
+  buf.len = sizeof(buffer);
+  r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 3); /* Once for process twice for the pipe. */
+  TUV_ASSERT(strcmp(buffer, output) == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_stdio_greater_than_3) {
+  int r;
+  uv_pipe_t pipe;
+  uv_stdio_container_t stdio[4];
+
+  init_process_options("spawn_helper5", exit_cb);
+
+  uv_pipe_init(uv_default_loop(), &pipe, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_IGNORE;
+  options.stdio[2].flags = UV_IGNORE;
+  options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[3].data.stream = (uv_stream_t*)&pipe;
+  options.stdio_count = 4;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &pipe, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
+  printf("output from stdio[3] is: %s", output);
+  TUV_ASSERT(strcmp("fourth stdio!\n", output) == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_ignored_stdio) {
+  int r;
+
+  init_process_options("spawn_helper6", exit_cb);
+
+  options.stdio = NULL;
+  options.stdio_count = 0;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_and_kill) {
+  int r;
+
+  init_process_options("spawn_helper4", kill_cb);
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_init(uv_default_loop(), &timer);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_start(&timer, timer_cb, 500, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 2); /* Once for process and once for timer. */
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_preserve_env) {
+  int r;
+  uv_pipe_t out;
+  uv_stdio_container_t stdio[2];
+
+  init_process_options("spawn_helper7", exit_cb);
+
+  uv_pipe_init(uv_default_loop(), &out, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[1].data.stream = (uv_stream_t*) &out;
+  options.stdio_count = 2;
+
+  r = putenv("ENV_TEST=testval");
+  TUV_ASSERT(r == 0);
+
+  /* Explicitly set options.env to NULL to test for env clobbering. */
+  options.env = NULL;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 2);
+
+  printf("output is: %s", output);
+  TUV_ASSERT(strcmp("testval", output) == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_detached) {
+  int r;
+
+  init_process_options("spawn_helper4", detach_failure_cb);
+
+  options.flags |= UV_PROCESS_DETACHED;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  uv_unref((uv_handle_t*)&process);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 0);
+
+  r = uv_kill(process.pid, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_kill(process.pid, 15);
+  TUV_ASSERT(r == 0);
+
+  return 0;
+}
+
+TEST_IMPL(spawn_and_kill_with_std) {
+  int r;
+  uv_pipe_t in, out, err;
+  uv_write_t write;
+  char message[] = "Nancy's joining me because the message this evening is "
+                   "not my message but ours.";
+  uv_buf_t buf;
+  uv_stdio_container_t stdio[3];
+
+  init_process_options("spawn_helper4", kill_cb);
+
+  r = uv_pipe_init(uv_default_loop(), &in, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_init(uv_default_loop(), &out, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_pipe_init(uv_default_loop(), &err, 0);
+  TUV_ASSERT(r == 0);
+
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+  options.stdio[0].data.stream = (uv_stream_t*)&in;
+  options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[1].data.stream = (uv_stream_t*)&out;
+  options.stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[2].data.stream = (uv_stream_t*)&err;
+  options.stdio_count = 3;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  buf = uv_buf_init(message, sizeof message);
+  r = uv_write(&write, (uv_stream_t*) &in, &buf, 1, write_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &err, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_init(uv_default_loop(), &timer);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_start(&timer, timer_cb, 500, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 5); /* process x 1, timer x 1, stdio x 3. */
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_and_ping) {
+  uv_write_t write_req;
+  uv_pipe_t in, out;
+  uv_buf_t buf;
+  uv_stdio_container_t stdio[2];
+  int r;
+
+  init_process_options("spawn_helper3", exit_cb);
+  buf = uv_buf_init("TEST", 4);
+
+  uv_pipe_init(uv_default_loop(), &out, 0);
+  uv_pipe_init(uv_default_loop(), &in, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+  options.stdio[0].data.stream = (uv_stream_t*)&in;
+  options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[1].data.stream = (uv_stream_t*)&out;
+  options.stdio_count = 2;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  /* Sending signum == 0 should check if the
+   * child process is still alive, not kill it.
+   */
+  r = uv_process_kill(&process, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(strcmp(output, "TEST") == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_same_stdout_stderr) {
+  uv_write_t write_req;
+  uv_pipe_t in, out;
+  uv_buf_t buf;
+  uv_stdio_container_t stdio[3];
+  int r;
+
+  init_process_options("spawn_helper3", exit_cb);
+  buf = uv_buf_init("TEST", 4);
+
+  uv_pipe_init(uv_default_loop(), &out, 0);
+  uv_pipe_init(uv_default_loop(), &in, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+  options.stdio[0].data.stream = (uv_stream_t*)&in;
+  options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[1].data.stream = (uv_stream_t*)&out;
+  options.stdio_count = 2;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  /* Sending signum == 0 should check if the
+   * child process is still alive, not kill it.
+   */
+  r = uv_process_kill(&process, 0);
+  TUV_ASSERT(r == 0);
+
+  r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(strcmp(output, "TEST") == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_closed_process_io) {
+  uv_pipe_t in;
+  uv_write_t write_req;
+  uv_buf_t buf;
+  uv_stdio_container_t stdio[2];
+  static char buffer[] = "hello-from-spawn_stdin\n";
+
+  init_process_options("spawn_helper3", exit_cb);
+
+  uv_pipe_init(uv_default_loop(), &in, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+  options.stdio[0].data.stream = (uv_stream_t*) &in;
+  options.stdio_count = 1;
+
+  close(0); /* Close process stdin. */
+
+  TUV_ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options));
+
+  buf = uv_buf_init(buffer, sizeof(buffer));
+  TUV_ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb));
+
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 2); /* process, child stdin */
+
+  return 0;
+}
+
+
+TEST_IMPL(kill) {
+  int r;
+
+#ifdef _WIN32
+  no_term_signal = 1;
+#endif
+
+  init_process_options("spawn_helper4", kill_cb);
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  /* Sending signum == 0 should check if the
+   * child process is still alive, not kill it.
+   */
+  r = uv_kill(process.pid, 0);
+  TUV_ASSERT(r == 0);
+
+  /* Kill the process. */
+  r = uv_kill(process.pid, /* SIGTERM */ 15);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
+
+
+#ifdef _WIN32
+TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) {
+  int r;
+  uv_pipe_t out;
+  char name[64];
+  HANDLE pipe_handle;
+  uv_stdio_container_t stdio[2];
+
+  init_process_options("spawn_helper2", exit_cb);
+
+  uv_pipe_init(uv_default_loop(), &out, 0);
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_IGNORE;
+  options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
+  options.stdio[1].data.stream = (uv_stream_t*)&out;
+  options.stdio_count = 2;
+
+  /* Create a pipe that'll cause a collision. */
+  snprintf(name,
+           sizeof(name),
+           "\\\\.\\pipe\\uv\\%p-%d",
+           &out,
+           GetCurrentProcessId());
+  pipe_handle = CreateNamedPipeA(name,
+                                PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+                                PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+                                10,
+                                65536,
+                                65536,
+                                0,
+                                NULL);
+  TUV_ASSERT(pipe_handle != INVALID_HANDLE_VALUE);
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */
+  printf("output is: %s", output);
+  TUV_ASSERT(strcmp("hello world\n", output) == 0);
+
+  return 0;
+}
+
+
+#if !defined(USING_UV_SHARED)
+int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr);
+WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target);
+
+TEST_IMPL(argument_escaping) {
+  const WCHAR* test_str[] = {
+    L"",
+    L"HelloWorld",
+    L"Hello World",
+    L"Hello\"World",
+    L"Hello World\\",
+    L"Hello\\\"World",
+    L"Hello\\World",
+    L"Hello\\\\World",
+    L"Hello World\\",
+    L"c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\""
+  };
+  const int count = sizeof(test_str) / sizeof(*test_str);
+  WCHAR** test_output;
+  WCHAR* command_line;
+  WCHAR** cracked;
+  size_t total_size = 0;
+  int i;
+  int num_args;
+  int result;
+
+  char* verbatim[] = {
+    "cmd.exe",
+    "/c",
+    "c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"",
+    NULL
+  };
+  WCHAR* verbatim_output;
+  WCHAR* non_verbatim_output;
+
+  test_output = calloc(count, sizeof(WCHAR*));
+  TUV_ASSERT(test_output != NULL);
+  for (i = 0; i < count; ++i) {
+    test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(WCHAR));
+    quote_cmd_arg(test_str[i], test_output[i]);
+    wprintf(L"input : %s\n", test_str[i]);
+    wprintf(L"output: %s\n", test_output[i]);
+    total_size += wcslen(test_output[i]) + 1;
+  }
+  command_line = calloc(total_size + 1, sizeof(WCHAR));
+  TUV_ASSERT(command_line != NULL);
+  for (i = 0; i < count; ++i) {
+    wcscat(command_line, test_output[i]);
+    wcscat(command_line, L" ");
+  }
+  command_line[total_size - 1] = L'\0';
+
+  wprintf(L"command_line: %s\n", command_line);
+
+  cracked = CommandLineToArgvW(command_line, &num_args);
+  for (i = 0; i < num_args; ++i) {
+    wprintf(L"%d: %s\t%s\n", i, test_str[i], cracked[i]);
+    TUV_ASSERT(wcscmp(test_str[i], cracked[i]) == 0);
+  }
+
+  LocalFree(cracked);
+  for (i = 0; i < count; ++i) {
+    free(test_output[i]);
+  }
+
+  result = make_program_args(verbatim, 1, &verbatim_output);
+  TUV_ASSERT(result == 0);
+  result = make_program_args(verbatim, 0, &non_verbatim_output);
+  TUV_ASSERT(result == 0);
+
+  wprintf(L"    verbatim_output: %s\n", verbatim_output);
+  wprintf(L"non_verbatim_output: %s\n", non_verbatim_output);
+
+  TUV_ASSERT(wcscmp(verbatim_output,
+                L"cmd.exe /c c:\\path\\to\\node.exe --eval "
+                L"\"require('c:\\\\path\\\\to\\\\test.js')\"") == 0);
+  TUV_ASSERT(wcscmp(non_verbatim_output,
+                L"cmd.exe /c \"c:\\path\\to\\node.exe --eval "
+                L"\\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0);
+
+  free(verbatim_output);
+  free(non_verbatim_output);
+
+  return 0;
+}
+
+int make_program_env(char** env_block, WCHAR** dst_ptr);
+
+TEST_IMPL(environment_creation) {
+  int i;
+  char* environment[] = {
+    "FOO=BAR",
+    "SYSTEM=ROOT", /* substring of a supplied var name */
+    "SYSTEMROOTED=OMG", /* supplied var name is a substring */
+    "TEMP=C:\\Temp",
+    "INVALID",
+    "BAZ=QUX",
+    "B_Z=QUX",
+    "B\xe2\x82\xacZ=QUX",
+    "B\xf0\x90\x80\x82Z=QUX",
+    "B\xef\xbd\xa1Z=QUX",
+    "B\xf0\xa3\x91\x96Z=QUX",
+    "BAZ", /* repeat, invalid variable */
+    NULL
+  };
+  WCHAR* wenvironment[] = {
+    L"BAZ=QUX",
+    L"B_Z=QUX",
+    L"B\x20acZ=QUX",
+    L"B\xd800\xdc02Z=QUX",
+    L"B\xd84d\xdc56Z=QUX",
+    L"B\xff61Z=QUX",
+    L"FOO=BAR",
+    L"SYSTEM=ROOT", /* substring of a supplied var name */
+    L"SYSTEMROOTED=OMG", /* supplied var name is a substring */
+    L"TEMP=C:\\Temp",
+  };
+  WCHAR* from_env[] = {
+    /* list should be kept in sync with list
+     * in process.c, minus variables in wenvironment */
+    L"HOMEDRIVE",
+    L"HOMEPATH",
+    L"LOGONSERVER",
+    L"PATH",
+    L"USERDOMAIN",
+    L"USERNAME",
+    L"USERPROFILE",
+    L"SYSTEMDRIVE",
+    L"SYSTEMROOT",
+    L"WINDIR",
+    /* test for behavior in the absence of a
+     * required-environment variable: */
+    L"ZTHIS_ENV_VARIABLE_DOES_NOT_EXIST",
+  };
+  int found_in_loc_env[ARRAY_SIZE(wenvironment)] = {0};
+  int found_in_usr_env[ARRAY_SIZE(from_env)] = {0};
+  WCHAR *expected[ARRAY_SIZE(from_env)];
+  int result;
+  WCHAR* str;
+  WCHAR* prev;
+  WCHAR* env;
+
+  for (i = 0; i < ARRAY_SIZE(from_env); i++) {
+      /* copy expected additions to environment locally */
+      size_t len = GetEnvironmentVariableW(from_env[i], NULL, 0);
+      if (len == 0) {
+        found_in_usr_env[i] = 1;
+        str = malloc(1 * sizeof(WCHAR));
+        *str = 0;
+        expected[i] = str;
+      } else {
+        size_t name_len = wcslen(from_env[i]);
+        str = malloc((name_len+1+len) * sizeof(WCHAR));
+        wmemcpy(str, from_env[i], name_len);
+        expected[i] = str;
+        str += name_len;
+        *str++ = L'=';
+        GetEnvironmentVariableW(from_env[i], str, len);
+     }
+  }
+
+  result = make_program_env(environment, &env);
+  TUV_ASSERT(result == 0);
+
+  for (str = env, prev = NULL; *str; prev = str, str += wcslen(str) + 1) {
+    int found = 0;
+#if 0
+    _cputws(str);
+    putchar('\n');
+#endif
+    for (i = 0; i < ARRAY_SIZE(wenvironment) && !found; i++) {
+      if (!wcscmp(str, wenvironment[i])) {
+        TUV_ASSERT(!found_in_loc_env[i]);
+        found_in_loc_env[i] = 1;
+        found = 1;
+      }
+    }
+    for (i = 0; i < ARRAY_SIZE(expected) && !found; i++) {
+      if (!wcscmp(str, expected[i])) {
+        TUV_ASSERT(!found_in_usr_env[i]);
+        found_in_usr_env[i] = 1;
+        found = 1;
+      }
+    }
+    if (prev) { /* verify sort order -- requires Vista */
+#if _WIN32_WINNT >= 0x0600 && \
+    (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR))
+      TUV_ASSERT(CompareStringOrdinal(prev, -1, str, -1, TRUE) == 1);
+#endif
+    }
+    TUV_ASSERT(found); /* verify that we expected this variable */
+  }
+
+  /* verify that we found all expected variables */
+  for (i = 0; i < ARRAY_SIZE(wenvironment); i++) {
+    TUV_ASSERT(found_in_loc_env[i]);
+  }
+  for (i = 0; i < ARRAY_SIZE(expected); i++) {
+    TUV_ASSERT(found_in_usr_env[i]);
+  }
+
+  return 0;
+}
+#endif
+
+/* Regression test for issue #909 */
+TEST_IMPL(spawn_with_an_odd_path) {
+  int r;
+
+  char newpath[2048];
+  char *path = getenv("PATH");
+  TUV_ASSERT(path != NULL);
+  snprintf(newpath, 2048, ";.;%s", path);
+  SetEnvironmentVariable("PATH", newpath);
+
+  init_process_options("", exit_cb);
+  options.file = options.args[0] = "program-that-had-better-not-exist";
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == UV_ENOENT || r == UV_EACCES);
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &process));
+  uv_close((uv_handle_t*) &process, NULL);
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  return 0;
+}
+#endif
+
+#ifndef _WIN32
+TEST_IMPL(spawn_setuid_fails) {
+  int r;
+
+  /* if root, become nobody. */
+  uv_uid_t uid = getuid();
+  if (uid == 0) {
+    struct passwd* pw;
+    pw = getpwnam("nobody");
+    TUV_ASSERT(pw != NULL);
+    TUV_ASSERT(0 == setgid(pw->pw_gid));
+    TUV_ASSERT(0 == setuid(pw->pw_uid));
+  }
+
+  init_process_options("spawn_helper1", fail_cb);
+
+  options.flags |= UV_PROCESS_SETUID;
+  options.uid = 0;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == UV_EPERM);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(close_cb_called == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_setgid_fails) {
+  int r;
+
+  /* if root, become nobody. */
+  uv_uid_t uid = getuid();
+  if (uid == 0) {
+    struct passwd* pw;
+    pw = getpwnam("nobody");
+    TUV_ASSERT(pw != NULL);
+    TUV_ASSERT(0 == setgid(pw->pw_gid));
+    TUV_ASSERT(0 == setuid(pw->pw_uid));
+  }
+
+  init_process_options("spawn_helper1", fail_cb);
+
+  options.flags |= UV_PROCESS_SETGID;
+  options.gid = 0;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == UV_EPERM);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(close_cb_called == 0);
+
+  return 0;
+}
+#endif
+
+
+#ifdef _WIN32
+
+static void exit_cb_unexpected(uv_process_t* process,
+                               int64_t exit_status,
+                               int term_signal) {
+  TUV_ASSERT(0 && "should not have been called");
+}
+
+
+TEST_IMPL(spawn_setuid_fails) {
+  int r;
+
+  init_process_options("spawn_helper1", exit_cb_unexpected);
+
+  options.flags |= UV_PROCESS_SETUID;
+  options.uid = (uv_uid_t) -42424242;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == UV_ENOTSUP);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(close_cb_called == 0);
+
+  return 0;
+}
+
+
+TEST_IMPL(spawn_setgid_fails) {
+  int r;
+
+  init_process_options("spawn_helper1", exit_cb_unexpected);
+
+  options.flags |= UV_PROCESS_SETGID;
+  options.gid = (uv_gid_t) -42424242;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == UV_ENOTSUP);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(close_cb_called == 0);
+
+  return 0;
+}
+#endif
+
+
+TEST_IMPL(spawn_auto_unref) {
+  init_process_options("spawn_helper1", NULL);
+  TUV_ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options));
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+  TUV_ASSERT(0 == uv_is_closing((uv_handle_t*) &process));
+  uv_close((uv_handle_t*) &process, NULL);
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+  TUV_ASSERT(1 == uv_is_closing((uv_handle_t*) &process));
+  return 0;
+}
+
+
+#ifndef _WIN32
+TEST_IMPL(spawn_fs_open) {
+  int fd;
+  uv_fs_t fs_req;
+  uv_pipe_t in;
+  uv_write_t write_req;
+  uv_buf_t buf;
+  uv_stdio_container_t stdio[1];
+
+  fd = uv_fs_open(NULL, &fs_req, "/dev/null", O_RDWR, 0, NULL);
+  TUV_ASSERT(fd >= 0);
+  uv_fs_req_cleanup(&fs_req);
+
+  init_process_options("spawn_helper8", exit_cb);
+
+  TUV_ASSERT(0 == uv_pipe_init(uv_default_loop(), &in, 0));
+
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
+  options.stdio[0].data.stream = (uv_stream_t*) &in;
+  options.stdio_count = 1;
+
+  TUV_ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options));
+
+  buf = uv_buf_init((char*) &fd, sizeof(fd));
+  TUV_ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb));
+
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+  TUV_ASSERT(0 == uv_fs_close(NULL, &fs_req, fd, NULL));
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 2);  /* One for `in`, one for process */
+
+  return 0;
+}
+#endif  /* !_WIN32 */
+
+
+#ifndef _WIN32
+TEST_IMPL(closed_fd_events) {
+  uv_stdio_container_t stdio[3];
+  uv_pipe_t pipe_handle;
+  int fd[2];
+
+  /* create a pipe and share it with a child process */
+  TUV_ASSERT(0 == pipe(fd));
+
+  /* spawn_helper4 blocks indefinitely. */
+  init_process_options("spawn_helper4", exit_cb);
+  options.stdio_count = 3;
+  options.stdio = stdio;
+  options.stdio[0].flags = UV_INHERIT_FD;
+  options.stdio[0].data.fd = fd[0];
+  options.stdio[1].flags = UV_IGNORE;
+  options.stdio[2].flags = UV_IGNORE;
+
+  TUV_ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options));
+  uv_unref((uv_handle_t*) &process);
+
+  /* read from the pipe with uv */
+  TUV_ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0));
+  TUV_ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0]));
+  fd[0] = -1;
+
+  TUV_ASSERT(0 == uv_read_start((uv_stream_t*) &pipe_handle, on_alloc, on_read_once));
+
+  TUV_ASSERT(1 == write(fd[1], "", 1));
+
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
+
+  /* should have received just one byte */
+  TUV_ASSERT(output_used == 1);
+
+  /* close the pipe and see if we still get events */
+  uv_close((uv_handle_t*) &pipe_handle, close_cb);
+
+  TUV_ASSERT(1 == write(fd[1], "", 1));
+
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &timer));
+  TUV_ASSERT(0 == uv_timer_start(&timer, timer_counter_cb, 10, 0));
+
+  /* see if any spurious events interrupt the timer */
+  if (1 == uv_run(uv_default_loop(), UV_RUN_ONCE))
+    /* have to run again to really trigger the timer */
+    TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
+
+  TUV_ASSERT(timer_counter == 1);
+
+  /* cleanup */
+  TUV_ASSERT(0 == uv_process_kill(&process, /* SIGTERM */ 15));
+  TUV_ASSERT(0 == close(fd[1]));
+
+  return 0;
+}
+#endif  /* !_WIN32 */
+
+TEST_IMPL(spawn_reads_child_path) {
+  int r;
+  int len;
+  char file[64];
+  char path[1024];
+  char* env[3];
+
+  /* Need to carry over the dynamic linker path when the test runner is
+   * linked against libuv.so, see https://github.com/libuv/libuv/issues/85.
+   */
+#if defined(__APPLE__)
+  static const char dyld_path_var[] = "DYLD_LIBRARY_PATH";
+#elif defined __MVS__
+  static const char dyld_path_var[] = "LIBPATH";
+#else
+  static const char dyld_path_var[] = "LD_LIBRARY_PATH";
+#endif
+
+  /* Set up the process, but make sure that the file to run is relative and */
+  /* requires a lookup into PATH */
+  init_process_options("spawn_helper1", exit_cb);
+
+  /* Set up the PATH env variable */
+  for (len = strlen(exepath);
+       exepath[len - 1] != '/' && exepath[len - 1] != '\\';
+       len--);
+  strcpy(file, exepath + len);
+  exepath[len] = 0;
+  strcpy(path, "PATH=");
+  strcpy(path + 5, exepath);
+
+  env[0] = path;
+  env[1] = getenv(dyld_path_var);
+  env[2] = NULL;
+
+  if (env[1] != NULL) {
+    static char buf[1024 + sizeof(dyld_path_var)];
+    snprintf(buf, sizeof(buf), "%s=%s", dyld_path_var, env[1]);
+    env[1] = buf;
+  }
+
+  options.file = file;
+  options.args[0] = file;
+  options.env = env;
+
+  r = uv_spawn(uv_default_loop(), &process, &options);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
+
+#ifndef _WIN32
+static int mpipe(int *fds) {
+  if (pipe(fds) == -1)
+    return -1;
+  if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
+      fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
+    close(fds[0]);
+    close(fds[1]);
+    return -1;
+  }
+  return 0;
+}
+#else
+static int mpipe(int *fds) {
+  SECURITY_ATTRIBUTES attr;
+  HANDLE readh, writeh;
+  attr.nLength = sizeof(attr);
+  attr.lpSecurityDescriptor = NULL;
+  attr.bInheritHandle = FALSE;
+  if (!CreatePipe(&readh, &writeh, &attr, 0))
+    return -1;
+  fds[0] = _open_osfhandle((intptr_t)readh, 0);
+  fds[1] = _open_osfhandle((intptr_t)writeh, 0);
+  if (fds[0] == -1 || fds[1] == -1) {
+    CloseHandle(readh);
+    CloseHandle(writeh);
+    return -1;
+  }
+  return 0;
+}
+#endif /* !_WIN32 */
+
+TEST_IMPL(spawn_inherit_streams) {
+  uv_process_t child_req;
+  uv_stdio_container_t child_stdio[2];
+  int fds_stdin[2];
+  int fds_stdout[2];
+  uv_pipe_t pipe_stdin_child;
+  uv_pipe_t pipe_stdout_child;
+  uv_pipe_t pipe_stdin_parent;
+  uv_pipe_t pipe_stdout_parent;
+  unsigned char ubuf[OUTPUT_SIZE - 1];
+  uv_buf_t buf;
+  unsigned int i;
+  int r;
+  uv_write_t write_req;
+  uv_loop_t* loop;
+
+  init_process_options("spawn_helper9", exit_cb);
+
+  loop = uv_default_loop();
+  TUV_ASSERT(uv_pipe_init(loop, &pipe_stdin_child, 0) == 0);
+  TUV_ASSERT(uv_pipe_init(loop, &pipe_stdout_child, 0) == 0);
+  TUV_ASSERT(uv_pipe_init(loop, &pipe_stdin_parent, 0) == 0);
+  TUV_ASSERT(uv_pipe_init(loop, &pipe_stdout_parent, 0) == 0);
+
+  TUV_ASSERT(mpipe(fds_stdin) != -1);
+  TUV_ASSERT(mpipe(fds_stdout) != -1);
+
+  TUV_ASSERT(uv_pipe_open(&pipe_stdin_child, fds_stdin[0]) == 0);
+  TUV_ASSERT(uv_pipe_open(&pipe_stdout_child, fds_stdout[1]) == 0);
+  TUV_ASSERT(uv_pipe_open(&pipe_stdin_parent, fds_stdin[1]) == 0);
+  TUV_ASSERT(uv_pipe_open(&pipe_stdout_parent, fds_stdout[0]) == 0);
+
+  child_stdio[0].flags = UV_INHERIT_STREAM;
+  child_stdio[0].data.stream = (uv_stream_t *)&pipe_stdin_child;
+
+  child_stdio[1].flags = UV_INHERIT_STREAM;
+  child_stdio[1].data.stream = (uv_stream_t *)&pipe_stdout_child;
+
+  options.stdio = child_stdio;
+  options.stdio_count = 2;
+
+  TUV_ASSERT(uv_spawn(loop, &child_req, &options) == 0);
+
+  uv_close((uv_handle_t*)&pipe_stdin_child, NULL);
+  uv_close((uv_handle_t*)&pipe_stdout_child, NULL);
+
+  buf = uv_buf_init((char*)ubuf, sizeof ubuf);
+  for (i = 0; i < sizeof ubuf; ++i)
+    ubuf[i] = i & 255u;
+  memset(output, 0, sizeof ubuf);
+
+  r = uv_write(&write_req,
+               (uv_stream_t*)&pipe_stdin_parent,
+               &buf,
+               1,
+               write_cb);
+  TUV_ASSERT(r == 0);
+
+  r = uv_read_start((uv_stream_t*)&pipe_stdout_parent, on_alloc, on_read);
+  TUV_ASSERT(r == 0);
+
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(exit_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 3);
+
+  r = memcmp(ubuf, output, sizeof ubuf);
+  TUV_ASSERT(r == 0);
+
+  return 0;
+}
+
+/* Helper for child process of spawn_inherit_streams */
+#ifndef _WIN32
+int spawn_stdin_stdout(void) {
+  char buf[1024];
+  char* pbuf;
+  for (;;) {
+    ssize_t r, w, c;
+    do {
+      r = read(0, buf, sizeof buf);
+    } while (r == -1 && errno == EINTR);
+    if (r == 0) {
+      return 1;
+    }
+    TUV_ASSERT(r > 0);
+    c = r;
+    pbuf = buf;
+    while (c) {
+      do {
+        w = write(1, pbuf, (size_t)c);
+      } while (w == -1 && errno == EINTR);
+      TUV_ASSERT(w >= 0);
+      pbuf = pbuf + w;
+      c = c - w;
+    }
+  }
+  return 2;
+}
+#else
+int spawn_stdin_stdout(void) {
+  char buf[1024];
+  char* pbuf;
+  HANDLE h_stdin = GetStdHandle(STD_INPUT_HANDLE);
+  HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
+  TUV_ASSERT(h_stdin != INVALID_HANDLE_VALUE);
+  TUV_ASSERT(h_stdout != INVALID_HANDLE_VALUE);
+  for (;;) {
+    DWORD n_read;
+    DWORD n_written;
+    DWORD to_write;
+    if (!ReadFile(h_stdin, buf, sizeof buf, &n_read, NULL)) {
+      TUV_ASSERT(GetLastError() == ERROR_BROKEN_PIPE);
+      return 1;
+    }
+    to_write = n_read;
+    pbuf = buf;
+    while (to_write) {
+      TUV_ASSERT(WriteFile(h_stdout, pbuf, to_write, &n_written, NULL));
+      to_write -= n_written;
+      pbuf += n_written;
+    }
+  }
+  return 2;
+}
+#endif /* !_WIN32 */
diff --git a/test/test_tcp_open.c b/test/test_tcp_open.c
new file mode 100644 (file)
index 0000000..7256731
--- /dev/null
@@ -0,0 +1,210 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+static int shutdown_cb_called = 0;
+static int connect_cb_called = 0;
+static int write_cb_called = 0;
+static int close_cb_called = 0;
+
+static uv_connect_t connect_req;
+static uv_shutdown_t shutdown_req;
+static uv_write_t write_req;
+
+
+static void startup(void) {
+  usleep(1000);
+}
+
+
+static uv_os_sock_t create_tcp_socket(void) {
+  uv_os_sock_t sock;
+
+  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+  TUV_ASSERT(sock >= 0);
+
+  {
+    /* Allow reuse of the port. */
+    int yes = 1;
+    int r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
+    TUV_ASSERT(r == 0);
+  }
+
+  return sock;
+}
+
+
+static void alloc_cb(uv_handle_t* handle,
+                     size_t suggested_size,
+                     uv_buf_t* buf) {
+#ifdef EMBED_LOW_MEMORY
+  static char slab[2048];
+#else
+  static char slab[65536];
+#endif
+  TUV_ASSERT(suggested_size <= sizeof(slab));
+  buf->base = slab;
+  buf->len = sizeof(slab);
+}
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  close_cb_called++;
+}
+
+
+static void shutdown_cb(uv_shutdown_t* req, int status) {
+  TUV_ASSERT(req == &shutdown_req);
+  TUV_ASSERT(status == 0);
+
+  /* Now we wait for the EOF */
+  shutdown_cb_called++;
+}
+
+
+static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) {
+  TUV_ASSERT(tcp != NULL);
+
+  if (nread > 0) {
+    TUV_ASSERT(nread == 4);
+    TUV_ASSERT(memcmp("PING", buf->base, nread) == 0);
+
+    /* tell server to close connection */
+    {
+      uv_buf_t buf = uv_buf_init((char*)"QS", 2);
+      int r = uv_write(&write_req, tcp, &buf, 1, NULL);
+      TUV_ASSERT(r == 0);
+    }
+  }
+  else if (nread == 0) {
+
+  }
+  else if (nread < 0) {
+    TUV_ASSERT(nread == UV_EOF);
+    uv_close((uv_handle_t*)tcp, close_cb);
+  }
+}
+
+
+static void write_cb(uv_write_t* req, int status) {
+  TUV_ASSERT(req != NULL);
+
+  if (status) {
+    TDLOG("tcp_open uv_write error: %s", uv_strerror(status));
+    TUV_ASSERT(0);
+  }
+
+  write_cb_called++;
+}
+
+
+static void connect_cb(uv_connect_t* req, int status) {
+  uv_buf_t buf = uv_buf_init((char*)"PING", 4);
+  uv_stream_t* stream;
+  int r;
+
+  if (status == UV__ECONNREFUSED) {
+    TDLOG("tcp_open Connection refused. Run server first.");
+    TUV_ASSERT(0);
+  }
+
+  TUV_ASSERT(req == &connect_req);
+  TUV_ASSERT(status == 0);
+
+  stream = req->handle;
+  connect_cb_called++;
+
+  r = uv_write(&write_req, stream, &buf, 1, write_cb);
+  TUV_ASSERT(r == 0);
+
+  /* Shutdown on drain. */
+  // r = uv_shutdown(&shutdown_req, stream, shutdown_cb);
+  //TUV_ASSERT(r == 0);
+
+  /* Start reading */
+  r = uv_read_start(stream, alloc_cb, read_cb);
+  TUV_ASSERT(r == 0);
+}
+
+
+TEST_IMPL(tcp_open) {
+  struct sockaddr_in addr;
+  uv_tcp_t client;
+  uv_os_sock_t sock;
+  int r;
+
+  shutdown_cb_called = 0;
+  connect_cb_called = 0;
+  write_cb_called = 0;
+  close_cb_called = 0;
+
+  TUV_ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
+
+  startup();
+  sock = create_tcp_socket();
+
+  r = uv_tcp_init(uv_default_loop(), &client);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_open(&client, sock);
+  TUV_ASSERT(r == 0);
+
+  r = uv_tcp_connect(&connect_req,
+                     &client,
+                     (const struct sockaddr*)&addr,
+                     connect_cb);
+  TUV_ASSERT(r == 0);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  //TUV_ASSERT(shutdown_cb_called == 1);
+  TUV_ASSERT(connect_cb_called == 1);
+  TUV_ASSERT(write_cb_called == 1);
+  TUV_ASSERT(close_cb_called == 1);
+
+  return 0;
+}
diff --git a/test/test_threadpool.c b/test/test_threadpool.c
new file mode 100644 (file)
index 0000000..a7742cc
--- /dev/null
@@ -0,0 +1,84 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+static int work_cb_count;
+static int after_work_cb_count;
+static uv_work_t work_req;
+static char data;
+
+
+static void work_cb(uv_work_t* req) {
+  TUV_ASSERT(req == &work_req);
+  TUV_ASSERT(req->data == &data);
+  work_cb_count++;
+}
+
+
+static void after_work_cb(uv_work_t* req, int status) {
+  TUV_ASSERT(status == 0);
+  TUV_ASSERT(req == &work_req);
+  TUV_ASSERT(req->data == &data);
+  after_work_cb_count++;
+}
+
+
+TEST_IMPL(threadpool_queue_work_simple) {
+  int r;
+
+  work_cb_count = 0;
+  after_work_cb_count = 0;
+
+  work_req.data = &data;
+  r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb);
+  TUV_ASSERT(r == 0);
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+
+  TUV_ASSERT(work_cb_count == 1);
+  TUV_ASSERT(after_work_cb_count == 1);
+
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
diff --git a/test/test_timer.c b/test/test_timer.c
new file mode 100644 (file)
index 0000000..524499c
--- /dev/null
@@ -0,0 +1,370 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <string.h>
+#include <uv.h>
+
+
+#include "uv-common.h"
+#include "runner.h"
+
+
+//-----------------------------------------------------------------------------
+
+TEST_IMPL(timer_init) {
+  uv_timer_t handle;
+
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &handle));
+  TUV_ASSERT(0 == uv_timer_get_repeat(&handle));
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) &handle));
+
+  // for platforms that needs cleaning
+  uv_close((uv_handle_t*) &handle, NULL);
+  uv_run(uv_default_loop(), UV_RUN_ONCE);
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+static int once_cb_called = 0;
+static int once_close_cb_called = 0;
+static int repeat_cb_called = 0;
+static int repeat_close_cb_called = 0;
+
+static uint64_t start_time = 0ll;
+
+
+static void once_close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(0 == uv_is_active(handle));
+
+  once_close_cb_called++;
+}
+
+
+static void once_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(0 == uv_is_active((uv_handle_t*) handle));
+
+  once_cb_called++;
+
+  uv_close((uv_handle_t*)handle, once_close_cb);
+
+  /* Just call this randomly for the code coverage. */
+  uv_update_time(uv_default_loop());
+}
+
+
+static void repeat_close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+
+  repeat_close_cb_called++;
+}
+
+
+static void repeat_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle != NULL);
+  TUV_ASSERT(1 == uv_is_active((uv_handle_t*) handle));
+
+  repeat_cb_called++;
+
+  if (repeat_cb_called == 5) {
+    uv_close((uv_handle_t*)handle, repeat_close_cb);
+  }
+}
+
+
+static void never_cb(uv_timer_t* handle) {
+  TUV_FATAL("never_cb should never be called");
+}
+
+
+TEST_IMPL(timer) {
+  uv_timer_t once_timers[10];
+  uv_timer_t *once;
+  uv_timer_t repeat, never;
+  unsigned int i;
+  int r;
+
+  once_cb_called = 0;
+  once_close_cb_called = 0;
+  repeat_cb_called = 0;
+  repeat_close_cb_called = 0;
+
+  start_time = uv_now(uv_default_loop());
+  TUV_ASSERT(0 < start_time);
+
+  /* Let 10 timers time out in 500 ms total. */
+  for (i = 0; i < ARRAY_SIZE(once_timers); i++) {
+    once = once_timers + i;
+    r = uv_timer_init(uv_default_loop(), once);
+    TUV_ASSERT(r == 0);
+    r = uv_timer_start(once, once_cb, i * 50, 0);
+    TUV_ASSERT(r == 0);
+  }
+
+  /* The 11th timer is a repeating timer that runs 4 times */
+  r = uv_timer_init(uv_default_loop(), &repeat);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&repeat, repeat_cb, 100, 100);
+  TUV_ASSERT(r == 0);
+
+  /* The 12th timer should not do anything. */
+  r = uv_timer_init(uv_default_loop(), &never);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&never, never_cb, 100, 100);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_stop(&never);
+  TUV_ASSERT(r == 0);
+  uv_unref((uv_handle_t*)&never);
+  // for platforms that needs cleaning
+  uv_close((uv_handle_t*) &never, NULL);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(once_cb_called == 10);
+  TUV_ASSERT(once_close_cb_called == 10);
+  TUV_ASSERT(repeat_cb_called == 5);
+  TUV_ASSERT(repeat_close_cb_called == 1);
+
+  TUV_ASSERT(500 <= uv_now(uv_default_loop()) - start_time);
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+
+//-----------------------------------------------------------------------------
+
+TEST_IMPL(timer_start_twice) {
+  uv_timer_t once;
+  int r;
+
+  once_cb_called = 0;
+
+  r = uv_timer_init(uv_default_loop(), &once);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&once, never_cb, 86400 * 1000, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&once, once_cb, 10, 0);
+  TUV_ASSERT(r == 0);
+  r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(once_cb_called == 1);
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+static int order_cb_called = 0;
+
+static void order_cb_a(uv_timer_t *handle) {
+  TUV_ASSERT(order_cb_called++ == *(int*)handle->data);
+}
+
+
+static void order_cb_b(uv_timer_t *handle) {
+  TUV_ASSERT(order_cb_called++ == *(int*)handle->data);
+}
+
+
+TEST_IMPL(timer_order) {
+  int first;
+  int second;
+  uv_timer_t handle_a;
+  uv_timer_t handle_b;
+
+  order_cb_called = 0;
+
+  first = 0;
+  second = 1;
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_a));
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_b));
+
+  /* Test for starting handle_a then handle_b */
+  handle_a.data = &first;
+  TUV_ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0));
+  handle_b.data = &second;
+  TUV_ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0));
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  TUV_ASSERT(order_cb_called == 2);
+
+  TUV_ASSERT(0 == uv_timer_stop(&handle_a));
+  TUV_ASSERT(0 == uv_timer_stop(&handle_b));
+
+  /* Test for starting handle_b then handle_a */
+  order_cb_called = 0;
+  handle_b.data = &first;
+  TUV_ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0));
+
+  handle_a.data = &second;
+  TUV_ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0));
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  TUV_ASSERT(order_cb_called == 2);
+
+  // for platforms that needs cleaning
+  uv_close((uv_handle_t*) &handle_a, NULL);
+  uv_close((uv_handle_t*) &handle_b, NULL);
+  uv_run(uv_default_loop(), UV_RUN_ONCE);
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+static uv_timer_t tiny_timer;
+static uv_timer_t huge_timer1;
+static uv_timer_t huge_timer2;
+
+
+static void tiny_timer_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle == &tiny_timer);
+  uv_close((uv_handle_t*) &tiny_timer, NULL);
+  uv_close((uv_handle_t*) &huge_timer1, NULL);
+  uv_close((uv_handle_t*) &huge_timer2, NULL);
+}
+
+
+TEST_IMPL(timer_huge_timeout) {
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer));
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1));
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer2));
+  TUV_ASSERT(0 == uv_timer_start(&tiny_timer, tiny_timer_cb, 1, 0));
+  TUV_ASSERT(0 == uv_timer_start(&huge_timer1, tiny_timer_cb, 0xffffffffffffLL, 0));
+  TUV_ASSERT(0 == uv_timer_start(&huge_timer2, tiny_timer_cb, (uint64_t) -1, 0));
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+static int huge_repeat_cb_ncalls;
+
+static void huge_repeat_cb(uv_timer_t* handle) {
+  if (huge_repeat_cb_ncalls == 0)
+    TUV_ASSERT(handle == &huge_timer1);
+  else
+    TUV_ASSERT(handle == &tiny_timer);
+
+  if (++huge_repeat_cb_ncalls == 10) {
+    uv_close((uv_handle_t*) &tiny_timer, NULL);
+    uv_close((uv_handle_t*) &huge_timer1, NULL);
+  }
+}
+
+
+TEST_IMPL(timer_huge_repeat) {
+  huge_repeat_cb_ncalls = 0;
+
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer));
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1));
+  TUV_ASSERT(0 == uv_timer_start(&tiny_timer, huge_repeat_cb, 2, 2));
+  TUV_ASSERT(0 == uv_timer_start(&huge_timer1, huge_repeat_cb, 1, (uint64_t) -1));
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+
+static unsigned int timer_run_once_timer_cb_called = 0;
+
+static void timer_run_once_timer_cb(uv_timer_t* handle) {
+  timer_run_once_timer_cb_called++;
+}
+
+
+TEST_IMPL(timer_run_once) {
+  uv_timer_t timer_handle;
+
+  timer_run_once_timer_cb_called = 0;
+
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle));
+  TUV_ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 0, 0));
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
+  TUV_ASSERT(1 == timer_run_once_timer_cb_called);
+
+  TUV_ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 1, 0));
+  // slow systems may have nano second resolution
+  // give some time to sleep so that time tick is changed
+  uv_sleep(1);
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
+  TUV_ASSERT(2 == timer_run_once_timer_cb_called);
+
+  uv_close((uv_handle_t*)&timer_handle, NULL);
+  TUV_ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
+
+  // for platforms that needs cleaning
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
+
+
+TEST_IMPL(timer_null_callback) {
+  uv_timer_t handle;
+
+  TUV_ASSERT(0 == uv_timer_init(uv_default_loop(), &handle));
+  TUV_ASSERT(UV_EINVAL == uv_timer_start(&handle, NULL, 100, 100));
+
+  // for platform that needs closing
+  tuv_timer_deinit(uv_default_loop(), &handle);
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
diff --git a/test/test_timer_again.c b/test/test_timer_again.c
new file mode 100644 (file)
index 0000000..311fb79
--- /dev/null
@@ -0,0 +1,149 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 "runner.h"
+
+
+static int close_cb_called = 0;
+static int repeat_1_cb_called = 0;
+static int repeat_2_cb_called = 0;
+static int repeat_2_cb_allowed = 0;
+
+static uv_timer_t dummy, repeat_1, repeat_2;
+
+static uint64_t start_time;
+
+
+static void close_cb(uv_handle_t* handle) {
+  TUV_ASSERT(handle != NULL);
+
+  close_cb_called++;
+}
+
+
+static void repeat_1_cb(uv_timer_t* handle) {
+  int r;
+
+  TUV_ASSERT(handle == &repeat_1);
+  TUV_ASSERT(uv_timer_get_repeat((uv_timer_t*)handle) == 50);
+
+  repeat_1_cb_called++;
+
+  r = uv_timer_again(&repeat_2);
+  TUV_ASSERT(r == 0);
+
+  if (repeat_1_cb_called == 10) {
+    uv_close((uv_handle_t*)handle, close_cb);
+    /* We're not calling uv_timer_again on repeat_2 any more, so after this */
+    /* timer_2_cb is expected. */
+    repeat_2_cb_allowed = 1;
+    return;
+  }
+}
+
+
+static void repeat_2_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle == &repeat_2);
+  TUV_ASSERT(repeat_2_cb_allowed);
+
+  repeat_2_cb_called++;
+
+  if (uv_timer_get_repeat(&repeat_2) == 0) {
+    TUV_ASSERT(0 == uv_is_active((uv_handle_t*) handle));
+    uv_close((uv_handle_t*)handle, close_cb);
+    return;
+  }
+
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_2) == 100);
+
+  /* This shouldn't take effect immediately. */
+  uv_timer_set_repeat(&repeat_2, 0);
+}
+
+
+TEST_IMPL(timer_again) {
+  int r;
+
+  close_cb_called = 0;
+  repeat_1_cb_called = 0;
+  repeat_2_cb_called = 0;
+  repeat_2_cb_allowed = 0;
+
+  start_time = uv_now(uv_default_loop());
+  TUV_ASSERT(0 < start_time);
+
+  /* Verify that it is not possible to uv_timer_again a never-started timer. */
+  r = uv_timer_init(uv_default_loop(), &dummy);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_again(&dummy);
+  TUV_ASSERT(r == UV_EINVAL);
+  uv_unref((uv_handle_t*)&dummy);
+
+  /* Start timer repeat_1. */
+  r = uv_timer_init(uv_default_loop(), &repeat_1);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&repeat_1, repeat_1_cb, 50, 0);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_1) == 0);
+
+  /* Actually make repeat_1 repeating. */
+  uv_timer_set_repeat(&repeat_1, 50);
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_1) == 50);
+
+  /*
+   * Start another repeating timer. It'll be again()ed by the repeat_1 so
+   * it should not time out until repeat_1 stops.
+   */
+  r = uv_timer_init(uv_default_loop(), &repeat_2);
+  TUV_ASSERT(r == 0);
+  r = uv_timer_start(&repeat_2, repeat_2_cb, 100, 100);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(uv_timer_get_repeat(&repeat_2) == 100);
+
+  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+  TUV_ASSERT(repeat_1_cb_called == 10);
+  TUV_ASSERT(repeat_2_cb_called == 2);
+  TUV_ASSERT(close_cb_called == 2);
+
+  // for platform that needs closing
+  tuv_timer_deinit(uv_default_loop(), &dummy);
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
diff --git a/test/test_walk_handles.c b/test/test_walk_handles.c
new file mode 100644 (file)
index 0000000..b671ed5
--- /dev/null
@@ -0,0 +1,97 @@
+/* Copyright 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#include <uv.h>
+
+#include "runner.h"
+
+
+static char magic_cookie[] = "magic cookie";
+static int seen_timer_handle;
+static uv_timer_t timer;
+
+
+static void walk_cb(uv_handle_t* handle, void* arg) {
+  TUV_ASSERT(arg == (void*)magic_cookie);
+
+  if (handle == (uv_handle_t*)&timer) {
+    seen_timer_handle++;
+  } else {
+    TUV_ASSERT(0 && "unexpected handle");
+  }
+}
+
+
+static void timer_cb(uv_timer_t* handle) {
+  TUV_ASSERT(handle == &timer);
+
+  uv_walk(handle->loop, walk_cb, magic_cookie);
+  uv_close((uv_handle_t*)handle, NULL);
+}
+
+
+TEST_IMPL(walk_handles) {
+  uv_loop_t* loop;
+  int r;
+
+  seen_timer_handle = 0;
+
+  loop = uv_default_loop();
+
+  r = uv_timer_init(loop, &timer);
+  TUV_ASSERT(r == 0);
+
+  r = uv_timer_start(&timer, timer_cb, 1, 0);
+  TUV_ASSERT(r == 0);
+
+  /* Start event loop, expect to see the timer handle in walk_cb. */
+  TUV_ASSERT(seen_timer_handle == 0);
+  r = uv_run(loop, UV_RUN_DEFAULT);
+  TUV_ASSERT(r == 0);
+  TUV_ASSERT(seen_timer_handle == 1);
+
+  /* Loop is finished, walk_cb should not see our timer handle. */
+  seen_timer_handle = 0;
+  uv_walk(loop, walk_cb, magic_cookie);
+  TUV_ASSERT(seen_timer_handle == 0);
+
+  TUV_ASSERT(0 == uv_loop_close(uv_default_loop()));
+
+  return 0;
+}
diff --git a/tools/apt-get-install-arm.sh b/tools/apt-get-install-arm.sh
new file mode 100755 (executable)
index 0000000..3aa7891
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Copyright 2016-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+sudo apt-get update -q
+sudo apt-get install -q -y \
+    gcc-arm-linux-gnueabihf libc6-dev-armhf-cross
diff --git a/tools/apt-get-install-deps.sh b/tools/apt-get-install-deps.sh
new file mode 100755 (executable)
index 0000000..4328ea3
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Copyright 2016-2017 Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+sudo apt-get update -q
+sudo apt-get install -q -y \
+    cmake gcc
diff --git a/tools/check-signed-off.sh b/tools/check-signed-off.sh
new file mode 100755 (executable)
index 0000000..2ef7674
--- /dev/null
@@ -0,0 +1,111 @@
+#!/bin/bash
+
+# Copyright 2015-2016 Samsung Electronics Co., Ltd.
+# Copyright 2016 University of Szeged
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Usage
+function print_usage
+{
+ echo "Usage: $0 [--help] [--tolerant] [--travis]"
+}
+
+function print_help
+{
+ echo "$0: Check Signed-off-by message of the latest commit"
+ echo ""
+ print_usage
+ echo ""
+ echo "Optional arguments:"
+ echo "  --help            print this help message"
+ echo "  --tolerant        check the existence of the message only but don't"
+ echo "                    require the name and email address to match the author"
+ echo "                    of the commit"
+ echo "  --travis          perform check in tolerant mode if on Travis CI and not"
+ echo "                    checking a pull request, perform strict check otherwise"
+ echo ""
+ echo "The last line of every commit message must follow the form of:"
+ echo "'libtuv-DCO-1.0-Signed-off-by: NAME EMAIL', where NAME and EMAIL must"
+ echo "match the name and email address of the author of the commit (unless in"
+ echo "tolerant mode)."
+}
+
+# Processing command line
+TOLERANT="no"
+while [ "$#" -gt 0 ]
+do
+ if [ "$1" == "--help" ]
+ then
+  print_help
+  exit 0
+ elif [ "$1" == "--tolerant" ]
+ then
+  TOLERANT="yes"
+  shift
+ elif [ "$1" == "--travis" ]
+ then
+  if [ "$TRAVIS_PULL_REQUEST" == "" ]
+  then
+   echo -e "\e[1;33mWarning! Travis-tolerant mode requested but not running on Travis CI! \e[0m"
+  elif [ "$TRAVIS_PULL_REQUEST" == "false" ]
+  then
+   TOLERANT="yes"
+  else
+   TOLERANT="no"
+  fi
+  shift
+ else
+  print_usage
+  exit 1
+ fi
+done
+
+# Determining latest commit
+parent_hashes=(`git show -s --format=%p HEAD | head -1`)
+
+if [ "${#parent_hashes[@]}" -eq 1 ]
+then
+ commit_hash=`git show -s --format=%h HEAD | head -1`
+elif [ "${#parent_hashes[@]}" -eq 2 ]
+then
+ commit_hash=${parent_hashes[1]}
+else
+ echo "$0: cannot handle commit with ${#parent_hashes[@]} parents ${parent_hashes[@]}"
+ exit 1
+fi
+
+# Checking the last line
+actual_signed_off_by_line=`git show -s --format=%B $commit_hash | sed '/^$/d' | tr -d '\015' | tail -n 1`
+
+if [ "$TOLERANT" == "no" ]
+then
+ author_name=`git show -s --format=%an $commit_hash`
+ author_email=`git show -s --format=%ae $commit_hash`
+ required_signed_off_by_line="libtuv-DCO-1.0-Signed-off-by: $author_name $author_email"
+
+ if [ "$actual_signed_off_by_line" != "$required_signed_off_by_line" ]
+ then
+  echo -e "\e[1;33mSigned-off-by message is incorrect. The following line should be at the end of the $commit_hash commit's message: '$required_signed_off_by_line'. \e[0m"
+  exit 1
+ fi
+else
+  echo -e "\e[1;33mWarning! The name and email address of the author of the $commit_hash commit is not checked in tolerant mode! \e[0m"
+  if echo "$actual_signed_off_by_line" | grep -q -v '^libtuv-DCO-1.0-Signed-off-by:'
+  then
+   echo -e "\e[1;33mSigned-off-by message is incorrect. The following line should be at the end of the $commit_hash commit's message: '$required_signed_off_by_line'. \e[0m"
+   exit 1
+  fi
+fi
+
+exit 0