diff --git a/.gitignore b/.gitignore index 5c9304ddc5e..2517b30ae92 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,7 @@ rustc TAGS version.ml version.texi -Makefile +./Makefile config.mk /rt/ /rustllvm/ diff --git a/configure b/configure index 881874abd70..10c4d7df32c 100755 --- a/configure +++ b/configure @@ -406,8 +406,4 @@ done copy ${CFG_SRC_DIR}Makefile.in ./Makefile -copy ${CFG_SRC_DIR}src/rt/libuv/Makefile rt/libuv/Makefile -copy ${CFG_SRC_DIR}src/rt/libuv/config-unix.mk rt/libuv/config-unix.mk -copy ${CFG_SRC_DIR}src/rt/libuv/config-mingw.mk rt/libuv/config-mingw.mk - step_msg "complete" diff --git a/mk/clean.mk b/mk/clean.mk index 9449c203ec8..682b536ed9b 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -54,3 +54,4 @@ clean: aux cp fn ky log pdf html pg toc tp vr cps, \ $(wildcard doc/*.$(ext))) $(Q)rm -Rf doc/version.texi + $(Q)rm -rf rt/libuv diff --git a/mk/libuv/mac/Makefile b/mk/libuv/mac/Makefile new file mode 100644 index 00000000000..b1e21e5f93e --- /dev/null +++ b/mk/libuv/mac/Makefile @@ -0,0 +1,359 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := ../../.. + +# The name of the builddir. +builddir_name ?= out + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Default + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + +# C++ apps need to be linked with g++. Not sure what's appropriate. +# +# Note, the flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override FLOCK via an envrionment variable as follows: +# +# export FLOCK= +# +# This will allow make to invoke N linker processes as specified in -jN. +FLOCK ?= ./gyp-mac-tool flock $(builddir)/linker.lock + + + +LINK ?= $(FLOCK) $(CXX) +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crs + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crs + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CCFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && libtool -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): Find out and document the difference between shared_library and +# loadable_module on mac. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass +# -bundle -single_module here (for osmesa.so). +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + @for p in $(POSTBUILDS); do eval $$p; done + ) +) +endef + +# Declare "all" target first so it is the default, even though we don't have the +# deps yet. +.PHONY: all +all: + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-benchmarks.target.mk)))),) + include src/rt/libuv/run-benchmarks.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-tests.target.mk)))),) + include src/rt/libuv/run-tests.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/uv.target.mk)))),) + include src/rt/libuv/uv.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = ./src/rt/libuv/build/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." "--depth=." "--generator-output=mk/libuv/mac" "-Dlibrary=static_library" "-Dtarget_arch=ia32" "-DOS=mac" src/rt/libuv/uv.gyp +#Makefile: $(srcdir)/src/rt/libuv/uv.gyp +# $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + # Rather than include each individual .d file, concatenate them into a + # single file which make is able to load faster. We split this into + # commands that take 1000 files at a time to avoid overflowing the + # command line. + $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps) + + ifneq ($(word 1001,$(d_files)),) + $(error Found unprocessed dependency files (gyp didn't generate enough rules!)) + endif + + # make looks for ways to re-generate included makefiles, but in our case, we + # don't have a direct way. Explicitly telling make that it has nothing to do + # for them makes it go faster. + $(depsdir)/all.deps: ; + + include $(depsdir)/all.deps +endif diff --git a/mk/libuv/mac/gyp-mac-tool b/mk/libuv/mac/gyp-mac-tool new file mode 100755 index 00000000000..7eb894b23ef --- /dev/null +++ b/mk/libuv/mac/gyp-mac-tool @@ -0,0 +1,188 @@ +#!/usr/bin/python +# Generated by gyp. Do not edit. +# Copyright (c) 2011 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import os +import fcntl +import plistlib +import shutil +import string +import subprocess +import sys + +def main(args): + executor = MacTool() + executor.Dispatch(args) + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecCopyInfoPlist(self, source, dest): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Go through all the environment variables and replace them as variables in + # the file. + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + lines = string.replace(lines, evar, os.environ[key]) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If either is missing, four + # '?' characters are used instead. + package_type = plist['CFBundlePackageType'] + if len(package_type) != 4: + package_type = '?' * 4 + signature_code = plist['CFBundleSignature'] + if len(signature_code) != 4: + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + def ExecCopyBundleResource(self, source, dest): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest) + # TODO: Given that files with arbitrary extensions can be copied to the + # bundle, we will want to get rid of this whitelist eventually. + elif extension in [ + '.icns', '.manifest', '.pak', '.pdf', '.png', '.sb', '.sh', + '.ttf', '.sdef']: + shutil.copyfile(source, dest) + else: + raise NotImplementedError( + "Don't know how to copy bundle resources of type %s while copying " + "%s to %s)" % (extension, source, dest)) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + args = ['/Developer/usr/bin/ibtool', '--errors', '--warnings', + '--notices', '--output-format', 'human-readable-text', '--compile', + dest, source] + subprocess.call(args) + + def _CopyStringsFile(self, source, dest): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + fp = open(dest, 'w') + args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code', + 'UTF-16', source] + subprocess.call(args, stdout=fp) + fp.close() + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16BE" + elif header.startswith("\xFF\xFE"): + return "UTF-16LE" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/mk/libuv/mac/src/rt/libuv/run-benchmarks.target.mk b/mk/libuv/mac/src/rt/libuv/run-benchmarks.target.mk new file mode 100644 index 00000000000..df9853a5040 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/run-benchmarks.target.mk @@ -0,0 +1,91 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-benchmarks +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := -fasm-blocks \ + -mpascal-strings \ + -gdwarf-2 \ + -arch i386 + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Default := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ping-pongs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pound.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pump.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-sizes.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-udp-packet-storm.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/dns-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-benchmarks.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(builddir)/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -arch i386 \ + -L$(builddir) + +LIBS := -framework Carbon \ + -framework CoreServices + +$(builddir)/run-benchmarks: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-benchmarks: LIBS := $(LIBS) +$(builddir)/run-benchmarks: LD_INPUTS := $(OBJS) $(builddir)/libuv.a +$(builddir)/run-benchmarks: TOOLSET := $(TOOLSET) +$(builddir)/run-benchmarks: $(OBJS) $(builddir)/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-benchmarks +# Add target alias +.PHONY: run-benchmarks +run-benchmarks: $(builddir)/run-benchmarks + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-benchmarks + diff --git a/mk/libuv/mac/src/rt/libuv/run-tests.target.mk b/mk/libuv/mac/src/rt/libuv/run-tests.target.mk new file mode 100644 index 00000000000..bb40dc55de6 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/run-tests.target.mk @@ -0,0 +1,114 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-tests +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := -fasm-blocks \ + -mpascal-strings \ + -gdwarf-2 \ + -arch i386 + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Default := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-tests.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-callback-stack.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-connection-fail.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-delayed-accept.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fail-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-get-currentexe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getsockname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-hrtime.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-idle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-loop-handles.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pass-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ping-pong.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pipe-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ref.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-shutdown-eof.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind6-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-close.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-write-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-writealot.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer-again.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-dgram-too-big.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-ipv6.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-send-and-recv.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(builddir)/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -arch i386 \ + -L$(builddir) + +LIBS := -framework Carbon \ + -framework CoreServices + +$(builddir)/run-tests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-tests: LIBS := $(LIBS) +$(builddir)/run-tests: LD_INPUTS := $(OBJS) $(builddir)/libuv.a +$(builddir)/run-tests: TOOLSET := $(TOOLSET) +$(builddir)/run-tests: $(OBJS) $(builddir)/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-tests +# Add target alias +.PHONY: run-tests +run-tests: $(builddir)/run-tests + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-tests + diff --git a/mk/libuv/mac/src/rt/libuv/uv.Makefile b/mk/libuv/mac/src/rt/libuv/uv.Makefile new file mode 100644 index 00000000000..fd944e922e5 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/uv.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= mk/libuv/mac/./src/rt/libuv/out +.PHONY: all +all: + $(MAKE) -C ../../.. uv run-tests run-benchmarks diff --git a/mk/libuv/mac/src/rt/libuv/uv.target.mk b/mk/libuv/mac/src/rt/libuv/uv.target.mk new file mode 100644 index 00000000000..dce473cf2f7 --- /dev/null +++ b/mk/libuv/mac/src/rt/libuv/uv.target.mk @@ -0,0 +1,141 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := uv +DEFS_Default := '-DHAVE_CONFIG_H' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_GNU_SOURCE' \ + '-DEIO_STACKSIZE=262144' \ + '-DEV_CONFIG_H="config_darwin.h"' \ + '-DEIO_CONFIG_H="config_darwin.h"' + +# Flags passed to all source files. +CFLAGS_Default := -fasm-blocks \ + -mpascal-strings \ + -gdwarf-2 \ + -arch i386 + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Default := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include \ + -I$(srcdir)/src/rt/libuv/include/uv-private \ + -I$(srcdir)/src/rt/libuv/src \ + -I$(srcdir)/src/rt/libuv/src/unix/ev \ + -I$(srcdir)/src/rt/libuv/src/ares/config_darwin + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/src/uv-common.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__close_sockets.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__get_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__read_line.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__timeval.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_cancel.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_data.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_destroy.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_name.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_fds.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyaddr.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getnameinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getopt.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getsock.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_library_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_llist.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_mkquery.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_nowarn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_options.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_a_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_aaaa_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_mx_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ns_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ptr_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_srv_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_txt_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_query.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_search.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_send.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strcasecmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strdup.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strerror.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_timeout.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_version.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_writev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/bitncmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_net_pton.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_ntop.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/core.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/uv-eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/udp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tcp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/pipe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/stream.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/cares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/eio/eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/ev/ev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/darwin.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -arch i386 \ + -L$(builddir) + +LIBS := -lm + +$(builddir)/libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/libuv.a: LIBS := $(LIBS) +$(builddir)/libuv.a: TOOLSET := $(TOOLSET) +$(builddir)/libuv.a: $(OBJS) FORCE_DO_CMD + $(call do_cmd,alink) + +all_deps += $(builddir)/libuv.a +# Add target alias +.PHONY: uv +uv: $(builddir)/libuv.a + +# Add target alias to "all" target. +.PHONY: all +all: uv + diff --git a/mk/libuv/unix/Makefile b/mk/libuv/unix/Makefile new file mode 100644 index 00000000000..89d244fc025 --- /dev/null +++ b/mk/libuv/unix/Makefile @@ -0,0 +1,337 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := ../../.. + +# The name of the builddir. +builddir_name ?= out + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Default + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + +# C++ apps need to be linked with g++. Not sure what's appropriate. +# +# Note, the flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override FLOCK via an envrionment variable as follows: +# +# export FLOCK= +# +# This will allow make to invoke N linker processes as specified in -jN. +FLOCK ?= flock $(builddir)/linker.lock + + + +LINK ?= $(FLOCK) $(CXX) +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crsT + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crsT + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = AR($(TOOLSET)) $@ +cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter %.o,$^) + +# Due to circular dependencies between libraries :(, we wrap the +# special "figure out circular dependencies" flags around the entire +# input list during linking. +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS) + +# We support two kinds of shared objects (.so): +# 1) shared_library, which is just bundling together many dependent libraries +# into a link line. +# 2) loadable_module, which is generating a module intended for dlopen(). +# +# They differ only slightly: +# In the former case, we want to package all dependent code into the .so. +# In the latter case, we want to package just the API exposed by the +# outermost module. +# This means shared_library uses --whole-archive, while loadable_module doesn't. +# (Note that --whole-archive is incompatible with the --start-group used in +# normal linking.) + +# Other shared-object link notes: +# - Set SONAME to the library filename so our binaries don't reference +# the local, absolute paths used on the link command-line. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 1,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + @for p in $(POSTBUILDS); do eval $$p; done + ) +) +endef + +# Declare "all" target first so it is the default, even though we don't have the +# deps yet. +.PHONY: all +all: + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-benchmarks.target.mk)))),) + include src/rt/libuv/run-benchmarks.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-tests.target.mk)))),) + include src/rt/libuv/run-tests.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/uv.target.mk)))),) + include src/rt/libuv/uv.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = ./src/rt/libuv/build/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." "--depth=." "--generator-output=mk/libuv/unix" "-Dlibrary=static_library" "-Dtarget_arch=ia32" "-DOS=linux" src/rt/libuv/uv.gyp +#Makefile: $(srcdir)/src/rt/libuv/uv.gyp +# $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + # Rather than include each individual .d file, concatenate them into a + # single file which make is able to load faster. We split this into + # commands that take 1000 files at a time to avoid overflowing the + # command line. + $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps) + + ifneq ($(word 1001,$(d_files)),) + $(error Found unprocessed dependency files (gyp didn't generate enough rules!)) + endif + + # make looks for ways to re-generate included makefiles, but in our case, we + # don't have a direct way. Explicitly telling make that it has nothing to do + # for them makes it go faster. + $(depsdir)/all.deps: ; + + include $(depsdir)/all.deps +endif diff --git a/mk/libuv/unix/src/rt/libuv/run-benchmarks.target.mk b/mk/libuv/unix/src/rt/libuv/run-benchmarks.target.mk new file mode 100644 index 00000000000..ad3900cea63 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/run-benchmarks.target.mk @@ -0,0 +1,78 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-benchmarks +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ping-pongs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pound.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pump.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-sizes.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-udp-packet-storm.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/dns-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-benchmarks.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -pthread + +LIBS := -lrt + +$(builddir)/run-benchmarks: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-benchmarks: LIBS := $(LIBS) +$(builddir)/run-benchmarks: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-benchmarks: TOOLSET := $(TOOLSET) +$(builddir)/run-benchmarks: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-benchmarks +# Add target alias +.PHONY: run-benchmarks +run-benchmarks: $(builddir)/run-benchmarks + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-benchmarks + diff --git a/mk/libuv/unix/src/rt/libuv/run-tests.target.mk b/mk/libuv/unix/src/rt/libuv/run-tests.target.mk new file mode 100644 index 00000000000..605892fea05 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/run-tests.target.mk @@ -0,0 +1,101 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-tests +DEFS_Default := '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-tests.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-callback-stack.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-connection-fail.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-delayed-accept.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fail-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-get-currentexe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getsockname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-hrtime.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-idle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-loop-handles.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pass-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ping-pong.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pipe-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ref.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-shutdown-eof.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind6-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-close.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-write-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-writealot.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer-again.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-dgram-too-big.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-ipv6.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-send-and-recv.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := -pthread + +LIBS := -lrt + +$(builddir)/run-tests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-tests: LIBS := $(LIBS) +$(builddir)/run-tests: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-tests: TOOLSET := $(TOOLSET) +$(builddir)/run-tests: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-tests +# Add target alias +.PHONY: run-tests +run-tests: $(builddir)/run-tests + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-tests + diff --git a/mk/libuv/unix/src/rt/libuv/uv.Makefile b/mk/libuv/unix/src/rt/libuv/uv.Makefile new file mode 100644 index 00000000000..579a5915782 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/uv.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= mk/libuv/unix/./src/rt/libuv/out +.PHONY: all +all: + $(MAKE) -C ../../.. uv run-tests run-benchmarks diff --git a/mk/libuv/unix/src/rt/libuv/uv.target.mk b/mk/libuv/unix/src/rt/libuv/uv.target.mk new file mode 100644 index 00000000000..dc5264341b7 --- /dev/null +++ b/mk/libuv/unix/src/rt/libuv/uv.target.mk @@ -0,0 +1,134 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := uv +DEFS_Default := '-DHAVE_CONFIG_H' \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_GNU_SOURCE' \ + '-DEIO_STACKSIZE=262144' \ + '-DEV_CONFIG_H="config_linux.h"' \ + '-DEIO_CONFIG_H="config_linux.h"' + +# Flags passed to all source files. +CFLAGS_Default := -g \ + --std=gnu89 \ + -pedantic \ + -Wall \ + -Wextra \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include \ + -I$(srcdir)/src/rt/libuv/include/uv-private \ + -I$(srcdir)/src/rt/libuv/src \ + -I$(srcdir)/src/rt/libuv/src/unix/ev \ + -I$(srcdir)/src/rt/libuv/src/ares/config_linux + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/src/uv-common.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__close_sockets.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__get_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__read_line.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__timeval.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_cancel.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_data.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_destroy.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_name.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_fds.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyaddr.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getnameinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getopt.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getsock.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_library_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_llist.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_mkquery.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_nowarn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_options.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_a_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_aaaa_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_mx_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ns_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ptr_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_srv_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_txt_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_query.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_search.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_send.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strcasecmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strdup.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strerror.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_timeout.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_version.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_writev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/bitncmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_net_pton.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_ntop.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/core.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/uv-eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/udp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tcp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/pipe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/stream.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/cares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/eio/eio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/ev/ev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/unix/linux.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := -lm + +$(obj).target/src/rt/libuv/libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(obj).target/src/rt/libuv/libuv.a: LIBS := $(LIBS) +$(obj).target/src/rt/libuv/libuv.a: TOOLSET := $(TOOLSET) +$(obj).target/src/rt/libuv/libuv.a: $(OBJS) FORCE_DO_CMD + $(call do_cmd,alink) + +all_deps += $(obj).target/src/rt/libuv/libuv.a +# Add target alias +.PHONY: uv +uv: $(obj).target/src/rt/libuv/libuv.a + +# Add target alias to "all" target. +.PHONY: all +all: uv + diff --git a/mk/libuv/win/Makefile b/mk/libuv/win/Makefile new file mode 100644 index 00000000000..5808ed4c3fc --- /dev/null +++ b/mk/libuv/win/Makefile @@ -0,0 +1,337 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := ../../.. + +# The name of the builddir. +builddir_name ?= out + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Default + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + +# C++ apps need to be linked with g++. Not sure what's appropriate. +# +# Note, the flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override FLOCK via an envrionment variable as follows: +# +# export FLOCK= +# +# This will allow make to invoke N linker processes as specified in -jN. +FLOCK ?= flock $(builddir)/linker.lock + + + +LINK ?= $(FLOCK) $(CXX) +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crsT + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crsT + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = AR($(TOOLSET)) $@ +cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter %.o,$^) + +# Due to circular dependencies between libraries :(, we wrap the +# special "figure out circular dependencies" flags around the entire +# input list during linking. +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS) + +# We support two kinds of shared objects (.so): +# 1) shared_library, which is just bundling together many dependent libraries +# into a link line. +# 2) loadable_module, which is generating a module intended for dlopen(). +# +# They differ only slightly: +# In the former case, we want to package all dependent code into the .so. +# In the latter case, we want to package just the API exposed by the +# outermost module. +# This means shared_library uses --whole-archive, while loadable_module doesn't. +# (Note that --whole-archive is incompatible with the --start-group used in +# normal linking.) + +# Other shared-object link notes: +# - Set SONAME to the library filename so our binaries don't reference +# the local, absolute paths used on the link command-line. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 1,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + @for p in $(POSTBUILDS); do eval $$p; done + ) +) +endef + +# Declare "all" target first so it is the default, even though we don't have the +# deps yet. +.PHONY: all +all: + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-benchmarks.target.mk)))),) + include src/rt/libuv/run-benchmarks.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/run-tests.target.mk)))),) + include src/rt/libuv/run-tests.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/rt/libuv/uv.target.mk)))),) + include src/rt/libuv/uv.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = ./src/rt/libuv/build/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." "--depth=." "--generator-output=mk/libuv/win" "-Dlibrary=static_library" "-Dtarget_arch=ia32" "-DOS=win" src/rt/libuv/uv.gyp +#Makefile: $(srcdir)/src/rt/libuv/uv.gyp +# $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + # Rather than include each individual .d file, concatenate them into a + # single file which make is able to load faster. We split this into + # commands that take 1000 files at a time to avoid overflowing the + # command line. + $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps) + + ifneq ($(word 1001,$(d_files)),) + $(error Found unprocessed dependency files (gyp didn't generate enough rules!)) + endif + + # make looks for ways to re-generate included makefiles, but in our case, we + # don't have a direct way. Explicitly telling make that it has nothing to do + # for them makes it go faster. + $(depsdir)/all.deps: ; + + include $(depsdir)/all.deps +endif diff --git a/mk/libuv/win/src/rt/libuv/run-benchmarks.target.mk b/mk/libuv/win/src/rt/libuv/run-benchmarks.target.mk new file mode 100644 index 00000000000..0a3cbf31b5b --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/run-benchmarks.target.mk @@ -0,0 +1,79 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-benchmarks +DEFS_Default := + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-ping-pongs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pound.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-pump.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-sizes.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/benchmark-udp-packet-storm.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/dns-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-benchmarks.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-win.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := ws2_32.lib \ + -lws2_32.lib + +$(builddir)/run-benchmarks: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-benchmarks: LIBS := $(LIBS) +$(builddir)/run-benchmarks: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-benchmarks: TOOLSET := $(TOOLSET) +$(builddir)/run-benchmarks: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-benchmarks +# Add target alias +.PHONY: run-benchmarks +run-benchmarks: $(builddir)/run-benchmarks + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-benchmarks + diff --git a/mk/libuv/win/src/rt/libuv/run-tests.target.mk b/mk/libuv/win/src/rt/libuv/run-tests.target.mk new file mode 100644 index 00000000000..438b41ea520 --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/run-tests.target.mk @@ -0,0 +1,102 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-tests +DEFS_Default := + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/run-tests.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-callback-stack.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-connection-fail.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-delayed-accept.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fail-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-get-currentexe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-getsockname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-hrtime.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-idle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-loop-handles.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pass-always.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ping-pong.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-pipe-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-ref.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-shutdown-eof.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-spawn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-bind6-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-close.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-write-error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tcp-writealot.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer-again.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-dgram-too-big.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-ipv6.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/test-udp-send-and-recv.o \ + $(obj).target/$(TARGET)/src/rt/libuv/test/runner-win.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/rt/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := ws2_32.lib \ + -lws2_32.lib + +$(builddir)/run-tests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-tests: LIBS := $(LIBS) +$(builddir)/run-tests: LD_INPUTS := $(OBJS) $(obj).target/src/rt/libuv/libuv.a +$(builddir)/run-tests: TOOLSET := $(TOOLSET) +$(builddir)/run-tests: $(OBJS) $(obj).target/src/rt/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-tests +# Add target alias +.PHONY: run-tests +run-tests: $(builddir)/run-tests + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-tests + diff --git a/mk/libuv/win/src/rt/libuv/uv.Makefile b/mk/libuv/win/src/rt/libuv/uv.Makefile new file mode 100644 index 00000000000..f7bfa935ff3 --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/uv.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= mk/libuv/win/./src/rt/libuv/out +.PHONY: all +all: + $(MAKE) -C ../../.. uv run-tests run-benchmarks diff --git a/mk/libuv/win/src/rt/libuv/uv.target.mk b/mk/libuv/win/src/rt/libuv/uv.target.mk new file mode 100644 index 00000000000..e8f7a135ac5 --- /dev/null +++ b/mk/libuv/win/src/rt/libuv/uv.target.mk @@ -0,0 +1,135 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := uv +DEFS_Default := '-DHAVE_CONFIG_H' \ + '-D_WIN32_WINNT=0x0502' \ + '-DEIO_STACKSIZE=262144' \ + '-D_GNU_SOURCE' + +# Flags passed to all source files. +CFLAGS_Default := + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/rt/libuv/include \ + -I$(srcdir)/src/rt/libuv/include/uv-private \ + -I$(srcdir)/src/rt/libuv/src \ + -I$(srcdir)/src/rt/libuv/src/ares/config_win32 + +OBJS := $(obj).target/$(TARGET)/src/rt/libuv/src/uv-common.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__close_sockets.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__get_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__read_line.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares__timeval.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_cancel.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_data.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_destroy.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_name.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_expand_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_fds.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_hostent.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_free_string.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyaddr.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_gethostbyname.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getnameinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getopt.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_getsock.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_library_init.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_llist.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_mkquery.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_nowarn.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_options.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_a_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_aaaa_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_mx_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ns_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_ptr_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_srv_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_parse_txt_reply.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_query.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_search.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_send.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strcasecmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strdup.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_strerror.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_timeout.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_version.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/ares_writev.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/bitncmp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_net_pton.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/inet_ntop.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/ares/windows_port.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/async.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/cares.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/core.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/error.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/fs.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/fs-event.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/getaddrinfo.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/handle.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/loop-watcher.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/pipe.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/process.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/req.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/stdio.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/stream.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/tcp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/tty.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/threadpool.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/threads.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/timer.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/udp.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/util.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/winapi.o \ + $(obj).target/$(TARGET)/src/rt/libuv/src/win/winsock.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := + +$(obj).target/src/rt/libuv/libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(obj).target/src/rt/libuv/libuv.a: LIBS := $(LIBS) +$(obj).target/src/rt/libuv/libuv.a: TOOLSET := $(TOOLSET) +$(obj).target/src/rt/libuv/libuv.a: $(OBJS) FORCE_DO_CMD + $(call do_cmd,alink) + +all_deps += $(obj).target/src/rt/libuv/libuv.a +# Add target alias +.PHONY: uv +uv: $(obj).target/src/rt/libuv/libuv.a + +# Add target alias to "all" target. +.PHONY: all +all: uv + diff --git a/mk/platform.mk b/mk/platform.mk index a58e75ace81..a74c4017fec 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -50,7 +50,7 @@ ifneq ($(findstring darwin,$(CFG_OSTYPE)),) CFG_LIB_NAME=lib$(1).dylib CFG_UNIXY := 1 CFG_LDENV := DYLD_LIBRARY_PATH - CFG_GCCISH_LINK_FLAGS += -dynamiclib -lpthread + CFG_GCCISH_LINK_FLAGS += -dynamiclib -lpthread -framework CoreServices CFG_GCCISH_DEF_FLAG := -Wl,-exported_symbols_list, # Darwin has a very blurry notion of "64 bit", and claims it's running # "on an i386" when the whole userspace is 64-bit and the compiler diff --git a/mk/rt.mk b/mk/rt.mk index f2ed226b266..41fa5eb9880 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -68,11 +68,22 @@ RUNTIME_HDR := rt/globals.h \ rt/test/rust_test_util.h \ rt/arch/i386/context.h \ +ifeq ($(CFG_WINDOWSY), 1) + LIBUV_OSTYPE := win + LIBUV_LIB := rt/libuv/Default/obj.target/src/rt/libuv/libuv.a +else ifeq ($(CFG_OSTYPE), apple-darwin) + LIBUV_OSTYPE := mac + LIBUV_LIB := rt/libuv/Default/libuv.a +else + LIBUV_OSTYPE := unix + LIBUV_LIB := rt/libuv/Default/obj.target/src/rt/libuv/libuv.a +endif + RUNTIME_DEF := rt/rustrt$(CFG_DEF_SUFFIX) RUNTIME_INCS := -I $(S)src/rt/isaac -I $(S)src/rt/uthash \ -I $(S)src/rt/arch/i386 -I $(S)src/rt/libuv/include RUNTIME_OBJS := $(RUNTIME_CS:.cpp=.o) $(RUNTIME_LL:.ll=.o) $(RUNTIME_S:.S=.o) -RUNTIME_LIBS := rt/libuv/uv.a +RUNTIME_LIBS := $(LIBUV_LIB) rt/%.o: rt/%.cpp $(MKFILES) @$(call E, compile: $@) @@ -105,12 +116,18 @@ rt/$(CFG_RUNTIME): $(RUNTIME_OBJS) $(MKFILES) $(RUNTIME_HDR) $(RUNTIME_DEF) $(RU # FIXME: For some reason libuv's makefiles can't figure out the correct definition # of CC on the mingw I'm using, so we are explicitly using gcc. Also, we # have to list environment variables first on windows... mysterious -rt/libuv/uv.a: $(wildcard \ +$(LIBUV_LIB): $(wildcard \ $(S)src/rt/libuv/* \ $(S)src/rt/libuv/*/* \ $(S)src/rt/libuv/*/*/* \ $(S)src/rt/libuv/*/*/*/*) - $(Q)CFLAGS=\"-m32\" LDFLAGS=\"-m32\" CC=$(CC) $(MAKE) -C rt/libuv + $(Q)$(MAKE) -C $(S)mk/libuv/$(LIBUV_OSTYPE) \ + CFLAGS="-m32" LDFLAGS="-m32" \ + CC="$(CFG_GCCISH_CROSS)$(CC)" \ + CXX="$(CFG_GCCISH_CROSS)$(CXX)" \ + AR="$(CFG_GCCISH_CROSS)$(AR)" \ + builddir_name="$(CFG_BUILD_DIR)/rt/libuv" \ + V=$(VERBOSE) FLOCK= uv # These could go in rt.mk or rustllvm.mk, they're needed for both. diff --git a/src/etc/gyp-uv b/src/etc/gyp-uv new file mode 100755 index 00000000000..1e24f5eaa37 --- /dev/null +++ b/src/etc/gyp-uv @@ -0,0 +1,36 @@ +#!/bin/sh + +set -e + +cd `dirname $0` +cd ../.. + +args="--depth . -Dlibrary=static_library -Dtarget_arch=ia32" + +./src/rt/libuv/build/gyp/gyp src/rt/libuv/uv.gyp $args \ + -f make-mac \ + --generator-output mk/libuv/mac \ + -DOS=mac + +./src/rt/libuv/build/gyp/gyp src/rt/libuv/uv.gyp $args \ + -f make-linux \ + --generator-output mk/libuv/unix \ + -DOS=linux + +./src/rt/libuv/build/gyp/gyp src/rt/libuv/uv.gyp $args \ + -f make-linux \ + --generator-output mk/libuv/win \ + -DOS=win + +# Comment out the gyp auto regeneration +for os in mac unix win; do + sed -i ".save" \ + -e 's/^\(Makefile: $(srcdir)\/src\/rt\/libuv\/uv\.gyp\)/#\1/' \ + mk/libuv/$os/Makefile + + sed -i ".save" \ + -e 's/^\( $(call do_cmd,regen_makefile)\)/#\1/' \ + mk/libuv/$os/Makefile + + rm mk/libuv/$os/Makefile.save +done diff --git a/src/rt/libuv/.gitignore b/src/rt/libuv/.gitignore index 9d142354bf3..09164c65eb0 100644 --- a/src/rt/libuv/.gitignore +++ b/src/rt/libuv/.gitignore @@ -7,37 +7,23 @@ *.orig *.sdf *.suo +/out/ +/build/gyp + +/test/run-tests +/test/run-tests.exe +/test/run-tests.dSYM +/test/run-benchmarks +/test/run-benchmarks.exe +/test/run-benchmarks.dSYM + +*.sln +*.vcproj +*.vcxproj *.vcxproj.filters *.vcxproj.user -ev/.deps/ -ev/.libs/ -ev/Makefile -ev/config.h -ev/config.log -ev/config.status -ev/libtool -ev/stamp-h1 -ev/autom4te.cache -/msvs/ipch/ -/build/ -test/run-tests -test/run-benchmarks -test/run-tests.exe -test/run-benchmarks.exe -test/run-benchmarks.dSYM/ -test/run-tests.dSYM/ - - -c-ares/.deps/ -c-ares/.libs/ -c-ares/Makefile -c-ares/acountry -c-ares/adig -c-ares/ahost -c-ares/ares_config.h -c-ares/config.log -c-ares/config.status -c-ares/libcares.pc -c-ares/libtool -c-ares/stamp-h1 -c-ares/stamp-h2 +_UpgradeReport_Files/ +UpgradeLog*.XML +Debug +Release +ipch diff --git a/src/rt/libuv/.mailmap b/src/rt/libuv/.mailmap new file mode 100644 index 00000000000..520f71be68f --- /dev/null +++ b/src/rt/libuv/.mailmap @@ -0,0 +1,8 @@ +# update AUTHORS with: +# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS + + + + +San-Tai Hsu +Isaac Z. Schlueter diff --git a/src/rt/libuv/AUTHORS b/src/rt/libuv/AUTHORS index 4b4cab70f13..eefcd7dc1ee 100644 --- a/src/rt/libuv/AUTHORS +++ b/src/rt/libuv/AUTHORS @@ -3,9 +3,24 @@ Ryan Dahl Bert Belder Josh Roesslein Alan Gutierrez +Joshua Peek Igor Zinkovsky -Vanilla Hsu +San-Tai Hsu Ben Noordhuis Henry Rawas Robert Mustacchi Matt Stevens +Paul Querna +Shigeki Ohtsu +Tom Hughes +Peter Bright +Jeroen Janssen +Andrea Lattuada +Augusto Henrique Hentz +Clifford Heath +Jorge Chamorro Bieling +Luis Lavena +Matthew Sporleder +Erick Tryzelaar +Isaac Z. Schlueter +Pieter Noordhuis diff --git a/src/rt/libuv/LIBUV_REVISION b/src/rt/libuv/LIBUV_REVISION deleted file mode 100644 index f079f657765..00000000000 --- a/src/rt/libuv/LIBUV_REVISION +++ /dev/null @@ -1,5 +0,0 @@ -This subtree is pulled from: -ee599ec1141cc48f895de1f9d148033babdf9c2a - -When pulling in a new version of libuv, please update this file to ensure that -everyone correctly rebuilds libuv. diff --git a/src/rt/libuv/Makefile b/src/rt/libuv/Makefile index b8d3c685952..59be3de08f3 100644 --- a/src/rt/libuv/Makefile +++ b/src/rt/libuv/Makefile @@ -18,30 +18,13 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -include ../../config.mk -CFLAGS=-m32 -LDFLAGS=-m32 -CC=gcc - -S:=$(shell cd ../../; cd $(CFG_SRC_DIR)src/rt/libuv; pwd) - -ifdef VERBOSE - Q := - EE = -else - Q := @ - EE = echo $(1) -endif - -VPATH:=$(S) - uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') ifdef MSVC uname_S := MINGW endif -CPPFLAGS += -I$(S)/include +CPPFLAGS += -Iinclude -Iinclude/uv-private CARES_OBJS = CARES_OBJS += src/ares/ares__close_sockets.o @@ -94,8 +77,43 @@ else include config-unix.mk endif -all: uv.a +TESTS=test/echo-server.c test/test-*.c +BENCHMARKS=test/echo-server.c test/dns-server.c test/benchmark-*.c + +all: uv.a test/run-tests$(E) test/run-benchmarks$(E) $(CARES_OBJS): %.o: %.c - @$(call EE, compile: $@) - $(Q)$(CC) -o $*.o -c $(CFLAGS) $(CPPFLAGS) $< -DHAVE_CONFIG_H + $(CC) -o $*.o -c $(CFLAGS) $(CPPFLAGS) $< -DHAVE_CONFIG_H + +test/run-tests$(E): test/*.h test/run-tests.c $(RUNNER_SRC) test/runner-unix.c $(TESTS) uv.a + $(CC) $(CPPFLAGS) $(RUNNER_CFLAGS) -o test/run-tests test/run-tests.c \ + test/runner.c $(RUNNER_SRC) $(TESTS) uv.a $(RUNNER_LIBS) $(RUNNER_LINKFLAGS) + +test/run-benchmarks$(E): test/*.h test/run-benchmarks.c test/runner.c $(RUNNER_SRC) $(BENCHMARKS) uv.a + $(CC) $(CPPFLAGS) $(RUNNER_CFLAGS) -o test/run-benchmarks test/run-benchmarks.c \ + test/runner.c $(RUNNER_SRC) $(BENCHMARKS) uv.a $(RUNNER_LIBS) $(RUNNER_LINKFLAGS) + +test/echo.o: test/echo.c test/echo.h + $(CC) $(CPPFLAGS) $(CFLAGS) -c test/echo.c -o test/echo.o + + +.PHONY: clean clean-platform distclean distclean-platform test bench + + +test: test/run-tests$(E) + test/run-tests + +#test-%: test/run-tests$(E) +# test/run-tests $(@:test-%=%) + +bench: test/run-benchmarks$(E) + test/run-benchmarks + +#bench-%: test/run-benchmarks$(E) +# test/run-benchmarks $(@:bench-%=%) + +clean: clean-platform + $(RM) -f src/*.o *.a test/run-tests$(E) test/run-benchmarks$(E) + +distclean: distclean-platform + $(RM) -f src/*.o *.a test/run-tests$(E) test/run-benchmarks$(E) diff --git a/src/rt/libuv/README b/src/rt/libuv/README deleted file mode 100644 index 4e2c2cd2614..00000000000 --- a/src/rt/libuv/README +++ /dev/null @@ -1,16 +0,0 @@ -This is the new networking layer for Node. Its purpose is to abstract -IOCP on windows and libev on Unix systems. We intend to eventually contain -all platform differences in this library. - -http://nodejs.org/ - -(This was previously called liboio) - -Supported Platforms: - -Microsoft Windows operating systems since Windows XP sp2. It can be built -with either Visual Studio or MinGW. - -Linux 2.6 and MacOS using the GCC toolchain. - -Solaris 121 and later using GCC toolchain. diff --git a/src/rt/libuv/README.md b/src/rt/libuv/README.md new file mode 100644 index 00000000000..9fc4ae62ebd --- /dev/null +++ b/src/rt/libuv/README.md @@ -0,0 +1,92 @@ +# libuv + +libuv is a new platform layer for Node. Its purpose is to abstract IOCP on +windows and libev on Unix systems. We intend to eventually contain all +platform differences in this library. + +http://nodejs.org/ + +## Features + +Implemented: + + * Non-blocking TCP sockets + + * Non-blocking named pipes + + * UDP + + * Timers + + * Child process spawning + + * Asynchronous DNS via c-ares or `uv_getaddrinfo`. + + * Asynchronous file system APIs `uv_fs_*` + + * High resolution time `uv_hrtime` + + * Current executable path look up `uv_exepath` + + * Thread pool scheduling `uv_queue_work` + +In-progress: + + * File system events (Currently supports inotify, `ReadDirectoryChangesW` + and will support kqueue and event ports in the near future.) + `uv_fs_event_t` + + * VT100 TTY `uv_tty_t` + + * Socket sharing between processes `uv_ipc_t` + + +## Documentation + +See `include/uv.h`. + + +## Build Instructions + +For GCC (including MinGW) there are two methods building: via normal +makefiles or via GYP. GYP is a meta-build system which can generate MSVS, +Makefile, and XCode backends. It is best used for integration into other +projects. The old (more stable) system is using Makefiles. + +To build via Makefile simply execute: + + make + +To build with Visual Studio run the vcbuilds.bat file which will +checkout the GYP code into build/gyp and generate the uv.sln and +related files. + +Windows users can also build from cmd-line using msbuild. This is +done by running vcbuild.bat from Visual Studio command prompt. + +To have GYP generate build script for another system you will need to +checkout GYP into the project tree manually: + + svn co http://gyp.googlecode.com/svn/trunk build/gyp + +Unix users run + + ./gyp_uv -f make + make + +Macintosh users run + + ./gyp_uv -f xcode + xcodebuild -project uv.xcodeproj -configuration Release -target All + + +## Supported Platforms + +Microsoft Windows operating systems since Windows XP SP2. It can be built +with either Visual Studio or MinGW. + +Linux 2.6 using the GCC toolchain. + +MacOS using the GCC or XCode toolchain. + +Solaris 121 and later using GCC toolchain. diff --git a/src/rt/libuv/common.gypi b/src/rt/libuv/common.gypi new file mode 100644 index 00000000000..9a0be4dc394 --- /dev/null +++ b/src/rt/libuv/common.gypi @@ -0,0 +1,164 @@ +{ + 'variables': { + 'visibility%': 'hidden', # V8's visibility setting + 'target_arch%': 'ia32', # set v8's target architecture + 'host_arch%': 'ia32', # set v8's host architecture + 'library%': 'static_library', # allow override to 'shared_library' for DLL/.so builds + 'component%': 'static_library', # NB. these names match with what V8 expects + 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way + }, + + 'target_defaults': { + 'default_configuration': 'Debug', + 'configurations': { + 'Debug': { + 'defines': [ 'DEBUG', '_DEBUG' ], + 'cflags': [ '-g', '-O0' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'target_conditions': [ + ['library=="static_library"', { + 'RuntimeLibrary': 1, # static debug + }, { + 'RuntimeLibrary': 3, # DLL debug + }], + ], + 'Optimization': 0, # /Od, no optimization + 'MinimalRebuild': 'true', + 'OmitFramePointers': 'false', + 'BasicRuntimeChecks': 3, # /RTC1 + }, + 'VCLinkerTool': { + 'LinkIncremental': 2, # enable incremental linking + }, + }, + }, + 'Release': { + 'defines': [ 'NDEBUG' ], + 'cflags': [ '-O3', '-fomit-frame-pointer', '-fdata-sections', '-ffunction-sections' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'target_conditions': [ + ['library=="static_library"', { + 'RuntimeLibrary': 0, # static release + }, { + 'RuntimeLibrary': 2, # debug release + }], + ], + 'Optimization': 3, # /Ox, full optimization + 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size + 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible + 'WholeProgramOptimization': 'true', # /GL, whole program optimization, needed for LTCG + 'OmitFramePointers': 'true', + 'EnableFunctionLevelLinking': 'true', + 'EnableIntrinsicFunctions': 'true', + 'AdditionalOptions': [ + '/MP', # compile across multiple CPUs + ], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': [ + '/LTCG', # link time code generation + ], + }, + 'VCLinkerTool': { + 'LinkTimeCodeGeneration': 1, # link-time code generation + 'OptimizeReferences': 2, # /OPT:REF + 'EnableCOMDATFolding': 2, # /OPT:ICF + 'LinkIncremental': 1, # disable incremental linking + }, + }, + } + }, + 'msvs_settings': { + 'VCCLCompilerTool': { + 'StringPooling': 'true', # pool string literals + 'DebugInformationFormat': 3, # Generate a PDB + 'WarningLevel': 3, + 'BufferSecurityCheck': 'true', + 'ExceptionHandling': 1, # /EHsc + 'SuppressStartupBanner': 'true', + 'WarnAsError': 'false', + }, + 'VCLibrarianTool': { + }, + 'VCLinkerTool': { + 'GenerateDebugInformation': 'true', + 'RandomizedBaseAddress': 2, # enable ASLR + 'DataExecutionPrevention': 2, # enable DEP + 'AllowIsolation': 'true', + 'SuppressStartupBanner': 'true', + 'target_conditions': [ + ['_type=="executable"', { + 'SubSystem': 1, # console executable + }], + ], + }, + }, + 'conditions': [ + ['OS == "win"', { + 'msvs_cygwin_shell': 0, # prevent actions from trying to use cygwin + 'defines': [ + 'WIN32', + # we don't really want VC++ warning us about + # how dangerous C functions are... + '_CRT_SECURE_NO_DEPRECATE', + # ... or that C implementations shouldn't use + # POSIX names + '_CRT_NONSTDC_NO_DEPRECATE', + ], + }], + [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { + 'cflags': [ '-Wall', '-pthread', ], + 'cflags_cc': [ '-fno-rtti', '-fno-exceptions' ], + 'ldflags': [ '-pthread', ], + 'conditions': [ + [ 'host_arch != target_arch and target_arch=="ia32"', { + 'cflags': [ '-m32' ], + 'ldflags': [ '-m32' ], + }], + [ 'OS=="linux"', { + 'cflags': [ '-ansi' ], + }], + [ 'visibility=="hidden"', { + 'cflags': [ '-fvisibility=hidden' ], + }], + ], + }], + ['OS=="mac"', { + 'xcode_settings': { + 'ALWAYS_SEARCH_USER_PATHS': 'NO', + 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks + 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic + # (Equivalent to -fPIC) + 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions + 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti + 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings + # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden + 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics + 'GCC_VERSION': '4.2', + 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof + 'MACOSX_DEPLOYMENT_TARGET': '10.4', # -mmacosx-version-min=10.4 + 'PREBINDING': 'NO', # No -Wl,-prebind + 'USE_HEADERMAP': 'NO', + 'OTHER_CFLAGS': [ + '-fno-strict-aliasing', + ], + 'WARNING_CFLAGS': [ + '-Wall', + '-Wendif-labels', + '-W', + '-Wno-unused-parameter', + ], + }, + 'target_conditions': [ + ['_type!="static_library"', { + 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']}, + }], + ], + }], + ], + }, +} diff --git a/src/rt/libuv/config-mingw.mk b/src/rt/libuv/config-mingw.mk index 7c975359af9..d4daca8a1ff 100644 --- a/src/rt/libuv/config-mingw.mk +++ b/src/rt/libuv/config-mingw.mk @@ -20,37 +20,41 @@ # Use make -f Makefile.gcc PREFIX=i686-w64-mingw32- # for cross compilation -CC ?= $(PREFIX)gcc -AR ?= $(PREFIX)ar +CC = $(PREFIX)gcc +AR = $(PREFIX)ar E=.exe -CFLAGS+=$(CPPFLAGS) -g --std=gnu89 -D_WIN32_WINNT=0x0501 -I$(S)/src/ares/config_win32 +CFLAGS=$(CPPFLAGS) -g --std=gnu89 -D_WIN32_WINNT=0x0501 -Isrc/ares/config_win32 LINKFLAGS=-lm CARES_OBJS += src/ares/windows_port.o +WIN_SRCS=$(wildcard src/win/*.c) +WIN_OBJS=$(WIN_SRCS:.c=.o) -uv.a: src/uv-win.o src/uv-common.o src/uv-eio.o src/eio/eio.o $(CARES_OBJS) - @$(call EE, ar: $@) - $(Q)$(AR) rcs uv.a $^ +RUNNER_CFLAGS=$(CFLAGS) -D_GNU_SOURCE # Need _GNU_SOURCE for strdup? +RUNNER_LINKFLAGS=$(LINKFLAGS) +RUNNER_LIBS=-lws2_32 +RUNNER_SRC=test/runner-win.c -src/uv-win.o: src/uv-win.c include/uv.h include/uv-win.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CFLAGS) -c $< -o $@ +uv.a: $(WIN_OBJS) src/uv-common.o $(CARES_OBJS) + $(AR) rcs uv.a src/win/*.o src/uv-common.o $(CARES_OBJS) -src/uv-common.o: src/uv-common.c include/uv.h include/uv-win.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CFLAGS) -c $< -o $@ +src/win/%.o: src/win/%.c src/win/internal.h + $(CC) $(CFLAGS) -o $@ -c $< + +src/uv-common.o: src/uv-common.c include/uv.h include/uv-private/uv-win.h + $(CC) $(CFLAGS) -c src/uv-common.c -o src/uv-common.o EIO_CPPFLAGS += $(CPPFLAGS) -EIO_CPPFLAGS += -DEIO_CONFIG_H=\"$(EIO_CONFIG)\" EIO_CPPFLAGS += -DEIO_STACKSIZE=65536 EIO_CPPFLAGS += -D_GNU_SOURCE -src/eio/eio.o: src/eio/eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(EIO_CPPFLAGS) $(CFLAGS) -c $< -o $@ - -src/uv-eio.o: src/uv-eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(CPPFLAGS) -I$(S)src/eio/ $(CFLAGS) -c $< -o $@ +clean-platform: + -rm -f src/ares/*.o + -rm -f src/eio/*.o + -rm -f src/win/*.o +distclean-platform: + -rm -f src/ares/*.o + -rm -f src/eio/*.o + -rm -f src/win/*.o diff --git a/src/rt/libuv/config-unix.mk b/src/rt/libuv/config-unix.mk index afdb92431c8..59bf8f17f8f 100644 --- a/src/rt/libuv/config-unix.mk +++ b/src/rt/libuv/config-unix.mk @@ -18,95 +18,127 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -CC ?= $(PREFIX)gcc -AR ?= $(PREFIX)ar +CC = $(PREFIX)gcc +AR = $(PREFIX)ar E= -CSTDFLAG=--std=c89 -pedantic -CFLAGS+=-g -CPPFLAGS += -I$(S)/src/ev +CSTDFLAG=--std=c89 -pedantic -Wall -Wextra -Wno-unused-parameter +CFLAGS += -g +CPPFLAGS += -Isrc/unix/ev LINKFLAGS=-lm CPPFLAGS += -D_LARGEFILE_SOURCE CPPFLAGS += -D_FILE_OFFSET_BITS=64 +OBJS += src/unix/core.o +OBJS += src/unix/fs.o +OBJS += src/unix/cares.o +OBJS += src/unix/udp.o +OBJS += src/unix/error.o +OBJS += src/unix/process.o +OBJS += src/unix/tcp.o +OBJS += src/unix/pipe.o +OBJS += src/unix/tty.o +OBJS += src/unix/stream.o + ifeq (SunOS,$(uname_S)) EV_CONFIG=config_sunos.h EIO_CONFIG=config_sunos.h -CPPFLAGS += -I$(S)/src/ares/config_sunos +CPPFLAGS += -Isrc/ares/config_sunos -D__EXTENSIONS__ -D_XOPEN_SOURCE=500 LINKFLAGS+=-lsocket -lnsl -UV_OS_FILE=uv-sunos.c +OBJS += src/unix/sunos.o endif ifeq (Darwin,$(uname_S)) EV_CONFIG=config_darwin.h EIO_CONFIG=config_darwin.h -CPPFLAGS += -I$(S)/src/ares/config_darwin +CPPFLAGS += -Isrc/ares/config_darwin LINKFLAGS+=-framework CoreServices -UV_OS_FILE=uv-darwin.c +OBJS += src/unix/darwin.o endif ifeq (Linux,$(uname_S)) EV_CONFIG=config_linux.h EIO_CONFIG=config_linux.h -CSTDFLAG += -D_XOPEN_SOURCE=600 -CPPFLAGS += -I$(S)/src/ares/config_linux +CSTDFLAG += -D_GNU_SOURCE +CPPFLAGS += -Isrc/ares/config_linux LINKFLAGS+=-lrt -UV_OS_FILE=uv-linux.c +OBJS += src/unix/linux.o endif ifeq (FreeBSD,$(uname_S)) EV_CONFIG=config_freebsd.h EIO_CONFIG=config_freebsd.h -CPPFLAGS += -I$(S)/src/ares/config_freebsd +CPPFLAGS += -Isrc/ares/config_freebsd LINKFLAGS+= -UV_OS_FILE=uv-freebsd.c +OBJS += src/unix/freebsd.o +endif + +ifeq (NetBSD,$(uname_S)) +EV_CONFIG=config_netbsd.h +EIO_CONFIG=config_netbsd.h +CPPFLAGS += -Isrc/ares/config_netbsd +LINKFLAGS+= +OBJS += src/unix/netbsd.o endif ifneq (,$(findstring CYGWIN,$(uname_S))) EV_CONFIG=config_cygwin.h EIO_CONFIG=config_cygwin.h -CPPFLAGS += -I$(S)/src/ares/config_cygwin +# We drop the --std=c89, it hides CLOCK_MONOTONIC on cygwin +CSTDFLAG = -D_GNU_SOURCE +CPPFLAGS += -Isrc/ares/config_cygwin LINKFLAGS+= -UV_OS_FILE=uv-cygwin.c +OBJS += src/unix/cygwin.o endif # Need _GNU_SOURCE for strdup? RUNNER_CFLAGS=$(CFLAGS) -D_GNU_SOURCE +RUNNER_LINKFLAGS=$(LINKFLAGS) + +ifeq (SunOS,$(uname_S)) +RUNNER_LINKFLAGS += -pthreads +else +RUNNER_LINKFLAGS += -pthread +endif -RUNNER_LINKFLAGS=$(LINKFLAGS) -pthread RUNNER_LIBS= RUNNER_SRC=test/runner-unix.c -uv.a: src/uv-unix.o src/uv-common.o src/uv-platform.o src/ev/ev.o src/uv-eio.o src/eio/eio.o $(CARES_OBJS) - @$(call EE, ar: $@) - $(Q)$(AR) rcs uv.a $^ +uv.a: $(OBJS) src/uv-common.o src/unix/ev/ev.o src/unix/uv-eio.o src/unix/eio/eio.o $(CARES_OBJS) + $(AR) rcs uv.a $(OBJS) src/uv-common.o src/unix/uv-eio.o src/unix/ev/ev.o src/unix/eio/eio.o $(CARES_OBJS) -src/uv-platform.o: src/$(UV_OS_FILE) include/uv.h include/uv-unix.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ +src/unix/%.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h + $(CC) $(CSTDFLAG) $(CPPFLAGS) -Isrc $(CFLAGS) -c $< -o $@ -src/uv-unix.o: src/uv-unix.c include/uv.h include/uv-unix.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CSTDFLAG) $(CPPFLAGS) -I$(S)/eio $(CFLAGS) -c $< -o $@ +src/uv-common.o: src/uv-common.c include/uv.h include/uv-private/uv-unix.h + $(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c src/uv-common.c -o src/uv-common.o -src/uv-common.o: src/uv-common.c include/uv.h include/uv-unix.h - @$(call EE, compile: $@) - $(Q)$(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ - -src/ev/ev.o: src/ev/ev.c - @$(call EE, compile: $@) - $(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -DEV_CONFIG_H=\"$(EV_CONFIG)\" +src/unix/ev/ev.o: src/unix/ev/ev.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c src/unix/ev/ev.c -o src/unix/ev/ev.o -DEV_CONFIG_H=\"$(EV_CONFIG)\" EIO_CPPFLAGS += $(CPPFLAGS) EIO_CPPFLAGS += -DEIO_CONFIG_H=\"$(EIO_CONFIG)\" -EIO_CPPFLAGS += -DEIO_STACKSIZE=65536 +EIO_CPPFLAGS += -DEIO_STACKSIZE=262144 EIO_CPPFLAGS += -D_GNU_SOURCE -src/eio/eio.o: src/eio/eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(EIO_CPPFLAGS) $(CFLAGS) -c $< -o $@ +src/unix/eio/eio.o: src/unix/eio/eio.c + $(CC) $(EIO_CPPFLAGS) $(CFLAGS) -c src/unix/eio/eio.c -o src/unix/eio/eio.o -src/uv-eio.o: src/uv-eio.c - @$(call EE, compile: $@) - $(Q)$(CC) $(CPPFLAGS) -I$(S)/src/eio/ $(CSTDFLAG) $(CFLAGS) -c $< -o $@ +src/unix/uv-eio.o: src/unix/uv-eio.c + $(CC) $(CPPFLAGS) -Isrc/unix/eio/ $(CSTDFLAG) $(CFLAGS) -c src/unix/uv-eio.c -o src/unix/uv-eio.o + + +clean-platform: + -rm -f src/ares/*.o + -rm -f src/unix/ev/*.o + -rm -f src/unix/eio/*.o + -rm -f src/unix/*.o + -rm -rf test/run-tests.dSYM run-benchmarks.dSYM + +distclean-platform: + -rm -f src/ares/*.o + -rm -f src/unix/ev/*.o + -rm -f src/unix/*.o + -rm -f src/unix/eio/*.o + -rm -rf test/run-tests.dSYM run-benchmarks.dSYM diff --git a/src/rt/libuv/doc/desired-api.md b/src/rt/libuv/doc/desired-api.md deleted file mode 100644 index 9faa57591e9..00000000000 --- a/src/rt/libuv/doc/desired-api.md +++ /dev/null @@ -1,159 +0,0 @@ -Warning: this is not actual API but desired API. - -# `uv_handle_t` - -This is the abstract base class of all types of handles. All handles have in -common: - -* When handles are initialized, the reference count to the event loop is - increased by one. - -* The user owns the `uv_handle_t` memory and is in charge of freeing it. - -* In order to free resources associated with a handle, one must `uv_close()` - and wait for the `uv_close_cb` callback. After the close callback has been - made, the user is allowed to the `uv_handle_t` object. - -* The `uv_close_cb` is always made directly off the event loop. That is, it - is not called from `uv_close()`. - - - -# `uv_tcp_server_t` - -A TCP server class that is a subclass of `uv_handle_t`. This can be bound to -an address and begin accepting new TCP sockets. - - int uv_bind4(uv_tcp_server_t* tcp_server, struct sockaddr_in* address); - int uv_bind6(uv_tcp_server_t* tcp_server, struct sockaddr_in6* address); - -Binds the TCP server to an address. The `address` can be created with -`uv_ip4_addr()`. Call this before `uv_listen()` - -Returns zero on success, -1 on failure. Errors in order of least-seriousness: - -* `UV_EADDRINUSE` There is already another socket bound to the specified - address. - -* `UV_EADDRNOTAVAIL` The `address` parameter is an IP address that is not - -* `UV_EINVAL` The server is already bound to an address. - -* `UV_EFAULT` Memory of `address` parameter is unintelligible. - - - int uv_listen(uv_tcp_server_t*, int backlog, uv_connection_cb cb); - -Begins listening for connections. The accept callback is level-triggered. - - - int uv_accept(uv_tcp_server_t* server, - uv_tcp_t* client); - -Accepts a connection. This should be called after the accept callback is -made. The `client` parameter should be uninitialized memory; `uv_accept` is -used instead of `uv_tcp_init` for server-side `uv_tcp_t` initialization. - -Return value 0 indicates success, -1 failure. Possible errors: - -* `UV_EAGAIN` There are no connections. Wait for the `uv_connection_cb` callback - to be called again. - -* `UV_EFAULT` The memory of either `server` is unintelligible. - - - -# `uv_stream_t` - -An abstract subclass of `uv_handle_t`. Streams represent something that -reads and/or writes data. Streams can be half or full-duplex. TCP sockets -are streams, files are streams with offsets. - - int uv_read_start(uv_stream_t* stream, - uv_alloc_cb alloc_cb, - uv_read_cb read_cb); - -Starts the stream reading continuously. The `alloc_cb` is used to allow the -user to implement various means of supplying the stream with buffers to -fill. The `read_cb` returns buffers to the user filled with data. - -Sometimes the buffers returned to the user do not contain data. This does -not indicate EOF as in other systems. EOF is made via the `uv_eof_cb` which -can be set like this `uv_set_eof_cb(stream, eof_cb);` - - - int uv_read_stop(uv_stream_t* stream); - -Stops reading from the stream. - - int uv_write_req_init(uv_write_req_t*, - uv_stream_t*, - uv_buf_t bufs[], - int butcnf); - -Initiates a write request on a stream. - - int uv_shutdown_req_init(uv_shutdown_req_t*, uv_stream_t*) - -Initiates a shutdown of outgoing data once the write queue drains. - - - -# `uv_tcp_t` - -The TCP handle class represents one endpoint of a duplex TCP stream. -`uv_tcp_t` is a subclass of `uv_stream_t`. A TCP handle can represent a -client side connection (one that has been used with uv_connect_req_init`) -or a server-side connection (one that was initialized with `uv_accept`) - - int uv_connect_req_init(uv_connect_req_t* req, - uv_tcp_t* socket, - struct sockaddr* addr); - -Initiates a request to open a connection. - - - -# `uv_req_t` - -Abstract class represents an asynchronous request. This is a subclass of `uv_handle_t`. - - -# `uv_connect_req_t` - -Subclass of `uv_req_t`. Represents a request for a TCP connection. Operates -on `uv_tcp_t` handles. Like other types of requests the `close_cb` indicates -completion of the request. - - int uv_connect_req_init(uv_connect_req_t* req, - uv_tcp_t* socket, - struct sockaddr* addr); - -Initializes the connection request. Returning 0 indicates success, -1 if -there was an error. The following values can be retrieved from -`uv_last_error` in the case of an error: - -* ??? - - -# `uv_shutdown_req_t` - -Subclass of `uv_req_t`. Represents an ongoing shutdown request. Once the -write queue of the parent `uv_stream_t` is drained, the outbound data -channel is shutdown. Once a shutdown request is initiated on a stream, the -stream will allow no more writes. - - int uv_shutdown_req_init(uv_shutdown_req_t*, - uv_stream_t* parent); - -Initializes the shutdown request. - - -# `uv_write_req_t` - - int uv_write_req_init(uv_write_req_t*, - uv_stream_t*, - uv_buf_t bufs[], - int butcnf); - -Initiates a write request on a stream. diff --git a/src/rt/libuv/gyp_uv b/src/rt/libuv/gyp_uv new file mode 100755 index 00000000000..a7a9689c265 --- /dev/null +++ b/src/rt/libuv/gyp_uv @@ -0,0 +1,60 @@ +#!/usr/bin/env python +import glob +import os +import shlex +import sys + +script_dir = os.path.dirname(__file__) +uv_root = os.path.normpath(script_dir) + +sys.path.insert(0, os.path.join(uv_root, 'build', 'gyp', 'pylib')) +import gyp + +# Directory within which we want all generated files (including Makefiles) +# to be written. +output_dir = os.path.join(os.path.abspath(uv_root), 'out') + +def run_gyp(args): + rc = gyp.main(args) + if rc != 0: + print 'Error running GYP' + sys.exit(rc) + +if __name__ == '__main__': + args = sys.argv[1:] + + # GYP bug. + # On msvs it will crash if it gets an absolute path. + # On Mac/make it will crash if it doesn't get an absolute path. + if sys.platform == 'win32': + args.append(os.path.join(uv_root, 'uv.gyp')) + common_fn = os.path.join(uv_root, 'common.gypi') + options_fn = os.path.join(uv_root, 'options.gypi') + else: + args.append(os.path.join(os.path.abspath(uv_root), 'uv.gyp')) + common_fn = os.path.join(os.path.abspath(uv_root), 'common.gypi') + options_fn = os.path.join(os.path.abspath(uv_root), 'options.gypi') + + if os.path.exists(common_fn): + args.extend(['-I', common_fn]) + + if os.path.exists(options_fn): + args.extend(['-I', options_fn]) + + args.append('--depth=' + uv_root) + + # There's a bug with windows which doesn't allow this feature. + if sys.platform != 'win32': + + # Tell gyp to write the Makefiles into output_dir + args.extend(['--generator-output', output_dir]) + + # Tell make to write its output into the same dir + args.extend(['-Goutput_dir=' + output_dir]) + + args.append('-Dtarget_arch=ia32') + args.append('-Dcomponent=static_library') + args.append('-Dlibrary=static_library') + gyp_args = list(args) + print gyp_args + run_gyp(gyp_args) diff --git a/src/rt/libuv/include/eio.h b/src/rt/libuv/include/uv-private/eio.h similarity index 78% rename from src/rt/libuv/include/eio.h rename to src/rt/libuv/include/uv-private/eio.h index 3dd3267678a..450df6ba299 100644 --- a/src/rt/libuv/include/eio.h +++ b/src/rt/libuv/include/uv-private/eio.h @@ -1,7 +1,7 @@ /* * libeio API header * - * Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann + * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- @@ -45,17 +45,9 @@ extern "C" { #endif #include +#include #include -#ifdef __OpenBSD__ -# include -#endif - -#ifdef _WIN32 -# define uid_t int -# define gid_t int -#endif - typedef struct eio_req eio_req; typedef struct eio_dirent eio_dirent; @@ -67,12 +59,35 @@ typedef int (*eio_cb)(eio_req *req); #ifndef EIO_STRUCT_STAT # ifdef _WIN32 -# define EIO_STRUCT_STAT struct _stati64 +# define EIO_STRUCT_STAT struct _stati64 +# define EIO_STRUCT_STATI64 # else -# define EIO_STRUCT_STAT struct stat +# define EIO_STRUCT_STAT struct stat # endif #endif +#ifdef _WIN32 + typedef int eio_uid_t; + typedef int eio_gid_t; + typedef int eio_mode_t; + #ifdef __MINGW32__ /* no intptr_t */ + typedef ssize_t eio_ssize_t; + #else + typedef intptr_t eio_ssize_t; /* or SSIZE_T */ + #endif + #if __GNUC__ + typedef long long eio_ino_t; + #else + typedef __int64 eio_ino_t; /* unsigned not supported by msvc */ + #endif +#else + typedef uid_t eio_uid_t; + typedef gid_t eio_gid_t; + typedef ssize_t eio_ssize_t; + typedef ino_t eio_ino_t; + typedef mode_t eio_mode_t; +#endif + #ifndef EIO_STRUCT_STATVFS # define EIO_STRUCT_STATVFS struct statvfs #endif @@ -119,7 +134,7 @@ struct eio_dirent unsigned short namelen; /* size of filename without trailing 0 */ unsigned char type; /* one of EIO_DT_* */ signed char score; /* internal use */ - ino_t inode; /* the inode number, if available, otherwise unspecified */ + eio_ino_t inode; /* the inode number, if available, otherwise unspecified */ }; /* eio_msync flags */ @@ -131,14 +146,12 @@ enum }; /* eio_mtouch flags */ - enum { EIO_MT_MODIFY = 1 }; /* eio_sync_file_range flags */ - enum { EIO_SYNC_FILE_RANGE_WAIT_BEFORE = 1, @@ -146,10 +159,16 @@ enum EIO_SYNC_FILE_RANGE_WAIT_AFTER = 4 }; -typedef double eio_tstamp; /* feel free to use double in your code directly */ +/* eio_fallocate flags */ +enum +{ + EIO_FALLOC_FL_KEEP_SIZE = 1 /* MUST match the value in linux/falloc.h */ +}; + +/* timestamps and differences - feel free to use double in your code directly */ +typedef double eio_tstamp; /* the eio request structure */ - enum { EIO_CUSTOM, @@ -162,12 +181,12 @@ enum EIO_UTIME, EIO_FUTIME, EIO_CHMOD, EIO_FCHMOD, EIO_CHOWN, EIO_FCHOWN, - EIO_SYNC, EIO_FSYNC, EIO_FDATASYNC, - EIO_MSYNC, EIO_MTOUCH, EIO_SYNC_FILE_RANGE, + EIO_SYNC, EIO_FSYNC, EIO_FDATASYNC, EIO_SYNCFS, + EIO_MSYNC, EIO_MTOUCH, EIO_SYNC_FILE_RANGE, EIO_FALLOCATE, EIO_MLOCK, EIO_MLOCKALL, EIO_UNLINK, EIO_RMDIR, EIO_MKDIR, EIO_RENAME, EIO_MKNOD, EIO_READDIR, - EIO_LINK, EIO_SYMLINK, EIO_READLINK, + EIO_LINK, EIO_SYMLINK, EIO_READLINK, EIO_REALPATH, EIO_GROUP, EIO_NOP, EIO_BUSY }; @@ -194,9 +213,9 @@ struct eio_req { eio_req volatile *next; /* private ETP */ - ssize_t result; /* result of syscall, e.g. result = read (... */ - off_t offs; /* read, write, truncate, readahead, sync_file_range: file offset */ - size_t size; /* read, write, readahead, sendfile, msync, mlock, sync_file_range: length */ + eio_ssize_t result; /* result of syscall, e.g. result = read (... */ + off_t offs; /* read, write, truncate, readahead, sync_file_range, fallocate: file offset, mknod: dev_t */ + size_t size; /* read, write, readahead, sendfile, msync, mlock, sync_file_range, fallocate: length */ void *ptr1; /* all applicable requests: pathname, old name; readdir: optional eio_dirents */ void *ptr2; /* all applicable requests: new name or memory buffer; readdir: name strings */ eio_tstamp nv1; /* utime, futime: atime; busy: sleep time */ @@ -204,16 +223,22 @@ struct eio_req int type; /* EIO_xxx constant ETP */ int int1; /* all applicable requests: file descriptor; sendfile: output fd; open, msync, mlockall, readdir: flags */ - long int2; /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, sync_file_range: flags */ - long int3; /* chown, fchown: gid; mknod: dev_t */ + long int2; /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, sync_file_range, fallocate: flags */ + long int3; /* chown, fchown: gid */ int errorno; /* errno value on syscall return */ +#if __i386 || __amd64 + unsigned char cancelled; +#else + sig_atomic_t cancelled; +#endif + unsigned char flags; /* private */ signed char pri; /* the priority */ void *data; eio_cb finish; - void (*destroy)(eio_req *req); /* called when requets no longer needed */ + void (*destroy)(eio_req *req); /* called when request no longer needed */ void (*feed)(eio_req *req); /* only used for group requests */ EIO_REQ_MEMBERS @@ -223,10 +248,9 @@ struct eio_req /* _private_ request flags */ enum { - EIO_FLAG_CANCELLED = 0x01, /* request was cancelled */ - EIO_FLAG_PTR1_FREE = 0x02, /* need to free(ptr1) */ - EIO_FLAG_PTR2_FREE = 0x04, /* need to free(ptr2) */ - EIO_FLAG_GROUPADD = 0x08 /* some request was added to the group */ + EIO_FLAG_PTR1_FREE = 0x01, /* need to free(ptr1) */ + EIO_FLAG_PTR2_FREE = 0x02, /* need to free(ptr2) */ + EIO_FLAG_GROUPADD = 0x04 /* some request was added to the group */ }; /* undocumented/unsupported/private helper */ @@ -254,14 +278,15 @@ void eio_set_max_poll_reqs (unsigned int nreqs); void eio_set_min_parallel (unsigned int nthreads); void eio_set_max_parallel (unsigned int nthreads); void eio_set_max_idle (unsigned int nthreads); +void eio_set_idle_timeout (unsigned int seconds); unsigned int eio_nreqs (void); /* number of requests in-flight */ unsigned int eio_nready (void); /* number of not-yet handled requests */ -unsigned int eio_npending (void); /* numbe rof finished but unhandled requests */ +unsigned int eio_npending (void); /* number of finished but unhandled requests */ unsigned int eio_nthreads (void); /* number of worker threads in use currently */ /*****************************************************************************/ -/* convinience wrappers */ +/* convenience wrappers */ #ifndef EIO_NO_WRAPPERS eio_req *eio_nop (int pri, eio_cb cb, void *data); /* does nothing except go through the whole process */ @@ -269,11 +294,13 @@ eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data); /* ti eio_req *eio_sync (int pri, eio_cb cb, void *data); eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data); eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data); +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data); eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data); eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data); eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data); +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data); eio_req *eio_close (int fd, int pri, eio_cb cb, void *data); eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data); eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); @@ -282,28 +309,29 @@ eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data); /* stat buffer= eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data); +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data); eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data); eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data); -eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data); eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data); eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data); +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data); eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data); eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data); eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_custom (eio_cb execute, int pri, eio_cb cb, void *data); +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data); #endif /*****************************************************************************/ @@ -319,7 +347,7 @@ void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the g /* request api */ /* true if the request was cancelled, useful in the invoke callback */ -#define EIO_CANCELLED(req) ((req)->flags & EIO_FLAG_CANCELLED) +#define EIO_CANCELLED(req) ((req)->cancelled) #define EIO_RESULT(req) ((req)->result) /* returns a pointer to the result buffer allocated by eio */ @@ -332,21 +360,13 @@ void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the g void eio_submit (eio_req *req); /* cancel a request as soon fast as possible, if possible */ void eio_cancel (eio_req *req); -/* destroy a request that has never been submitted */ -void eio_destroy (eio_req *req); /*****************************************************************************/ -/* convinience functions */ +/* convenience functions */ -ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count); - -/*****************************************************************************/ -/* export these so node_file can use these function instead of pread/write */ - -#if !HAVE_PREADWRITE -ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset); -ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset); -#endif +eio_ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count); +eio_ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset); +eio_ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset); #ifdef __cplusplus } diff --git a/src/rt/libuv/include/ev.h b/src/rt/libuv/include/uv-private/ev.h similarity index 96% rename from src/rt/libuv/include/ev.h rename to src/rt/libuv/include/uv-private/ev.h index 526b9f1bcb9..1db1e266493 100644 --- a/src/rt/libuv/include/ev.h +++ b/src/rt/libuv/include/uv-private/ev.h @@ -48,6 +48,12 @@ EV_CPP(extern "C" {) +#ifdef __GNUC__ +# define EV_MAYBE_UNUSED __attribute__ ((unused)) +#else +# define EV_MAYBE_UNUSED +#endif + /*****************************************************************************/ /* pre-4.0 compatibility */ @@ -539,7 +545,7 @@ void ev_set_syserr_cb (void (*cb)(const char *msg)); struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)); EV_INLINE struct ev_loop * -ev_default_loop_uc_ (void) +EV_MAYBE_UNUSED ev_default_loop_uc_ (void) { extern struct ev_loop *ev_default_loop_ptr; @@ -547,7 +553,7 @@ ev_default_loop_uc_ (void) } EV_INLINE int -ev_is_default_loop (EV_P) +EV_MAYBE_UNUSED ev_is_default_loop (EV_P) { return EV_A == EV_DEFAULT_UC; } @@ -807,14 +813,14 @@ void ev_async_send (EV_P_ ev_async *w); #define EVUNLOOP_ONE EVBREAK_ONE #define EVUNLOOP_ALL EVBREAK_ALL #if EV_PROTOTYPES - EV_INLINE void ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); } - EV_INLINE void ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); } - EV_INLINE void ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); } - EV_INLINE void ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); } + EV_INLINE void EV_MAYBE_UNUSED ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); } + EV_INLINE void EV_MAYBE_UNUSED ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); } + EV_INLINE void EV_MAYBE_UNUSED ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); } + EV_INLINE void EV_MAYBE_UNUSED ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); } #if EV_FEATURE_API - EV_INLINE unsigned int ev_loop_count (EV_P) { return ev_iteration (EV_A); } - EV_INLINE unsigned int ev_loop_depth (EV_P) { return ev_depth (EV_A); } - EV_INLINE void ev_loop_verify (EV_P) { ev_verify (EV_A); } + EV_INLINE unsigned int EV_MAYBE_UNUSED ev_loop_count (EV_P) { return ev_iteration (EV_A); } + EV_INLINE unsigned int EV_MAYBE_UNUSED ev_loop_depth (EV_P) { return ev_depth (EV_A); } + EV_INLINE void EV_MAYBE_UNUSED ev_loop_verify (EV_P) { ev_verify (EV_A); } #endif #endif #else diff --git a/src/rt/libuv/include/ngx-queue.h b/src/rt/libuv/include/uv-private/ngx-queue.h similarity index 100% rename from src/rt/libuv/include/ngx-queue.h rename to src/rt/libuv/include/uv-private/ngx-queue.h diff --git a/src/rt/libuv/include/tree.h b/src/rt/libuv/include/uv-private/tree.h similarity index 100% rename from src/rt/libuv/include/tree.h rename to src/rt/libuv/include/uv-private/tree.h diff --git a/src/rt/libuv/src/uv-freebsd.c b/src/rt/libuv/include/uv-private/uv-linux.h similarity index 73% rename from src/rt/libuv/src/uv-freebsd.c rename to src/rt/libuv/include/uv-private/uv-linux.h index 108455d8804..7f7b05932d9 100644 --- a/src/rt/libuv/src/uv-freebsd.c +++ b/src/rt/libuv/include/uv-private/uv-linux.h @@ -1,4 +1,5 @@ /* 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 @@ -18,32 +19,11 @@ * IN THE SOFTWARE. */ -#include "uv.h" +#ifndef UV_LINUX_H +#define UV_LINUX_H +#define UV_FS_EVENT_PRIVATE_FIELDS \ + ev_io read_watcher; \ + uv_fs_event_cb cb; \ -int uv_exepath(char* buffer, size_t* size) { - uint32_t usize; - int result; - char* path; - char* fullpath; - - if (!buffer || !size) { - return -1; - } - - int mib[4]; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PATHNAME; - mib[3] = -1; - - size_t cb = *size; - if (sysctl(mib, 4, buffer, &cb, NULL, 0) < 0) { - *size = 0; - return -1; - } - *size = strlen(buffer); - - return 0; -} +#endif /* UV_LINUX_H */ diff --git a/src/rt/libuv/include/uv-unix.h b/src/rt/libuv/include/uv-private/uv-unix.h similarity index 59% rename from src/rt/libuv/include/uv-unix.h rename to src/rt/libuv/include/uv-private/uv-unix.h index f68a7fd6fd9..5fa132872dc 100644 --- a/src/rt/libuv/include/uv-unix.h +++ b/src/rt/libuv/include/uv-private/uv-unix.h @@ -25,6 +25,11 @@ #include "ngx-queue.h" #include "ev.h" +#include "eio.h" + +#if defined(__linux__) +#include "uv-private/uv-linux.h" +#endif #include #include @@ -39,16 +44,52 @@ typedef struct { size_t len; } uv_buf_t; +typedef int uv_file; + +/* Stub. Remove it once all platforms support the file watcher API. */ +#ifndef UV_FS_EVENT_PRIVATE_FIELDS +#define UV_FS_EVENT_PRIVATE_FIELDS /* empty */ +#endif + +#define UV_LOOP_PRIVATE_FIELDS \ + ares_channel channel; \ + /* \ + * While the channel is active this timer is called once per second to be \ + * sure that we're always calling ares_process. See the warning above the \ + * definition of ares_timeout(). \ + */ \ + ev_timer timer; \ + struct ev_loop* ev; + #define UV_REQ_BUFSML_SIZE (4) -#define UV_REQ_PRIVATE_FIELDS \ - int write_index; \ - ev_timer timer; \ +#define UV_REQ_PRIVATE_FIELDS /* empty */ + +#define UV_WRITE_PRIVATE_FIELDS \ ngx_queue_t queue; \ + int write_index; \ uv_buf_t* bufs; \ int bufcnt; \ + int error; \ uv_buf_t bufsml[UV_REQ_BUFSML_SIZE]; +#define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */ + +#define UV_CONNECT_PRIVATE_FIELDS \ + ngx_queue_t queue; + +#define UV_UDP_SEND_PRIVATE_FIELDS \ + ngx_queue_t queue; \ + struct sockaddr_storage addr; \ + socklen_t addrlen; \ + uv_buf_t* bufs; \ + int bufcnt; \ + ssize_t status; \ + uv_udp_send_cb send_cb; \ + uv_buf_t bufsml[UV_REQ_BUFSML_SIZE]; \ + +#define UV_PRIVATE_REQ_TYPES /* empty */ + /* TODO: union or classes please! */ #define UV_HANDLE_PRIVATE_FIELDS \ @@ -59,20 +100,35 @@ typedef struct { #define UV_STREAM_PRIVATE_FIELDS \ uv_read_cb read_cb; \ - uv_alloc_cb alloc_cb; - - -/* UV_TCP */ -#define UV_TCP_PRIVATE_FIELDS \ - int delayed_error; \ - uv_connection_cb connection_cb; \ - int accepted_fd; \ - uv_req_t *connect_req; \ - uv_req_t *shutdown_req; \ + uv_alloc_cb alloc_cb; \ + uv_connect_t *connect_req; \ + uv_shutdown_t *shutdown_req; \ ev_io read_watcher; \ ev_io write_watcher; \ ngx_queue_t write_queue; \ - ngx_queue_t write_completed_queue; + ngx_queue_t write_completed_queue; \ + int delayed_error; \ + uv_connection_cb connection_cb; \ + int accepted_fd; + + +/* UV_TCP */ +#define UV_TCP_PRIVATE_FIELDS + + +/* UV_UDP */ +#define UV_UDP_PRIVATE_FIELDS \ + uv_alloc_cb alloc_cb; \ + uv_udp_recv_cb recv_cb; \ + ev_io read_watcher; \ + ev_io write_watcher; \ + ngx_queue_t write_queue; \ + ngx_queue_t write_completed_queue; \ + + +/* UV_NAMED_PIPE */ +#define UV_PIPE_PRIVATE_FIELDS \ + const char* pipe_fname; /* strdup'ed */ /* UV_PREPARE */ \ @@ -117,4 +173,16 @@ typedef struct { struct addrinfo* res; \ int retcode; +#define UV_PROCESS_PRIVATE_FIELDS \ + ev_child child_watcher; + +#define UV_FS_PRIVATE_FIELDS \ + struct stat statbuf; \ + eio_req* eio; + +#define UV_WORK_PRIVATE_FIELDS \ + eio_req* eio; + +#define UV_TTY_PRIVATE_FIELDS /* empty */ + #endif /* UV_UNIX_H */ diff --git a/src/rt/libuv/include/uv-private/uv-win.h b/src/rt/libuv/include/uv-private/uv-win.h new file mode 100644 index 00000000000..f203d672891 --- /dev/null +++ b/src/rt/libuv/include/uv-private/uv-win.h @@ -0,0 +1,281 @@ +/* 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 + +#include +#include +#include +#include +#include +#include + +#include "tree.h" + +#define MAX_PIPENAME_LEN 256 + +/** + * 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; + +RB_HEAD(uv_timer_tree_s, uv_timer_s); + +#define UV_LOOP_PRIVATE_FIELDS \ + /* The loop's I/O completion port */ \ + HANDLE iocp; \ + /* Reference count that keeps the event loop alive */ \ + int refs; \ + /* The current time according to the event loop. in msecs. */ \ + int64_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; \ + ares_channel ares_chan; \ + int ares_active_sockets; \ + uv_timer_t ares_polling_timer; \ + /* Last error code */ \ + uv_err_t last_error; + +#define UV_REQ_TYPE_PRIVATE \ + /* TODO: remove the req suffix */ \ + UV_ARES_EVENT_REQ, \ + UV_ARES_CLEANUP_REQ, \ + UV_GETADDRINFO_REQ, \ + UV_PROCESS_EXIT, \ + UV_PROCESS_CLOSE, \ + UV_UDP_RECV, \ + UV_FS_EVENT_REQ + +#define UV_REQ_PRIVATE_FIELDS \ + union { \ + /* Used by I/O operations */ \ + struct { \ + OVERLAPPED overlapped; \ + size_t queued_bytes; \ + }; \ + }; \ + struct uv_req_s* next_req; + +#define UV_WRITE_PRIVATE_FIELDS \ + /* empty */ + +#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]; \ + struct uv_tcp_accept_s* next_pending; \ + } uv_tcp_accept_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; \ + uv_alloc_cb alloc_cb; \ + uv_read_cb read_cb; \ + uv_req_t read_req; \ + union { \ + struct { uv_stream_connection_fields }; \ + struct { uv_stream_server_fields }; \ + }; + +#define uv_tcp_server_fields \ + uv_tcp_accept_t* accept_reqs; \ + uv_tcp_accept_t* pending_accepts; + +#define uv_tcp_connection_fields \ + uv_buf_t read_buffer; + +#define UV_TCP_PRIVATE_FIELDS \ + SOCKET socket; \ + uv_err_t bind_error; \ + union { \ + struct { uv_tcp_server_fields }; \ + struct { uv_tcp_connection_fields }; \ + }; + +#define UV_UDP_PRIVATE_FIELDS \ + SOCKET socket; \ + unsigned int reqs_pending; \ + 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; + +#define uv_pipe_server_fields \ + uv_pipe_accept_t accept_reqs[4]; \ + uv_pipe_accept_t* pending_accepts; + +#define uv_pipe_connection_fields \ + uv_timer_t* eof_timer; + +#define UV_PIPE_PRIVATE_FIELDS \ + HANDLE handle; \ + wchar_t* name; \ + union { \ + struct { uv_pipe_server_fields }; \ + struct { uv_pipe_connection_fields }; \ + }; + +#define UV_TIMER_PRIVATE_FIELDS \ + RB_ENTRY(uv_timer_s) tree_entry; \ + int64_t due; \ + int64_t repeat; \ + 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_ARES_TASK_PRIVATE_FIELDS \ + struct uv_req_s ares_req; \ + SOCKET sock; \ + HANDLE h_wait; \ + WSAEVENT h_event; \ + HANDLE h_close_event; + +#define UV_GETADDRINFO_PRIVATE_FIELDS \ + struct uv_req_s getadddrinfo_req; \ + uv_getaddrinfo_cb getaddrinfo_cb; \ + void* alloc; \ + wchar_t* node; \ + wchar_t* service; \ + struct addrinfoW* hints; \ + struct addrinfoW* res; \ + int retcode; + +#define UV_PROCESS_PRIVATE_FIELDS \ + struct uv_process_exit_s { \ + UV_REQ_FIELDS \ + } exit_req; \ + struct uv_process_close_s { \ + UV_REQ_FIELDS \ + } close_req; \ + HANDLE child_stdio[3]; \ + int exit_signal; \ + DWORD spawn_errno; \ + HANDLE wait_handle; \ + HANDLE process_handle; \ + HANDLE close_handle; + +#define UV_FS_PRIVATE_FIELDS \ + int flags; \ + int last_error; \ + struct _stati64 stat; \ + void* arg0; \ + union { \ + struct { \ + void* arg1; \ + void* arg2; \ + void* arg3; \ + }; \ + struct { \ + ssize_t arg4; \ + ssize_t arg5; \ + }; \ + }; + +#define UV_WORK_PRIVATE_FIELDS \ + +#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_t* filew; \ + int is_path_dir; \ + char* buffer; + +#define UV_TTY_PRIVATE_FIELDS /* empty */ + +int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, + char* utf8Buffer, size_t utf8Size); +int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, + size_t utf16Size); diff --git a/src/rt/libuv/include/uv-win.h b/src/rt/libuv/include/uv-win.h deleted file mode 100644 index e6254fee09a..00000000000 --- a/src/rt/libuv/include/uv-win.h +++ /dev/null @@ -1,130 +0,0 @@ -/* 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 0x0501 -#endif - -#include -#include -#include -#include -#include - -#include "tree.h" - -/** - * 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; - -#define UV_REQ_PRIVATE_FIELDS \ - union { \ - /* Used by I/O operations */ \ - struct { \ - OVERLAPPED overlapped; \ - size_t queued_bytes; \ - }; \ - }; \ - int flags; \ - uv_err_t error; \ - struct uv_req_s* next_req; - -#define UV_STREAM_PRIVATE_FIELDS \ - uv_alloc_cb alloc_cb; \ - uv_read_cb read_cb; \ - struct uv_req_s read_req; \ - -#define uv_tcp_connection_fields \ - unsigned int write_reqs_pending; \ - uv_req_t* shutdown_req; - -#define uv_tcp_server_fields \ - uv_connection_cb connection_cb; \ - SOCKET accept_socket; \ - struct uv_req_s accept_req; \ - char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; - -#define UV_TCP_PRIVATE_FIELDS \ - unsigned int reqs_pending; \ - union { \ - SOCKET socket; \ - HANDLE handle; \ - }; \ - union { \ - struct { uv_tcp_connection_fields }; \ - struct { uv_tcp_server_fields }; \ - }; - -#define UV_TIMER_PRIVATE_FIELDS \ - RB_ENTRY(uv_timer_s) tree_entry; \ - int64_t due; \ - int64_t repeat; \ - uv_timer_cb timer_cb; - -#define UV_ASYNC_PRIVATE_FIELDS \ - struct uv_req_s async_req; \ - /* 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; \ - uv_err_t error; - -#define UV_ARES_TASK_PRIVATE_FIELDS \ - struct uv_req_s ares_req; \ - SOCKET sock; \ - HANDLE h_wait; \ - WSAEVENT h_event; \ - HANDLE h_close_event; - -#define UV_GETADDRINFO_PRIVATE_FIELDS \ - struct uv_req_s getadddrinfo_req; \ - uv_getaddrinfo_cb getaddrinfo_cb; \ - void* alloc; \ - wchar_t* node; \ - wchar_t* service; \ - struct addrinfoW* hints; \ - struct addrinfoW* res; \ - int retcode; - -int uv_utf16_to_utf8(wchar_t* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size); -int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, size_t utf16Size); diff --git a/src/rt/libuv/include/uv.h b/src/rt/libuv/include/uv.h index 6aecb282008..b8bdc093022 100644 --- a/src/rt/libuv/include/uv.h +++ b/src/rt/libuv/include/uv.h @@ -19,6 +19,8 @@ * IN THE SOFTWARE. */ +/* See uv_loop_new for an introduction. */ + #ifndef UV_H #define UV_H #ifdef __cplusplus @@ -39,27 +41,77 @@ extern "C" { typedef intptr_t ssize_t; #endif +typedef struct uv_loop_s uv_loop_t; +typedef struct uv_ares_task_s uv_ares_task_t; typedef struct uv_err_s uv_err_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_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_req_s uv_req_t; typedef struct uv_async_s uv_async_t; typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; - +typedef struct uv_process_s uv_process_t; +typedef struct uv_counters_s uv_counters_t; +/* Request types */ +typedef struct uv_req_s uv_req_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; +/* uv_fs_event_t is a subclass of uv_handle_t. */ +typedef struct uv_fs_event_s uv_fs_event_t; +typedef struct uv_work_s uv_work_t; #if defined(__unix__) || defined(__POSIX__) || defined(__APPLE__) -# include "uv-unix.h" +# include "uv-private/uv-unix.h" #else -# include "uv-win.h" +# include "uv-private/uv-win.h" #endif -/* The status parameter is 0 if the request completed successfully, +/* + * This function must be called before any other functions in libuv. + * + * All functions besides uv_run() are non-blocking. + * + * All callbacks in libuv are made asynchronously. That is they are never + * made by the function that takes them as a parameter. + */ +uv_loop_t* uv_loop_new(); +void uv_loop_delete(uv_loop_t*); + + +/* + * Returns the default loop. + */ +uv_loop_t* uv_default_loop(); + +/* + * This function starts the event loop. It blocks until the reference count + * of the loop drops to zero. + */ +int uv_run(uv_loop_t*); + +/* + * Manually modify the event loop's reference count. Useful if the user wants + * to have a handle or timeout that doesn't keep the loop alive. + */ +void uv_ref(uv_loop_t*); +void uv_unref(uv_loop_t*); + +void uv_update_time(uv_loop_t*); +int64_t uv_now(uv_loop_t*); + + +/* + * The status parameter is 0 if the request completed successfully, * and should be -1 if the request was cancelled or failed. * For uv_close_cb, -1 means that the handle was closed due to an error. * Error details can be obtained by calling uv_last_error(). @@ -67,12 +119,12 @@ typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; * In the case of uv_read_cb the uv_buf_t returned should be freed by the * user. */ -typedef uv_buf_t (*uv_alloc_cb)(uv_stream_t* tcp, size_t suggested_size); -typedef void (*uv_read_cb)(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf); -typedef void (*uv_write_cb)(uv_req_t* req, int status); -typedef void (*uv_connect_cb)(uv_req_t* req, int status); -typedef void (*uv_shutdown_cb)(uv_req_t* req, int status); -typedef void (*uv_connection_cb)(uv_handle_t* server, int status); +typedef uv_buf_t (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size); +typedef void (*uv_read_cb)(uv_stream_t* stream, ssize_t nread, 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_timer_cb)(uv_timer_t* handle, int status); /* TODO: do these really need a status argument? */ @@ -80,7 +132,21 @@ typedef void (*uv_async_cb)(uv_async_t* handle, int status); typedef void (*uv_prepare_cb)(uv_prepare_t* handle, int status); typedef void (*uv_check_cb)(uv_check_t* handle, int status); typedef void (*uv_idle_cb)(uv_idle_t* handle, int status); -typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status, struct addrinfo* res); +typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status, + struct addrinfo* res); +typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal); +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); + +/* +* This will be called repeatedly after the uv_fs_event_t is initialized. +* If uv_fs_event_t was initialized with a directory the filename parameter +* will be a relative path to a file contained in the directory. +* The events paramenter is an ORed mask of enum uv_fs_event elements. +*/ +typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, + int events, int status); /* Expand this list if necessary. */ @@ -106,6 +172,7 @@ typedef enum { UV_EINVAL, UV_EISCONN, UV_EMFILE, + UV_EMSGSIZE, UV_ENETDOWN, UV_ENETUNREACH, UV_ENFILE, @@ -116,6 +183,8 @@ typedef enum { UV_ENOTCONN, UV_ENOTSOCK, UV_ENOTSUP, + UV_ENOENT, + UV_EPIPE, UV_EPROTO, UV_EPROTONOSUPPORT, UV_EPROTOTYPE, @@ -124,12 +193,15 @@ typedef enum { UV_EAIFAMNOSUPPORT, UV_EAINONAME, UV_EAISERVICE, - UV_EAISOCKTYPE + UV_EAISOCKTYPE, + UV_ESHUTDOWN, + UV_EEXIST } uv_err_code; typedef enum { UV_UNKNOWN_HANDLE = 0, UV_TCP, + UV_UDP, UV_NAMED_PIPE, UV_TTY, UV_FILE, @@ -138,9 +210,10 @@ typedef enum { UV_CHECK, UV_IDLE, UV_ASYNC, - UV_ARES, UV_ARES_TASK, - UV_GETADDRINFO + UV_ARES_EVENT, + UV_PROCESS, + UV_FS_EVENT } uv_handle_type; typedef enum { @@ -150,7 +223,12 @@ typedef enum { UV_READ, UV_WRITE, UV_SHUTDOWN, - UV_WAKEUP + UV_WAKEUP, + UV_UDP_SEND, + UV_FS, + UV_WORK, + UV_GETADDRINFO, + UV_REQ_TYPE_PRIVATE } uv_req_type; @@ -162,27 +240,55 @@ struct uv_err_s { }; -struct uv_req_s { - /* read-only */ - uv_req_type type; - /* public */ - uv_handle_t* handle; - void *(*cb)(void *); - void* data; - /* private */ +/* + * Most functions return boolean: 0 for success and -1 for failure. + * On error the user should then call uv_last_error() to determine + * the error code. + */ +uv_err_t uv_last_error(uv_loop_t*); +char* uv_strerror(uv_err_t err); +const char* uv_err_name(uv_err_t err); + + +#define UV_REQ_FIELDS \ + /* read-only */ \ + uv_req_type type; \ + /* public */ \ + void* data; \ + /* private */ \ UV_REQ_PRIVATE_FIELDS + +/* Abstract base class of all requests. */ +struct uv_req_s { + UV_REQ_FIELDS }; -/* - * Initialize a request for use with uv_write, uv_shutdown, or uv_connect. - */ -void uv_req_init(uv_req_t* req, uv_handle_t* handle, void *(*cb)(void *)); -int uv_shutdown(uv_req_t* req); +/* Platform-specific request types */ +UV_PRIVATE_REQ_TYPES + + +/* + * uv_shutdown_t is a subclass of uv_req_t + * + * Shutdown the outgoing (write) side of a duplex stream. It waits for + * pending write requests to complete. The handle should refer to a + * initialized stream. req should be an uninitalized shutdown request + * struct. The cb is a called after shutdown is complete. + */ +int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb); + +struct uv_shutdown_s { + UV_REQ_FIELDS + uv_stream_t* handle; + uv_shutdown_cb cb; + UV_SHUTDOWN_PRIVATE_FIELDS +}; #define UV_HANDLE_FIELDS \ /* read-only */ \ + uv_loop_t* loop; \ uv_handle_type type; \ /* public */ \ uv_close_cb close_cb; \ @@ -204,8 +310,21 @@ int uv_is_active(uv_handle_t* handle); /* * Request handle to be closed. close_cb will be called asynchronously after * this call. This MUST be called on each handle before memory is released. + * + * Note that handles that wrap file descriptors are closed immediately but + * close_cb will still be deferred to the next iteration of the event loop. + * It gives you a chance to free up any resources associated with the handle. */ -int uv_close(uv_handle_t* handle, uv_close_cb close_cb); +void uv_close(uv_handle_t* handle, uv_close_cb close_cb); + + +/* + * Constructor for uv_buf_t. + * Due to platform differences the user cannot rely on the ordering of the + * base and len members of the uv_buf_t struct. The user is responsible for + * freeing base after the uv_buf_t is done. Return struct passed by value. + */ +uv_buf_t uv_buf_init(char* base, size_t len); #define UV_STREAM_FIELDS \ @@ -214,13 +333,23 @@ int uv_close(uv_handle_t* handle, uv_close_cb close_cb); /* private */ \ UV_STREAM_PRIVATE_FIELDS -/* The abstract base class for all streams. */ +/* + * 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, uv_tty_t + * and soon uv_file_t. + */ struct uv_stream_s { UV_HANDLE_FIELDS UV_STREAM_FIELDS }; -/* This call is used in conjunction with uv_listen() to accept incoming +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); + +/* + * This call is used in conjunction with uv_listen() to accept incoming * connections. Call uv_accept after receiving a uv_connection_cb to accept * the connection. Before calling uv_accept use uv_*_init() must be * called on the client. Non-zero return value indicates an error. @@ -230,9 +359,10 @@ struct uv_stream_s { * once, it may fail. It is suggested to only call uv_accept once per * uv_connection_cb call. */ -int uv_accept(uv_handle_t* server, uv_stream_t* client); +int uv_accept(uv_stream_t* server, uv_stream_t* client); -/* Read data from an incoming stream. The callback will be made several +/* + * Read data from an incoming stream. The callback will be made several * several times until there is no more data to read or uv_read_stop is * called. When we've reached EOF nread will be set to -1 and the error is * set to UV_EOF. When nread == -1 the buf parameter might not point to a @@ -245,7 +375,16 @@ int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb); int uv_read_stop(uv_stream_t*); -/* Write data to stream. Buffers are written in order. Example: +typedef enum { + UV_STDIN = 0, + UV_STDOUT, + UV_STDERR +} uv_std_type; + +uv_stream_t* uv_std_handle(uv_loop_t*, uv_std_type type); + +/* + * Write data to stream. Buffers are written in order. Example: * * uv_buf_t a[] = { * { .base = "1", .len = 1 }, @@ -258,17 +397,27 @@ int uv_read_stop(uv_stream_t*); * }; * * // writes "1234" - * uv_write(req, a, 2); - * uv_write(req, b, 2); + * uv_write(req, stream, a, 2); + * uv_write(req, stream, b, 2); * */ -int uv_write(uv_req_t* req, uv_buf_t bufs[], int bufcnt); +int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb); + +/* uv_write_t is a subclass of uv_req_t */ +struct uv_write_s { + UV_REQ_FIELDS + uv_write_cb cb; + uv_stream_t* handle; + UV_WRITE_PRIVATE_FIELDS +}; + /* - * A subclass of uv_stream_t representing a TCP stream or TCP server. In the - * future this will probably be split into two classes - one a stream and - * the other a server. + * uv_tcp_t is a subclass of uv_stream_t + * + * Represents a TCP stream or TCP server. */ struct uv_tcp_s { UV_HANDLE_FIELDS @@ -276,28 +425,251 @@ struct uv_tcp_s { UV_TCP_PRIVATE_FIELDS }; -int uv_tcp_init(uv_tcp_t* handle); +int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle); int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in); int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6); +int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, int* namelen); +int uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, int* namelen); -int uv_tcp_connect(uv_req_t* req, struct sockaddr_in); -int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6); +/* + * uv_tcp_connect, uv_tcp_connect6 + * These functions establish IPv4 and IPv6 TCP connections. Provide an + * initialized TCP handle and an uninitialized uv_connect_t*. The callback + * will be made when the connection is estabished. + */ +int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in address, uv_connect_cb cb); +int uv_tcp_connect6(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in6 address, uv_connect_cb cb); -int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_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 +}; /* - * Subclass of uv_handle_t. libev wrapper. Every active prepare handle gets - * its callback called exactly once per loop iteration, just before the - * system blocks to wait for completed i/o. + * UDP support. + */ + +enum uv_udp_flags { + /* Disables dual stack mode. Used with uv_udp_bind6(). */ + 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 +}; + +/* + * Called after a uv_udp_send() or uv_udp_send6(). status 0 indicates + * success otherwise error. + */ +typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); + +/* + * Callback that is invoked when a new UDP datagram is received. + * + * handle UDP handle. + * nread Number of bytes that have been received. + * 0 if there is no more data to read. You may + * discard or repurpose the read buffer. + * -1 if a transmission error was detected. + * buf uv_buf_t with the received data. + * addr struct sockaddr_in or struct sockaddr_in6. + * Valid for the duration of the callback only. + * flags One or more OR'ed UV_UDP_* constants. + * Right now only UV_UDP_PARTIAL is used. + */ +typedef void (*uv_udp_recv_cb)(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, + struct sockaddr* addr, unsigned flags); + +/* uv_udp_t is a subclass of uv_handle_t */ +struct uv_udp_s { + UV_HANDLE_FIELDS + 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 +}; + +/* + * Initialize a new UDP handle. The actual socket is created lazily. + * Returns 0 on success. + */ +int uv_udp_init(uv_loop_t*, uv_udp_t* handle); + +/* + * Bind to a IPv4 address and port. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * addr struct sockaddr_in with the address and port to bind to. + * flags Unused. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_bind(uv_udp_t* handle, struct sockaddr_in addr, unsigned flags); + +/* + * Bind to a IPv6 address and port. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * addr struct sockaddr_in with the address and port to bind to. + * flags Should be 0 or UV_UDP_IPV6ONLY. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags); +int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, int* namelen); + +/* + * Send data. If the socket has not previously been bound with `uv_udp_bind` + * or `uv_udp_bind6`, it is bound to 0.0.0.0 (the "all interfaces" address) + * and a random port number. + * + * Arguments: + * req UDP request handle. Need not be initialized. + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * bufs List of buffers to send. + * bufcnt Number of buffers in `bufs`. + * addr Address of the remote peer. See `uv_ip4_addr`. + * send_cb Callback to invoke when the data has been sent out. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in addr, uv_udp_send_cb send_cb); + +/* + * Send data. If the socket has not previously been bound with `uv_udp_bind6`, + * it is bound to ::0 (the "all interfaces" address) and a random port number. + * + * Arguments: + * req UDP request handle. Need not be initialized. + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * bufs List of buffers to send. + * bufcnt Number of buffers in `bufs`. + * addr Address of the remote peer. See `uv_ip6_addr`. + * send_cb Callback to invoke when the data has been sent out. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_send6(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in6 addr, uv_udp_send_cb send_cb); + +/* + * Send data. If the socket has not previously been bound with `uv_udp_bind` + * or `uv_udp_bind6`, it is bound to 0.0.0.0 (the "all interfaces" address) + * and a random port number. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * alloc_cb Callback to invoke when temporary storage is needed. + * recv_cb Callback to invoke with received data. + * + * Returns: + * 0 on success, -1 on error. + */ +int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, + uv_udp_recv_cb recv_cb); + +/* + * Stop listening for incoming datagrams. + * + * Arguments: + * handle UDP handle. Should have been initialized with `uv_udp_init`. + * + * Returns: + * 0 on success, -1 on error. + */ +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 +}; + +int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd); + +/* + * Set mode. 0 for normal, 1 for raw. + */ +int uv_tty_set_mode(uv_tty_t*, int mode); + +/* + * Gets the current Window size. On success zero is returned. + */ +int uv_tty_get_winsize(uv_tty_t*, int* width, int* height); + +/* + * Used to detect what type of stream should be used with a given file + * descriptor. Usually this will be used during initialization to guess the + * type of the stdio streams. + * For isatty() functionality use this function and test for UV_TTY. + */ +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 + UV_PIPE_PRIVATE_FIELDS +}; + +int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle); + +/* + * Opens an existing file descriptor or HANDLE as a pipe. + */ +void uv_pipe_open(uv_pipe_t*, uv_file file); + +int uv_pipe_bind(uv_pipe_t* handle, const char* name); + +int uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, + const char* name, uv_connect_cb cb); + + +/* + * uv_prepare_t is a subclass of uv_handle_t. + * + * libev wrapper. Every active prepare handle gets its callback called + * exactly once per loop iteration, just before the system blocks to wait + * for completed i/o. */ struct uv_prepare_s { UV_HANDLE_FIELDS UV_PREPARE_PRIVATE_FIELDS }; -int uv_prepare_init(uv_prepare_t* prepare); +int uv_prepare_init(uv_loop_t*, uv_prepare_t* prepare); int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb); @@ -305,16 +677,17 @@ int uv_prepare_stop(uv_prepare_t* prepare); /* - * Subclass of uv_handle_t. libev wrapper. Every active check handle gets - * its callback called exactly once per loop iteration, just after the - * system returns from blocking. + * uv_check_t is a subclass of uv_handle_t. + * + * libev wrapper. Every active check handle gets its callback called exactly + * once per loop iteration, just after the system returns from blocking. */ struct uv_check_s { UV_HANDLE_FIELDS UV_CHECK_PRIVATE_FIELDS }; -int uv_check_init(uv_check_t* check); +int uv_check_init(uv_loop_t*, uv_check_t* check); int uv_check_start(uv_check_t* check, uv_check_cb cb); @@ -322,17 +695,19 @@ int uv_check_stop(uv_check_t* check); /* - * Subclass of uv_handle_t. libev wrapper. Every active idle handle gets its - * callback called repeatedly until it is stopped. This happens after all - * other types of callbacks are processed. When there are multiple "idle" - * handles active, their callbacks are called in turn. + * uv_idle_t is a subclass of uv_handle_t. + * + * libev wrapper. Every active idle handle gets its callback called + * repeatedly until it is stopped. This happens after all other types of + * callbacks are processed. When there are multiple "idle" handles active, + * their callbacks are called in turn. */ struct uv_idle_s { UV_HANDLE_FIELDS UV_IDLE_PRIVATE_FIELDS }; -int uv_idle_init(uv_idle_t* idle); +int uv_idle_init(uv_loop_t*, uv_idle_t* idle); int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb); @@ -340,7 +715,9 @@ int uv_idle_stop(uv_idle_t* idle); /* - * Subclass of uv_handle_t. libev wrapper. uv_async_send wakes up the event + * uv_async_t is a subclass of uv_handle_t. + * + * libev wrapper. uv_async_send wakes up the event * loop and calls the async handle's callback There is no guarantee that * every uv_async_send call leads to exactly one invocation of the callback; * The only guarantee is that the callback function is called at least once @@ -352,23 +729,31 @@ struct uv_async_s { UV_ASYNC_PRIVATE_FIELDS }; -int uv_async_init(uv_async_t* async, uv_async_cb async_cb); +int uv_async_init(uv_loop_t*, uv_async_t* async, uv_async_cb async_cb); +/* + * This can be called from other threads to wake up a libuv thread. + * + * libuv is single threaded at the moment. + */ int uv_async_send(uv_async_t* async); /* - * Subclass of uv_handle_t. Wraps libev's ev_timer watcher. Used to get - * woken up at a specified time in the future. + * uv_timer_t is a subclass of uv_handle_t. + * + * Wraps libev's ev_timer watcher. Used to get woken up at a specified time + * in the future. */ struct uv_timer_s { UV_HANDLE_FIELDS UV_TIMER_PRIVATE_FIELDS }; -int uv_timer_init(uv_timer_t* timer); +int uv_timer_init(uv_loop_t*, uv_timer_t* timer); -int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, int64_t repeat); +int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, + int64_t repeat); int uv_timer_stop(uv_timer_t* timer); @@ -391,63 +776,286 @@ int64_t uv_timer_get_repeat(uv_timer_t* timer); /* c-ares integration initialize and terminate */ -int uv_ares_init_options(ares_channel *channelptr, - struct ares_options *options, - int optmask); +int uv_ares_init_options(uv_loop_t*, + ares_channel *channelptr, + struct ares_options *options, + int optmask); -void uv_ares_destroy(ares_channel channel); +/* TODO remove the loop argument from this function? */ +void uv_ares_destroy(uv_loop_t*, ares_channel channel); /* - * Subclass of uv_handle_t. Used for integration of getaddrinfo. + * uv_getaddrinfo_t is a subclass of uv_req_t + * + * Request object for uv_getaddrinfo. */ struct uv_getaddrinfo_s { - UV_HANDLE_FIELDS + UV_REQ_FIELDS + /* read-only */ + uv_loop_t* loop; \ UV_GETADDRINFO_PRIVATE_FIELDS }; -/* uv_getaddrinfo - * return code of UV_OK means that request is accepted, - * and callback will be called with result. - * Other return codes mean that there will not be a callback. +/* + * Asynchronous getaddrinfo(3). + * + * Return code 0 means that request is accepted and callback will be called + * with result. Other return codes mean that there will not be a callback. * Input arguments may be released after return from this call. - * Callback must not call freeaddrinfo + * + * uv_freeaddrinfo() must be called after completion to free the addrinfo + * structure. */ - int uv_getaddrinfo(uv_getaddrinfo_t* handle, + int uv_getaddrinfo(uv_loop_t*, + uv_getaddrinfo_t* handle, uv_getaddrinfo_cb getaddrinfo_cb, const char* node, const char* service, const struct addrinfo* hints); +void uv_freeaddrinfo(struct addrinfo* ai); + +/* uv_spawn() options */ +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 concatinates 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. + */ + char* cwd; + + /* + * TODO describe how this works. + */ + int windows_verbatim_arguments; + + /* + * The user should supply pointers to initialized uv_pipe_t structs for + * stdio. This is used to to send or receive input from the subprocess. + * The user is reponsible for calling uv_close on them. + */ + uv_pipe_t* stdin_stream; + uv_pipe_t* stdout_stream; + uv_pipe_t* stderr_stream; +} uv_process_options_t; /* - * Most functions return boolean: 0 for success and -1 for failure. - * On error the user should then call uv_last_error() to determine - * the error code. + * uv_process_t is a subclass of uv_handle_t */ -uv_err_t uv_last_error(); -char* uv_strerror(uv_err_t err); -const char* uv_err_name(uv_err_t err); +struct uv_process_s { + UV_HANDLE_FIELDS + uv_exit_cb exit_cb; + int pid; + UV_PROCESS_PRIVATE_FIELDS +}; -void uv_init(); -int uv_run(); +/* Initializes uv_process_t and starts the process. */ +int uv_spawn(uv_loop_t*, uv_process_t*, uv_process_options_t options); /* - * Manually modify the event loop's reference count. Useful if the user wants - * to have a handle or timeout that doesn't keep the loop alive. + * Kills the process with the specified signal. The user must still + * call uv_close on the process. */ -void uv_ref(); -void uv_unref(); +int uv_process_kill(uv_process_t*, int signum); -void uv_update_time(); -int64_t uv_now(); +/* + * 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 +}; + +/* Queues a work request to execute asynchronously on the thread pool. */ +int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, + uv_after_work_cb after_work_cb); + + + + +/* + * File System Methods. + * + * The uv_fs_* functions execute a blocking system call asynchronously (in a + * thread pool) and call the specified callback in the specified loop after + * completion. If the user gives NULL as the callback the blocking system + * call will be called synchronously. req should be a pointer to an + * uninitialized uv_fs_t object. + * + * uv_fs_req_cleanup() must be called after completion of the uv_fs_ + * function to free any internal memory allocations associted with the + * request. + */ + +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_CHMOD, + UV_FS_FCHMOD, + UV_FS_FSYNC, + UV_FS_FDATASYNC, + UV_FS_UNLINK, + UV_FS_RMDIR, + UV_FS_MKDIR, + UV_FS_RENAME, + UV_FS_READDIR, + UV_FS_LINK, + UV_FS_SYMLINK, + UV_FS_READLINK, + UV_FS_CHOWN, + UV_FS_FCHOWN +} uv_fs_type; + +/* uv_fs_t is a subclass of uv_req_t */ +struct uv_fs_s { + UV_REQ_FIELDS + uv_loop_t* loop; + uv_fs_type fs_type; + uv_fs_cb cb; + ssize_t result; + void* ptr; + char* path; + int errorno; + UV_FS_PRIVATE_FIELDS +}; + +void uv_fs_req_cleanup(uv_fs_t* req); + +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + int mode, uv_fs_cb cb); + +int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb); + +int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb); + +int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb); + +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + uv_fs_cb cb); + +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb); + +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + +int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, + off_t offset, uv_fs_cb cb); + +int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, + uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb); + +int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb); + +int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb); + +int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, + double mtime, uv_fs_cb cb); + +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + +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 + +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 uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb); + +int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, + uv_fs_cb cb); + +int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid, + int gid, uv_fs_cb cb); + +int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, + int gid, uv_fs_cb cb); + + +enum uv_fs_event { + UV_RENAME = 1, + UV_CHANGE = 2 +}; + + +struct uv_fs_event_s { + UV_HANDLE_FIELDS + char* filename; + UV_FS_EVENT_PRIVATE_FIELDS +}; + + +/* +* If filename is a directory then we will watch for all events in that +* directory. If filename is a file - we will only get events from that +* file. Subdirectories are not watched. +*/ +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb); /* Utility */ + +/* Convert string ip addresses to binary structures */ struct sockaddr_in uv_ip4_addr(const char* ip, int port); struct sockaddr_in6 uv_ip6_addr(const char* ip, int port); +/* Convert binary addresses to strings */ +int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size); +int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size); + /* Gets the executable path */ int uv_exepath(char* buffer, size_t* size); @@ -463,33 +1071,67 @@ int uv_exepath(char* buffer, size_t* size); extern uint64_t uv_hrtime(void); -/* the presence of this union forces similar struct layout */ +/* the presence of these unions force similar struct layout */ union uv_any_handle { uv_tcp_t tcp; + uv_pipe_t pipe; uv_prepare_t prepare; uv_check_t check; uv_idle_t idle; uv_async_t async; uv_timer_t timer; uv_getaddrinfo_t getaddrinfo; + uv_fs_event_t fs_event; }; -/* Diagnostic counters */ -typedef struct { +union uv_any_req { + uv_req_t req; + uv_write_t write; + uv_connect_t connect; + uv_shutdown_t shutdown; + uv_fs_t fs_req; + uv_work_t work_req; +}; + + +struct uv_counters_s { + uint64_t eio_init; uint64_t req_init; uint64_t handle_init; + uint64_t stream_init; uint64_t tcp_init; + uint64_t udp_init; + uint64_t pipe_init; + uint64_t tty_init; uint64_t prepare_init; uint64_t check_init; uint64_t idle_init; uint64_t async_init; uint64_t timer_init; -} uv_counters_t; + uint64_t process_init; + uint64_t fs_event_init; +}; -uv_counters_t* uv_counters(); + +struct uv_loop_s { + UV_LOOP_PRIVATE_FIELDS + /* list used for ares task handles */ + uv_ares_task_t* uv_ares_handles_; + /* Various thing for libeio. */ + uv_async_t uv_eio_want_poll_notifier; + uv_async_t uv_eio_done_poll_notifier; + uv_idle_t uv_eio_poller; + /* Diagnostic counters */ + uv_counters_t counters; + /* The last error */ + uv_err_t last_err; + /* User data - use this for whatever. */ + void* data; +}; /* Don't export the private CPP symbols. */ +#undef UV_REQ_TYPE_PRIVATE #undef UV_REQ_PRIVATE_FIELDS #undef UV_STREAM_PRIVATE_FIELDS #undef UV_TCP_PRIVATE_FIELDS @@ -499,6 +1141,9 @@ uv_counters_t* uv_counters(); #undef UV_ASYNC_PRIVATE_FIELDS #undef UV_TIMER_PRIVATE_FIELDS #undef UV_GETADDRINFO_PRIVATE_FIELDS +#undef UV_FS_REQ_PRIVATE_FIELDS +#undef UV_WORK_PRIVATE_FIELDS +#undef UV_FS_EVENT_PRIVATE_FIELDS #ifdef __cplusplus } diff --git a/src/rt/libuv/msvs/c-ares.vcxproj b/src/rt/libuv/msvs/c-ares.vcxproj deleted file mode 100644 index 4ada6ccb3df..00000000000 --- a/src/rt/libuv/msvs/c-ares.vcxproj +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - - - - StaticLibrary - true - - - StaticLibrary - true - - - StaticLibrary - false - - - StaticLibrary - false - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;HAVE_CONFIG_H;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - WIN32;HAVE_CONFIG_H;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - WIN32;HAVE_CONFIG_H;_LIB;%(PreprocessorDefinitions) - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - WIN32;HAVE_CONFIG_H;_LIB;%(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/rt/libuv/msvs/libuv-benchmark.vcxproj b/src/rt/libuv/msvs/libuv-benchmark.vcxproj deleted file mode 100644 index 5727a1aa3c8..00000000000 --- a/src/rt/libuv/msvs/libuv-benchmark.vcxproj +++ /dev/null @@ -1,167 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - - - - Application - true - - - Application - true - - - Application - false - - - Application - false - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - {301fe650-cd34-14e5-6b63-42e383fa02bc} - - - - - - diff --git a/src/rt/libuv/msvs/libuv-test.vcxproj b/src/rt/libuv/msvs/libuv-test.vcxproj deleted file mode 100644 index 5b47daeb201..00000000000 --- a/src/rt/libuv/msvs/libuv-test.vcxproj +++ /dev/null @@ -1,180 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744} - - - - Application - true - - - Application - true - - - Application - false - - - Application - false - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - true - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - true - Console - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - MachineX86 - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - ..\include;..\src\ares\config_win32 - - - true - Console - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {301fe650-cd34-14e5-6b63-42e383fa02bc} - - - - - - diff --git a/src/rt/libuv/msvs/libuv.sln b/src/rt/libuv/msvs/libuv.sln deleted file mode 100644 index a4eca0a6d4b..00000000000 --- a/src/rt/libuv/msvs/libuv.sln +++ /dev/null @@ -1,56 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuv", "libuv.vcxproj", "{301FE650-CD34-14E5-6B63-42E383FA02BC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuv-test", "libuv-test.vcxproj", "{1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuv-benchmark", "libuv-benchmark.vcxproj", "{6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "c-ares", "c-ares.vcxproj", "{2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|Win32.ActiveCfg = Debug|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|Win32.Build.0 = Debug|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|x64.ActiveCfg = Debug|x64 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Debug|x64.Build.0 = Debug|x64 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|Win32.ActiveCfg = Release|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|Win32.Build.0 = Release|Win32 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|x64.ActiveCfg = Release|x64 - {301FE650-CD34-14E5-6B63-42E383FA02BC}.Release|x64.Build.0 = Release|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|Win32.ActiveCfg = Debug|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|Win32.Build.0 = Debug|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|x64.ActiveCfg = Debug|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Debug|x64.Build.0 = Debug|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|Win32.ActiveCfg = Release|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|Win32.Build.0 = Release|Win32 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|x64.ActiveCfg = Release|x64 - {1D7C3F6C-A4AF-DD73-2D20-B2FC919B3744}.Release|x64.Build.0 = Release|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|Win32.ActiveCfg = Debug|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|Win32.Build.0 = Debug|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|x64.ActiveCfg = Debug|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Debug|x64.Build.0 = Debug|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|Win32.ActiveCfg = Release|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|Win32.Build.0 = Release|Win32 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|x64.ActiveCfg = Release|x64 - {6CCBDAFD-7A11-133D-357B-E2D2F4C621E4}.Release|x64.Build.0 = Release|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|Win32.ActiveCfg = Debug|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|Win32.Build.0 = Debug|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|x64.ActiveCfg = Debug|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Debug|x64.Build.0 = Debug|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|Win32.ActiveCfg = Release|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|Win32.Build.0 = Release|Win32 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|x64.ActiveCfg = Release|x64 - {2B6A4644-EBA9-DFB5-AF35-6C56EDF05C7F}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/rt/libuv/msvs/libuv.vcxproj b/src/rt/libuv/msvs/libuv.vcxproj deleted file mode 100644 index c896c6970f0..00000000000 --- a/src/rt/libuv/msvs/libuv.vcxproj +++ /dev/null @@ -1,131 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - Win32Proj - {301FE650-CD34-14E5-6B63-42E383FA02BC} - - - - StaticLibrary - true - - - StaticLibrary - true - - - StaticLibrary - false - - - StaticLibrary - false - - - - - - - - - - - - - - - - - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - $(SolutionDir)..\build\$(Platform)\$(Configuration)\ - - - - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - ..\include;..\src\ares\config_win32 - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - - - - - MultiThreaded - ..\include;..\src\ares\config_win32 - - - - - {2b6a4644-eba9-dfb5-af35-6c56edf05c7f} - true - true - false - true - false - - - - - - - - - - - - - - - - - - diff --git a/src/rt/libuv/src/ares/ares_parse_a_reply.c b/src/rt/libuv/src/ares/ares_parse_a_reply.c index 93012549c90..9926c27c577 100644 --- a/src/rt/libuv/src/ares/ares_parse_a_reply.c +++ b/src/rt/libuv/src/ares/ares_parse_a_reply.c @@ -238,6 +238,8 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, for (i = 0; i < naddrs; i++) hostent->h_addr_list[i] = (char *) &addrs[i]; hostent->h_addr_list[naddrs] = NULL; + if (!naddrs && addrs) + free(addrs); *host = hostent; return ARES_SUCCESS; } diff --git a/src/rt/libuv/src/ares/config_netbsd/ares_config.h b/src/rt/libuv/src/ares/config_netbsd/ares_config.h new file mode 100644 index 00000000000..5e3ae688645 --- /dev/null +++ b/src/rt/libuv/src/ares/config_netbsd/ares_config.h @@ -0,0 +1,510 @@ +/* ares_config.h. Generated from ares_config.h.in by configure. */ +/* ares_config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* define this if ares is built for a big endian system */ +/* #undef ARES_BIG_ENDIAN */ + +/* when building as static part of libcurl */ +/* #undef BUILDING_LIBCURL */ + +/* when building c-ares library */ +/* #undef CARES_BUILDING_LIBRARY */ + +/* when not building a shared library */ +/* #undef CARES_STATICLIB */ + +/* Define to 1 to enable hiding of library internal symbols. */ +/* #undef CARES_SYMBOL_HIDING */ + +/* Definition to make a library symbol externally visible. */ +/* #undef CARES_SYMBOL_SCOPE_EXTERN */ + +/* if a /etc/inet dir is being used */ +/* #undef ETC_INET */ + +/* Define to the type qualifier of arg 1 for getnameinfo. */ +#define GETNAMEINFO_QUAL_ARG1 const + +/* Define to the type of arg 1 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG1 struct sockaddr * + +/* Define to the type of arg 2 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG2 socklen_t + +/* Define to the type of args 4 and 6 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG46 size_t + +/* Define to the type of arg 7 for getnameinfo. */ +#define GETNAMEINFO_TYPE_ARG7 int + +/* Specifies the number of arguments to getservbyport_r */ +#define GETSERVBYPORT_R_ARGS 4 + +/* Specifies the size of the buffer to pass to getservbyport_r */ +#define GETSERVBYPORT_R_BUFSIZE sizeof(struct servent_data) + +/* Define to 1 if you have AF_INET6. */ +#define HAVE_AF_INET6 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_NAMESER_COMPAT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_NAMESER_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the `bitncmp' function. */ +/* #undef HAVE_BITNCMP */ + +/* Define to 1 if bool is an available type. */ +#define HAVE_BOOL_T 1 + +/* Define to 1 if you have the clock_gettime function and monotonic timer. */ +#define HAVE_CLOCK_GETTIME_MONOTONIC 1 + +/* Define to 1 if you have the closesocket function. */ +/* #undef HAVE_CLOSESOCKET */ + +/* Define to 1 if you have the CloseSocket camel case function. */ +/* #undef HAVE_CLOSESOCKET_CAMEL */ + +/* Define to 1 if you have the connect function. */ +#define HAVE_CONNECT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the fcntl function. */ +#define HAVE_FCNTL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have a working fcntl O_NONBLOCK function. */ +#define HAVE_FCNTL_O_NONBLOCK 1 + +/* Define to 1 if you have the freeaddrinfo function. */ +#define HAVE_FREEADDRINFO 1 + +/* Define to 1 if you have a working getaddrinfo function. */ +#define HAVE_GETADDRINFO 1 + +/* Define to 1 if the getaddrinfo function is threadsafe. */ +/* #undef HAVE_GETADDRINFO_THREADSAFE */ + +/* Define to 1 if you have the gethostbyaddr function. */ +#define HAVE_GETHOSTBYADDR 1 + +/* Define to 1 if you have the gethostbyname function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have the getnameinfo function. */ +#define HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the getservbyport_r function. */ +#define HAVE_GETSERVBYPORT_R 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `if_indextoname' function. */ +#define HAVE_IF_INDEXTONAME 1 + +/* Define to 1 if you have the `inet_net_pton' function. */ +#define HAVE_INET_NET_PTON 1 + +/* Define to 1 if inet_net_pton supports IPv6. */ +/* #undef HAVE_INET_NET_PTON_IPV6 */ + +/* Define to 1 if you have a IPv6 capable working inet_ntop function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have a IPv6 capable working inet_pton function. */ +#define HAVE_INET_PTON 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the ioctl function. */ +#define HAVE_IOCTL 1 + +/* Define to 1 if you have the ioctlsocket function. */ +/* #undef HAVE_IOCTLSOCKET */ + +/* Define to 1 if you have the IoctlSocket camel case function. */ +/* #undef HAVE_IOCTLSOCKET_CAMEL */ + +/* Define to 1 if you have a working IoctlSocket camel case FIONBIO function. + */ +/* #undef HAVE_IOCTLSOCKET_CAMEL_FIONBIO */ + +/* Define to 1 if you have a working ioctlsocket FIONBIO function. */ +/* #undef HAVE_IOCTLSOCKET_FIONBIO */ + +/* Define to 1 if you have a working ioctl FIONBIO function. */ +#define HAVE_IOCTL_FIONBIO 1 + +/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */ +#define HAVE_IOCTL_SIOCGIFADDR 1 + +/* Define to 1 if you have the `resolve' library (-lresolve). */ +/* #undef HAVE_LIBRESOLVE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* if your compiler supports LL */ +#define HAVE_LL 1 + +/* Define to 1 if the compiler supports the 'long long' data type. */ +#define HAVE_LONGLONG 1 + +/* Define to 1 if you have the malloc.h header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the memory.h header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the MSG_NOSIGNAL flag. */ +/* #undef HAVE_MSG_NOSIGNAL */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have PF_INET6. */ +#define HAVE_PF_INET6 1 + +/* Define to 1 if you have the recv function. */ +#define HAVE_RECV 1 + +/* Define to 1 if you have the recvfrom function. */ +#define HAVE_RECVFROM 1 + +/* Define to 1 if you have the send function. */ +#define HAVE_SEND 1 + +/* Define to 1 if you have the setsockopt function. */ +#define HAVE_SETSOCKOPT 1 + +/* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */ +/* #undef HAVE_SETSOCKOPT_SO_NONBLOCK */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if sig_atomic_t is an available typedef. */ +#define HAVE_SIG_ATOMIC_T 1 + +/* Define to 1 if sig_atomic_t is already defined as volatile. */ +/* #undef HAVE_SIG_ATOMIC_T_VOLATILE */ + +/* Define to 1 if your struct sockaddr_in6 has sin6_scope_id. */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* Define to 1 if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the strcasecmp function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the strcmpi function. */ +/* #undef HAVE_STRCMPI */ + +/* Define to 1 if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the stricmp function. */ +/* #undef HAVE_STRICMP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the strncasecmp function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to 1 if you have the strncmpi function. */ +/* #undef HAVE_STRNCMPI */ + +/* Define to 1 if you have the strnicmp function. */ +/* #undef HAVE_STRNICMP */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STROPTS_H */ + +/* Define to 1 if you have struct addrinfo. */ +#define HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if you have struct in6_addr. */ +#define HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if you have struct sockaddr_in6. */ +#define HAVE_STRUCT_SOCKADDR_IN6 1 + +/* if struct sockaddr_storage is defined */ +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if you have the timeval struct. */ +#define HAVE_STRUCT_TIMEVAL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the windows.h header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to 1 if you have the winsock2.h header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if you have the winsock.h header file. */ +/* #undef HAVE_WINSOCK_H */ + +/* Define to 1 if you have the writev function. */ +#define HAVE_WRITEV 1 + +/* Define to 1 if you have the ws2tcpip.h header file. */ +/* #undef HAVE_WS2TCPIP_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if you are building a native Windows target. */ +/* #undef NATIVE_WINDOWS */ + +/* Define to 1 if you need the malloc.h header file even with stdlib.h */ +/* #undef NEED_MALLOC_H */ + +/* Define to 1 if you need the memory.h header file even with stdlib.h */ +/* #undef NEED_MEMORY_H */ + +/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */ +/* #undef NEED_REENTRANT */ + +/* Define to 1 if _THREAD_SAFE preprocessor symbol must be defined. */ +/* #undef NEED_THREAD_SAFE */ + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* cpu-machine-OS */ +#define OS "i386-unknown-openbsd4.7" + +/* Name of package */ +#define PACKAGE "c-ares" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "c-ares mailing list => http://cool.haxx.se/mailman/listinfo/c-ares" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "c-ares" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "c-ares 1.7.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "c-ares" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.7.1" + +/* a suitable file/device to read random data from */ +#define RANDOM_FILE "/dev/urandom" + +/* Define to the type of arg 1 for recvfrom. */ +#define RECVFROM_TYPE_ARG1 int + +/* Define to the type pointed by arg 2 for recvfrom. */ +#define RECVFROM_TYPE_ARG2 void + +/* Define to 1 if the type pointed by arg 2 for recvfrom is void. */ +#define RECVFROM_TYPE_ARG2_IS_VOID 1 + +/* Define to the type of arg 3 for recvfrom. */ +#define RECVFROM_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for recvfrom. */ +#define RECVFROM_TYPE_ARG4 int + +/* Define to the type pointed by arg 5 for recvfrom. */ +#define RECVFROM_TYPE_ARG5 struct sockaddr + +/* Define to 1 if the type pointed by arg 5 for recvfrom is void. */ +/* #undef RECVFROM_TYPE_ARG5_IS_VOID */ + +/* Define to the type pointed by arg 6 for recvfrom. */ +#define RECVFROM_TYPE_ARG6 socklen_t + +/* Define to 1 if the type pointed by arg 6 for recvfrom is void. */ +/* #undef RECVFROM_TYPE_ARG6_IS_VOID */ + +/* Define to the function return type for recvfrom. */ +#define RECVFROM_TYPE_RETV int + +/* Define to the type of arg 1 for recv. */ +#define RECV_TYPE_ARG1 int + +/* Define to the type of arg 2 for recv. */ +#define RECV_TYPE_ARG2 void * + +/* Define to the type of arg 3 for recv. */ +#define RECV_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for recv. */ +#define RECV_TYPE_ARG4 int + +/* Define to the function return type for recv. */ +#define RECV_TYPE_RETV int + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to the type qualifier of arg 2 for send. */ +#define SEND_QUAL_ARG2 const + +/* Define to the type of arg 1 for send. */ +#define SEND_TYPE_ARG1 int + +/* Define to the type of arg 2 for send. */ +#define SEND_TYPE_ARG2 void * + +/* Define to the type of arg 3 for send. */ +#define SEND_TYPE_ARG3 size_t + +/* Define to the type of arg 4 for send. */ +#define SEND_TYPE_ARG4 int + +/* Define to the function return type for send. */ +#define SEND_TYPE_RETV int + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 4 + +/* The size of `struct in6_addr', as computed by sizeof. */ +#define SIZEOF_STRUCT_IN6_ADDR 16 + +/* The size of `struct in_addr', as computed by sizeof. */ +#define SIZEOF_STRUCT_IN_ADDR 4 + +/* The size of `time_t', as computed by sizeof. */ +#define SIZEOF_TIME_T 4 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to disable non-blocking sockets. */ +/* #undef USE_BLOCKING_SOCKETS */ + +/* Version number of package */ +#define VERSION "1.7.1" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if OS is AIX. */ +#ifndef _ALL_SOURCE +/* # undef _ALL_SOURCE */ +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Type to use in place of in_addr_t when system does not provide it. */ +/* #undef in_addr_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* the signed version of size_t */ +/* #undef ssize_t */ diff --git a/src/rt/libuv/src/ares/inet_ntop.h b/src/rt/libuv/src/ares/inet_ntop.h index c583488f75e..1b5389e893c 100644 --- a/src/rt/libuv/src/ares/inet_ntop.h +++ b/src/rt/libuv/src/ares/inet_ntop.h @@ -18,6 +18,7 @@ */ #ifdef HAVE_INET_NTOP +#include #define ares_inet_ntop(w,x,y,z) inet_ntop(w,x,y,z) #else const char *ares_inet_ntop(int af, const void *src, char *dst, size_t size); diff --git a/src/rt/libuv/src/eio/Changes b/src/rt/libuv/src/eio/Changes deleted file mode 100644 index baa94eca9ca..00000000000 --- a/src/rt/libuv/src/eio/Changes +++ /dev/null @@ -1,35 +0,0 @@ -Revision history for libeio - -TODO: maybe add mincore support? available on at least darwin, solaris, linux, freebsd -TODO: openbsd requites stdint.h for intptr_t - why posix? - -1.0 - - readdir: correctly handle malloc failures. - - readdir: new flags argument, can return inode - and possibly filetype, can sort in various ways. - - readdir: stop immediately when cancelled, do - not continue reading the directory. - - fix return value of eio_sendfile_sync. - - include sys/mman.h for msync. - - added EIO_STACKSIZE. - - added msync, mtouch support (untested). - - added sync_file_range (untested). - - fixed custom support. - - use a more robust feed-add detection method. - - "outbundled" from IO::AIO. - - eio_set_max_polltime did not properly convert time to ticks. - - tentatively support darwin in sendfile. - - fix freebsd/darwin sendfile. - - also use sendfile emulation for ENOTSUP and EOPNOTSUPP - error codes. - - add OS-independent EIO_MT_* and EIO_MS_* flag enums. - - add eio_statvfs/eio_fstatvfs. - - add eio_mlock/eio_mlockall and OS-independent MCL_* flag enums. - - no longer set errno to 0 before making syscalls, this only lures - people into the trap of believing errno shows success or failure. - - "fix" demo.c so that it works as non-root. - - suppoert utimes seperately from futimes, as some systems have - utimes but not futimes. - - use _POSIX_MEMLOCK_RANGE for mlock. - - do not (errornously) overwrite CFLAGS in configure.ac. - diff --git a/src/rt/libuv/src/eio/autogen.sh b/src/rt/libuv/src/eio/autogen.sh deleted file mode 100755 index bd3387c46c7..00000000000 --- a/src/rt/libuv/src/eio/autogen.sh +++ /dev/null @@ -1,5 +0,0 @@ -libtoolize -aclocal -automake --add-missing -autoconf -autoheader diff --git a/src/rt/libuv/src/eio/eio.pod b/src/rt/libuv/src/eio/eio.pod deleted file mode 100644 index c83768ebf19..00000000000 --- a/src/rt/libuv/src/eio/eio.pod +++ /dev/null @@ -1,303 +0,0 @@ -=head1 NAME - -libeio - truly asynchronous POSIX I/O - -=head1 SYNOPSIS - - #include - -=head1 DESCRIPTION - -The newest version of this document is also available as an html-formatted -web page you might find easier to navigate when reading it for the first -time: L. - -Note that this library is a by-product of the C perl -module, and many of the subtler points regarding requets lifetime -and so on are only documented in its documentation at the -moment: L. - -=head2 FEATURES - -This library provides fully asynchronous versions of most POSIX functions -dealign with I/O. Unlike most asynchronous libraries, this not only -includes C and C, but also C, C, C and -similar functions, as well as less rarely ones such as C, C -or C. - -It also offers wrappers around C (Solaris, Linux, HP-UX and -FreeBSD, with emulation on other platforms) and C (Linux, with -emulation elsewhere>). - -The goal is to enable you to write fully non-blocking programs. For -example, in a game server, you would not want to freeze for a few seconds -just because the server is running a backup and you happen to call -C. - -=head2 TIME REPRESENTATION - -Libeio represents time as a single floating point number, representing the -(fractional) number of seconds since the (POSIX) epoch (somewhere near -the beginning of 1970, details are complicated, don't ask). This type is -called C, but it is guarenteed to be of type C (or -better), so you can freely use C yourself. - -Unlike the name component C might indicate, it is also used for -time differences throughout libeio. - -=head2 FORK SUPPORT - -Calling C is fully supported by this module. It is implemented in these steps: - - 1. wait till all requests in "execute" state have been handled - (basically requests that are already handed over to the kernel). - 2. fork - 3. in the parent, continue business as usual, done - 4. in the child, destroy all ready and pending requests and free the - memory used by the worker threads. This gives you a fully empty - libeio queue. - -=head1 INITIALISATION/INTEGRATION - -Before you can call any eio functions you first have to initialise the -library. The library integrates into any event loop, but can also be used -without one, including in polling mode. - -You have to provide the necessary glue yourself, however. - -=over 4 - -=item int eio_init (void (*want_poll)(void), void (*done_poll)(void)) - -This function initialises the library. On success it returns C<0>, on -failure it returns C<-1> and sets C appropriately. - -It accepts two function pointers specifying callbacks as argument, both of -which can be C<0>, in which case the callback isn't called. - -=item want_poll callback - -The C callback is invoked whenever libeio wants attention (i.e. -it wants to be polled by calling C). It is "edge-triggered", -that is, it will only be called once when eio wants attention, until all -pending requests have been handled. - -This callback is called while locks are being held, so I. That includes -C. What you should do is notify some other thread, or wake up -your event loop, and then call C. - -=item done_poll callback - -This callback is invoked when libeio detects that all pending requests -have been handled. It is "edge-triggered", that is, it will only be -called once after C. To put it differently, C and -C are invoked in pairs: after C you have to call -C until either C indicates that everything has been -handled or C has been called, which signals the same. - -Note that C might return after C and C -have been called again, so watch out for races in your code. - -As with C, this callback is called while lcoks are being held, -so you I. - -=item int eio_poll () - -This function has to be called whenever there are pending requests that -need finishing. You usually call this after C has indicated -that you should do so, but you can also call this function regularly to -poll for new results. - -If any request invocation returns a non-zero value, then C -immediately returns with that value as return value. - -Otherwise, if all requests could be handled, it returns C<0>. If for some -reason not all requests have been handled, i.e. some are still pending, it -returns C<-1>. - -=back - -For libev, you would typically use an C watcher: the -C callback would invoke C to wake up the event -loop. Inside the callback set for the watcher, one would call C (followed by C again if C indicates that not -all requests have been handled yet). The race is taken care of because -libev resets/rearms the async watcher before calling your callback, -and therefore, before calling C. This might result in (some) -spurious wake-ups, but is generally harmless. - -For most other event loops, you would typically use a pipe - the event -loop should be told to wait for read readyness on the read end. In -C you would write a single byte, in C you would try -to read that byte, and in the callback for the read end, you would call -C. The race is avoided here because the event loop should invoke -your callback again and again until the byte has been read (as the pipe -read callback does not read it, only C). - -=head2 CONFIGURATION - -The functions in this section can sometimes be useful, but the default -configuration will do in most case, so you should skip this section on -first reading. - -=over 4 - -=item eio_set_max_poll_time (eio_tstamp nseconds) - -This causes C to return after it has detected that it was -running for C seconds or longer (this number can be fractional). - -This can be used to limit the amount of time spent handling eio requests, -for example, in interactive programs, you might want to limit this time to -C<0.01> seconds or so. - -Note that: - -a) libeio doesn't know how long your request callbacks take, so the time -spent in C is up to one callback invocation longer then this -interval. - -b) this is implemented by calling C after each request, -which can be costly. - -c) at least one request will be handled. - -=item eio_set_max_poll_reqs (unsigned int nreqs) - -When C is non-zero, then C will not handle more than -C requests per invocation. This is a less costly way to limit the -amount of work done by C then setting a time limit. - -If you know your callbacks are generally fast, you could use this to -encourage interactiveness in your programs by setting it to C<10>, C<100> -or even C<1000>. - -=item eio_set_min_parallel (unsigned int nthreads) - -Make sure libeio can handle at least this many requests in parallel. It -might be able handle more. - -=item eio_set_max_parallel (unsigned int nthreads) - -Set the maximum number of threads that libeio will spawn. - -=item eio_set_max_idle (unsigned int nthreads) - -Libeio uses threads internally to handle most requests, and will start and stop threads on demand. - -This call can be used to limit the number of idle threads (threads without -work to do): libeio will keep some threads idle in preperation for more -requests, but never longer than C threads. - -In addition to this, libeio will also stop threads when they are idle for -a few seconds, regardless of this setting. - -=item unsigned int eio_nthreads () - -Return the number of worker threads currently running. - -=item unsigned int eio_nreqs () - -Return the number of requests currently handled by libeio. This is the -total number of requests that have been submitted to libeio, but not yet -destroyed. - -=item unsigned int eio_nready () - -Returns the number of ready requests, i.e. requests that have been -submitted but have not yet entered the execution phase. - -=item unsigned int eio_npending () - -Returns the number of pending requests, i.e. requests that have been -executed and have results, but have not been finished yet by a call to -C). - -=back - - -=head1 ANATOMY OF AN EIO REQUEST - -#TODO - - -=head1 HIGH LEVEL REQUEST API - -#TODO - -=back - - -=head1 LOW LEVEL REQUEST API - -#TODO - -=head1 EMBEDDING - -Libeio can be embedded directly into programs. This functionality is not -documented and not (yet) officially supported. - -Note that, when including C, you are responsible for defining -the compilation environment (C<_LARGEFILE_SOURCE>, C<_GNU_SOURCE> etc.). - -If you need to know how, check the C perl module, which does -exactly that. - - -=head1 COMPILETIME CONFIGURATION - -These symbols, if used, must be defined when compiling F. - -=over 4 - -=item EIO_STACKSIZE - -This symbol governs the stack size for each eio thread. Libeio itself -was written to use very little stackspace, but when using C -requests, you might want to increase this. - -If this symbol is undefined (the default) then libeio will use its default -stack size (C currently). If it is defined, but -C<0>, then the default operating system stack size will be used. In all -other cases, the value must be an expression that evaluates to the desired -stack size. - -=back - - -=head1 PORTABILITY REQUIREMENTS - -In addition to a working ISO-C implementation, libeio relies on a few -additional extensions: - -=over 4 - -=item POSIX threads - -To be portable, this module uses threads, specifically, the POSIX threads -library must be available (and working, which partially excludes many xBSD -systems, where C is buggy). - -=item POSIX-compatible filesystem API - -This is actually a harder portability requirement: The libeio API is quite -demanding regarding POSIX API calls (symlinks, user/group management -etc.). - -=item C must hold a time value in seconds with enough accuracy - -The type C is used to represent timestamps. It is required to -have at least 51 bits of mantissa (and 9 bits of exponent), which is good -enough for at least into the year 4000. This requirement is fulfilled by -implementations implementing IEEE 754 (basically all existing ones). - -=back - -If you know of other additional requirements drop me a note. - - -=head1 AUTHOR - -Marc Lehmann . - diff --git a/src/rt/libuv/src/unix/cares.c b/src/rt/libuv/src/unix/cares.c new file mode 100644 index 00000000000..a2466f59e6f --- /dev/null +++ b/src/rt/libuv/src/unix/cares.c @@ -0,0 +1,185 @@ +/* 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 +#include +#include + + +/* + * This is called once per second by loop->timer. It is used to + * constantly callback into c-ares for possibly processing timeouts. + */ +static void uv__ares_timeout(struct ev_loop* ev, struct ev_timer* watcher, + int revents) { + uv_loop_t* loop = ev_userdata(ev); + + assert(ev == loop->ev); + assert((uv_loop_t*)watcher->data == loop); + assert(watcher == &loop->timer); + assert(revents == EV_TIMER); + assert(!uv_ares_handles_empty(loop)); + + ares_process_fd(loop->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); +} + + +static void uv__ares_io(struct ev_loop* ev, struct ev_io* watcher, + int revents) { + uv_loop_t* loop = ev_userdata(ev); + + assert(ev == loop->ev); + + /* Reset the idle timer */ + ev_timer_again(ev, &loop->timer); + + /* Process DNS responses */ + ares_process_fd(loop->channel, + revents & EV_READ ? watcher->fd : ARES_SOCKET_BAD, + revents & EV_WRITE ? watcher->fd : ARES_SOCKET_BAD); +} + + +/* Allocates and returns a new uv_ares_task_t */ +static uv_ares_task_t* uv__ares_task_create(int fd) { + uv_ares_task_t* h = malloc(sizeof(uv_ares_task_t)); + + if (h == NULL) { + uv_fatal_error(ENOMEM, "malloc"); + } + + h->sock = fd; + + ev_io_init(&h->read_watcher, uv__ares_io, fd, EV_READ); + ev_io_init(&h->write_watcher, uv__ares_io, fd, EV_WRITE); + + h->read_watcher.data = h; + h->write_watcher.data = h; + + return h; +} + + +/* Callback from ares when socket operation is started */ +static void uv__ares_sockstate_cb(void* data, ares_socket_t sock, + int read, int write) { + uv_loop_t* loop = data; + uv_ares_task_t* h; + + assert((uv_loop_t*)loop->timer.data == loop); + + h = uv_find_ares_handle(loop, sock); + + if (read || write) { + if (!h) { + /* New socket */ + + /* If this is the first socket then start the timer. */ + if (!ev_is_active(&loop->timer)) { + assert(uv_ares_handles_empty(loop)); + ev_timer_again(loop->ev, &loop->timer); + } + + h = uv__ares_task_create(sock); + uv_add_ares_handle(loop, h); + } + + if (read) { + ev_io_start(loop->ev, &h->read_watcher); + } else { + ev_io_stop(loop->ev, &h->read_watcher); + } + + if (write) { + ev_io_start(loop->ev, &h->write_watcher); + } else { + ev_io_stop(loop->ev, &h->write_watcher); + } + + } else { + /* + * read == 0 and write == 0 this is c-ares's way of notifying us that + * the socket is now closed. We must free the data associated with + * socket. + */ + assert(h && "When an ares socket is closed we should have a handle for it"); + + ev_io_stop(loop->ev, &h->read_watcher); + ev_io_stop(loop->ev, &h->write_watcher); + + uv_remove_ares_handle(h); + free(h); + + if (uv_ares_handles_empty(loop)) { + ev_timer_stop(loop->ev, &loop->timer); + } + } +} + + +/* c-ares integration initialize and terminate */ +/* TODO: share this with windows? */ +int uv_ares_init_options(uv_loop_t* loop, ares_channel *channelptr, + struct ares_options *options, int optmask) { + int rc; + + /* only allow single init at a time */ + if (loop->channel != NULL) { + uv_err_new_artificial(loop, UV_EALREADY); + return -1; + } + + /* set our callback as an option */ + options->sock_state_cb = uv__ares_sockstate_cb; + options->sock_state_cb_data = loop; + optmask |= ARES_OPT_SOCK_STATE_CB; + + /* We do the call to ares_init_option for caller. */ + rc = ares_init_options(channelptr, options, optmask); + + /* if success, save channel */ + if (rc == ARES_SUCCESS) { + loop->channel = *channelptr; + } + + /* + * Initialize the timeout timer. The timer won't be started until the + * first socket is opened. + */ + ev_timer_init(&loop->timer, uv__ares_timeout, 1., 1.); + loop->timer.data = loop; + + return rc; +} + + +/* TODO share this with windows? */ +void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) { + /* only allow destroy if did init */ + if (loop->channel) { + ev_timer_stop(loop->ev, &loop->timer); + ares_destroy(channel); + loop->channel = NULL; + } +} diff --git a/src/rt/libuv/src/unix/core.c b/src/rt/libuv/src/unix/core.c new file mode 100644 index 00000000000..80b3bf6ed9e --- /dev/null +++ b/src/rt/libuv/src/unix/core.c @@ -0,0 +1,799 @@ +/* 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 "unix/internal.h" + +#include /* NULL */ +#include /* printf */ +#include +#include /* strerror */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* PATH_MAX */ +#include /* writev */ + +#ifdef __sun +# include +# include +#endif + +#if defined(__APPLE__) +#include /* _NSGetExecutablePath */ +#endif + +#if defined(__FreeBSD__) +#include +#include +#endif + +static uv_loop_t default_loop_struct; +static uv_loop_t* default_loop_ptr; + +void uv__next(EV_P_ ev_idle* watcher, int revents); +static void uv__finish_close(uv_handle_t* handle); + + + +#ifndef __GNUC__ +#define __attribute__(a) +#endif + + +void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { + uv_udp_t* udp; + uv_async_t* async; + uv_timer_t* timer; + uv_stream_t* stream; + uv_process_t* process; + + handle->close_cb = close_cb; + + switch (handle->type) { + case UV_NAMED_PIPE: + uv_pipe_cleanup((uv_pipe_t*)handle); + /* Fall through. */ + + case UV_TTY: + case UV_TCP: + stream = (uv_stream_t*)handle; + + uv_read_stop(stream); + ev_io_stop(stream->loop->ev, &stream->write_watcher); + + uv__close(stream->fd); + stream->fd = -1; + + if (stream->accepted_fd >= 0) { + uv__close(stream->accepted_fd); + stream->accepted_fd = -1; + } + + assert(!ev_is_active(&stream->read_watcher)); + assert(!ev_is_active(&stream->write_watcher)); + break; + + case UV_UDP: + udp = (uv_udp_t*)handle; + uv__udp_watcher_stop(udp, &udp->read_watcher); + uv__udp_watcher_stop(udp, &udp->write_watcher); + uv__close(udp->fd); + udp->fd = -1; + break; + + case UV_PREPARE: + uv_prepare_stop((uv_prepare_t*) handle); + break; + + case UV_CHECK: + uv_check_stop((uv_check_t*) handle); + break; + + case UV_IDLE: + uv_idle_stop((uv_idle_t*) handle); + break; + + case UV_ASYNC: + async = (uv_async_t*)handle; + ev_async_stop(async->loop->ev, &async->async_watcher); + ev_ref(async->loop->ev); + break; + + case UV_TIMER: + timer = (uv_timer_t*)handle; + if (ev_is_active(&timer->timer_watcher)) { + ev_ref(timer->loop->ev); + } + ev_timer_stop(timer->loop->ev, &timer->timer_watcher); + break; + + case UV_PROCESS: + process = (uv_process_t*)handle; + ev_child_stop(process->loop->ev, &process->child_watcher); + break; + + case UV_FS_EVENT: + uv__fs_event_destroy((uv_fs_event_t*)handle); + break; + + default: + assert(0); + } + + handle->flags |= UV_CLOSING; + + /* This is used to call the on_close callback in the next loop. */ + ev_idle_start(handle->loop->ev, &handle->next_watcher); + ev_feed_event(handle->loop->ev, &handle->next_watcher, EV_IDLE); + assert(ev_is_pending(&handle->next_watcher)); +} + + +uv_loop_t* uv_loop_new() { + uv_loop_t* loop = calloc(1, sizeof(uv_loop_t)); + loop->ev = ev_loop_new(0); + ev_set_userdata(loop->ev, loop); + return loop; +} + + +void uv_loop_delete(uv_loop_t* loop) { + uv_ares_destroy(loop, loop->channel); + ev_loop_destroy(loop->ev); + free(loop); +} + + +uv_loop_t* uv_default_loop() { + if (!default_loop_ptr) { + default_loop_ptr = &default_loop_struct; +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + default_loop_struct.ev = ev_default_loop(EVBACKEND_KQUEUE); +#else + default_loop_struct.ev = ev_default_loop(EVFLAG_AUTO); +#endif + ev_set_userdata(default_loop_struct.ev, default_loop_ptr); + } + assert(default_loop_ptr->ev == EV_DEFAULT_UC); + return default_loop_ptr; +} + + +int uv_run(uv_loop_t* loop) { + ev_run(loop->ev, 0); + return 0; +} + + +void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, + uv_handle_type type) { + loop->counters.handle_init++; + + handle->loop = loop; + handle->type = type; + handle->flags = 0; + + ev_init(&handle->next_watcher, uv__next); + handle->next_watcher.data = handle; + + /* Ref the loop until this handle is closed. See uv__finish_close. */ + ev_ref(loop->ev); +} + + +void uv__finish_close(uv_handle_t* handle) { + uv_loop_t* loop = handle->loop; + + assert(handle->flags & UV_CLOSING); + assert(!(handle->flags & UV_CLOSED)); + handle->flags |= UV_CLOSED; + + switch (handle->type) { + case UV_PREPARE: + assert(!ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher)); + break; + + case UV_CHECK: + assert(!ev_is_active(&((uv_check_t*)handle)->check_watcher)); + break; + + case UV_IDLE: + assert(!ev_is_active(&((uv_idle_t*)handle)->idle_watcher)); + break; + + case UV_ASYNC: + assert(!ev_is_active(&((uv_async_t*)handle)->async_watcher)); + break; + + case UV_TIMER: + assert(!ev_is_active(&((uv_timer_t*)handle)->timer_watcher)); + break; + + case UV_NAMED_PIPE: + case UV_TCP: + case UV_TTY: + assert(!ev_is_active(&((uv_stream_t*)handle)->read_watcher)); + assert(!ev_is_active(&((uv_stream_t*)handle)->write_watcher)); + assert(((uv_stream_t*)handle)->fd == -1); + uv__stream_destroy((uv_stream_t*)handle); + break; + + case UV_UDP: + assert(!ev_is_active(&((uv_udp_t*)handle)->read_watcher)); + assert(!ev_is_active(&((uv_udp_t*)handle)->write_watcher)); + assert(((uv_udp_t*)handle)->fd == -1); + uv__udp_destroy((uv_udp_t*)handle); + break; + + case UV_PROCESS: + assert(!ev_is_active(&((uv_process_t*)handle)->child_watcher)); + break; + + case UV_FS_EVENT: + break; + + default: + assert(0); + break; + } + + ev_idle_stop(loop->ev, &handle->next_watcher); + + if (handle->close_cb) { + handle->close_cb(handle); + } + + ev_unref(loop->ev); +} + + +void uv__next(EV_P_ ev_idle* watcher, int revents) { + uv_handle_t* handle = watcher->data; + assert(watcher == &handle->next_watcher); + assert(revents == EV_IDLE); + + /* For now this function is only to handle the closing event, but we might + * put more stuff here later. + */ + assert(handle->flags & UV_CLOSING); + uv__finish_close(handle); +} + + +void uv_ref(uv_loop_t* loop) { + ev_ref(loop->ev); +} + + +void uv_unref(uv_loop_t* loop) { + ev_unref(loop->ev); +} + + +void uv_update_time(uv_loop_t* loop) { + ev_now_update(loop->ev); +} + + +int64_t uv_now(uv_loop_t* loop) { + return (int64_t)(ev_now(loop->ev) * 1000); +} + + +void uv__req_init(uv_req_t* req) { + /* loop->counters.req_init++; */ + req->type = UV_UNKNOWN_REQ; +} + + +static void uv__prepare(EV_P_ ev_prepare* w, int revents) { + uv_prepare_t* prepare = w->data; + + if (prepare->prepare_cb) { + prepare->prepare_cb(prepare, 0); + } +} + + +int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare) { + uv__handle_init(loop, (uv_handle_t*)prepare, UV_PREPARE); + loop->counters.prepare_init++; + + ev_prepare_init(&prepare->prepare_watcher, uv__prepare); + prepare->prepare_watcher.data = prepare; + + prepare->prepare_cb = NULL; + + return 0; +} + + +int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) { + int was_active = ev_is_active(&prepare->prepare_watcher); + + prepare->prepare_cb = cb; + + ev_prepare_start(prepare->loop->ev, &prepare->prepare_watcher); + + if (!was_active) { + ev_unref(prepare->loop->ev); + } + + return 0; +} + + +int uv_prepare_stop(uv_prepare_t* prepare) { + int was_active = ev_is_active(&prepare->prepare_watcher); + + ev_prepare_stop(prepare->loop->ev, &prepare->prepare_watcher); + + if (was_active) { + ev_ref(prepare->loop->ev); + } + return 0; +} + + + +static void uv__check(EV_P_ ev_check* w, int revents) { + uv_check_t* check = w->data; + + if (check->check_cb) { + check->check_cb(check, 0); + } +} + + +int uv_check_init(uv_loop_t* loop, uv_check_t* check) { + uv__handle_init(loop, (uv_handle_t*)check, UV_CHECK); + loop->counters.check_init++; + + ev_check_init(&check->check_watcher, uv__check); + check->check_watcher.data = check; + + check->check_cb = NULL; + + return 0; +} + + +int uv_check_start(uv_check_t* check, uv_check_cb cb) { + int was_active = ev_is_active(&check->check_watcher); + + check->check_cb = cb; + + ev_check_start(check->loop->ev, &check->check_watcher); + + if (!was_active) { + ev_unref(check->loop->ev); + } + + return 0; +} + + +int uv_check_stop(uv_check_t* check) { + int was_active = ev_is_active(&check->check_watcher); + + ev_check_stop(check->loop->ev, &check->check_watcher); + + if (was_active) { + ev_ref(check->loop->ev); + } + + return 0; +} + + +static void uv__idle(EV_P_ ev_idle* w, int revents) { + uv_idle_t* idle = (uv_idle_t*)(w->data); + + if (idle->idle_cb) { + idle->idle_cb(idle, 0); + } +} + + + +int uv_idle_init(uv_loop_t* loop, uv_idle_t* idle) { + uv__handle_init(loop, (uv_handle_t*)idle, UV_IDLE); + loop->counters.idle_init++; + + ev_idle_init(&idle->idle_watcher, uv__idle); + idle->idle_watcher.data = idle; + + idle->idle_cb = NULL; + + return 0; +} + + +int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) { + int was_active = ev_is_active(&idle->idle_watcher); + + idle->idle_cb = cb; + ev_idle_start(idle->loop->ev, &idle->idle_watcher); + + if (!was_active) { + ev_unref(idle->loop->ev); + } + + return 0; +} + + +int uv_idle_stop(uv_idle_t* idle) { + int was_active = ev_is_active(&idle->idle_watcher); + + ev_idle_stop(idle->loop->ev, &idle->idle_watcher); + + if (was_active) { + ev_ref(idle->loop->ev); + } + + return 0; +} + + +int uv_is_active(uv_handle_t* handle) { + switch (handle->type) { + case UV_TIMER: + return ev_is_active(&((uv_timer_t*)handle)->timer_watcher); + + case UV_PREPARE: + return ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher); + + case UV_CHECK: + return ev_is_active(&((uv_check_t*)handle)->check_watcher); + + case UV_IDLE: + return ev_is_active(&((uv_idle_t*)handle)->idle_watcher); + + default: + return 1; + } +} + + +static void uv__async(EV_P_ ev_async* w, int revents) { + uv_async_t* async = w->data; + + if (async->async_cb) { + async->async_cb(async, 0); + } +} + + +int uv_async_init(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb) { + uv__handle_init(loop, (uv_handle_t*)async, UV_ASYNC); + loop->counters.async_init++; + + ev_async_init(&async->async_watcher, uv__async); + async->async_watcher.data = async; + + async->async_cb = async_cb; + + /* Note: This does not have symmetry with the other libev wrappers. */ + ev_async_start(loop->ev, &async->async_watcher); + ev_unref(loop->ev); + + return 0; +} + + +int uv_async_send(uv_async_t* async) { + ev_async_send(async->loop->ev, &async->async_watcher); + return 0; +} + + +static void uv__timer_cb(EV_P_ ev_timer* w, int revents) { + uv_timer_t* timer = w->data; + + if (!ev_is_active(w)) { + ev_ref(EV_A); + } + + if (timer->timer_cb) { + timer->timer_cb(timer, 0); + } +} + + +int uv_timer_init(uv_loop_t* loop, uv_timer_t* timer) { + uv__handle_init(loop, (uv_handle_t*)timer, UV_TIMER); + loop->counters.timer_init++; + + ev_init(&timer->timer_watcher, uv__timer_cb); + timer->timer_watcher.data = timer; + + return 0; +} + + +int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, + int64_t repeat) { + if (ev_is_active(&timer->timer_watcher)) { + return -1; + } + + timer->timer_cb = cb; + ev_timer_set(&timer->timer_watcher, timeout / 1000.0, repeat / 1000.0); + ev_timer_start(timer->loop->ev, &timer->timer_watcher); + ev_unref(timer->loop->ev); + return 0; +} + + +int uv_timer_stop(uv_timer_t* timer) { + if (ev_is_active(&timer->timer_watcher)) { + ev_ref(timer->loop->ev); + } + + ev_timer_stop(timer->loop->ev, &timer->timer_watcher); + return 0; +} + + +int uv_timer_again(uv_timer_t* timer) { + if (!ev_is_active(&timer->timer_watcher)) { + uv_err_new(timer->loop, EINVAL); + return -1; + } + + ev_timer_again(timer->loop->ev, &timer->timer_watcher); + return 0; +} + +void uv_timer_set_repeat(uv_timer_t* timer, int64_t repeat) { + assert(timer->type == UV_TIMER); + timer->timer_watcher.repeat = repeat / 1000.0; +} + +int64_t uv_timer_get_repeat(uv_timer_t* timer) { + assert(timer->type == UV_TIMER); + return (int64_t)(1000 * timer->timer_watcher.repeat); +} + + +static int uv_getaddrinfo_done(eio_req* req) { + uv_getaddrinfo_t* handle = req->data; + struct addrinfo *res = handle->res; + handle->res = NULL; + + uv_unref(handle->loop); + + free(handle->hints); + free(handle->service); + free(handle->hostname); + + if (handle->retcode != 0) { + /* TODO how to display gai error strings? */ + uv_err_new(handle->loop, handle->retcode); + } + + handle->cb(handle, handle->retcode, res); + + return 0; +} + + +static void getaddrinfo_thread_proc(eio_req *req) { + uv_getaddrinfo_t* handle = req->data; + + handle->retcode = getaddrinfo(handle->hostname, + handle->service, + handle->hints, + &handle->res); +} + + +/* stub implementation of uv_getaddrinfo */ +int uv_getaddrinfo(uv_loop_t* loop, + uv_getaddrinfo_t* handle, + uv_getaddrinfo_cb cb, + const char* hostname, + const char* service, + const struct addrinfo* hints) { + eio_req* req; + uv_eio_init(loop); + + if (handle == NULL || cb == NULL || + (hostname == NULL && service == NULL)) { + uv_err_new_artificial(loop, UV_EINVAL); + return -1; + } + + uv__req_init((uv_req_t*)handle); + handle->type = UV_GETADDRINFO; + handle->loop = loop; + handle->cb = cb; + + /* TODO don't alloc so much. */ + + if (hints) { + handle->hints = malloc(sizeof(struct addrinfo)); + memcpy(&handle->hints, hints, sizeof(struct addrinfo)); + } + else { + handle->hints = NULL; + } + + /* TODO security! check lengths, check return values. */ + + handle->hostname = hostname ? strdup(hostname) : NULL; + handle->service = service ? strdup(service) : NULL; + handle->res = NULL; + handle->retcode = 0; + + /* TODO check handle->hostname == NULL */ + /* TODO check handle->service == NULL */ + + uv_ref(loop); + + req = eio_custom(getaddrinfo_thread_proc, EIO_PRI_DEFAULT, + uv_getaddrinfo_done, handle); + assert(req); + assert(req->data == handle); + + return 0; +} + + +void uv_freeaddrinfo(struct addrinfo* ai) { + freeaddrinfo(ai); +} + + +/* Open a socket in non-blocking close-on-exec mode, atomically if possible. */ +int uv__socket(int domain, int type, int protocol) { +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) + return socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); +#else + int sockfd; + + if ((sockfd = socket(domain, type, protocol)) == -1) { + return -1; + } + + if (uv__nonblock(sockfd, 1) == -1 || uv__cloexec(sockfd, 1) == -1) { + uv__close(sockfd); + return -1; + } + + return sockfd; +#endif +} + + +int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t slen) { + int peerfd; + + assert(sockfd >= 0); + + do { +#if defined(HAVE_ACCEPT4) + peerfd = accept4(sockfd, saddr, &slen, SOCK_NONBLOCK | SOCK_CLOEXEC); +#else + if ((peerfd = accept(sockfd, saddr, &slen)) != -1) { + if (uv__cloexec(peerfd, 1) == -1 || uv__nonblock(peerfd, 1) == -1) { + uv__close(peerfd); + return -1; + } + } +#endif + } + while (peerfd == -1 && errno == EINTR); + + return peerfd; +} + + +int uv__close(int fd) { + int status; + + /* + * Retry on EINTR. You may think this is academic but on linux + * and probably other Unices too, close(2) is interruptible. + * Failing to handle EINTR is a common source of fd leaks. + */ + do { + status = close(fd); + } + while (status == -1 && errno == EINTR); + + return status; +} + + +int uv__nonblock(int fd, int set) { + int flags; + + if ((flags = fcntl(fd, F_GETFL)) == -1) { + return -1; + } + + if (set) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + + if (fcntl(fd, F_SETFL, flags) == -1) { + return -1; + } + + return 0; +} + + +int uv__cloexec(int fd, int set) { + int flags; + + if ((flags = fcntl(fd, F_GETFD)) == -1) { + return -1; + } + + if (set) { + flags |= FD_CLOEXEC; + } else { + flags &= ~FD_CLOEXEC; + } + + if (fcntl(fd, F_SETFD, flags) == -1) { + return -1; + } + + return 0; +} + + +/* TODO move to uv-common.c? */ +size_t uv__strlcpy(char* dst, const char* src, size_t size) { + const char *org; + + if (size == 0) { + return 0; + } + + org = src; + while (--size && *src) { + *dst++ = *src++; + } + *dst = '\0'; + + return src - org; +} + + +uv_stream_t* uv_std_handle(uv_loop_t* loop, uv_std_type type) { + assert(0 && "implement me"); + return NULL; +} + diff --git a/src/rt/libuv/src/uv-cygwin.c b/src/rt/libuv/src/unix/cygwin.c similarity index 83% rename from src/rt/libuv/src/uv-cygwin.c rename to src/rt/libuv/src/unix/cygwin.c index d56a8e839ba..6702eb9234f 100644 --- a/src/rt/libuv/src/uv-cygwin.c +++ b/src/rt/libuv/src/unix/cygwin.c @@ -20,8 +20,10 @@ #include "uv.h" +#include #include #include +#include #include #undef NANOSEC @@ -50,3 +52,17 @@ int uv_exepath(char* buffer, size_t* size) { buffer[*size] = '\0'; return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/uv-darwin.c b/src/rt/libuv/src/unix/darwin.c similarity index 82% rename from src/rt/libuv/src/uv-darwin.c rename to src/rt/libuv/src/unix/darwin.c index fa2948cd217..af72b4b0ec8 100644 --- a/src/rt/libuv/src/uv-darwin.c +++ b/src/rt/libuv/src/unix/darwin.c @@ -19,11 +19,16 @@ */ #include "uv.h" +#include "internal.h" +#include #include +#include + #include #include #include +#include /* _NSGetExecutablePath */ uint64_t uv_hrtime() { @@ -62,3 +67,17 @@ int uv_exepath(char* buffer, size_t* size) { *size = strlen(buffer); return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/eio/Changes b/src/rt/libuv/src/unix/eio/Changes new file mode 100644 index 00000000000..9d3e3231c12 --- /dev/null +++ b/src/rt/libuv/src/unix/eio/Changes @@ -0,0 +1,63 @@ +Revision history for libeio + +TODO: maybe add mincore support? available on at least darwin, solaris, linux, freebsd +TODO: openbsd requires stdint.h for intptr_t - why posix? + +TODO: make mtouch/readdir maybe others cancellable in-request +TODO: fadvise request +1.0 + - fix a deadlock where a wakeup signal could be missed when + a timeout occured at the same time. + - use nonstandard but maybe-working-on-bsd fork technique. + - use fewer time() syscalls when waiting for new requests. + - fix a path-memory-leak in readdir when using the wrappers + (reported by Thomas L. Shinnick). + - support a max_idle value of 0. + - support setting of idle timeout value (eio_set_idle_timeout). + - readdir: correctly handle malloc failures. + - readdir: new flags argument, can return inode + and possibly filetype, can sort in various ways. + - readdir: stop immediately when cancelled, do + not continue reading the directory. + - fix return value of eio_sendfile_sync. + - include sys/mman.h for msync. + - added EIO_STACKSIZE. + - added msync, mtouch support (untested). + - added sync_file_range (untested). + - fixed custom support. + - use a more robust feed-add detection method. + - "outbundled" from IO::AIO. + - eio_set_max_polltime did not properly convert time to ticks. + - tentatively support darwin in sendfile. + - fix freebsd/darwin sendfile. + - also use sendfile emulation for ENOTSUP and EOPNOTSUPP + error codes. + - add OS-independent EIO_MT_* and EIO_MS_* flag enums. + - add eio_statvfs/eio_fstatvfs. + - add eio_mlock/eio_mlockall and OS-independent MCL_* flag enums. + - no longer set errno to 0 before making syscalls, this only lures + people into the trap of believing errno shows success or failure. + - "fix" demo.c so that it works as non-root. + - suppoert utimes seperately from futimes, as some systems have + utimes but not futimes. + - use _POSIX_MEMLOCK_RANGE for mlock. + - do not (errornously) overwrite CFLAGS in configure.ac. + - mknod used int3 for dev_t (§2 bit), not offs (64 bit). + - fix memory corruption in eio_readdirx for the flags + combination EIO_READDIR_STAT_ORDER | EIO_READDIR_DIRS_FIRST. + - port to openbsd (another blatantly broken non-UNIX/POSIX platform). + - fix eio_custom prototype. + - work around a Linux (and likely FreeBSD and other kernels) bug + where sendfile would not transfer all the requested bytes on + large transfers, using a heuristic. + - use libecb, and apply lots of minor space optimisations. + - disable sendfile on darwin, broken as everything else. + - add realpath request and implementation. + - cancelled requests will still invoke their request callbacks. + - add fallocate. + - do not acquire any locks when forking. + - incorporated some mingw32 changes by traviscline. + - added syncfs support, using direct syscall. + - set thread name on linux (ps -L/Hcx, top, gdb). + - remove useless use of volatile variables. + diff --git a/src/rt/libuv/src/eio/LICENSE b/src/rt/libuv/src/unix/eio/LICENSE similarity index 100% rename from src/rt/libuv/src/eio/LICENSE rename to src/rt/libuv/src/unix/eio/LICENSE diff --git a/src/rt/libuv/src/eio/Makefile.am b/src/rt/libuv/src/unix/eio/Makefile.am similarity index 81% rename from src/rt/libuv/src/eio/Makefile.am rename to src/rt/libuv/src/unix/eio/Makefile.am index 857d26b6190..e9866c0d5e7 100644 --- a/src/rt/libuv/src/eio/Makefile.am +++ b/src/rt/libuv/src/unix/eio/Makefile.am @@ -10,6 +10,6 @@ include_HEADERS = eio.h lib_LTLIBRARIES = libeio.la -libeio_la_SOURCES = eio.c xthread.h config.h +libeio_la_SOURCES = eio.c ecb.h xthread.h config.h libeio_la_LDFLAGS = -version-info $(VERSION_INFO) diff --git a/src/rt/libuv/src/eio/aclocal.m4 b/src/rt/libuv/src/unix/eio/aclocal.m4 similarity index 100% rename from src/rt/libuv/src/eio/aclocal.m4 rename to src/rt/libuv/src/unix/eio/aclocal.m4 diff --git a/src/rt/libuv/src/unix/eio/autogen.sh b/src/rt/libuv/src/unix/eio/autogen.sh new file mode 100755 index 00000000000..8056ee7f9be --- /dev/null +++ b/src/rt/libuv/src/unix/eio/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +autoreconf --install --symlink --force diff --git a/src/rt/libuv/src/eio/config.h.in b/src/rt/libuv/src/unix/eio/config.h.in similarity index 100% rename from src/rt/libuv/src/eio/config.h.in rename to src/rt/libuv/src/unix/eio/config.h.in diff --git a/src/rt/libuv/src/eio/config_cygwin.h b/src/rt/libuv/src/unix/eio/config_cygwin.h similarity index 97% rename from src/rt/libuv/src/eio/config_cygwin.h rename to src/rt/libuv/src/unix/eio/config_cygwin.h index f149a6b352a..f64e7fe441a 100644 --- a/src/rt/libuv/src/eio/config_cygwin.h +++ b/src/rt/libuv/src/unix/eio/config_cygwin.h @@ -7,6 +7,9 @@ /* fdatasync(2) is available */ #define HAVE_FDATASYNC 1 +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ #define HAVE_FUTIMES 1 diff --git a/src/rt/libuv/src/unix/eio/config_darwin.h b/src/rt/libuv/src/unix/eio/config_darwin.h new file mode 100644 index 00000000000..f406759e09e --- /dev/null +++ b/src/rt/libuv/src/unix/eio/config_darwin.h @@ -0,0 +1,141 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* fallocate(2) is available */ +/* #undef HAVE_FALLOCATE */ + +/* fdatasync(2) is available */ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 +#define HAVE_FDATASYNC 1 +#else +#define HAVE_FDATASYNC 0 +#endif + +/* futimes(2) is available */ +#define HAVE_FUTIMES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* posix_fadvise(2) is available */ +/* #undef HAVE_POSIX_FADVISE */ + +/* posix_madvise(2) is available */ +#define HAVE_POSIX_MADVISE 1 + +/* prctl(PR_SET_NAME) is available */ +/* #undef HAVE_PRCTL_SET_NAME */ + +/* pread(2) and pwrite(2) are available */ +#define HAVE_PREADWRITE 1 + +/* readahead(2) is available (linux) */ +/* #undef HAVE_READAHEAD */ + +/* sendfile(2) is available and supported */ +#define HAVE_SENDFILE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* sync_file_range(2) is available */ +/* #undef HAVE_SYNC_FILE_RANGE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PRCTL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* syscall(__NR_syncfs) is available */ +/* #undef HAVE_SYS_SYNCFS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCALL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libeio" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "1.0" + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ diff --git a/src/rt/libuv/src/eio/config_freebsd.h b/src/rt/libuv/src/unix/eio/config_freebsd.h similarity index 97% rename from src/rt/libuv/src/eio/config_freebsd.h rename to src/rt/libuv/src/unix/eio/config_freebsd.h index b740cdd8787..3c93d260f79 100644 --- a/src/rt/libuv/src/eio/config_freebsd.h +++ b/src/rt/libuv/src/unix/eio/config_freebsd.h @@ -7,6 +7,9 @@ /* fdatasync(2) is available */ /* #undef HAVE_FDATASYNC */ +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ #define HAVE_FUTIMES 1 diff --git a/src/rt/libuv/src/eio/config_linux.h b/src/rt/libuv/src/unix/eio/config_linux.h similarity index 85% rename from src/rt/libuv/src/eio/config_linux.h rename to src/rt/libuv/src/unix/eio/config_linux.h index 9823f624085..606301faf2a 100644 --- a/src/rt/libuv/src/eio/config_linux.h +++ b/src/rt/libuv/src/unix/eio/config_linux.h @@ -2,12 +2,7 @@ /* config.h.in. Generated from configure.ac by autoheader. */ #include - -#define LINUX_VERSION_CODE_FOR(major, minor, patch) \ - (((major & 255) << 16) | ((minor & 255) << 8) | (patch & 255)) - -#define LINUX_VERSION_AT_LEAST(major, minor, patch) \ - (LINUX_VERSION_CODE >= LINUX_VERSION_CODE_FOR(major, minor, patch)) +#include /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 @@ -15,6 +10,9 @@ /* fdatasync(2) is available */ #define HAVE_FDATASYNC 1 +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ #define HAVE_FUTIMES 1 @@ -45,8 +43,12 @@ /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 -/* sync_file_range(2) is available */ -#define HAVE_SYNC_FILE_RANGE LINUX_VERSION_AT_LEAST(2, 6, 17) +/* sync_file_range(2) is available if kernel >= 2.6.17 and glibc >= 2.6 */ +#if LINUX_VERSION_CODE >= 0x020611 && __GLIBC_PREREQ(2, 6) +#define HAVE_SYNC_FILE_RANGE 1 +#else +#define HAVE_SYNC_FILE_RANGE 0 +#endif /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 diff --git a/src/rt/libuv/src/eio/config_darwin.h b/src/rt/libuv/src/unix/eio/config_netbsd.h similarity index 90% rename from src/rt/libuv/src/eio/config_darwin.h rename to src/rt/libuv/src/unix/eio/config_netbsd.h index 84a3440df0d..31f18e60465 100644 --- a/src/rt/libuv/src/eio/config_darwin.h +++ b/src/rt/libuv/src/unix/eio/config_netbsd.h @@ -4,9 +4,11 @@ /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 -/* fdatasync(2) is not available on 10.5 but is on 10.6 - * How should we deal with this? */ -/* #define HAVE_FDATASYNC 0 */ +/* fdatasync(2) is available */ +/* #undef HAVE_FDATASYNC */ + +/* utimes(2) is available */ +#define HAVE_UTIMES 1 /* futimes(2) is available */ #define HAVE_FUTIMES 1 @@ -24,7 +26,7 @@ /* #undef HAVE_READAHEAD */ /* sendfile(2) is available and supported */ -#define HAVE_SENDFILE 1 +#define HAVE_SENDFILE 0 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 @@ -69,9 +71,6 @@ /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "" -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - /* Define to the version of this package. */ #define PACKAGE_VERSION "" diff --git a/src/rt/libuv/src/eio/config_sunos.h b/src/rt/libuv/src/unix/eio/config_sunos.h similarity index 97% rename from src/rt/libuv/src/eio/config_sunos.h rename to src/rt/libuv/src/unix/eio/config_sunos.h index 8f878efd956..01d049c3e62 100644 --- a/src/rt/libuv/src/eio/config_sunos.h +++ b/src/rt/libuv/src/unix/eio/config_sunos.h @@ -7,6 +7,9 @@ /* fdatasync(2) is available */ #define HAVE_FDATASYNC 1 +/* utimes(2) is available */ +#define HAVE_UTIMES 1 + /* futimes(2) is available */ /* #undef HAVE_FUTIMES */ diff --git a/src/rt/libuv/src/eio/configure.ac b/src/rt/libuv/src/unix/eio/configure.ac similarity index 75% rename from src/rt/libuv/src/eio/configure.ac rename to src/rt/libuv/src/unix/eio/configure.ac index 9f4cea9d12d..9faffad538b 100644 --- a/src/rt/libuv/src/eio/configure.ac +++ b/src/rt/libuv/src/unix/eio/configure.ac @@ -5,17 +5,17 @@ AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE(libeio,1.0) AM_MAINTAINER_MODE + +AC_GNU_SOURCE + AC_PROG_LIBTOOL AC_PROG_CC if test "x$GCC" = xyes ; then - CFLAGS="$CFLAGS -O3" + CFLAGS="-O3 $CFLAGS" fi -dnl somebody will forgive me -CFLAGS="-D_GNU_SOURCE $CFLAGS" - m4_include([libeio.m4]) AC_CONFIG_FILES([Makefile]) diff --git a/src/rt/libuv/src/eio/demo.c b/src/rt/libuv/src/unix/eio/demo.c similarity index 100% rename from src/rt/libuv/src/eio/demo.c rename to src/rt/libuv/src/unix/eio/demo.c diff --git a/src/rt/libuv/src/unix/eio/ecb.h b/src/rt/libuv/src/unix/eio/ecb.h new file mode 100644 index 00000000000..a4aabc10916 --- /dev/null +++ b/src/rt/libuv/src/unix/eio/ecb.h @@ -0,0 +1,370 @@ +/* + * libecb - http://software.schmorp.de/pkg/libecb + * + * Copyright (©) 2009-2011 Marc Alexander Lehmann + * Copyright (©) 2011 Emanuele Giaquinta + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, 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 MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, 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 OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ECB_H +#define ECB_H + +#ifdef _WIN32 + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef signed short int16_t; + typedef unsigned short uint16_t; + typedef signed int int32_t; + typedef unsigned int uint32_t; + #if __GNUC__ + typedef signed long long int64_t; + typedef unsigned long long uint64_t; + #else /* _MSC_VER || __BORLANDC__ */ + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + #endif +#else + #include +#endif + +/* many compilers define _GNUC_ to some versions but then only implement + * what their idiot authors think are the "more important" extensions, + * causing enourmous grief in return for some better fake benchmark numbers. + * or so. + * we try to detect these and simply assume they are not gcc - if they have + * an issue with that they should have done it right in the first place. + */ +#ifndef ECB_GCC_VERSION + #if !defined(__GNUC_MINOR__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__llvm__) || defined(__clang__) + #define ECB_GCC_VERSION(major,minor) 0 + #else + #define ECB_GCC_VERSION(major,minor) (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) + #endif +#endif + +/*****************************************************************************/ + +#ifndef ECB_MEMORY_FENCE + #if ECB_GCC_VERSION(2,5) + #if defined(__x86) || defined(__i386) + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("lock; orb $0, -1(%%esp)" : : : "memory") + #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE /* non-lock xchg might be enough */ + #define ECB_MEMORY_FENCE_RELEASE do { } while (0) /* unlikely to change in future cpus */ + #elif __amd64 + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mfence" : : : "memory") + #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("lfence" : : : "memory") + #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("sfence") /* play safe - not needed in any current cpu */ + #endif + #endif +#endif + +#ifndef ECB_MEMORY_FENCE + #if ECB_GCC_VERSION(4,4) + #define ECB_MEMORY_FENCE __sync_synchronize () + #define ECB_MEMORY_FENCE_ACQUIRE ({ char dummy = 0; __sync_lock_test_and_set (&dummy, 1); }) + #define ECB_MEMORY_FENCE_RELEASE ({ char dummy = 1; __sync_lock_release (&dummy ); }) + #elif _MSC_VER >= 1400 /* VC++ 2005 */ + #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier) + #define ECB_MEMORY_FENCE _ReadWriteBarrier () + #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier () /* according to msdn, _ReadBarrier is not a load fence */ + #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier () + #elif defined(_WIN32) + #include + #define ECB_MEMORY_FENCE MemoryBarrier () /* actually just xchg on x86... scary */ + #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE + #define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE + #endif +#endif + +#ifndef ECB_MEMORY_FENCE + /* + * if you get undefined symbol references to pthread_mutex_lock, + * or failure to find pthread.h, then you should implement + * the ECB_MEMORY_FENCE operations for your cpu/compiler + * OR proide pthread.h and link against the posix thread library + * of your system. + */ + #include + + static pthread_mutex_t ecb_mf_lock = PTHREAD_MUTEX_INITIALIZER; + #define ECB_MEMORY_FENCE do { pthread_mutex_lock (&ecb_mf_lock); pthread_mutex_unlock (&ecb_mf_lock); } while (0) + #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE + #define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE +#endif + +/*****************************************************************************/ + +#define ECB_C99 (__STDC_VERSION__ >= 199901L) + +#if __cplusplus + #define ecb_inline static inline +#elif ECB_GCC_VERSION(2,5) + #define ecb_inline static __inline__ +#elif ECB_C99 + #define ecb_inline static inline +#else + #define ecb_inline static +#endif + +#if ECB_GCC_VERSION(3,3) + #define ecb_restrict __restrict__ +#elif ECB_C99 + #define ecb_restrict restrict +#else + #define ecb_restrict +#endif + +typedef int ecb_bool; + +#define ECB_CONCAT_(a, b) a ## b +#define ECB_CONCAT(a, b) ECB_CONCAT_(a, b) +#define ECB_STRINGIFY_(a) # a +#define ECB_STRINGIFY(a) ECB_STRINGIFY_(a) + +#define ecb_function_ ecb_inline + +#if ECB_GCC_VERSION(3,1) + #define ecb_attribute(attrlist) __attribute__(attrlist) + #define ecb_is_constant(expr) __builtin_constant_p (expr) + #define ecb_expect(expr,value) __builtin_expect ((expr),(value)) + #define ecb_prefetch(addr,rw,locality) __builtin_prefetch (addr, rw, locality) +#else + #define ecb_attribute(attrlist) + #define ecb_is_constant(expr) 0 + #define ecb_expect(expr,value) (expr) + #define ecb_prefetch(addr,rw,locality) +#endif + +/* no emulation for ecb_decltype */ +#if ECB_GCC_VERSION(4,5) + #define ecb_decltype(x) __decltype(x) +#elif ECB_GCC_VERSION(3,0) + #define ecb_decltype(x) __typeof(x) +#endif + +#define ecb_noinline ecb_attribute ((__noinline__)) +#define ecb_noreturn ecb_attribute ((__noreturn__)) +#define ecb_unused ecb_attribute ((__unused__)) +#define ecb_const ecb_attribute ((__const__)) +#define ecb_pure ecb_attribute ((__pure__)) + +#if ECB_GCC_VERSION(4,3) + #define ecb_artificial ecb_attribute ((__artificial__)) + #define ecb_hot ecb_attribute ((__hot__)) + #define ecb_cold ecb_attribute ((__cold__)) +#else + #define ecb_artificial + #define ecb_hot + #define ecb_cold +#endif + +/* put around conditional expressions if you are very sure that the */ +/* expression is mostly true or mostly false. note that these return */ +/* booleans, not the expression. */ +#define ecb_expect_false(expr) ecb_expect (!!(expr), 0) +#define ecb_expect_true(expr) ecb_expect (!!(expr), 1) +/* for compatibility to the rest of the world */ +#define ecb_likely(expr) ecb_expect_true (expr) +#define ecb_unlikely(expr) ecb_expect_false (expr) + +/* count trailing zero bits and count # of one bits */ +#if ECB_GCC_VERSION(3,4) + /* we assume int == 32 bit, long == 32 or 64 bit and long long == 64 bit */ + #define ecb_ld32(x) (__builtin_clz (x) ^ 31) + #define ecb_ld64(x) (__builtin_clzll (x) ^ 63) + #define ecb_ctz32(x) __builtin_ctz (x) + #define ecb_ctz64(x) __builtin_ctzll (x) + #define ecb_popcount32(x) __builtin_popcount (x) + /* no popcountll */ +#else + ecb_function_ int ecb_ctz32 (uint32_t x) ecb_const; + ecb_function_ int + ecb_ctz32 (uint32_t x) + { + int r = 0; + + x &= ~x + 1; /* this isolates the lowest bit */ + +#if ECB_branchless_on_i386 + r += !!(x & 0xaaaaaaaa) << 0; + r += !!(x & 0xcccccccc) << 1; + r += !!(x & 0xf0f0f0f0) << 2; + r += !!(x & 0xff00ff00) << 3; + r += !!(x & 0xffff0000) << 4; +#else + if (x & 0xaaaaaaaa) r += 1; + if (x & 0xcccccccc) r += 2; + if (x & 0xf0f0f0f0) r += 4; + if (x & 0xff00ff00) r += 8; + if (x & 0xffff0000) r += 16; +#endif + + return r; + } + + ecb_function_ int ecb_ctz64 (uint64_t x) ecb_const; + ecb_function_ int + ecb_ctz64 (uint64_t x) + { + int shift = x & 0xffffffffU ? 0 : 32; + return ecb_ctz32 (x >> shift) + shift; + } + + ecb_function_ int ecb_popcount32 (uint32_t x) ecb_const; + ecb_function_ int + ecb_popcount32 (uint32_t x) + { + x -= (x >> 1) & 0x55555555; + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + x = ((x >> 4) + x) & 0x0f0f0f0f; + x *= 0x01010101; + + return x >> 24; + } + + /* you have the choice beetween something with a table lookup, */ + /* something using lots of bit arithmetic and a simple loop */ + /* we went for the loop */ + ecb_function_ int ecb_ld32 (uint32_t x) ecb_const; + ecb_function_ int ecb_ld32 (uint32_t x) + { + int r = 0; + + if (x >> 16) { x >>= 16; r += 16; } + if (x >> 8) { x >>= 8; r += 8; } + if (x >> 4) { x >>= 4; r += 4; } + if (x >> 2) { x >>= 2; r += 2; } + if (x >> 1) { r += 1; } + + return r; + } + + ecb_function_ int ecb_ld64 (uint64_t x) ecb_const; + ecb_function_ int ecb_ld64 (uint64_t x) + { + int r = 0; + + if (x >> 32) { x >>= 32; r += 32; } + + return r + ecb_ld32 (x); + } +#endif + +/* popcount64 is only available on 64 bit cpus as gcc builtin */ +/* so for this version we are lazy */ +ecb_function_ int ecb_popcount64 (uint64_t x) ecb_const; +ecb_function_ int +ecb_popcount64 (uint64_t x) +{ + return ecb_popcount32 (x) + ecb_popcount32 (x >> 32); +} + +ecb_inline uint8_t ecb_rotl8 (uint8_t x, unsigned int count) ecb_const; +ecb_inline uint8_t ecb_rotr8 (uint8_t x, unsigned int count) ecb_const; +ecb_inline uint16_t ecb_rotl16 (uint16_t x, unsigned int count) ecb_const; +ecb_inline uint16_t ecb_rotr16 (uint16_t x, unsigned int count) ecb_const; +ecb_inline uint32_t ecb_rotl32 (uint32_t x, unsigned int count) ecb_const; +ecb_inline uint32_t ecb_rotr32 (uint32_t x, unsigned int count) ecb_const; +ecb_inline uint64_t ecb_rotl64 (uint64_t x, unsigned int count) ecb_const; +ecb_inline uint64_t ecb_rotr64 (uint64_t x, unsigned int count) ecb_const; + +ecb_inline uint8_t ecb_rotl8 (uint8_t x, unsigned int count) { return (x >> ( 8 - count)) | (x << count); } +ecb_inline uint8_t ecb_rotr8 (uint8_t x, unsigned int count) { return (x << ( 8 - count)) | (x >> count); } +ecb_inline uint16_t ecb_rotl16 (uint16_t x, unsigned int count) { return (x >> (16 - count)) | (x << count); } +ecb_inline uint16_t ecb_rotr16 (uint16_t x, unsigned int count) { return (x << (16 - count)) | (x >> count); } +ecb_inline uint32_t ecb_rotl32 (uint32_t x, unsigned int count) { return (x >> (32 - count)) | (x << count); } +ecb_inline uint32_t ecb_rotr32 (uint32_t x, unsigned int count) { return (x << (32 - count)) | (x >> count); } +ecb_inline uint64_t ecb_rotl64 (uint64_t x, unsigned int count) { return (x >> (64 - count)) | (x << count); } +ecb_inline uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { return (x << (64 - count)) | (x >> count); } + +#if ECB_GCC_VERSION(4,3) + #define ecb_bswap16(x) (__builtin_bswap32 (x) >> 16) + #define ecb_bswap32(x) __builtin_bswap32 (x) + #define ecb_bswap64(x) __builtin_bswap64 (x) +#else + ecb_function_ uint16_t ecb_bswap16 (uint16_t x) ecb_const; + ecb_function_ uint16_t + ecb_bswap16 (uint16_t x) + { + return ecb_rotl16 (x, 8); + } + + ecb_function_ uint32_t ecb_bswap32 (uint32_t x) ecb_const; + ecb_function_ uint32_t + ecb_bswap32 (uint32_t x) + { + return (((uint32_t)ecb_bswap16 (x)) << 16) | ecb_bswap16 (x >> 16); + } + + ecb_function_ uint64_t ecb_bswap64 (uint64_t x) ecb_const; + ecb_function_ uint64_t + ecb_bswap64 (uint64_t x) + { + return (((uint64_t)ecb_bswap32 (x)) << 32) | ecb_bswap32 (x >> 32); + } +#endif + +#if ECB_GCC_VERSION(4,5) + #define ecb_unreachable() __builtin_unreachable () +#else + /* this seems to work fine, but gcc always emits a warning for it :/ */ + ecb_function_ void ecb_unreachable (void) ecb_noreturn; + ecb_function_ void ecb_unreachable (void) { } +#endif + +/* try to tell the compiler that some condition is definitely true */ +#define ecb_assume(cond) do { if (!(cond)) ecb_unreachable (); } while (0) + +ecb_function_ unsigned char ecb_byteorder_helper (void) ecb_const; +ecb_function_ unsigned char +ecb_byteorder_helper (void) +{ + const uint32_t u = 0x11223344; + return *(unsigned char *)&u; +} + +ecb_function_ ecb_bool ecb_big_endian (void) ecb_const; +ecb_function_ ecb_bool ecb_big_endian (void) { return ecb_byteorder_helper () == 0x11; } +ecb_function_ ecb_bool ecb_little_endian (void) ecb_const; +ecb_function_ ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44; } + +#if ECB_GCC_VERSION(3,0) || ECB_C99 + #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0)) +#else + #define ecb_mod(m,n) ((m) < 0 ? ((n) - 1 - ((-1 - (m)) % (n))) : ((m) % (n))) +#endif + +#if ecb_cplusplus_does_not_suck + /* does not work for local types (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm) */ + template + static inline int ecb_array_length (const T (&arr)[N]) + { + return N; + } +#else + #define ecb_array_length(name) (sizeof (name) / sizeof (name [0])) +#endif + +#endif + diff --git a/src/rt/libuv/src/eio/eio.3 b/src/rt/libuv/src/unix/eio/eio.3 similarity index 100% rename from src/rt/libuv/src/eio/eio.3 rename to src/rt/libuv/src/unix/eio/eio.3 diff --git a/src/rt/libuv/src/eio/eio.c b/src/rt/libuv/src/unix/eio/eio.c similarity index 63% rename from src/rt/libuv/src/eio/eio.c rename to src/rt/libuv/src/unix/eio/eio.c index 7f48add4019..a005db5d3b4 100644 --- a/src/rt/libuv/src/eio/eio.c +++ b/src/rt/libuv/src/unix/eio/eio.c @@ -1,7 +1,7 @@ /* * libeio implementation * - * Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann + * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- @@ -37,16 +37,22 @@ * either the BSD or the GPL. */ -#include "eio.h" - -#ifdef EIO_STACKSIZE -# define XTHREAD_STACKSIZE EIO_STACKSIZE +#ifdef EIO_CONFIG_H +# include EIO_CONFIG_H #endif -// For statically-linked pthreads-w32, use: -// #ifdef _WIN32 -// # define PTW32_STATIC_LIB 1 -// #endif +/* Undone by libuv for easy build scripts. + * #ifndef _WIN32 + * # include "config.h" + * #endif + */ + +#include "eio.h" +#include "ecb.h" + +#ifdef EIO_STACKSIZE +# define X_STACKSIZE EIO_STACKSIZE +#endif #include "xthread.h" #include @@ -60,10 +66,25 @@ #include #include -#ifndef _WIN32 -#include +/* intptr_t comes from unistd.h, says POSIX/UNIX/tradition */ +/* intptr_t only comes from stdint.h, says idiot openbsd coder */ +#if HAVE_STDINT_H +# include #endif +#ifndef ECANCELED +# define ECANCELED EDOM +#endif +#ifndef ELOOP +# define ELOOP EDOM +#endif + +#if !defined(ENOTSOCK) && defined(WSAENOTSOCK) +# define ENOTSOCK WSAENOTSOCK +#endif + +static void eio_destroy (eio_req *req); + #ifndef EIO_FINISH # define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0 #endif @@ -76,65 +97,216 @@ # define EIO_FEED(req) do { if ((req)->feed ) (req)->feed (req); } while (0) #endif +#ifndef EIO_FD_TO_WIN32_HANDLE +# define EIO_FD_TO_WIN32_HANDLE(fd) _get_osfhandle (fd) +#endif +#ifndef EIO_WIN32_HANDLE_TO_FD +# define EIO_WIN32_HANDLE_TO_FD(handle) _open_osfhandle (handle, 0) +#endif + +#define EIO_ERRNO(errval,retval) ((errno = errval), retval) + +#define EIO_ENOSYS() EIO_ERRNO (ENOSYS, -1) + #ifdef _WIN32 -# include -# include -# include -# include -# include -# include -# include + #include -# define ENOTSOCK WSAENOTSOCK -# define EOPNOTSUPP WSAEOPNOTSUPP -# define ECANCELED 140 + #undef PAGESIZE + #define PAGESIZE 4096 /* GetSystemInfo? */ -# ifndef EIO_STRUCT_DIRENT -# define EIO_STRUCT_DIRENT struct dirent -# endif + /* TODO: look at how perl does stat (non-sloppy), unlink (ro-files), utime, link */ + + #ifdef EIO_STRUCT_STATI64 + /* look at perl's non-sloppy stat */ + #define stat(path,buf) _stati64 (path,buf) + #define fstat(fd,buf) _fstati64 (fd,buf) + #endif + #define lstat(path,buf) stat (path,buf) + #define fsync(fd) (FlushFileBuffers ((HANDLE)EIO_FD_TO_WIN32_HANDLE (fd)) ? 0 : EIO_ERRNO (EBADF, -1)) + #define mkdir(path,mode) _mkdir (path) + #define link(old,neu) (CreateHardLink (neu, old, 0) ? 0 : EIO_ERRNO (ENOENT, -1)) + + #define chmod(path,mode) _chmod (path, mode) + #define dup(fd) _dup (fd) + #define dup2(fd1,fd2) _dup2 (fd1, fd2) + + #define fchmod(fd,mode) EIO_ENOSYS () + #define chown(path,uid,gid) EIO_ENOSYS () + #define fchown(fd,uid,gid) EIO_ENOSYS () + #define truncate(path,offs) EIO_ENOSYS () /* far-miss: SetEndOfFile */ + #define ftruncate(fd,offs) EIO_ENOSYS () /* near-miss: SetEndOfFile */ + #define mknod(path,mode,dev) EIO_ENOSYS () + #define sync() EIO_ENOSYS () + #define readlink(path,buf,s) EIO_ENOSYS () + #define statvfs(path,buf) EIO_ENOSYS () + #define fstatvfs(fd,buf) EIO_ENOSYS () + + #define getcwd(buf,s) _getcwd(buf, s) + #define rmdir(path) _rmdir(path) + + /* rename() uses MoveFile, which fails to overwrite */ + #define rename(old,neu) eio__rename (old, neu) + + static int + eio__rename (const char *old, const char *neu) + { + if (MoveFileEx (old, neu, MOVEFILE_REPLACE_EXISTING)) + return 0; + + /* should steal _dosmaperr */ + switch (GetLastError ()) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_NO_MORE_FILES: + case ERROR_BAD_NETPATH: + case ERROR_BAD_NET_NAME: + case ERROR_BAD_PATHNAME: + case ERROR_FILENAME_EXCED_RANGE: + errno = ENOENT; + break; + + default: + errno = EACCES; + break; + } + + return -1; + } + + /* we could even stat and see if it exists */ + static int + symlink (const char *old, const char *neu) + { + #if WINVER >= 0x0600 + if (CreateSymbolicLink (neu, old, 1)) + return 0; + + if (CreateSymbolicLink (neu, old, 0)) + return 0; + #endif + + return EIO_ERRNO (ENOENT, -1); + } + + /* POSIX API only */ + #ifndef CreateHardLink + # define CreateHardLink(neu,old,flags) 0 + #endif + #define CreateSymbolicLink(neu,old,flags) 0 + + struct statvfs + { + int dummy; + }; + + #define DT_DIR EIO_DT_DIR + #define DT_REG EIO_DT_REG + #define D_NAME(entp) entp.cFileName + #define D_TYPE(entp) (entp.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? DT_DIR : DT_REG) + +#include +#define utime(path, times) _utime(path, times) +#define utimbuf _utimbuf + +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#else + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#endif + +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +static int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag; + + if (NULL != tv) + { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + /*converting file time to unix epoch*/ + tmpres -= DELTA_EPOCH_IN_MICROSECS; + tmpres /= 10; /*convert into microseconds*/ + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + if (NULL != tz) + { + if (!tzflag) + { + _tzset(); + tzflag++; + } + tz->tz_minuteswest = _timezone / 60; + tz->tz_dsttime = _daylight; + } + + return 0; +} #else -# ifdef EIO_CONFIG_H -# include EIO_CONFIG_H -# else -# include "config.h" -# endif + #include + #include + #include + #include + #include + #include -# include -# include -# include + #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES + #include + #endif + + #define D_NAME(entp) entp->d_name + + /* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ + #if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ + #define _DIRENT_HAVE_D_TYPE /* sigh */ + #define D_INO(de) (de)->d_fileno + #define D_NAMLEN(de) (de)->d_namlen + #elif __linux || defined d_ino || _XOPEN_SOURCE >= 600 + #define D_INO(de) (de)->d_ino + #endif + + #ifdef _D_EXACT_NAMLEN + #undef D_NAMLEN + #define D_NAMLEN(de) _D_EXACT_NAMLEN (de) + #endif + + #ifdef _DIRENT_HAVE_D_TYPE + #define D_TYPE(de) (de)->d_type + #endif + + #ifndef EIO_STRUCT_DIRENT + #define EIO_STRUCT_DIRENT struct dirent + #endif + +#endif + +#if HAVE_UTIMES # include -# include -# include - -#if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES -# include #endif -/* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ -# if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ -# define _DIRENT_HAVE_D_TYPE /* sigh */ -# define D_INO(de) (de)->d_fileno -# define D_NAMLEN(de) (de)->d_namlen -# elif __linux || defined d_ino || _XOPEN_SOURCE >= 600 -# define D_INO(de) (de)->d_ino -# endif - -#ifdef _D_EXACT_NAMLEN -# undef D_NAMLEN -# define D_NAMLEN(de) _D_EXACT_NAMLEN (de) +#if HAVE_SYS_SYSCALL_H +# include #endif -# ifdef _DIRENT_HAVE_D_TYPE -# define D_TYPE(de) (de)->d_type -# endif - -# ifndef EIO_STRUCT_DIRENT -# define EIO_STRUCT_DIRENT struct dirent -# endif - +#if HAVE_SYS_PRCTL_H +# include #endif #if HAVE_SENDFILE @@ -159,12 +331,9 @@ # define D_INO(de) 0 #endif #ifndef D_NAMLEN -# define D_NAMLEN(de) strlen ((de)->d_name) +# define D_NAMLEN(entp) strlen (D_NAME (entp)) #endif -/* number of seconds after which an idle threads exit */ -#define IDLE_TIMEOUT 10 - /* used for struct dirent, AIX doesn't provide it */ #ifndef NAME_MAX # define NAME_MAX 4096 @@ -179,29 +348,16 @@ #define EIO_BUFSIZE 65536 #define dBUF \ - char *eio_buf; \ - ETP_WORKER_LOCK (self); \ - self->dbuf = eio_buf = malloc (EIO_BUFSIZE); \ - ETP_WORKER_UNLOCK (self); \ + char *eio_buf = malloc (EIO_BUFSIZE); \ errno = ENOMEM; \ if (!eio_buf) \ - return -1; + return -1 + +#define FUBd \ + free (eio_buf) #define EIO_TICKS ((1000000 + 1023) >> 10) -/*****************************************************************************/ - -#if __GNUC__ >= 3 -# define expect(expr,value) __builtin_expect ((expr),(value)) -#else -# define expect(expr,value) (expr) -#endif - -#define expect_false(expr) expect ((expr) != 0, 0) -#define expect_true(expr) expect ((expr) != 0, 1) - -/*****************************************************************************/ - #define ETP_PRI_MIN EIO_PRI_MIN #define ETP_PRI_MAX EIO_PRI_MAX @@ -214,29 +370,13 @@ static int eio_finish (eio_req *req); static void eio_execute (struct etp_worker *self, eio_req *req); #define ETP_EXECUTE(wrk,req) eio_execute (wrk,req) -#define ETP_WORKER_CLEAR(req) \ - if (wrk->dbuf) \ - { \ - free (wrk->dbuf); \ - wrk->dbuf = 0; \ - } \ - \ - if (wrk->dirp) \ - { \ - closedir (wrk->dirp); \ - wrk->dirp = 0; \ - } - -#define ETP_WORKER_COMMON \ - void *dbuf; \ - DIR *dirp; - /*****************************************************************************/ #define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) /* calculate time difference in ~1/EIO_TICKS of a second */ -static int tvdiff (struct timeval *tv1, struct timeval *tv2) +ecb_inline int +tvdiff (struct timeval *tv1, struct timeval *tv2) { return (tv2->tv_sec - tv1->tv_sec ) * EIO_TICKS + ((tv2->tv_usec - tv1->tv_usec) >> 10); @@ -250,16 +390,23 @@ static void (*done_poll_cb) (void); static unsigned int max_poll_time; /* reslock */ static unsigned int max_poll_reqs; /* reslock */ -static volatile unsigned int nreqs; /* reqlock */ -static volatile unsigned int nready; /* reqlock */ -static volatile unsigned int npending; /* reqlock */ -static volatile unsigned int max_idle = 4; +static unsigned int nreqs; /* reqlock */ +static unsigned int nready; /* reqlock */ +static unsigned int npending; /* reqlock */ +static unsigned int max_idle = 4; /* maximum number of threads that can idle indefinitely */ +static unsigned int idle_timeout = 10; /* number of seconds after which an idle threads exit */ -static xmutex_t wrklock = X_MUTEX_INIT; -static xmutex_t reslock = X_MUTEX_INIT; -static xmutex_t reqlock = X_MUTEX_INIT; -static xcond_t reqwait = X_COND_INIT; +static xmutex_t wrklock; +static xmutex_t reslock; +static xmutex_t reqlock; +static xcond_t reqwait; +/* Fix for test-fs-sir-writes-alot */ +/* Apple's OSX can't safely write() concurrently from 2 threads */ +/* for more info see the thread "fs.write Data Munging" in the nodejs google group */ +/* http://groups.google.com/group/nodejs/browse_thread/thread/c11f8b683f37cef/b18ad9e0a15314c5 */ +/* And the thread "write()s and pwrite()s from multiple threads in OSX" in libev@lists.schmorp.de */ +/* http://lists.schmorp.de/pipermail/libev/2010q4/001185.html */ #if defined (__APPLE__) static xmutex_t apple_bug_writelock = X_MUTEX_INIT; #endif @@ -270,7 +417,7 @@ static xmutex_t apple_bug_writelock = X_MUTEX_INIT; * normal read/write by using a mutex. slows down execution a lot, * but that's your problem, not mine. */ -static xmutex_t preadwritelock = X_MUTEX_INIT; +static xmutex_t preadwritelock; #endif typedef struct etp_worker @@ -283,22 +430,25 @@ typedef struct etp_worker /* locked by reslock, reqlock or wrklock */ ETP_REQ *req; /* currently processed request */ +#ifdef ETP_WORKER_COMMON ETP_WORKER_COMMON +#endif } etp_worker; -static etp_worker wrk_first = { &wrk_first, &wrk_first, 0 }; /* NOT etp */ +static etp_worker wrk_first; /* NOT etp */ #define ETP_WORKER_LOCK(wrk) X_LOCK (wrklock) #define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock) /* worker threads management */ -static void etp_worker_clear (etp_worker *wrk) +static void ecb_cold +etp_worker_clear (etp_worker *wrk) { - ETP_WORKER_CLEAR (wrk); } -static void etp_worker_free (etp_worker *wrk) +static void ecb_cold +etp_worker_free (etp_worker *wrk) { wrk->next->prev = wrk->prev; wrk->prev->next = wrk->next; @@ -306,7 +456,8 @@ static void etp_worker_free (etp_worker *wrk) free (wrk); } -static unsigned int etp_nreqs (void) +static unsigned int +etp_nreqs (void) { int retval; if (WORDACCESS_UNSAFE) X_LOCK (reqlock); @@ -315,7 +466,8 @@ static unsigned int etp_nreqs (void) return retval; } -static unsigned int etp_nready (void) +static unsigned int +etp_nready (void) { unsigned int retval; @@ -326,7 +478,8 @@ static unsigned int etp_nready (void) return retval; } -static unsigned int etp_npending (void) +static unsigned int +etp_npending (void) { unsigned int retval; @@ -337,7 +490,8 @@ static unsigned int etp_npending (void) return retval; } -static unsigned int etp_nthreads (void) +static unsigned int +etp_nthreads (void) { unsigned int retval; @@ -361,7 +515,19 @@ typedef struct { static etp_reqq req_queue; static etp_reqq res_queue; -static int reqq_push (etp_reqq *q, ETP_REQ *req) +static void ecb_noinline ecb_cold +reqq_init (etp_reqq *q) +{ + int pri; + + for (pri = 0; pri < ETP_NUM_PRI; ++pri) + q->qs[pri] = q->qe[pri] = 0; + + q->size = 0; +} + +static int ecb_noinline +reqq_push (etp_reqq *q, ETP_REQ *req) { int pri = req->pri; req->next = 0; @@ -377,7 +543,8 @@ static int reqq_push (etp_reqq *q, ETP_REQ *req) return q->size++; } -static ETP_REQ *reqq_shift (etp_reqq *q) +static ETP_REQ * ecb_noinline +reqq_shift (etp_reqq *q) { int pri; @@ -402,46 +569,19 @@ static ETP_REQ *reqq_shift (etp_reqq *q) abort (); } -static void etp_atfork_prepare (void) +static int ecb_cold +etp_init (void (*want_poll)(void), void (*done_poll)(void)) { - X_LOCK (wrklock); - X_LOCK (reqlock); - X_LOCK (reslock); -#if !HAVE_PREADWRITE - X_LOCK (preadwritelock); -#endif -} + X_MUTEX_CREATE (wrklock); + X_MUTEX_CREATE (reslock); + X_MUTEX_CREATE (reqlock); + X_COND_CREATE (reqwait); -static void etp_atfork_parent (void) -{ -#if !HAVE_PREADWRITE - X_UNLOCK (preadwritelock); -#endif - X_UNLOCK (reslock); - X_UNLOCK (reqlock); - X_UNLOCK (wrklock); -} + reqq_init (&req_queue); + reqq_init (&res_queue); -static void etp_atfork_child (void) -{ - ETP_REQ *prv; - - while ((prv = reqq_shift (&req_queue))) - ETP_DESTROY (prv); - - while ((prv = reqq_shift (&res_queue))) - ETP_DESTROY (prv); - - while (wrk_first.next != &wrk_first) - { - etp_worker *wrk = wrk_first.next; - - if (wrk->req) - ETP_DESTROY (wrk->req); - - etp_worker_clear (wrk); - etp_worker_free (wrk); - } + wrk_first.next = + wrk_first.prev = &wrk_first; started = 0; idle = 0; @@ -449,22 +589,6 @@ static void etp_atfork_child (void) nready = 0; npending = 0; - etp_atfork_parent (); -} - -static void -etp_once_init (void) -{ - X_THREAD_ATFORK (etp_atfork_prepare, etp_atfork_parent, etp_atfork_child); -} - -static int -etp_init (void (*want_poll)(void), void (*done_poll)(void)) -{ - static pthread_once_t doinit = PTHREAD_ONCE_INIT; - - pthread_once (&doinit, etp_once_init); - want_poll_cb = want_poll; done_poll_cb = done_poll; @@ -473,7 +597,8 @@ etp_init (void (*want_poll)(void), void (*done_poll)(void)) X_THREAD_PROC (etp_proc); -static void etp_start_thread (void) +static void ecb_cold +etp_start_thread (void) { etp_worker *wrk = calloc (1, sizeof (etp_worker)); @@ -496,19 +621,21 @@ static void etp_start_thread (void) X_UNLOCK (wrklock); } -static void etp_maybe_start_thread (void) +static void +etp_maybe_start_thread (void) { - if (expect_true (etp_nthreads () >= wanted)) + if (ecb_expect_true (etp_nthreads () >= wanted)) return; /* todo: maybe use idle here, but might be less exact */ - if (expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ())) + if (ecb_expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ())) return; etp_start_thread (); } -static void etp_end_thread (void) +static void ecb_cold +etp_end_thread (void) { eio_req *req = calloc (1, sizeof (eio_req)); @@ -525,7 +652,8 @@ static void etp_end_thread (void) X_UNLOCK (wrklock); } -static int etp_poll (void) +static int +etp_poll (void) { unsigned int maxreqs; unsigned int maxtime; @@ -565,7 +693,7 @@ static int etp_poll (void) --nreqs; X_UNLOCK (reqlock); - if (expect_false (req->type == EIO_GROUP && req->size)) + if (ecb_expect_false (req->type == EIO_GROUP && req->size)) { req->int1 = 1; /* mark request as delayed */ continue; @@ -573,11 +701,11 @@ static int etp_poll (void) else { int res = ETP_FINISH (req); - if (expect_false (res)) + if (ecb_expect_false (res)) return res; } - if (expect_false (maxreqs && !--maxreqs)) + if (ecb_expect_false (maxreqs && !--maxreqs)) break; if (maxtime) @@ -593,23 +721,23 @@ static int etp_poll (void) return -1; } -static void etp_cancel (ETP_REQ *req) +static void +etp_cancel (ETP_REQ *req) { - X_LOCK (wrklock); - req->flags |= EIO_FLAG_CANCELLED; - X_UNLOCK (wrklock); + req->cancelled = 1; eio_grp_cancel (req); } -static void etp_submit (ETP_REQ *req) +static void +etp_submit (ETP_REQ *req) { req->pri -= ETP_PRI_MIN; - if (expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN; - if (expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN; + if (ecb_expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN; + if (ecb_expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN; - if (expect_false (req->type == EIO_GROUP)) + if (ecb_expect_false (req->type == EIO_GROUP)) { /* I hope this is worth it :/ */ X_LOCK (reqlock); @@ -638,34 +766,47 @@ static void etp_submit (ETP_REQ *req) } } -static void etp_set_max_poll_time (double nseconds) +static void ecb_cold +etp_set_max_poll_time (double nseconds) { if (WORDACCESS_UNSAFE) X_LOCK (reslock); max_poll_time = nseconds * EIO_TICKS; if (WORDACCESS_UNSAFE) X_UNLOCK (reslock); } -static void etp_set_max_poll_reqs (unsigned int maxreqs) +static void ecb_cold +etp_set_max_poll_reqs (unsigned int maxreqs) { if (WORDACCESS_UNSAFE) X_LOCK (reslock); max_poll_reqs = maxreqs; if (WORDACCESS_UNSAFE) X_UNLOCK (reslock); } -static void etp_set_max_idle (unsigned int nthreads) +static void ecb_cold +etp_set_max_idle (unsigned int nthreads) { if (WORDACCESS_UNSAFE) X_LOCK (reqlock); - max_idle = nthreads <= 0 ? 1 : nthreads; + max_idle = nthreads; if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); } -static void etp_set_min_parallel (unsigned int nthreads) +static void ecb_cold +etp_set_idle_timeout (unsigned int seconds) +{ + if (WORDACCESS_UNSAFE) X_LOCK (reqlock); + idle_timeout = seconds; + if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); +} + +static void ecb_cold +etp_set_min_parallel (unsigned int nthreads) { if (wanted < nthreads) wanted = nthreads; } -static void etp_set_max_parallel (unsigned int nthreads) +static void ecb_cold +etp_set_max_parallel (unsigned int nthreads) { if (wanted > nthreads) wanted = nthreads; @@ -676,7 +817,8 @@ static void etp_set_max_parallel (unsigned int nthreads) /*****************************************************************************/ -static void grp_try_feed (eio_req *grp) +static void +grp_try_feed (eio_req *grp) { while (grp->size < grp->int2 && !EIO_CANCELLED (grp)) { @@ -693,7 +835,8 @@ static void grp_try_feed (eio_req *grp) } } -static int grp_dec (eio_req *grp) +static int +grp_dec (eio_req *grp) { --grp->size; @@ -707,7 +850,8 @@ static int grp_dec (eio_req *grp) return 0; } -void eio_destroy (eio_req *req) +static void +eio_destroy (eio_req *req) { if ((req)->flags & EIO_FLAG_PTR1_FREE) free (req->ptr1); if ((req)->flags & EIO_FLAG_PTR2_FREE) free (req->ptr2); @@ -715,7 +859,8 @@ void eio_destroy (eio_req *req) EIO_DESTROY (req); } -static int eio_finish (eio_req *req) +static int +eio_finish (eio_req *req) { int res = EIO_FINISH (req); @@ -733,7 +878,7 @@ static int eio_finish (eio_req *req) res2 = grp_dec (grp); - if (!res && res2) + if (!res) res = res2; } @@ -742,63 +887,81 @@ static int eio_finish (eio_req *req) return res; } -void eio_grp_cancel (eio_req *grp) +void +eio_grp_cancel (eio_req *grp) { for (grp = grp->grp_first; grp; grp = grp->grp_next) eio_cancel (grp); } -void eio_cancel (eio_req *req) +void +eio_cancel (eio_req *req) { etp_cancel (req); } -void eio_submit (eio_req *req) +void +eio_submit (eio_req *req) { etp_submit (req); } -unsigned int eio_nreqs (void) +unsigned int +eio_nreqs (void) { return etp_nreqs (); } -unsigned int eio_nready (void) +unsigned int +eio_nready (void) { return etp_nready (); } -unsigned int eio_npending (void) +unsigned int +eio_npending (void) { return etp_npending (); } -unsigned int eio_nthreads (void) +unsigned int ecb_cold +eio_nthreads (void) { return etp_nthreads (); } -void eio_set_max_poll_time (double nseconds) +void ecb_cold +eio_set_max_poll_time (double nseconds) { etp_set_max_poll_time (nseconds); } -void eio_set_max_poll_reqs (unsigned int maxreqs) +void ecb_cold +eio_set_max_poll_reqs (unsigned int maxreqs) { etp_set_max_poll_reqs (maxreqs); } -void eio_set_max_idle (unsigned int nthreads) +void ecb_cold +eio_set_max_idle (unsigned int nthreads) { etp_set_max_idle (nthreads); } -void eio_set_min_parallel (unsigned int nthreads) +void ecb_cold +eio_set_idle_timeout (unsigned int seconds) +{ + etp_set_idle_timeout (seconds); +} + +void ecb_cold +eio_set_min_parallel (unsigned int nthreads) { etp_set_min_parallel (nthreads); } -void eio_set_max_parallel (unsigned int nthreads) +void ecb_cold +eio_set_max_parallel (unsigned int nthreads) { etp_set_max_parallel (nthreads); } @@ -817,10 +980,10 @@ int eio_poll (void) # define pread eio__pread # define pwrite eio__pwrite -ssize_t +eio_ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset) { - ssize_t res; + eio_ssize_t res; off_t ooffset; X_LOCK (preadwritelock); @@ -833,10 +996,10 @@ eio__pread (int fd, void *buf, size_t count, off_t offset) return res; } -ssize_t +eio_ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset) { - ssize_t res; + eio_ssize_t res; off_t ooffset; X_LOCK (preadwritelock); @@ -878,7 +1041,8 @@ eio__utimes (const char *filename, const struct timeval times[2]) # undef futimes # define futimes(fd,times) eio__futimes (fd, times) -static int eio__futimes (int fd, const struct timeval tv[2]) +static int +eio__futimes (int fd, const struct timeval tv[2]) { errno = ENOSYS; return -1; @@ -886,25 +1050,31 @@ static int eio__futimes (int fd, const struct timeval tv[2]) #endif -#ifdef _WIN32 -# define fsync(fd) (FlushFileBuffers((HANDLE)_get_osfhandle(fd)) ? 0 : -1) -#endif - #if !HAVE_FDATASYNC # undef fdatasync # define fdatasync(fd) fsync (fd) #endif -// Use unicode and big file aware stat on windows -#ifdef _WIN32 -# undef stat -# undef fstat -# define stat _stati64 -# define fstat _fstati64 +static int +eio__syncfs (int fd) +{ + int res; + +#if HAVE_SYS_SYNCFS + res = (int)syscall (__NR_syncfs, (int)(fd)); +#else + res = -1; + errno = ENOSYS; #endif + if (res < 0 && errno == ENOSYS && fd >= 0) + sync (); + + return res; +} + /* sync_file_range always needs emulation */ -int +static int eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags) { #if HAVE_SYNC_FILE_RANGE @@ -931,11 +1101,22 @@ eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags) return fdatasync (fd); } +static int +eio__fallocate (int fd, int mode, off_t offset, size_t len) +{ +#if HAVE_FALLOCATE + return fallocate (fd, mode, offset, len); +#else + errno = ENOSYS; + return -1; +#endif +} + #if !HAVE_READAHEAD # undef readahead # define readahead(fd,offset,count) eio__readahead (fd, offset, count, self) -static ssize_t +static eio_ssize_t eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) { size_t todo = count; @@ -950,6 +1131,8 @@ eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) todo -= len; } + FUBd; + errno = 0; return count; } @@ -957,93 +1140,118 @@ eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) #endif /* sendfile always needs emulation */ -static ssize_t -eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) +static eio_ssize_t +eio__sendfile (int ofd, int ifd, off_t offset, size_t count) { - ssize_t res; + eio_ssize_t written = 0; + eio_ssize_t res; if (!count) return 0; + for (;;) + { +#ifdef __APPLE__ +# undef HAVE_SENDFILE /* broken, as everything on os x */ +#endif #if HAVE_SENDFILE # if __linux - res = sendfile (ofd, ifd, &offset, count); + off_t soffset = offset; + res = sendfile (ofd, ifd, &soffset, count); # elif __FreeBSD__ - /* - * Of course, the freebsd sendfile is a dire hack with no thoughts - * wasted on making it similar to other I/O functions. - */ - { - off_t sbytes; - res = sendfile (ifd, ofd, offset, count, 0, &sbytes, 0); + /* + * Of course, the freebsd sendfile is a dire hack with no thoughts + * wasted on making it similar to other I/O functions. + */ + off_t sbytes; + res = sendfile (ifd, ofd, offset, count, 0, &sbytes, 0); - #if 0 /* according to the manpage, this is correct, but broken behaviour */ - /* freebsd' sendfile will return 0 on success */ - /* freebsd 8 documents it as only setting *sbytes on EINTR and EAGAIN, but */ - /* not on e.g. EIO or EPIPE - sounds broken */ - if ((res < 0 && (errno == EAGAIN || errno == EINTR) && sbytes) || res == 0) - res = sbytes; - #endif + #if 0 /* according to the manpage, this is correct, but broken behaviour */ + /* freebsd' sendfile will return 0 on success */ + /* freebsd 8 documents it as only setting *sbytes on EINTR and EAGAIN, but */ + /* not on e.g. EIO or EPIPE - sounds broken */ + if ((res < 0 && (errno == EAGAIN || errno == EINTR) && sbytes) || res == 0) + res = sbytes; + #endif - /* according to source inspection, this is correct, and useful behaviour */ - if (sbytes) - res = sbytes; - } + /* according to source inspection, this is correct, and useful behaviour */ + if (sbytes) + res = sbytes; # elif defined (__APPLE__) + off_t sbytes = count; + res = sendfile (ifd, ofd, offset, &sbytes, 0, 0); - { - off_t sbytes = count; - res = sendfile (ifd, ofd, offset, &sbytes, 0, 0); - - /* according to the manpage, sbytes is always valid */ - if (sbytes) - res = sbytes; - } + /* according to the manpage, sbytes is always valid */ + if (sbytes) + res = sbytes; # elif __hpux - res = sendfile (ofd, ifd, offset, count, 0, 0); + res = sendfile (ofd, ifd, offset, count, 0, 0); # elif __solaris - { - struct sendfilevec vec; - size_t sbytes; + struct sendfilevec vec; + size_t sbytes; - vec.sfv_fd = ifd; - vec.sfv_flag = 0; - vec.sfv_off = offset; - vec.sfv_len = count; + vec.sfv_fd = ifd; + vec.sfv_flag = 0; + vec.sfv_off = offset; + vec.sfv_len = count; - res = sendfilev (ofd, &vec, 1, &sbytes); + res = sendfilev (ofd, &vec, 1, &sbytes); - if (res < 0 && sbytes) - res = sbytes; - } + if (res < 0 && sbytes) + res = sbytes; # endif -//#elif defined (_WIN32) -// -// /* does not work, just for documentation of what would need to be done */ -// { -// HANDLE h = TO_SOCKET (ifd); -// SetFilePointer (h, offset, 0, FILE_BEGIN); -// res = TransmitFile (TO_SOCKET (ofd), h, count, 0, 0, 0, 0); -// } +#elif defined (_WIN32) && 0 + /* does not work, just for documentation of what would need to be done */ + /* actually, cannot be done like this, as TransmitFile changes the file offset, */ + /* libeio guarantees that the file offset does not change, and windows */ + /* has no way to get an independent handle to the same file description */ + HANDLE h = TO_SOCKET (ifd); + SetFilePointer (h, offset, 0, FILE_BEGIN); + res = TransmitFile (TO_SOCKET (ofd), h, count, 0, 0, 0, 0); #else - res = -1; - errno = ENOSYS; + res = -1; + errno = ENOSYS; #endif - if (res < 0 + /* we assume sendfile can copy at least 128mb in one go */ + if (res <= 128 * 1024 * 1024) + { + if (res > 0) + written += res; + + if (written) + return written; + + break; + } + else + { + /* if we requested more, then probably the kernel was lazy */ + written += res; + offset += res; + count -= res; + + if (!count) + return written; + } + } + + if (res < 0 && (errno == ENOSYS || errno == EINVAL || errno == ENOTSOCK /* BSDs */ #ifdef ENOTSUP /* sigh, if the steenking pile called openbsd would only try to at least compile posix code... */ || errno == ENOTSUP #endif +#ifdef EOPNOTSUPP /* windows */ || errno == EOPNOTSUPP /* BSDs */ +#endif #if __solaris || errno == EAFNOSUPPORT || errno == EPROTOTYPE #endif @@ -1057,7 +1265,7 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) while (count) { - ssize_t cnt; + eio_ssize_t cnt; cnt = pread (ifd, eio_buf, count > EIO_BUFSIZE ? EIO_BUFSIZE : count, offset); @@ -1079,16 +1287,299 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) res += cnt; count -= cnt; } + + FUBd; } return res; } +#ifdef PAGESIZE +# define eio_pagesize() PAGESIZE +#else +static intptr_t +eio_pagesize (void) +{ + static intptr_t page; + + if (!page) + page = sysconf (_SC_PAGESIZE); + + return page; +} +#endif + +static void +eio_page_align (void **addr, size_t *length) +{ + intptr_t mask = eio_pagesize () - 1; + + /* round down addr */ + intptr_t adj = mask & (intptr_t)*addr; + + *addr = (void *)((intptr_t)*addr - adj); + *length += adj; + + /* round up length */ + *length = (*length + mask) & ~mask; +} + +#if !_POSIX_MEMLOCK +# define eio__mlockall(a) EIO_ENOSYS () +#else + +static int +eio__mlockall (int flags) +{ + #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 7 + extern int mallopt (int, int); + mallopt (-6, 238); /* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=473812 */ + #endif + + if (EIO_MCL_CURRENT != MCL_CURRENT + || EIO_MCL_FUTURE != MCL_FUTURE) + { + flags = 0 + | (flags & EIO_MCL_CURRENT ? MCL_CURRENT : 0) + | (flags & EIO_MCL_FUTURE ? MCL_FUTURE : 0); + } + + return mlockall (flags); +} +#endif + +#if !_POSIX_MEMLOCK_RANGE +# define eio__mlock(a,b) EIO_ENOSYS () +#else + +static int +eio__mlock (void *addr, size_t length) +{ + eio_page_align (&addr, &length); + + return mlock (addr, length); +} + +#endif + +#if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO) +# define eio__msync(a,b,c) EIO_ENOSYS () +#else + +static int +eio__msync (void *mem, size_t len, int flags) +{ + eio_page_align (&mem, &len); + + if (EIO_MS_ASYNC != MS_SYNC + || EIO_MS_INVALIDATE != MS_INVALIDATE + || EIO_MS_SYNC != MS_SYNC) + { + flags = 0 + | (flags & EIO_MS_ASYNC ? MS_ASYNC : 0) + | (flags & EIO_MS_INVALIDATE ? MS_INVALIDATE : 0) + | (flags & EIO_MS_SYNC ? MS_SYNC : 0); + } + + return msync (mem, len, flags); +} + +#endif + +static int +eio__mtouch (eio_req *req) +{ + void *mem = req->ptr2; + size_t len = req->size; + int flags = req->int1; + + eio_page_align (&mem, &len); + + { + intptr_t addr = (intptr_t)mem; + intptr_t end = addr + len; + intptr_t page = eio_pagesize (); + + if (addr < end) + if (flags & EIO_MT_MODIFY) /* modify */ + do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len && !EIO_CANCELLED (req)); + else + do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len && !EIO_CANCELLED (req)); + } + + return 0; +} + +/*****************************************************************************/ +/* requests implemented outside eio_execute, because they are so large */ + +static void +eio__realpath (eio_req *req, etp_worker *self) +{ + char *rel = req->ptr1; + char *res; + char *tmp1, *tmp2; +#if SYMLOOP_MAX > 32 + int symlinks = SYMLOOP_MAX; +#else + int symlinks = 32; +#endif + + req->result = -1; + + errno = EINVAL; + if (!rel) + return; + + errno = ENOENT; + if (!*rel) + return; + + if (!req->ptr2) + { + X_LOCK (wrklock); + req->flags |= EIO_FLAG_PTR2_FREE; + X_UNLOCK (wrklock); + req->ptr2 = malloc (PATH_MAX * 3); + + errno = ENOMEM; + if (!req->ptr2) + return; + } + + res = req->ptr2; + tmp1 = res + PATH_MAX; + tmp2 = tmp1 + PATH_MAX; + +#if 0 /* disabled, the musl way to do things is just too racy */ +#if __linux && defined(O_NONBLOCK) && defined(O_NOATIME) + /* on linux we may be able to ask the kernel */ + { + int fd = open (rel, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_NOATIME); + + if (fd >= 0) + { + sprintf (tmp1, "/proc/self/fd/%d", fd); + req->result = readlink (tmp1, res, PATH_MAX); + close (fd); + + /* here we should probably stat the open file and the disk file, to make sure they still match */ + + if (req->result > 0) + goto done; + } + else if (errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EIO) + return; + } +#endif +#endif + + if (*rel != '/') + { + if (!getcwd (res, PATH_MAX)) + return; + + if (res [1]) /* only use if not / */ + res += strlen (res); + } + + while (*rel) + { + eio_ssize_t len, linklen; + char *beg = rel; + + while (*rel && *rel != '/') + ++rel; + + len = rel - beg; + + if (!len) /* skip slashes */ + { + ++rel; + continue; + } + + if (beg [0] == '.') + { + if (len == 1) + continue; /* . - nop */ + + if (beg [1] == '.' && len == 2) + { + /* .. - back up one component, if possible */ + + while (res != req->ptr2) + if (*--res == '/') + break; + + continue; + } + } + + errno = ENAMETOOLONG; + if (res + 1 + len + 1 >= tmp1) + return; + + /* copy one component */ + *res = '/'; + memcpy (res + 1, beg, len); + + /* zero-terminate, for readlink */ + res [len + 1] = 0; + + /* now check if it's a symlink */ + linklen = readlink (req->ptr2, tmp1, PATH_MAX); + + if (linklen < 0) + { + if (errno != EINVAL) + return; + + /* it's a normal directory. hopefully */ + res += len + 1; + } + else + { + /* yay, it was a symlink - build new path in tmp2 */ + int rellen = strlen (rel); + + errno = ENAMETOOLONG; + if (linklen + 1 + rellen >= PATH_MAX) + return; + + errno = ELOOP; + if (!--symlinks) + return; + + if (*tmp1 == '/') + res = req->ptr2; /* symlink resolves to an absolute path */ + + /* we need to be careful, as rel might point into tmp2 already */ + memmove (tmp2 + linklen + 1, rel, rellen + 1); + tmp2 [linklen] = '/'; + memcpy (tmp2, tmp1, linklen); + + rel = tmp2; + } + } + + /* special case for the lone root path */ + if (res == req->ptr2) + *res++ = '/'; + + req->result = res - (char *)req->ptr2; + +done: + req->ptr2 = realloc (req->ptr2, req->result); /* trade time for space savings */ +} + static signed char eio_dent_cmp (const eio_dirent *a, const eio_dirent *b) { - return a->score - b->score ? a->score - b->score /* works because our signed char is always 0..100 */ - : a->inode < b->inode ? -1 : a->inode > b->inode ? 1 : 0; + return a->score - b->score ? a->score - b->score /* works because our signed char is always 0..100 */ + : a->inode < b->inode ? -1 + : a->inode > b->inode ? 1 + : 0; } #define EIO_DENT_CMP(i,op,j) eio_dent_cmp (&i, &j) op 0 @@ -1097,41 +1588,41 @@ eio_dent_cmp (const eio_dirent *a, const eio_dirent *b) #define EIO_SORT_FAST 60 /* when to only use insertion sort */ static void -eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits) +eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) { - unsigned char bits [9 + sizeof (ino_t) * 8]; + unsigned char bits [9 + sizeof (eio_ino_t) * 8]; unsigned char *bit = bits; assert (CHAR_BIT == 8); assert (sizeof (eio_dirent) * 8 < 256); - assert (offsetof (eio_dirent, inode)); /* we use 0 as sentinel */ - assert (offsetof (eio_dirent, score)); /* we use 0 as sentinel */ + assert (offsetof (eio_dirent, inode)); /* we use bit #0 as sentinel */ + assert (offsetof (eio_dirent, score)); /* we use bit #0 as sentinel */ if (size <= EIO_SORT_FAST) return; /* first prepare an array of bits to test in our radix sort */ - /* try to take endianness into account, as well as differences in ino_t sizes */ + /* try to take endianness into account, as well as differences in eio_ino_t sizes */ /* inode_bits must contain all inodes ORed together */ /* which is used to skip bits that are 0 everywhere, which is very common */ { - ino_t endianness; + eio_ino_t endianness; int i, j; /* we store the byte offset of byte n into byte n of "endianness" */ - for (i = 0; i < sizeof (ino_t); ++i) + for (i = 0; i < sizeof (eio_ino_t); ++i) ((unsigned char *)&endianness)[i] = i; *bit++ = 0; - for (i = 0; i < sizeof (ino_t); ++i) + for (i = 0; i < sizeof (eio_ino_t); ++i) { /* shifting off the byte offsets out of "endianness" */ int offs = (offsetof (eio_dirent, inode) + (endianness & 0xff)) * 8; endianness >>= 8; for (j = 0; j < 8; ++j) - if (inode_bits & (((ino_t)1) << (i * 8 + j))) + if (inode_bits & (((eio_ino_t)1) << (i * 8 + j))) *bit++ = offs + j; } @@ -1142,9 +1633,9 @@ eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, ino_t /* now actually do the sorting (a variant of MSD radix sort) */ { - eio_dirent *base_stk [9 + sizeof (ino_t) * 8], *base; - eio_dirent *end_stk [9 + sizeof (ino_t) * 8], *end; - unsigned char *bit_stk [9 + sizeof (ino_t) * 8]; + eio_dirent *base_stk [9 + sizeof (eio_ino_t) * 8], *base; + eio_dirent *end_stk [9 + sizeof (eio_ino_t) * 8], *end; + unsigned char *bit_stk [9 + sizeof (eio_ino_t) * 8]; int stk_idx = 0; base_stk [stk_idx] = dents; @@ -1233,7 +1724,7 @@ eio_dent_insertion_sort (eio_dirent *dents, int size) } static void -eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits) +eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) { if (size <= 1) return; /* our insertion sort relies on size > 0 */ @@ -1251,47 +1742,112 @@ eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_ static void eio__scandir (eio_req *req, etp_worker *self) { - DIR *dirp; - EIO_STRUCT_DIRENT *entp; char *name, *names; - int namesalloc = 4096; + int namesalloc = 4096 - sizeof (void *) * 4; int namesoffs = 0; int flags = req->int1; eio_dirent *dents = 0; int dentalloc = 128; int dentoffs = 0; - ino_t inode_bits = 0; + eio_ino_t inode_bits = 0; +#ifdef _WIN32 + HANDLE dirp; + WIN32_FIND_DATA entp; +#else + DIR *dirp; + EIO_STRUCT_DIRENT *entp; +#endif req->result = -1; if (!(flags & EIO_READDIR_DENTS)) flags &= ~(EIO_READDIR_DIRS_FIRST | EIO_READDIR_STAT_ORDER); - X_LOCK (wrklock); - /* the corresponding closedir is in ETP_WORKER_CLEAR */ - self->dirp = dirp = opendir (req->ptr1); +#ifdef _WIN32 + { + int len = strlen ((const char *)req->ptr1); + char *path = malloc (MAX_PATH); + const char *fmt; + + if (!len) + fmt = "./*"; + else if (((const char *)req->ptr1)[len - 1] == '/' || ((const char *)req->ptr1)[len - 1] == '\\') + fmt = "%s*"; + else + fmt = "%s/*"; + + _snprintf (path, MAX_PATH, fmt, (const char *)req->ptr1); + dirp = FindFirstFile (path, &entp); + free (path); + + if (dirp == INVALID_HANDLE_VALUE) + { + dirp = 0; + + /* should steal _dosmaperr */ + switch (GetLastError ()) + { + case ERROR_FILE_NOT_FOUND: + req->result = 0; + break; + + case ERROR_INVALID_NAME: + case ERROR_PATH_NOT_FOUND: + case ERROR_NO_MORE_FILES: + errno = ENOENT; + break; + + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + + default: + errno = EINVAL; + break; + } + } + } +#else + dirp = opendir (req->ptr1); +#endif + + if (req->flags & EIO_FLAG_PTR1_FREE) + free (req->ptr1); + req->flags |= EIO_FLAG_PTR1_FREE | EIO_FLAG_PTR2_FREE; req->ptr1 = dents = flags ? malloc (dentalloc * sizeof (eio_dirent)) : 0; req->ptr2 = names = malloc (namesalloc); - X_UNLOCK (wrklock); if (dirp && names && (!flags || dents)) for (;;) { + int done; + +#ifdef _WIN32 + done = !dirp; +#else errno = 0; entp = readdir (dirp); + done = !entp; +#endif - if (!entp) + if (done) { +#ifndef _WIN32 + int old_errno = errno; + closedir (dirp); + errno = old_errno; + if (errno) break; +#endif /* sort etc. */ req->int1 = flags; req->result = dentoffs; if (flags & EIO_READDIR_STAT_ORDER) - eio_dent_sort (dents, dentoffs, 0, inode_bits); /* sort by inode exclusively */ + eio_dent_sort (dents, dentoffs, flags & EIO_READDIR_DIRS_FIRST ? 7 : 0, inode_bits); else if (flags & EIO_READDIR_DIRS_FIRST) if (flags & EIO_READDIR_FOUND_UNKNOWN) eio_dent_sort (dents, dentoffs, 7, inode_bits); /* sort by score and inode */ @@ -1303,7 +1859,6 @@ eio__scandir (eio_req *req, etp_worker *self) /* now partition dirs to the front, and non-dirs to the back */ /* by walking from both sides and swapping if necessary */ - /* also clear score, so it doesn't influence sorting */ while (oth > dir) { if (dir->type == EIO_DT_DIR) @@ -1316,7 +1871,7 @@ eio__scandir (eio_req *req, etp_worker *self) } } - /* now sort the dirs only */ + /* now sort the dirs only (dirs all have the same score) */ eio_dent_sort (dents, dir - dents, 0, inode_bits); } @@ -1324,19 +1879,17 @@ eio__scandir (eio_req *req, etp_worker *self) } /* now add the entry to our list(s) */ - name = entp->d_name; + name = D_NAME (entp); /* skip . and .. entries */ if (name [0] != '.' || (name [1] && (name [1] != '.' || name [2]))) { int len = D_NAMLEN (entp) + 1; - while (expect_false (namesoffs + len > namesalloc)) + while (ecb_expect_false (namesoffs + len > namesalloc)) { namesalloc *= 2; - X_LOCK (wrklock); req->ptr2 = names = realloc (names, namesalloc); - X_UNLOCK (wrklock); if (!names) break; @@ -1348,12 +1901,10 @@ eio__scandir (eio_req *req, etp_worker *self) { struct eio_dirent *ent; - if (expect_false (dentoffs == dentalloc)) + if (ecb_expect_false (dentoffs == dentalloc)) { dentalloc *= 2; - X_LOCK (wrklock); req->ptr1 = dents = realloc (dents, dentalloc * sizeof (eio_dirent)); - X_UNLOCK (wrklock); if (!dents) break; @@ -1443,133 +1994,17 @@ eio__scandir (eio_req *req, etp_worker *self) errno = ECANCELED; break; } + +#ifdef _WIN32 + if (!FindNextFile (dirp, &entp)) + { + FindClose (dirp); + dirp = 0; + } +#endif } } -#ifdef PAGESIZE -# define eio_pagesize() PAGESIZE - -#elif defined(_WIN32) - /* Windows */ - static intptr_t - eio_pagesize (void) - { - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwPageSize; - } - -#else - /* POSIX */ - static intptr_t - eio_pagesize (void) - { - static intptr_t page; - - if (!page) - page = sysconf (_SC_PAGESIZE); - - return page; - } -#endif - -static void -eio_page_align (void **addr, size_t *length) -{ - intptr_t mask = eio_pagesize () - 1; - - /* round down addr */ - intptr_t adj = mask & (intptr_t)*addr; - - *addr = (void *)((intptr_t)*addr - adj); - *length += adj; - - /* round up length */ - *length = (*length + mask) & ~mask; -} - -#if !_POSIX_MEMLOCK -# define eio__mlockall(a) ((errno = ENOSYS), -1) -#else - -static int -eio__mlockall (int flags) -{ - #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 7 - extern int mallopt (int, int); - mallopt (-6, 238); /* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=473812 */ - #endif - - if (EIO_MCL_CURRENT != MCL_CURRENT - || EIO_MCL_FUTURE != MCL_FUTURE) - { - flags = 0 - | (flags & EIO_MCL_CURRENT ? MCL_CURRENT : 0) - | (flags & EIO_MCL_FUTURE ? MCL_FUTURE : 0); - } - - return mlockall (flags); -} -#endif - -#if !_POSIX_MEMLOCK_RANGE -# define eio__mlock(a,b) ((errno = ENOSYS), -1) -#else - -static int -eio__mlock (void *addr, size_t length) -{ - eio_page_align (&addr, &length); - - return mlock (addr, length); -} - -#endif - -#if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO) -# define eio__msync(a,b,c) ((errno = ENOSYS), -1) -#else - -int -eio__msync (void *mem, size_t len, int flags) -{ - eio_page_align (&mem, &len); - - if (EIO_MS_ASYNC != MS_SYNC - || EIO_MS_INVALIDATE != MS_INVALIDATE - || EIO_MS_SYNC != MS_SYNC) - { - flags = 0 - | (flags & EIO_MS_ASYNC ? MS_ASYNC : 0) - | (flags & EIO_MS_INVALIDATE ? MS_INVALIDATE : 0) - | (flags & EIO_MS_SYNC ? MS_SYNC : 0); - } - - return msync (mem, len, flags); -} - -#endif - -int -eio__mtouch (void *mem, size_t len, int flags) -{ - eio_page_align (&mem, &len); - - { - intptr_t addr = (intptr_t)mem; - intptr_t end = addr + len; - intptr_t page = eio_pagesize (); - - if (addr < end) - if (flags & EIO_MT_MODIFY) /* modify */ - do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len); - else - do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len); - } - - return 0; -} - /*****************************************************************************/ #define ALLOC(len) \ @@ -1593,11 +2028,17 @@ X_THREAD_PROC (etp_proc) struct timespec ts; etp_worker *self = (etp_worker *)thr_arg; - /* try to distribute timeouts somewhat randomly */ +#if HAVE_PRCTL_SET_NAME + prctl (PR_SET_NAME, (unsigned long)"eio_thread", 0, 0, 0); +#endif + + /* try to distribute timeouts somewhat evenly */ ts.tv_nsec = ((unsigned long)self & 1023UL) * (1000000000UL / 1024UL); for (;;) { + ts.tv_sec = 0; + X_LOCK (reqlock); for (;;) @@ -1607,23 +2048,28 @@ X_THREAD_PROC (etp_proc) if (req) break; + if (ts.tv_sec == 1) /* no request, but timeout detected, let's quit */ + { + X_UNLOCK (reqlock); + X_LOCK (wrklock); + --started; + X_UNLOCK (wrklock); + goto quit; + } + ++idle; - ts.tv_sec = time (0) + IDLE_TIMEOUT; - if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) + if (idle <= max_idle) + /* we are allowed to idle, so do so without any timeout */ + X_COND_WAIT (reqwait, reqlock); + else { - if (idle > max_idle) - { - --idle; - X_UNLOCK (reqlock); - X_LOCK (wrklock); - --started; - X_UNLOCK (wrklock); - goto quit; - } + /* initialise timeout once */ + if (!ts.tv_sec) + ts.tv_sec = time (0) + idle_timeout; - /* we are allowed to idle, so do so without any timeout */ - X_COND_WAIT (reqwait, reqlock); + if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) + ts.tv_sec = 1; /* assuming this is not a value computed above.,.. */ } --idle; @@ -1636,8 +2082,7 @@ X_THREAD_PROC (etp_proc) if (req->type < 0) goto quit; - if (!EIO_CANCELLED (req)) - ETP_EXECUTE (self, req); + ETP_EXECUTE (self, req); X_LOCK (reslock); @@ -1662,12 +2107,18 @@ quit: /*****************************************************************************/ -int eio_init (void (*want_poll)(void), void (*done_poll)(void)) +int ecb_cold +eio_init (void (*want_poll)(void), void (*done_poll)(void)) { +#if !HAVE_PREADWRITE + X_MUTEX_CREATE (preadwritelock); +#endif + return etp_init (want_poll, done_poll); } -static void eio_api_destroy (eio_req *req) +ecb_inline void +eio_api_destroy (eio_req *req) { free (req); } @@ -1696,14 +2147,23 @@ static void eio_api_destroy (eio_req *req) return 0; \ } -static void eio_execute (etp_worker *self, eio_req *req) +static void +eio_execute (etp_worker *self, eio_req *req) { + if (ecb_expect_false (EIO_CANCELLED (req))) + { + req->result = -1; + req->errorno = ECANCELED; + return; + } + switch (req->type) { case EIO_READ: ALLOC (req->size); req->result = req->offs >= 0 ? pread (req->int1, req->ptr2, req->size, req->offs) : read (req->int1, req->ptr2, req->size); break; + case EIO_WRITE: #if defined (__APPLE__) pthread_mutex_lock (&apple_bug_writelock); @@ -1719,18 +2179,15 @@ static void eio_execute (etp_worker *self, eio_req *req) break; case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break; - case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size, self); break; + case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size); break; case EIO_STAT: ALLOC (sizeof (EIO_STRUCT_STAT)); req->result = stat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break; -#ifndef _WIN32 case EIO_LSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); req->result = lstat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break; -#endif case EIO_FSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); req->result = fstat (req->int1, (EIO_STRUCT_STAT *)req->ptr2); break; -#ifndef _WIN32 case EIO_STATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); req->result = statvfs (req->ptr1, (EIO_STRUCT_STATVFS *)req->ptr2); break; case EIO_FSTATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); @@ -1738,46 +2195,37 @@ static void eio_execute (etp_worker *self, eio_req *req) case EIO_CHOWN: req->result = chown (req->ptr1, req->int2, req->int3); break; case EIO_FCHOWN: req->result = fchown (req->int1, req->int2, req->int3); break; -#endif - case EIO_CHMOD: req->result = chmod (req->ptr1, (mode_t)req->int2); break; -#ifndef _WIN32 - case EIO_FCHMOD: req->result = fchmod (req->int1, (mode_t)req->int2); break; + case EIO_CHMOD: req->result = chmod (req->ptr1, (eio_mode_t)req->int2); break; + case EIO_FCHMOD: req->result = fchmod (req->int1, (eio_mode_t)req->int2); break; case EIO_TRUNCATE: req->result = truncate (req->ptr1, req->offs); break; -#endif case EIO_FTRUNCATE: req->result = ftruncate (req->int1, req->offs); break; - case EIO_OPEN: req->result = open (req->ptr1, req->int1, (mode_t)req->int2); break; + case EIO_OPEN: req->result = open (req->ptr1, req->int1, (eio_mode_t)req->int2); break; case EIO_CLOSE: req->result = close (req->int1); break; case EIO_DUP2: req->result = dup2 (req->int1, req->int2); break; case EIO_UNLINK: req->result = unlink (req->ptr1); break; case EIO_RMDIR: req->result = rmdir (req->ptr1); break; -#ifdef _WIN32 - case EIO_MKDIR: req->result = mkdir (req->ptr1); break; -#else - case EIO_MKDIR: req->result = mkdir (req->ptr1, (mode_t)req->int2); break; -#endif + case EIO_MKDIR: req->result = mkdir (req->ptr1, (eio_mode_t)req->int2); break; case EIO_RENAME: req->result = rename (req->ptr1, req->ptr2); break; -#ifndef _WIN32 case EIO_LINK: req->result = link (req->ptr1, req->ptr2); break; case EIO_SYMLINK: req->result = symlink (req->ptr1, req->ptr2); break; - case EIO_MKNOD: req->result = mknod (req->ptr1, (mode_t)req->int2, (dev_t)req->int3); break; -#endif + case EIO_MKNOD: req->result = mknod (req->ptr1, (eio_mode_t)req->int2, (dev_t)req->offs); break; + + case EIO_REALPATH: eio__realpath (req, self); break; -#ifndef _WIN32 case EIO_READLINK: ALLOC (PATH_MAX); req->result = readlink (req->ptr1, req->ptr2, PATH_MAX); break; -#endif -#ifndef _WIN32 case EIO_SYNC: req->result = 0; sync (); break; -#endif case EIO_FSYNC: req->result = fsync (req->int1); break; case EIO_FDATASYNC: req->result = fdatasync (req->int1); break; + case EIO_SYNCFS: req->result = eio__syncfs (req->int1); break; + case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break; case EIO_MSYNC: req->result = eio__msync (req->ptr2, req->size, req->int1); break; - case EIO_MTOUCH: req->result = eio__mtouch (req->ptr2, req->size, req->int1); break; + case EIO_MTOUCH: req->result = eio__mtouch (req); break; case EIO_MLOCK: req->result = eio__mlock (req->ptr2, req->size); break; case EIO_MLOCKALL: req->result = eio__mlockall (req->int1); break; - case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break; + case EIO_FALLOCATE: req->result = eio__fallocate (req->int1, req->int2, req->offs, req->size); break; case EIO_READDIR: eio__scandir (req, self); break; @@ -1828,7 +2276,7 @@ static void eio_execute (etp_worker *self, eio_req *req) break; case EIO_CUSTOM: - ((void (*)(eio_req *))req->feed) (req); + req->feed (req); break; default: @@ -1867,6 +2315,21 @@ eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, vo REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; } +eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) +{ + REQ (EIO_FDATASYNC); req->int1 = fd; SEND; +} + +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data) +{ + REQ (EIO_SYNCFS); req->int1 = fd; SEND; +} + +eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) +{ + REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; +} + eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) { REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; @@ -1882,14 +2345,9 @@ eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data) REQ (EIO_MLOCKALL); req->int1 = flags; SEND; } -eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data) { - REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; -} - -eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) -{ - REQ (EIO_FDATASYNC); req->int1 = fd; SEND; + REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND; } eio_req *eio_close (int fd, int pri, eio_cb cb, void *data) @@ -1932,12 +2390,12 @@ eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) REQ (EIO_FTRUNCATE); req->int1 = fd; req->offs = offset; SEND; } -eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_FCHMOD); req->int1 = fd; req->int2 = (long)mode; SEND; } -eio_req *eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) { REQ (EIO_FCHOWN); req->int1 = fd; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } @@ -1952,7 +2410,7 @@ eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, in REQ (EIO_SENDFILE); req->int1 = out_fd; req->int2 = in_fd; req->offs = in_offset; req->size = length; SEND; } -eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_OPEN); PATH; req->int1 = flags; req->int2 = (long)mode; SEND; } @@ -1967,17 +2425,17 @@ eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void REQ (EIO_TRUNCATE); PATH; req->offs = offset; SEND; } -eio_req *eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) { REQ (EIO_CHOWN); PATH; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } -eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_CHMOD); PATH; req->int2 = (long)mode; SEND; } -eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) { REQ (EIO_MKDIR); PATH; req->int2 = (long)mode; SEND; } @@ -1993,6 +2451,11 @@ eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data) return eio__1path (EIO_READLINK, path, pri, cb, data); } +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data) +{ + return eio__1path (EIO_REALPATH, path, pri, cb, data); +} + eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data) { return eio__1path (EIO_STAT, path, pri, cb, data); @@ -2023,9 +2486,9 @@ eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *dat REQ (EIO_READDIR); PATH; req->int1 = flags; SEND; } -eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) { - REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->int3 = (long)dev; SEND; + REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->offs = (off_t)dev; SEND; } static eio_req * @@ -2059,9 +2522,9 @@ eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, return eio__2path (EIO_RENAME, path, new_path, pri, cb, data); } -eio_req *eio_custom (eio_cb execute, int pri, eio_cb cb, void *data) +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data) { - REQ (EIO_CUSTOM); req->feed = (void (*)(eio_req *))execute; SEND; + REQ (EIO_CUSTOM); req->feed = execute; SEND; } #endif @@ -2080,7 +2543,8 @@ eio_req *eio_grp (eio_cb cb, void *data) /*****************************************************************************/ /* grp functions */ -void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit) +void +eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit) { grp->int2 = limit; grp->feed = feed; @@ -2088,14 +2552,16 @@ void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit) grp_try_feed (grp); } -void eio_grp_limit (eio_req *grp, int limit) +void +eio_grp_limit (eio_req *grp, int limit) { grp->int2 = limit; grp_try_feed (grp); } -void eio_grp_add (eio_req *grp, eio_req *req) +void +eio_grp_add (eio_req *grp, eio_req *req) { assert (("cannot add requests to IO::AIO::GRP after the group finished", grp->int1 != 2)); @@ -2116,18 +2582,9 @@ void eio_grp_add (eio_req *grp, eio_req *req) /*****************************************************************************/ /* misc garbage */ -ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count) +eio_ssize_t +eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count) { - etp_worker wrk; - ssize_t ret; - - wrk.dbuf = 0; - - ret = eio__sendfile (ofd, ifd, offset, count, &wrk); - - if (wrk.dbuf) - free (wrk.dbuf); - - return ret; + return eio__sendfile (ofd, ifd, offset, count); } diff --git a/src/rt/libuv/src/unix/eio/eio.pod b/src/rt/libuv/src/unix/eio/eio.pod new file mode 100644 index 00000000000..da5f33c01da --- /dev/null +++ b/src/rt/libuv/src/unix/eio/eio.pod @@ -0,0 +1,969 @@ +=head1 NAME + +libeio - truly asynchronous POSIX I/O + +=head1 SYNOPSIS + + #include + +=head1 DESCRIPTION + +The newest version of this document is also available as an html-formatted +web page you might find easier to navigate when reading it for the first +time: L. + +Note that this library is a by-product of the C perl +module, and many of the subtler points regarding requests lifetime +and so on are only documented in its documentation at the +moment: L. + +=head2 FEATURES + +This library provides fully asynchronous versions of most POSIX functions +dealing with I/O. Unlike most asynchronous libraries, this not only +includes C and C, but also C, C, C and +similar functions, as well as less rarely ones such as C, C +or C. + +It also offers wrappers around C (Solaris, Linux, HP-UX and +FreeBSD, with emulation on other platforms) and C (Linux, with +emulation elsewhere>). + +The goal is to enable you to write fully non-blocking programs. For +example, in a game server, you would not want to freeze for a few seconds +just because the server is running a backup and you happen to call +C. + +=head2 TIME REPRESENTATION + +Libeio represents time as a single floating point number, representing the +(fractional) number of seconds since the (POSIX) epoch (somewhere near +the beginning of 1970, details are complicated, don't ask). This type is +called C, but it is guaranteed to be of type C (or +better), so you can freely use C yourself. + +Unlike the name component C might indicate, it is also used for +time differences throughout libeio. + +=head2 FORK SUPPORT + +Usage of pthreads in a program changes the semantics of fork +considerably. Specifically, only async-safe functions can be called after +fork. Libeio uses pthreads, so this applies, and makes using fork hard for +anything but relatively fork + exec uses. + +This library only works in the process that initialised it: Forking is +fully supported, but using libeio in any other process than the one that +called C is not. + +You might get around by not I libeio before (or after) forking in +the parent, and using it in the child afterwards. You could also try to +call the L function again in the child, which will brutally +reinitialise all data structures, which isn't POSIX conformant, but +typically works. + +Otherwise, the only recommendation you should follow is: treat fork code +the same way you treat signal handlers, and only ever call C in +the process that uses it, and only once ever. + +=head1 INITIALISATION/INTEGRATION + +Before you can call any eio functions you first have to initialise the +library. The library integrates into any event loop, but can also be used +without one, including in polling mode. + +You have to provide the necessary glue yourself, however. + +=over 4 + +=item int eio_init (void (*want_poll)(void), void (*done_poll)(void)) + +This function initialises the library. On success it returns C<0>, on +failure it returns C<-1> and sets C appropriately. + +It accepts two function pointers specifying callbacks as argument, both of +which can be C<0>, in which case the callback isn't called. + +There is currently no way to change these callbacks later, or to +"uninitialise" the library again. + +=item want_poll callback + +The C callback is invoked whenever libeio wants attention (i.e. +it wants to be polled by calling C). It is "edge-triggered", +that is, it will only be called once when eio wants attention, until all +pending requests have been handled. + +This callback is called while locks are being held, so I. That includes +C. What you should do is notify some other thread, or wake up +your event loop, and then call C. + +=item done_poll callback + +This callback is invoked when libeio detects that all pending requests +have been handled. It is "edge-triggered", that is, it will only be +called once after C. To put it differently, C and +C are invoked in pairs: after C you have to call +C until either C indicates that everything has been +handled or C has been called, which signals the same. + +Note that C might return after C and C +have been called again, so watch out for races in your code. + +As with C, this callback is called while locks are being held, +so you I. + +=item int eio_poll () + +This function has to be called whenever there are pending requests that +need finishing. You usually call this after C has indicated +that you should do so, but you can also call this function regularly to +poll for new results. + +If any request invocation returns a non-zero value, then C +immediately returns with that value as return value. + +Otherwise, if all requests could be handled, it returns C<0>. If for some +reason not all requests have been handled, i.e. some are still pending, it +returns C<-1>. + +=back + +For libev, you would typically use an C watcher: the +C callback would invoke C to wake up the event +loop. Inside the callback set for the watcher, one would call C. + +If C is configured to not handle all results in one go +(i.e. it returns C<-1>) then you should start an idle watcher that calls +C until it returns something C. + +A full-featured connector between libeio and libev would look as follows +(if C is handling all requests, it can of course be simplified a +lot by removing the idle watcher logic): + + static struct ev_loop *loop; + static ev_idle repeat_watcher; + static ev_async ready_watcher; + + /* idle watcher callback, only used when eio_poll */ + /* didn't handle all results in one call */ + static void + repeat (EV_P_ ev_idle *w, int revents) + { + if (eio_poll () != -1) + ev_idle_stop (EV_A_ w); + } + + /* eio has some results, process them */ + static void + ready (EV_P_ ev_async *w, int revents) + { + if (eio_poll () == -1) + ev_idle_start (EV_A_ &repeat_watcher); + } + + /* wake up the event loop */ + static void + want_poll (void) + { + ev_async_send (loop, &ready_watcher) + } + + void + my_init_eio () + { + loop = EV_DEFAULT; + + ev_idle_init (&repeat_watcher, repeat); + ev_async_init (&ready_watcher, ready); + ev_async_start (loop &watcher); + + eio_init (want_poll, 0); + } + +For most other event loops, you would typically use a pipe - the event +loop should be told to wait for read readiness on the read end. In +C you would write a single byte, in C you would try +to read that byte, and in the callback for the read end, you would call +C. + +You don't have to take special care in the case C doesn't handle +all requests, as the done callback will not be invoked, so the event loop +will still signal readiness for the pipe until I results have been +processed. + + +=head1 HIGH LEVEL REQUEST API + +Libeio has both a high-level API, which consists of calling a request +function with a callback to be called on completion, and a low-level API +where you fill out request structures and submit them. + +This section describes the high-level API. + +=head2 REQUEST SUBMISSION AND RESULT PROCESSING + +You submit a request by calling the relevant C function with the +required parameters, a callback of type C +(called C below) and a freely usable C argument. + +The return value will either be 0, in case something went really wrong +(which can basically only happen on very fatal errors, such as C +returning 0, which is rather unlikely), or a pointer to the newly-created +and submitted C. + +The callback will be called with an C which contains the +results of the request. The members you can access inside that structure +vary from request to request, except for: + +=over 4 + +=item C + +This contains the result value from the call (usually the same as the +syscall of the same name). + +=item C + +This contains the value of C after the call. + +=item C + +The C member simply stores the value of the C argument. + +=back + +The return value of the callback is normally C<0>, which tells libeio to +continue normally. If a callback returns a nonzero value, libeio will +stop processing results (in C) and will return the value to its +caller. + +Memory areas passed to libeio must stay valid as long as a request +executes, with the exception of paths, which are being copied +internally. Any memory libeio itself allocates will be freed after the +finish callback has been called. If you want to manage all memory passed +to libeio yourself you can use the low-level API. + +For example, to open a file, you could do this: + + static int + file_open_done (eio_req *req) + { + if (req->result < 0) + { + /* open() returned -1 */ + errno = req->errorno; + perror ("open"); + } + else + { + int fd = req->result; + /* now we have the new fd in fd */ + } + + return 0; + } + + /* the first three arguments are passed to open(2) */ + /* the remaining are priority, callback and data */ + if (!eio_open ("/etc/passwd", O_RDONLY, 0, 0, file_open_done, 0)) + abort (); /* something went wrong, we will all die!!! */ + +Note that you additionally need to call C when the C +indicates that requests are ready to be processed. + +=head2 CANCELLING REQUESTS + +Sometimes the need for a request goes away before the request is +finished. In that case, one can cancel the request by a call to +C: + +=over 4 + +=item eio_cancel (eio_req *req) + +Cancel the request (and all its subrequests). If the request is currently +executing it might still continue to execute, and in other cases it might +still take a while till the request is cancelled. + +Even if cancelled, the finish callback will still be invoked - the +callbacks of all cancellable requests need to check whether the request +has been cancelled by calling C: + + static int + my_eio_cb (eio_req *req) + { + if (EIO_CANCELLED (req)) + return 0; + } + +In addition, cancelled requests will I have C<< req->result >> +set to C<-1> and C to C, or I they were +successfully executed, despite being cancelled (e.g. when they have +already been executed at the time they were cancelled). + +C is still true for requests that have successfully +executed, as long as C was called on them at some point. + +=back + +=head2 AVAILABLE REQUESTS + +The following request functions are available. I of them return the +C on success and C<0> on failure, and I of them have the +same three trailing arguments: C, C and C. The C is +mandatory, but in most cases, you pass in C<0> as C and C<0> or some +custom data value as C. + +=head3 POSIX API WRAPPERS + +These requests simply wrap the POSIX call of the same name, with the same +arguments. If a function is not implemented by the OS and cannot be emulated +in some way, then all of these return C<-1> and set C to C. + +=over 4 + +=item eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) + +=item eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) + +=item eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_rmdir (const char *path, int pri, eio_cb cb, void *data) + +=item eio_unlink (const char *path, int pri, eio_cb cb, void *data) + +=item eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) + +=item eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) + +=item eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data) + +=item eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data) + +=item eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) + +=item eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) + +=item eio_close (int fd, int pri, eio_cb cb, void *data) + +=item eio_sync (int pri, eio_cb cb, void *data) + +=item eio_fsync (int fd, int pri, eio_cb cb, void *data) + +=item eio_fdatasync (int fd, int pri, eio_cb cb, void *data) + +=item eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) + +=item eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) + +=item eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) + +=item eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) + +=item eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) + +These have the same semantics as the syscall of the same name, their +return value is available as C<< req->result >> later. + +=item eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) + +=item eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) + +These two requests are called C and C, but actually wrap +C and C. On systems that lack these calls (such as cygwin), +libeio uses lseek/read_or_write/lseek and a mutex to serialise the +requests, so all these requests run serially and do not disturb each +other. However, they still disturb the file offset while they run, so it's +not safe to call these functions concurrently with non-libeio functions on +the same fd on these systems. + +Not surprisingly, pread and pwrite are not thread-safe on Darwin (OS/X), +so it is advised not to submit multiple requests on the same fd on this +horrible pile of garbage. + +=item eio_mlockall (int flags, int pri, eio_cb cb, void *data) + +Like C, but the flag value constants are called +C and C. + +=item eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) + +Just like msync, except that the flag values are called C, +C and C. + +=item eio_readlink (const char *path, int pri, eio_cb cb, void *data) + +If successful, the path read by C can be accessed via C<< +req->ptr2 >> and is I null-terminated, with the length specified as +C<< req->result >>. + + if (req->result >= 0) + { + char *target = strndup ((char *)req->ptr2, req->result); + + free (target); + } + +=item eio_realpath (const char *path, int pri, eio_cb cb, void *data) + +Similar to the realpath libc function, but unlike that one, C<< +req->result >> is C<-1> on failure. On success, the result is the length +of the returned path in C (which is I 0-terminated) - this is +similar to readlink. + +=item eio_stat (const char *path, int pri, eio_cb cb, void *data) + +=item eio_lstat (const char *path, int pri, eio_cb cb, void *data) + +=item eio_fstat (int fd, int pri, eio_cb cb, void *data) + +Stats a file - if C<< req->result >> indicates success, then you can +access the C-like structure via C<< req->ptr2 >>: + + EIO_STRUCT_STAT *statdata = (EIO_STRUCT_STAT *)req->ptr2; + +=item eio_statvfs (const char *path, int pri, eio_cb cb, void *data) + +=item eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) + +Stats a filesystem - if C<< req->result >> indicates success, then you can +access the C-like structure via C<< req->ptr2 >>: + + EIO_STRUCT_STATVFS *statdata = (EIO_STRUCT_STATVFS *)req->ptr2; + +=back + +=head3 READING DIRECTORIES + +Reading directories sounds simple, but can be rather demanding, especially +if you want to do stuff such as traversing a directory hierarchy or +processing all files in a directory. Libeio can assist these complex tasks +with it's C call. + +=over 4 + +=item eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data) + +This is a very complex call. It basically reads through a whole directory +(via the C, C and C calls) and returns either +the names or an array of C, depending on the C +argument. + +The C<< req->result >> indicates either the number of files found, or +C<-1> on error. On success, null-terminated names can be found as C<< req->ptr2 >>, +and C, if requested by C, can be found via C<< +req->ptr1 >>. + +Here is an example that prints all the names: + + int i; + char *names = (char *)req->ptr2; + + for (i = 0; i < req->result; ++i) + { + printf ("name #%d: %s\n", i, names); + + /* move to next name */ + names += strlen (names) + 1; + } + +Pseudo-entries such as F<.> and F<..> are never returned by C. + +C can be any combination of: + +=over 4 + +=item EIO_READDIR_DENTS + +If this flag is specified, then, in addition to the names in C, +also an array of C is returned, in C. A C looks like this: + + struct eio_dirent + { + int nameofs; /* offset of null-terminated name string in (char *)req->ptr2 */ + unsigned short namelen; /* size of filename without trailing 0 */ + unsigned char type; /* one of EIO_DT_* */ + signed char score; /* internal use */ + ino_t inode; /* the inode number, if available, otherwise unspecified */ + }; + +The only members you normally would access are C, which is the +byte-offset from C to the start of the name, C and C. + +C can be one of: + +C - if the type is not known (very common) and you have to C +the name yourself if you need to know, +one of the "standard" POSIX file types (C, C, C, +C, C, C, C) +or some OS-specific type (currently +C - multiplexed char device (v7+coherent), +C - xenix special named file, +C - multiplexed block device (v7+coherent), +C - HP-UX network special, +C - VxFS compressed, +C - solaris door, or +C). + +This example prints all names and their type: + + int i; + struct eio_dirent *ents = (struct eio_dirent *)req->ptr1; + char *names = (char *)req->ptr2; + + for (i = 0; i < req->result; ++i) + { + struct eio_dirent *ent = ents + i; + char *name = names + ent->nameofs; + + printf ("name #%d: %s (type %d)\n", i, name, ent->type); + } + +=item EIO_READDIR_DIRS_FIRST + +When this flag is specified, then the names will be returned in an order +where likely directories come first, in optimal C order. This is +useful when you need to quickly find directories, or you want to find all +directories while avoiding to stat() each entry. + +If the system returns type information in readdir, then this is used +to find directories directly. Otherwise, likely directories are names +beginning with ".", or otherwise names with no dots, of which names with +short names are tried first. + +=item EIO_READDIR_STAT_ORDER + +When this flag is specified, then the names will be returned in an order +suitable for stat()'ing each one. That is, when you plan to stat() +all files in the given directory, then the returned order will likely +be fastest. + +If both this flag and C are specified, then the +likely directories come first, resulting in a less optimal stat order. + +=item EIO_READDIR_FOUND_UNKNOWN + +This flag should not be specified when calling C. Instead, +it is being set by C (you can access the C via C<< +req->int1 >>, when any of the C's found were C. The +absence of this flag therefore indicates that all C's are known, +which can be used to speed up some algorithms. + +A typical use case would be to identify all subdirectories within a +directory - you would ask C for C. If +then this flag is I set, then all the entries at the beginning of the +returned array of type C are the directories. Otherwise, you +should start C'ing the entries starting at the beginning of the +array, stopping as soon as you found all directories (the count can be +deduced by the link count of the directory). + +=back + +=back + +=head3 OS-SPECIFIC CALL WRAPPERS + +These wrap OS-specific calls (usually Linux ones), and might or might not +be emulated on other operating systems. Calls that are not emulated will +return C<-1> and set C to C. + +=over 4 + +=item eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) + +Wraps the C syscall. The arguments follow the Linux version, but +libeio supports and will use similar calls on FreeBSD, HP/UX, Solaris and +Darwin. + +If the OS doesn't support some sendfile-like call, or the call fails, +indicating support for the given file descriptor type (for example, +Linux's sendfile might not support file to file copies), then libeio will +emulate the call in userspace, so there are almost no limitations on its +use. + +=item eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) + +Calls C. If the syscall is missing, then the call is +emulated by simply reading the data (currently in 64kiB chunks). + +=item eio_syncfs (int fd, int pri, eio_cb cb, void *data) + +Calls Linux' C syscall, if available. Returns C<-1> and sets +C to C if the call is missing I, +if the C is C<< >= 0 >>, so you can probe for the availability of the +syscall with a negative C argument and checking for C<-1/ENOSYS>. + +=item eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) + +Calls C. If the syscall is missing, then this is the same +as calling C. + +Flags can be any combination of C, +C and C. + +=item eio_fallocate (int fd, int mode, off_t offset, off_t len, int pri, eio_cb cb, void *data) + +Calls C (note: I C!). If the syscall is +missing, then it returns failure and sets C to C. + +The C argument can be C<0> (for behaviour similar to +C), or C, which keeps the size +of the file unchanged (but still preallocates space beyond end of file). + +=back + +=head3 LIBEIO-SPECIFIC REQUESTS + +These requests are specific to libeio and do not correspond to any OS call. + +=over 4 + +=item eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) + +Reads (C) or modifies (C as parameter and is expected to read +and modify any request-specific members. Specifically, it should set C<< +req->result >> to the result value, just like other requests. + +Here is an example that simply calls C, like C, but it +uses the C member as filename and uses a hardcoded C. If +you want to pass more/other parameters, you either need to pass some +struct or so via C or provide your own wrapper using the low-level +API. + + static int + my_open_done (eio_req *req) + { + int fd = req->result; + + return 0; + } + + static void + my_open (eio_req *req) + { + req->result = open (req->data, O_RDONLY); + } + + eio_custom (my_open, 0, my_open_done, "/etc/passwd"); + +=item eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data) + +This is a request that takes C seconds to execute, but otherwise +does nothing - it simply puts one of the worker threads to sleep for this +long. + +This request can be used to artificially increase load, e.g. for debugging +or benchmarking reasons. + +=item eio_nop (int pri, eio_cb cb, void *data) + +This request does nothing, except go through the whole request cycle. This +can be used to measure latency or in some cases to simplify code, but is +not really of much use. + +=back + +=head3 GROUPING AND LIMITING REQUESTS + +There is one more rather special request, C. It is a very special +aio request: Instead of doing something, it is a container for other eio +requests. + +There are two primary use cases for this: a) bundle many requests into a +single, composite, request with a definite callback and the ability to +cancel the whole request with its subrequests and b) limiting the number +of "active" requests. + +Further below you will find more discussion of these topics - first +follows the reference section detailing the request generator and other +methods. + +=over 4 + +=item eio_req *grp = eio_grp (eio_cb cb, void *data) + +Creates, submits and returns a group request. Note that it doesn't have a +priority, unlike all other requests. + +=item eio_grp_add (eio_req *grp, eio_req *req) + +Adds a request to the request group. + +=item eio_grp_cancel (eio_req *grp) + +Cancels all requests I the group, but I the group request +itself. You can cancel the group request I all subrequests via a +normal C call. + +=back + +=head4 GROUP REQUEST LIFETIME + +Left alone, a group request will instantly move to the pending state and +will be finished at the next call of C. + +The usefulness stems from the fact that, if a subrequest is added to a +group I a call to C, via C, then the group +will not finish until all the subrequests have finished. + +So the usage cycle of a group request is like this: after it is created, +you normally instantly add a subrequest. If none is added, the group +request will finish on it's own. As long as subrequests are added before +the group request is finished it will be kept from finishing, that is the +callbacks of any subrequests can, in turn, add more requests to the group, +and as long as any requests are active, the group request itself will not +finish. + +=head4 CREATING COMPOSITE REQUESTS + +Imagine you wanted to create an C request that opens a file, +reads it and closes it. This means it has to execute at least three eio +requests, but for various reasons it might be nice if that request looked +like any other eio request. + +This can be done with groups: + +=over 4 + +=item 1) create the request object + +Create a group that contains all further requests. This is the request you +can return as "the load request". + +=item 2) open the file, maybe + +Next, open the file with C and add the request to the group +request and you are finished setting up the request. + +If, for some reason, you cannot C (path is a null ptr?) you +can set C<< grp->result >> to C<-1> to signal an error and let the group +request finish on its own. + +=item 3) open callback adds more requests + +In the open callback, if the open was not successful, copy C<< +req->errorno >> to C<< grp->errorno >> and set C<< grp->errorno >> to +C<-1> to signal an error. + +Otherwise, malloc some memory or so and issue a read request, adding the +read request to the group. + +=item 4) continue issuing requests till finished + +In the real callback, check for errors and possibly continue with +C or any other eio request in the same way. + +As soon as no new requests are added the group request will finish. Make +sure you I set C<< grp->result >> to some sensible value. + +=back + +=head4 REQUEST LIMITING + + +#TODO + +void eio_grp_limit (eio_req *grp, int limit); + + +=back + + +=head1 LOW LEVEL REQUEST API + +#TODO + + +=head1 ANATOMY AND LIFETIME OF AN EIO REQUEST + +A request is represented by a structure of type C. To initialise +it, clear it to all zero bytes: + + eio_req req; + + memset (&req, 0, sizeof (req)); + +A more common way to initialise a new C is to use C: + + eio_req *req = calloc (1, sizeof (*req)); + +In either case, libeio neither allocates, initialises or frees the +C structure for you - it merely uses it. + +zero + +#TODO + +=head2 CONFIGURATION + +The functions in this section can sometimes be useful, but the default +configuration will do in most case, so you should skip this section on +first reading. + +=over 4 + +=item eio_set_max_poll_time (eio_tstamp nseconds) + +This causes C to return after it has detected that it was +running for C seconds or longer (this number can be fractional). + +This can be used to limit the amount of time spent handling eio requests, +for example, in interactive programs, you might want to limit this time to +C<0.01> seconds or so. + +Note that: + +=over 4 + +=item a) libeio doesn't know how long your request callbacks take, so the +time spent in C is up to one callback invocation longer then +this interval. + +=item b) this is implemented by calling C after each +request, which can be costly. + +=item c) at least one request will be handled. + +=back + +=item eio_set_max_poll_reqs (unsigned int nreqs) + +When C is non-zero, then C will not handle more than +C requests per invocation. This is a less costly way to limit the +amount of work done by C then setting a time limit. + +If you know your callbacks are generally fast, you could use this to +encourage interactiveness in your programs by setting it to C<10>, C<100> +or even C<1000>. + +=item eio_set_min_parallel (unsigned int nthreads) + +Make sure libeio can handle at least this many requests in parallel. It +might be able handle more. + +=item eio_set_max_parallel (unsigned int nthreads) + +Set the maximum number of threads that libeio will spawn. + +=item eio_set_max_idle (unsigned int nthreads) + +Libeio uses threads internally to handle most requests, and will start and stop threads on demand. + +This call can be used to limit the number of idle threads (threads without +work to do): libeio will keep some threads idle in preparation for more +requests, but never longer than C threads. + +In addition to this, libeio will also stop threads when they are idle for +a few seconds, regardless of this setting. + +=item unsigned int eio_nthreads () + +Return the number of worker threads currently running. + +=item unsigned int eio_nreqs () + +Return the number of requests currently handled by libeio. This is the +total number of requests that have been submitted to libeio, but not yet +destroyed. + +=item unsigned int eio_nready () + +Returns the number of ready requests, i.e. requests that have been +submitted but have not yet entered the execution phase. + +=item unsigned int eio_npending () + +Returns the number of pending requests, i.e. requests that have been +executed and have results, but have not been finished yet by a call to +C). + +=back + +=head1 EMBEDDING + +Libeio can be embedded directly into programs. This functionality is not +documented and not (yet) officially supported. + +Note that, when including C, you are responsible for defining +the compilation environment (C<_LARGEFILE_SOURCE>, C<_GNU_SOURCE> etc.). + +If you need to know how, check the C perl module, which does +exactly that. + + +=head1 COMPILETIME CONFIGURATION + +These symbols, if used, must be defined when compiling F. + +=over 4 + +=item EIO_STACKSIZE + +This symbol governs the stack size for each eio thread. Libeio itself +was written to use very little stackspace, but when using C +requests, you might want to increase this. + +If this symbol is undefined (the default) then libeio will use its default +stack size (C currently). If it is defined, but +C<0>, then the default operating system stack size will be used. In all +other cases, the value must be an expression that evaluates to the desired +stack size. + +=back + + +=head1 PORTABILITY REQUIREMENTS + +In addition to a working ISO-C implementation, libeio relies on a few +additional extensions: + +=over 4 + +=item POSIX threads + +To be portable, this module uses threads, specifically, the POSIX threads +library must be available (and working, which partially excludes many xBSD +systems, where C is buggy). + +=item POSIX-compatible filesystem API + +This is actually a harder portability requirement: The libeio API is quite +demanding regarding POSIX API calls (symlinks, user/group management +etc.). + +=item C must hold a time value in seconds with enough accuracy + +The type C is used to represent timestamps. It is required to +have at least 51 bits of mantissa (and 9 bits of exponent), which is good +enough for at least into the year 4000. This requirement is fulfilled by +implementations implementing IEEE 754 (basically all existing ones). + +=back + +If you know of other additional requirements drop me a note. + + +=head1 AUTHOR + +Marc Lehmann . + diff --git a/src/rt/libuv/src/eio/libeio.m4 b/src/rt/libuv/src/unix/eio/libeio.m4 similarity index 78% rename from src/rt/libuv/src/eio/libeio.m4 rename to src/rt/libuv/src/unix/eio/libeio.m4 index 5302cfcc91a..59151f5348a 100644 --- a/src/rt/libuv/src/eio/libeio.m4 +++ b/src/rt/libuv/src/unix/eio/libeio.m4 @@ -1,3 +1,7 @@ +dnl openbsd in it's neverending brokenness requires stdint.h for intptr_t, +dnl but that header isn't very portable... +AC_CHECK_HEADERS([stdint.h sys/syscall.h sys/prctl.h]) + AC_SEARCH_LIBS( pthread_create, [pthread pthreads pthreadVC2], @@ -119,6 +123,41 @@ int main (void) ],ac_cv_sync_file_range=yes,ac_cv_sync_file_range=no)]) test $ac_cv_sync_file_range = yes && AC_DEFINE(HAVE_SYNC_FILE_RANGE, 1, sync_file_range(2) is available) +AC_CACHE_CHECK(for fallocate, ac_cv_fallocate, [AC_LINK_IFELSE([ +#include +int main (void) +{ + int fd = 0; + int mode = 0; + off_t offset = 1; + off_t len = 1; + int res; + res = fallocate (fd, mode, offset, len); + return 0; +} +],ac_cv_fallocate=yes,ac_cv_fallocate=no)]) +test $ac_cv_fallocate = yes && AC_DEFINE(HAVE_FALLOCATE, 1, fallocate(2) is available) + +AC_CACHE_CHECK(for sys_syncfs, ac_cv_sys_syncfs, [AC_LINK_IFELSE([ +#include +#include +int main (void) +{ + int res = syscall (__NR_syncfs, (int)0); +} +],ac_cv_sys_syncfs=yes,ac_cv_sys_syncfs=no)]) +test $ac_cv_sys_syncfs = yes && AC_DEFINE(HAVE_SYS_SYNCFS, 1, syscall(__NR_syncfs) is available) + +AC_CACHE_CHECK(for prctl_set_name, ac_cv_prctl_set_name, [AC_LINK_IFELSE([ +#include +int main (void) +{ + char name[] = "test123"; + int res = prctl (PR_SET_NAME, (unsigned long)name, 0, 0, 0); +} +],ac_cv_prctl_set_name=yes,ac_cv_prctl_set_name=no)]) +test $ac_cv_prctl_set_name = yes && AC_DEFINE(HAVE_PRCTL_SET_NAME, 1, prctl(PR_SET_NAME) is available) + dnl ############################################################################# dnl # these checks exist for the benefit of IO::AIO diff --git a/src/rt/libuv/src/eio/xthread.h b/src/rt/libuv/src/unix/eio/xthread.h similarity index 76% rename from src/rt/libuv/src/eio/xthread.h rename to src/rt/libuv/src/unix/eio/xthread.h index 01e2933507d..7184c7bb73f 100644 --- a/src/rt/libuv/src/eio/xthread.h +++ b/src/rt/libuv/src/unix/eio/xthread.h @@ -2,7 +2,7 @@ #define XTHREAD_H_ /* whether word reads are potentially non-atomic. - * this is conservatice, likely most arches this runs + * this is conservative, likely most arches this runs * on have atomic word read/writes. */ #ifndef WORDACCESS_UNSAFE @@ -17,14 +17,6 @@ #ifdef _WIN32 -#ifndef __MINGW32__ -typedef int ssize_t -#endif - -#define NTDDI_VERSION NTDDI_WIN2K // needed to get win2000 api calls -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x400 -#endif #include //D #include #include @@ -34,18 +26,20 @@ typedef int ssize_t #include #include #define sigset_t int +#define sigfillset(a) #define pthread_sigmask(a,b,c) #define sigaddset(a,b) #define sigemptyset(s) -#define sigfillset(s) typedef pthread_mutex_t xmutex_t; #define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +#define X_MUTEX_CREATE(mutex) pthread_mutex_init (&(mutex), 0) #define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) #define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) typedef pthread_cond_t xcond_t; #define X_COND_INIT PTHREAD_COND_INITIALIZER +#define X_COND_CREATE(cond) pthread_cond_init (&(cond), 0) #define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) #define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) #define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) @@ -100,18 +94,27 @@ thread_create (xthread_t *tid, void *(*proc)(void *), void *arg) typedef pthread_mutex_t xmutex_t; #if __linux && defined (PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) -# define X_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +# define X_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +# define X_MUTEX_CREATE(mutex) \ + do { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init (&attr); \ + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ADAPTIVE_NP); \ + pthread_mutex_init (&(mutex), &attr); \ + } while (0) #else -# define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +# define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +# define X_MUTEX_CREATE(mutex) pthread_mutex_init (&(mutex), 0) #endif -#define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) -#define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) +#define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) +#define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) typedef pthread_cond_t xcond_t; -#define X_COND_INIT PTHREAD_COND_INITIALIZER -#define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) -#define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) -#define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) +#define X_COND_INIT PTHREAD_COND_INITIALIZER +#define X_COND_CREATE(cond) pthread_cond_init (&(cond), 0) +#define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) +#define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) +#define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) typedef pthread_t xthread_t; #define X_THREAD_PROC(name) static void *name (void *thr_arg) @@ -122,8 +125,8 @@ typedef pthread_t xthread_t; # define PTHREAD_STACK_MIN 0 #endif -#ifndef X_STACKSIZE -# define X_STACKSIZE sizeof (long) * 4096 +#ifndef XTHREAD_STACKSIZE +# define XTHREAD_STACKSIZE sizeof (void *) * 4096 #endif static int diff --git a/src/rt/libuv/src/unix/error.c b/src/rt/libuv/src/unix/error.c new file mode 100644 index 00000000000..3520eec719b --- /dev/null +++ b/src/rt/libuv/src/unix/error.c @@ -0,0 +1,110 @@ +/* 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. + */ + +/* + * TODO Share this code with Windows. + * See https://github.com/joyent/libuv/issues/76 + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include + + +/* TODO Expose callback to user to handle fatal error like V8 does. */ +void uv_fatal_error(const int errorno, const char* syscall) { + char* buf = NULL; + const char* errmsg; + + if (buf) { + errmsg = buf; + } else { + errmsg = "Unknown error"; + } + + if (syscall) { + fprintf(stderr, "\nlibuv fatal error. %s: (%d) %s\n", syscall, errorno, + errmsg); + } else { + fprintf(stderr, "\nlibuv fatal error. (%d) %s\n", errorno, errmsg); + } + + abort(); +} + + +uv_err_t uv_last_error(uv_loop_t* loop) { + return loop->last_err; +} + + +char* uv_strerror(uv_err_t err) { + return strerror(err.sys_errno_); +} + + +uv_err_code uv_translate_sys_error(int sys_errno) { + switch (sys_errno) { + case 0: return UV_OK; + case ENOENT: return UV_ENOENT; + case EACCES: return UV_EACCESS; + case EBADF: return UV_EBADF; + case EPIPE: return UV_EPIPE; + case EAGAIN: return UV_EAGAIN; + case ECONNRESET: return UV_ECONNRESET; + case EFAULT: return UV_EFAULT; + case EMFILE: return UV_EMFILE; + case EMSGSIZE: return UV_EMSGSIZE; + case EINVAL: return UV_EINVAL; + case ECONNREFUSED: return UV_ECONNREFUSED; + case EADDRINUSE: return UV_EADDRINUSE; + case EADDRNOTAVAIL: return UV_EADDRNOTAVAIL; + case ENOTCONN: return UV_ENOTCONN; + case EEXIST: return UV_EEXIST; + default: return UV_UNKNOWN; + } + + assert(0 && "unreachable"); + return -1; +} + + +uv_err_t uv_err_new_artificial(uv_loop_t* loop, int code) { + uv_err_t err; + err.sys_errno_ = 0; + err.code = code; + loop->last_err = err; + return err; +} + + +uv_err_t uv_err_new(uv_loop_t* loop, int sys_error) { + uv_err_t err; + err.sys_errno_ = sys_error; + err.code = uv_translate_sys_error(sys_error); + loop->last_err = err; + return err; +} diff --git a/src/rt/libuv/src/ev/Changes b/src/rt/libuv/src/unix/ev/Changes similarity index 100% rename from src/rt/libuv/src/ev/Changes rename to src/rt/libuv/src/unix/ev/Changes diff --git a/src/rt/libuv/src/ev/LICENSE b/src/rt/libuv/src/unix/ev/LICENSE similarity index 100% rename from src/rt/libuv/src/ev/LICENSE rename to src/rt/libuv/src/unix/ev/LICENSE diff --git a/src/rt/libuv/src/ev/Makefile.am b/src/rt/libuv/src/unix/ev/Makefile.am similarity index 100% rename from src/rt/libuv/src/ev/Makefile.am rename to src/rt/libuv/src/unix/ev/Makefile.am diff --git a/src/rt/libuv/src/ev/Makefile.in b/src/rt/libuv/src/unix/ev/Makefile.in similarity index 100% rename from src/rt/libuv/src/ev/Makefile.in rename to src/rt/libuv/src/unix/ev/Makefile.in diff --git a/src/rt/libuv/src/ev/README b/src/rt/libuv/src/unix/ev/README similarity index 100% rename from src/rt/libuv/src/ev/README rename to src/rt/libuv/src/unix/ev/README diff --git a/src/rt/libuv/src/ev/aclocal.m4 b/src/rt/libuv/src/unix/ev/aclocal.m4 similarity index 100% rename from src/rt/libuv/src/ev/aclocal.m4 rename to src/rt/libuv/src/unix/ev/aclocal.m4 diff --git a/src/rt/libuv/src/ev/autogen.sh b/src/rt/libuv/src/unix/ev/autogen.sh similarity index 100% rename from src/rt/libuv/src/ev/autogen.sh rename to src/rt/libuv/src/unix/ev/autogen.sh diff --git a/src/rt/libuv/src/ev/config.guess b/src/rt/libuv/src/unix/ev/config.guess similarity index 100% rename from src/rt/libuv/src/ev/config.guess rename to src/rt/libuv/src/unix/ev/config.guess diff --git a/src/rt/libuv/src/ev/config.h.in b/src/rt/libuv/src/unix/ev/config.h.in similarity index 100% rename from src/rt/libuv/src/ev/config.h.in rename to src/rt/libuv/src/unix/ev/config.h.in diff --git a/src/rt/libuv/src/ev/config.sub b/src/rt/libuv/src/unix/ev/config.sub similarity index 100% rename from src/rt/libuv/src/ev/config.sub rename to src/rt/libuv/src/unix/ev/config.sub diff --git a/src/rt/libuv/src/ev/config_cygwin.h b/src/rt/libuv/src/unix/ev/config_cygwin.h similarity index 100% rename from src/rt/libuv/src/ev/config_cygwin.h rename to src/rt/libuv/src/unix/ev/config_cygwin.h diff --git a/src/rt/libuv/src/ev/config_darwin.h b/src/rt/libuv/src/unix/ev/config_darwin.h similarity index 100% rename from src/rt/libuv/src/ev/config_darwin.h rename to src/rt/libuv/src/unix/ev/config_darwin.h diff --git a/src/rt/libuv/src/ev/config_freebsd.h b/src/rt/libuv/src/unix/ev/config_freebsd.h similarity index 100% rename from src/rt/libuv/src/ev/config_freebsd.h rename to src/rt/libuv/src/unix/ev/config_freebsd.h diff --git a/src/rt/libuv/src/ev/config_linux.h b/src/rt/libuv/src/unix/ev/config_linux.h similarity index 80% rename from src/rt/libuv/src/ev/config_linux.h rename to src/rt/libuv/src/unix/ev/config_linux.h index b147b591c43..a13b179f280 100644 --- a/src/rt/libuv/src/ev/config_linux.h +++ b/src/rt/libuv/src/unix/ev/config_linux.h @@ -2,12 +2,7 @@ /* config.h.in. Generated from configure.ac by autoheader. */ #include - -#define LINUX_VERSION_CODE_FOR(major, minor, patch) \ - (((major & 255) << 16) | ((minor & 255) << 8) | (patch & 255)) - -#define LINUX_VERSION_AT_LEAST(major, minor, patch) \ - (LINUX_VERSION_CODE >= LINUX_VERSION_CODE_FOR(major, minor, patch)) +#include /* Define to 1 if you have the `clock_gettime' function. */ /* #undef HAVE_CLOCK_GETTIME */ @@ -18,14 +13,26 @@ /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 -/* Define to 1 if you have the `epoll_ctl' function. */ +/* epoll_ctl(2) is available if kernel >= 2.6.9 and glibc >= 2.4 */ +#if LINUX_VERSION_CODE >= 0x020609 && __GLIBC_PREREQ(2, 4) #define HAVE_EPOLL_CTL 1 +#else +#define HAVE_EPOLL_CTL 0 +#endif -/* Define to 1 if you have the `eventfd' function. */ -#define HAVE_EVENTFD LINUX_VERSION_AT_LEAST(2, 6, 22) +/* eventfd(2) is available if kernel >= 2.6.22 and glibc >= 2.8 */ +#if LINUX_VERSION_CODE >= 0x020616 && __GLIBC_PREREQ(2, 8) +#define HAVE_EVENTFD 1 +#else +#define HAVE_EVENTFD 0 +#endif -/* Define to 1 if you have the `inotify_init' function. */ -#define HAVE_INOTIFY_INIT LINUX_VERSION_AT_LEAST(2, 6, 13) +/* inotify_init(2) is available if kernel >= 2.6.13 and glibc >= 2.4 */ +#if LINUX_VERSION_CODE >= 0x02060d && __GLIBC_PREREQ(2, 4) +#define HAVE_INOTIFY_INIT 1 +#else +#define HAVE_INOTIFY_INIT 0 +#endif /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 @@ -60,8 +67,12 @@ /* Define to 1 if you have the `select' function. */ #define HAVE_SELECT 1 -/* Define to 1 if you have the `signalfd' function. */ -#define HAVE_SIGNALFD LINUX_VERSION_AT_LEAST(2, 6, 22) +/* signalfd(2) is available if kernel >= 2.6.22 and glibc >= 2.8 */ +#if LINUX_VERSION_CODE >= 0x020616 && __GLIBC_PREREQ(2, 8) +#define HAVE_SIGNALFD 1 +#else +#define HAVE_SIGNALFD 0 +#endif /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 diff --git a/src/rt/libuv/src/unix/ev/config_netbsd.h b/src/rt/libuv/src/unix/ev/config_netbsd.h new file mode 100644 index 00000000000..ebebd4156d2 --- /dev/null +++ b/src/rt/libuv/src/unix/ev/config_netbsd.h @@ -0,0 +1,120 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* "use syscall interface for clock_gettime" */ +/* #undef HAVE_CLOCK_SYSCALL */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef HAVE_EPOLL_CTL */ + +/* Define to 1 if you have the `eventfd' function. */ +/* #undef HAVE_EVENTFD */ + +/* Define to 1 if you have the `inotify_init' function. */ +/* #undef HAVE_INOTIFY_INIT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `kqueue' function. */ +#define HAVE_KQUEUE 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef HAVE_LIBRT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `nanosleep' function. */ +/* #undef HAVE_NANOSLEEP */ + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PORT_H */ + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EVENTFD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EVENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_INOTIFY_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_QUEUE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libev" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "3.9" diff --git a/src/rt/libuv/src/ev/config_sunos.h b/src/rt/libuv/src/unix/ev/config_sunos.h similarity index 100% rename from src/rt/libuv/src/ev/config_sunos.h rename to src/rt/libuv/src/unix/ev/config_sunos.h diff --git a/src/rt/libuv/src/ev/configure b/src/rt/libuv/src/unix/ev/configure similarity index 100% rename from src/rt/libuv/src/ev/configure rename to src/rt/libuv/src/unix/ev/configure diff --git a/src/rt/libuv/src/ev/configure.ac b/src/rt/libuv/src/unix/ev/configure.ac similarity index 100% rename from src/rt/libuv/src/ev/configure.ac rename to src/rt/libuv/src/unix/ev/configure.ac diff --git a/src/rt/libuv/src/ev/depcomp b/src/rt/libuv/src/unix/ev/depcomp similarity index 100% rename from src/rt/libuv/src/ev/depcomp rename to src/rt/libuv/src/unix/ev/depcomp diff --git a/src/rt/libuv/src/ev/ev++.h b/src/rt/libuv/src/unix/ev/ev++.h similarity index 100% rename from src/rt/libuv/src/ev/ev++.h rename to src/rt/libuv/src/unix/ev/ev++.h diff --git a/src/rt/libuv/src/ev/ev.3 b/src/rt/libuv/src/unix/ev/ev.3 similarity index 100% rename from src/rt/libuv/src/ev/ev.3 rename to src/rt/libuv/src/unix/ev/ev.3 diff --git a/src/rt/libuv/src/ev/ev.c b/src/rt/libuv/src/unix/ev/ev.c similarity index 99% rename from src/rt/libuv/src/ev/ev.c rename to src/rt/libuv/src/unix/ev/ev.c index 4187b18af73..5e616c254fc 100644 --- a/src/rt/libuv/src/ev/ev.c +++ b/src/rt/libuv/src/unix/ev/ev.c @@ -3846,7 +3846,7 @@ ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) #if EV_IDLE_ENABLE if (types & EV_IDLE) - for (j = NUMPRI; i--; ) + for (j = NUMPRI; j--; ) for (i = idlecnt [j]; i--; ) cb (EV_A_ EV_IDLE, idles [j][i]); #endif diff --git a/src/rt/libuv/src/ev/ev.pod b/src/rt/libuv/src/unix/ev/ev.pod similarity index 100% rename from src/rt/libuv/src/ev/ev.pod rename to src/rt/libuv/src/unix/ev/ev.pod diff --git a/src/rt/libuv/src/ev/ev_epoll.c b/src/rt/libuv/src/unix/ev/ev_epoll.c similarity index 100% rename from src/rt/libuv/src/ev/ev_epoll.c rename to src/rt/libuv/src/unix/ev/ev_epoll.c diff --git a/src/rt/libuv/src/ev/ev_kqueue.c b/src/rt/libuv/src/unix/ev/ev_kqueue.c similarity index 100% rename from src/rt/libuv/src/ev/ev_kqueue.c rename to src/rt/libuv/src/unix/ev/ev_kqueue.c diff --git a/src/rt/libuv/src/ev/ev_poll.c b/src/rt/libuv/src/unix/ev/ev_poll.c similarity index 100% rename from src/rt/libuv/src/ev/ev_poll.c rename to src/rt/libuv/src/unix/ev/ev_poll.c diff --git a/src/rt/libuv/src/ev/ev_port.c b/src/rt/libuv/src/unix/ev/ev_port.c similarity index 100% rename from src/rt/libuv/src/ev/ev_port.c rename to src/rt/libuv/src/unix/ev/ev_port.c diff --git a/src/rt/libuv/src/ev/ev_select.c b/src/rt/libuv/src/unix/ev/ev_select.c similarity index 100% rename from src/rt/libuv/src/ev/ev_select.c rename to src/rt/libuv/src/unix/ev/ev_select.c diff --git a/src/rt/libuv/src/ev/ev_vars.h b/src/rt/libuv/src/unix/ev/ev_vars.h similarity index 100% rename from src/rt/libuv/src/ev/ev_vars.h rename to src/rt/libuv/src/unix/ev/ev_vars.h diff --git a/src/rt/libuv/src/ev/ev_win32.c b/src/rt/libuv/src/unix/ev/ev_win32.c similarity index 100% rename from src/rt/libuv/src/ev/ev_win32.c rename to src/rt/libuv/src/unix/ev/ev_win32.c diff --git a/src/rt/libuv/src/ev/ev_wrap.h b/src/rt/libuv/src/unix/ev/ev_wrap.h similarity index 100% rename from src/rt/libuv/src/ev/ev_wrap.h rename to src/rt/libuv/src/unix/ev/ev_wrap.h diff --git a/src/rt/libuv/src/ev/event.c b/src/rt/libuv/src/unix/ev/event.c similarity index 100% rename from src/rt/libuv/src/ev/event.c rename to src/rt/libuv/src/unix/ev/event.c diff --git a/src/rt/libuv/src/ev/event.h b/src/rt/libuv/src/unix/ev/event.h similarity index 100% rename from src/rt/libuv/src/ev/event.h rename to src/rt/libuv/src/unix/ev/event.h diff --git a/src/rt/libuv/src/ev/install-sh b/src/rt/libuv/src/unix/ev/install-sh similarity index 100% rename from src/rt/libuv/src/ev/install-sh rename to src/rt/libuv/src/unix/ev/install-sh diff --git a/src/rt/libuv/src/ev/libev.m4 b/src/rt/libuv/src/unix/ev/libev.m4 similarity index 100% rename from src/rt/libuv/src/ev/libev.m4 rename to src/rt/libuv/src/unix/ev/libev.m4 diff --git a/src/rt/libuv/src/ev/ltmain.sh b/src/rt/libuv/src/unix/ev/ltmain.sh similarity index 100% rename from src/rt/libuv/src/ev/ltmain.sh rename to src/rt/libuv/src/unix/ev/ltmain.sh diff --git a/src/rt/libuv/src/ev/missing b/src/rt/libuv/src/unix/ev/missing similarity index 100% rename from src/rt/libuv/src/ev/missing rename to src/rt/libuv/src/unix/ev/missing diff --git a/src/rt/libuv/src/ev/mkinstalldirs b/src/rt/libuv/src/unix/ev/mkinstalldirs similarity index 100% rename from src/rt/libuv/src/ev/mkinstalldirs rename to src/rt/libuv/src/unix/ev/mkinstalldirs diff --git a/src/rt/libuv/src/uv-linux.c b/src/rt/libuv/src/unix/freebsd.c similarity index 69% rename from src/rt/libuv/src/uv-linux.c rename to src/rt/libuv/src/unix/freebsd.c index dc718c6decd..449aad4c211 100644 --- a/src/rt/libuv/src/uv-linux.c +++ b/src/rt/libuv/src/unix/freebsd.c @@ -20,18 +20,19 @@ #include "uv.h" -#include -#include +#include +#include +#include + +#include +#include #include #undef NANOSEC #define NANOSEC 1000000000 -/* - * There's probably some way to get time from Linux than gettimeofday(). What - * it is, I don't know. - */ -uint64_t uv_hrtime() { + +uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * NANOSEC + ts.tv_nsec); @@ -43,13 +44,39 @@ int uv_exepath(char* buffer, size_t* size) { int result; char* path; char* fullpath; + int mib[4]; + size_t cb; if (!buffer || !size) { return -1; } - *size = readlink("/proc/self/exe", buffer, *size - 1); - if (*size <= 0) return -1; - buffer[*size] = '\0'; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + + cb = *size; + if (sysctl(mib, 4, buffer, &cb, NULL, 0) < 0) { + *size = 0; + return -1; + } + *size = strlen(buffer); + return 0; } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/fs.c b/src/rt/libuv/src/unix/fs.c new file mode 100644 index 00000000000..d745622835b --- /dev/null +++ b/src/rt/libuv/src/unix/fs.c @@ -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 "uv.h" +#include "internal.h" +#include "eio.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define ARGS1(a) (a) +#define ARGS2(a,b) (a), (b) +#define ARGS3(a,b,c) (a), (b), (c) +#define ARGS4(a,b,c,d) (a), (b), (c), (d) + +#define WRAP_EIO(type, eiofunc, func, args) \ + uv_fs_req_init(loop, req, type, path, cb); \ + if (cb) { \ + /* async */ \ + req->eio = eiofunc(args, EIO_PRI_DEFAULT, uv__fs_after, req); \ + if (!req->eio) { \ + uv_err_new(loop, ENOMEM); \ + return -1; \ + } \ + uv_ref(loop); \ + } else { \ + /* sync */ \ + req->result = func(args); \ + if (req->result) { \ + uv_err_new(loop, errno); \ + } \ + return req->result; \ + } \ + return 0; + + +static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, uv_fs_type fs_type, + const char* path, uv_fs_cb cb) { + /* Make sure the thread pool is initialized. */ + uv_eio_init(loop); + + uv__req_init((uv_req_t*) req); + req->type = UV_FS; + req->loop = loop; + req->fs_type = fs_type; + req->cb = cb; + req->result = 0; + req->ptr = NULL; + req->path = path ? strdup(path) : NULL; + req->errorno = 0; + req->eio = NULL; +} + + +void uv_fs_req_cleanup(uv_fs_t* req) { + free(req->path); + req->path = NULL; + + switch (req->fs_type) { + case UV_FS_READDIR: + assert((req->result == -1 && req->ptr == NULL) + || (req->result >= 0 && req->ptr != NULL)); + free(req->ptr); + req->ptr = NULL; + break; + + case UV_FS_STAT: + case UV_FS_LSTAT: + req->ptr = NULL; + break; + + default: + break; + } +} + + +static int uv__fs_after(eio_req* eio) { + char* name; + int namelen; + int buflen = 0; + uv_fs_t* req = eio->data; + int i; + + assert(req->cb); + + req->result = req->eio->result; + req->errorno = uv_translate_sys_error(req->eio->errorno); + + switch (req->fs_type) { + case UV_FS_READDIR: + if (req->eio->result == -1) + break; /* opendir() or readdir() operation failed. */ + + /* + * XXX This is pretty bad. + * We alloc and copy the large null terminated string list from libeio. + * This is done because libeio is going to free eio->ptr2 after this + * callback. We must keep it until uv_fs_req_cleanup. If we get rid of + * libeio this can be avoided. + */ + buflen = 0; + name = req->eio->ptr2; + for (i = 0; i < req->result; i++) { + namelen = strlen(name); + buflen += namelen + 1; + /* TODO check ENOMEM */ + name += namelen; + assert(*name == '\0'); + name++; + } + req->ptr = malloc(buflen); + memcpy(req->ptr, req->eio->ptr2, buflen); + break; + + case UV_FS_STAT: + case UV_FS_LSTAT: + case UV_FS_FSTAT: + req->ptr = req->eio->ptr2; + break; + + case UV_FS_READLINK: + if (req->result == -1) { + req->ptr = NULL; + } else { + assert(req->result > 0); + + if ((name = realloc(req->eio->ptr2, req->result + 1)) == NULL) { + /* Not enough memory. Reuse buffer, chop off last byte. */ + name = req->eio->ptr2; + req->result--; + } + + name[req->result] = '\0'; + req->ptr = name; + req->result = 0; + } + break; + + default: + break; + } + + uv_unref(req->loop); + req->eio = NULL; /* Freed by libeio */ + + req->cb(req); + return 0; +} + + +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_CLOSE, eio_close, close, ARGS1(file)); +} + + +int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + int mode, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_OPEN, path, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_open(path, flags, mode, EIO_PRI_DEFAULT, uv__fs_after, req); + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = open(path, flags, mode); + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + uv__cloexec(req->result, 1); + + return req->result; + } + + return 0; +} + + +int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file fd, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_READ, NULL, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_read(fd, buf, length, offset, EIO_PRI_DEFAULT, + uv__fs_after, req); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = offset < 0 ? + read(fd, buf, length) : + pread(fd, buf, length, offset); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + return req->result; + } + + return 0; +} + + +int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + WRAP_EIO(UV_FS_UNLINK, eio_unlink, unlink, ARGS1(path)) +} + + +int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_WRITE, NULL, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_write(file, buf, length, offset, EIO_PRI_DEFAULT, + uv__fs_after, req); + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = offset < 0 ? + write(file, buf, length) : + pwrite(file, buf, length, offset); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + return req->result; + } + + return 0; +} + + +int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + WRAP_EIO(UV_FS_MKDIR, eio_mkdir, mkdir, ARGS2(path, mode)) +} + + +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + WRAP_EIO(UV_FS_RMDIR, eio_rmdir, rmdir, ARGS1(path)) +} + + +int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + uv_fs_cb cb) { + int r; + struct dirent* entry; + size_t size = 0; + size_t d_namlen = 0; + + uv_fs_req_init(loop, req, UV_FS_READDIR, path, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_readdir(path, flags, EIO_PRI_DEFAULT, uv__fs_after, req); + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + DIR* dir = opendir(path); + if (!dir) { + uv_err_new(loop, errno); + req->result = -1; + return -1; + } + + /* req->result stores number of entries */ + req->result = 0; + + while ((entry = readdir(dir))) { + d_namlen = strlen(entry->d_name); + + /* Skip . and .. */ + if ((d_namlen == 1 && entry->d_name[0] == '.') || + (d_namlen == 2 && entry->d_name[0] == '.' && + entry->d_name[1] == '.')) { + continue; + } + + req->ptr = realloc(req->ptr, size + d_namlen + 1); + /* TODO check ENOMEM */ + memcpy((char*)req->ptr + size, entry->d_name, d_namlen); + size += d_namlen; + ((char*)req->ptr)[size] = '\0'; + size++; + req->result++; + } + + r = closedir(dir); + if (r) { + uv_err_new(loop, errno); + req->result = -1; + return -1; + } + + return req->result; + } + + return 0; +} + + +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + char* pathdup; + int pathlen; + + uv_fs_req_init(loop, req, UV_FS_STAT, path, cb); + + /* TODO do this without duplicating the string. */ + /* TODO security */ + pathdup = strdup(path); + pathlen = strlen(path); + + if (pathlen > 0 && path[pathlen - 1] == '\\') { + /* TODO do not modify input string */ + pathdup[pathlen - 1] = '\0'; + } + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_stat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + + free(pathdup); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = stat(pathdup, &req->statbuf); + + free(pathdup); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + req->ptr = &req->statbuf; + return req->result; + } + + return 0; +} + + +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + uv_fs_req_init(loop, req, UV_FS_FSTAT, NULL, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_fstat(file, EIO_PRI_DEFAULT, uv__fs_after, req); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = fstat(file, &req->statbuf); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + req->ptr = &req->statbuf; + return req->result; + } + + return 0; +} + + +int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, + uv_fs_cb cb) { + WRAP_EIO(UV_FS_RENAME, eio_rename, rename, ARGS2(path, new_path)) +} + + +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FSYNC, eio_fsync, fsync, ARGS1(file)) +} + + +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + char* path = NULL; +#ifdef __FreeBSD__ + /* freebsd doesn't have fdatasync, do a full fsync instead. */ + WRAP_EIO(UV_FS_FDATASYNC, eio_fdatasync, fsync, ARGS1(file)) +#else + WRAP_EIO(UV_FS_FDATASYNC, eio_fdatasync, fdatasync, ARGS1(file)) +#endif +} + + +int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, off_t offset, + uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FTRUNCATE, eio_ftruncate, ftruncate, ARGS2(file, offset)) +} + + +int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, + off_t in_offset, size_t length, uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_SENDFILE, eio_sendfile, eio_sendfile_sync, + ARGS4(out_fd, in_fd, in_offset, length)) +} + + +int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + WRAP_EIO(UV_FS_CHMOD, eio_chmod, chmod, ARGS2(path, mode)) +} + + +static int _utime(const char* path, double atime, double mtime) { + struct utimbuf buf; + buf.actime = atime; + buf.modtime = mtime; + return utime(path, &buf); +} + + +int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb) { + WRAP_EIO(UV_FS_UTIME, eio_utime, _utime, ARGS3(path, atime, mtime)) +} + + +#if defined(HAVE_FUTIMES) +static int _futime(const uv_file file, double atime, double mtime) { + struct timeval tv[2]; + + /* FIXME possible loss of precision in floating-point arithmetic? */ + tv[0].tv_sec = atime; + tv[0].tv_usec = (unsigned long)(atime * 1000000) % 1000000; + + tv[1].tv_sec = mtime; + tv[1].tv_usec = (unsigned long)(mtime * 1000000) % 1000000; + + return futimes(file, tv); +} +#endif + + +int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, + double mtime, uv_fs_cb cb) { +#if defined(HAVE_FUTIMES) + const char* path = NULL; + + uv_fs_req_init(loop, req, UV_FS_FUTIME, path, cb); + + WRAP_EIO(UV_FS_FUTIME, eio_futime, _futime, ARGS3(file, atime, mtime)) +#else + uv_err_new(loop, ENOSYS); + return -1; +#endif +} + + +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + char* pathdup; + int pathlen; + + uv_fs_req_init(loop, req, UV_FS_LSTAT, path, cb); + + /* TODO do this without duplicating the string. */ + /* TODO security */ + pathdup = strdup(path); + pathlen = strlen(path); + + if (pathlen > 0 && path[pathlen - 1] == '\\') { + /* TODO do not modify input string */ + pathdup[pathlen - 1] = '\0'; + } + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_lstat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + + free(pathdup); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = lstat(pathdup, &req->statbuf); + + free(pathdup); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + req->ptr = &req->statbuf; + return req->result; + } + + return 0; +} + + +int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb) { + WRAP_EIO(UV_FS_LINK, eio_link, link, ARGS2(path, new_path)) +} + + +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) { + WRAP_EIO(UV_FS_SYMLINK, eio_symlink, symlink, ARGS2(path, new_path)) +} + + +int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb) { + ssize_t size; + int status; + char* buf; + + status = -1; + + uv_fs_req_init(loop, req, UV_FS_READLINK, path, cb); + + if (cb) { + if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req))) { + uv_ref(loop); + return 0; + } else { + uv_err_new(loop, ENOMEM); + return -1; + } + } else { + /* pathconf(_PC_PATH_MAX) may return -1 to signify that path + * lengths have no upper limit or aren't suitable for malloc'ing. + */ + if ((size = pathconf(path, _PC_PATH_MAX)) == -1) { +#if defined(PATH_MAX) + size = PATH_MAX; +#else + size = 4096; +#endif + } + + if ((buf = malloc(size + 1)) == NULL) { + uv_err_new(loop, ENOMEM); + return -1; + } + + if ((size = readlink(path, buf, size)) == -1) { + req->errorno = errno; + req->result = -1; + free(buf); + } else { + /* Cannot conceivably fail since it shrinks the buffer. */ + buf = realloc(buf, size + 1); + buf[size] = '\0'; + req->result = 0; + req->ptr = buf; + } + + return req->result; + } + + assert(0 && "unreachable"); +} + + +int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, + uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FCHMOD, eio_fchmod, fchmod, ARGS2(file, mode)) +} + + +int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid, + int gid, uv_fs_cb cb) { + WRAP_EIO(UV_FS_CHOWN, eio_chown, chown, ARGS3(path, uid, gid)) +} + + +int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, int gid, + uv_fs_cb cb) { + char* path = NULL; + WRAP_EIO(UV_FS_FCHOWN, eio_fchown, fchown, ARGS3(file, uid, gid)) +} + + +static void uv__work(eio_req* eio) { + uv_work_t* req = eio->data; + if (req->work_cb) { + req->work_cb(req); + } +} + + +static int uv__after_work(eio_req *eio) { + uv_work_t* req = eio->data; + uv_unref(req->loop); + if (req->after_work_cb) { + req->after_work_cb(req); + } + return 0; +} + + +int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, + uv_after_work_cb after_work_cb) { + void* data = req->data; + + uv_eio_init(loop); + + uv__req_init((uv_req_t*) req); + uv_ref(loop); + req->loop = loop; + req->data = data; + req->work_cb = work_cb; + req->after_work_cb = after_work_cb; + + req->eio = eio_custom(uv__work, EIO_PRI_DEFAULT, uv__after_work, req); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + return 0; +} diff --git a/src/rt/libuv/src/unix/internal.h b/src/rt/libuv/src/unix/internal.h new file mode 100644 index 00000000000..42283ca2a51 --- /dev/null +++ b/src/rt/libuv/src/unix/internal.h @@ -0,0 +1,129 @@ +/* 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 "uv-eio.h" + +#include /* offsetof */ + +#if defined(__linux__) + +#include +#include + +#undef HAVE_FUTIMES +#undef HAVE_PIPE2 +#undef HAVE_ACCEPT4 + +/* futimes() requires linux >= 2.6.22 and glib >= 2.6 */ +#if LINUX_VERSION_CODE >= 0x20616 && __GLIBC_PREREQ(2, 6) +#define HAVE_FUTIMES +#endif + +/* pipe2() requires linux >= 2.6.27 and glibc >= 2.9 */ +#if LINUX_VERSION_CODE >= 0x2061B && __GLIBC_PREREQ(2, 9) +#define HAVE_PIPE2 +#endif + +/* accept4() requires linux >= 2.6.28 and glib >= 2.10 */ +#if LINUX_VERSION_CODE >= 0x2061C && __GLIBC_PREREQ(2, 10) +#define HAVE_ACCEPT4 +#endif + +#endif /* __linux__ */ + +#ifdef __APPLE__ +# define HAVE_FUTIMES +#endif + +#ifdef __FreeBSD__ +# define HAVE_FUTIMES +#endif + +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member))) + +#define SAVE_ERRNO(block) \ + do { \ + int _saved_errno = errno; \ + do { block; } while (0); \ + errno = _saved_errno; \ + } \ + while (0); + +/* flags */ +enum { + UV_CLOSING = 0x00000001, /* uv_close() called but not finished. */ + UV_CLOSED = 0x00000002, /* close(2) finished. */ + UV_READING = 0x00000004, /* uv_read_start() called. */ + UV_SHUTTING = 0x00000008, /* uv_shutdown() called but not complete. */ + UV_SHUT = 0x00000010, /* Write side closed. */ + UV_READABLE = 0x00000020, /* The stream is readable */ + UV_WRITABLE = 0x00000040 /* The stream is writable */ +}; + +size_t uv__strlcpy(char* dst, const char* src, size_t size); + +int uv__close(int fd); +void uv__req_init(uv_req_t*); +void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type); + + +int uv__nonblock(int fd, int set) __attribute__((unused)); +int uv__cloexec(int fd, int set) __attribute__((unused)); +int uv__socket(int domain, int type, int protocol); + +/* error */ +uv_err_code uv_translate_sys_error(int sys_errno); +uv_err_t uv_err_new(uv_loop_t* loop, int sys_error); +uv_err_t uv_err_new_artificial(uv_loop_t* loop, int code); +void uv_fatal_error(const int errorno, const char* syscall); + +/* 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); +void uv__stream_io(EV_P_ ev_io* watcher, int revents); +void uv__server_io(EV_P_ ev_io* watcher, int revents); +int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t len); +int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, + socklen_t addrlen, uv_connect_cb cb); + +/* tcp */ +int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); + +/* pipe */ +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); +void uv__pipe_accept(EV_P_ ev_io* watcher, int revents); +int uv_pipe_cleanup(uv_pipe_t* handle); + +/* udp */ +void uv__udp_destroy(uv_udp_t* handle); +void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w); + +/* fs */ +void uv__fs_event_destroy(uv_fs_event_t* handle); + +#endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/src/rt/libuv/src/unix/linux.c b/src/rt/libuv/src/unix/linux.c new file mode 100644 index 00000000000..0b4ce64ddf3 --- /dev/null +++ b/src/rt/libuv/src/unix/linux.c @@ -0,0 +1,183 @@ +/* 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 +#include +#include +#include +#include + +#include +#include +#include + +#undef NANOSEC +#define NANOSEC 1000000000 + + +/* Don't look aghast, this is exactly how glibc's basename() works. */ +static char* basename_r(const char* path) { + char* s = strrchr(path, '/'); + return s ? (s + 1) : (char*)path; +} + + +/* + * There's probably some way to get time from Linux than gettimeofday(). What + * it is, I don't know. + */ +uint64_t uv_hrtime() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (ts.tv_sec * NANOSEC + ts.tv_nsec); +} + + +int uv_exepath(char* buffer, size_t* size) { + if (!buffer || !size) { + return -1; + } + + *size = readlink("/proc/self/exe", buffer, *size - 1); + if (*size <= 0) return -1; + buffer[*size] = '\0'; + return 0; +} + + +static int new_inotify_fd(void) { +#if defined(IN_NONBLOCK) && defined(IN_CLOEXEC) + return inotify_init1(IN_NONBLOCK | IN_CLOEXEC); +#else + int fd; + + if ((fd = inotify_init()) == -1) + return -1; + + if (uv__cloexec(fd, 1) || uv__nonblock(fd, 1)) { + SAVE_ERRNO(uv__close(fd)); + fd = -1; + } + + return fd; +#endif +} + + +static void uv__inotify_read(EV_P_ ev_io* w, int revents) { + struct inotify_event* e; + uv_fs_event_t* handle; + const char* filename; + ssize_t size; + int events; + char *p; + /* needs to be large enough for sizeof(inotify_event) + strlen(filename) */ + char buf[4096]; + + handle = container_of(w, uv_fs_event_t, read_watcher); + + do { + do { + size = read(handle->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 = (void*)p; + + events = 0; + if (e->mask & (IN_ATTRIB|IN_MODIFY)) + events |= UV_CHANGE; + if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) + events |= UV_RENAME; + + /* 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. + */ + filename = e->len ? e->name : basename_r(handle->filename); + + handle->cb(handle, filename, events, 0); + } + } + while (handle->fd != -1); /* handle might've been closed by callback */ +} + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + int flags; + int fd; + + /* + * TODO share a single inotify fd across the event loop? + * We'll run into fs.inotify.max_user_instances if we + * keep creating new inotify fds. + */ + if ((fd = new_inotify_fd()) == -1) { + uv_err_new(loop, errno); + return -1; + } + + flags = IN_ATTRIB + | IN_CREATE + | IN_MODIFY + | IN_DELETE + | IN_DELETE_SELF + | IN_MOVED_FROM + | IN_MOVED_TO; + + if (inotify_add_watch(fd, filename, flags) == -1) { + uv_err_new(loop, errno); + uv__close(fd); + return -1; + } + + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + handle->filename = strdup(filename); /* this should go! */ + handle->cb = cb; + handle->fd = fd; + + ev_io_init(&handle->read_watcher, uv__inotify_read, fd, EV_READ); + ev_io_start(loop->ev, &handle->read_watcher); + + return 0; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + ev_io_stop(handle->loop->ev, &handle->read_watcher); + uv__close(handle->fd); + handle->fd = -1; + free(handle->filename); +} diff --git a/src/rt/libuv/src/unix/netbsd.c b/src/rt/libuv/src/unix/netbsd.c new file mode 100644 index 00000000000..0ba79997545 --- /dev/null +++ b/src/rt/libuv/src/unix/netbsd.c @@ -0,0 +1,85 @@ +/* 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 +#include +#include + +#include +#include + +#include +#include + +#undef NANOSEC +#define NANOSEC 1000000000 + + +uint64_t uv_hrtime(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (ts.tv_sec * NANOSEC + ts.tv_nsec); +} + + +int uv_exepath(char* buffer, size_t* size) { + uint32_t usize; + int result; + char* path; + char* fullpath; + int mib[4]; + size_t cb; + pid_t mypid; + + if (!buffer || !size) { + return -1; + } + + 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) < 0) { + *size = 0; + return -1; + } + *size = strlen(buffer); + + return 0; +} + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/pipe.c b/src/rt/libuv/src/unix/pipe.c new file mode 100644 index 00000000000..50dc635b7e4 --- /dev/null +++ b/src/rt/libuv/src/unix/pipe.c @@ -0,0 +1,272 @@ +/* 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 +#include +#include +#include +#include +#include + +int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle) { + uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); + loop->counters.pipe_init++; + handle->pipe_fname = NULL; + return 0; +} + + +int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + struct sockaddr_un saddr; + const char* pipe_fname; + int saved_errno; + int sockfd; + int status; + int bound; + + saved_errno = errno; + pipe_fname = NULL; + sockfd = -1; + status = -1; + bound = 0; + + /* Already bound? */ + if (handle->fd >= 0) { + uv_err_new_artificial(handle->loop, UV_EINVAL); + goto out; + } + + /* Make a copy of the file name, it outlives this function's scope. */ + if ((pipe_fname = strdup(name)) == NULL) { + uv_err_new(handle->loop, ENOMEM); + goto out; + } + + /* We've got a copy, don't touch the original any more. */ + name = NULL; + + if ((sockfd = uv__socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + memset(&saddr, 0, sizeof saddr); + uv__strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + saddr.sun_family = AF_UNIX; + + if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr) == -1) { + /* On EADDRINUSE: + * + * We hold the file lock so there is no other process listening + * on the socket. Ergo, it's stale - remove it. + * + * This assumes that the other process uses locking too + * but that's a good enough assumption for now. + */ + if (errno != EADDRINUSE + || unlink(pipe_fname) == -1 + || bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr) == -1) { + /* Convert ENOENT to EACCES for compatibility with Windows. */ + uv_err_new(handle->loop, (errno == ENOENT) ? EACCES : errno); + goto out; + } + } + bound = 1; + + /* Success. */ + handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */ + handle->fd = sockfd; + status = 0; + +out: + /* Clean up on error. */ + if (status) { + if (bound) { + /* unlink() before close() to avoid races. */ + assert(pipe_fname != NULL); + unlink(pipe_fname); + } + uv__close(sockfd); + + free((void*)pipe_fname); + } + + errno = saved_errno; + return status; +} + + +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->fd == -1) { + uv_err_new_artificial(handle->loop, UV_EINVAL); + goto out; + } + assert(handle->fd >= 0); + + if ((status = listen(handle->fd, backlog)) == -1) { + uv_err_new(handle->loop, errno); + } else { + handle->connection_cb = cb; + ev_io_init(&handle->read_watcher, uv__pipe_accept, handle->fd, EV_READ); + ev_io_start(handle->loop->ev, &handle->read_watcher); + } + +out: + errno = saved_errno; + return status; +} + + +int uv_pipe_cleanup(uv_pipe_t* handle) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + 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. + * + * This is less of an issue now that we attach a file lock + * to the socket but it's still a best practice. + */ + unlink(handle->pipe_fname); + free((void*)handle->pipe_fname); + } + + errno = saved_errno; + return status; +} + + +void uv_pipe_open(uv_pipe_t* handle, uv_file fd) { + uv__stream_open((uv_stream_t*)handle, fd, UV_READABLE | UV_WRITABLE); +} + + +int uv_pipe_connect(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + uv_connect_cb cb) { + struct sockaddr_un saddr; + int saved_errno; + int sockfd; + int status; + int r; + + saved_errno = errno; + sockfd = -1; + status = -1; + + if ((sockfd = uv__socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + memset(&saddr, 0, sizeof saddr); + uv__strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + saddr.sun_family = AF_UNIX; + + /* We don't check for EINPROGRESS. Think about it: the socket + * is either there or not. + */ + do { + r = connect(sockfd, (struct sockaddr*)&saddr, sizeof saddr); + } + while (r == -1 && errno == EINTR); + + if (r == -1) { + uv_err_new(handle->loop, errno); + uv__close(sockfd); + goto out; + } + + uv__stream_open((uv_stream_t*)handle, sockfd, UV_READABLE | UV_WRITABLE); + + ev_io_start(handle->loop->ev, &handle->read_watcher); + ev_io_start(handle->loop->ev, &handle->write_watcher); + + status = 0; + +out: + handle->delayed_error = status; /* Passed to callback. */ + handle->connect_req = req; + req->handle = (uv_stream_t*)handle; + req->type = UV_CONNECT; + req->cb = cb; + ngx_queue_init(&req->queue); + + /* Run callback on next tick. */ + ev_feed_event(handle->loop->ev, &handle->read_watcher, EV_CUSTOM); + assert(ev_is_pending(&handle->read_watcher)); + + /* Mimic the Windows pipe implementation, always + * return 0 and let the callback handle errors. + */ + errno = saved_errno; + return 0; +} + + +/* TODO merge with uv__server_io()? */ +void uv__pipe_accept(EV_P_ ev_io* watcher, int revents) { + struct sockaddr_un saddr; + uv_pipe_t* pipe; + int saved_errno; + int sockfd; + + saved_errno = errno; + pipe = watcher->data; + + assert(pipe->type == UV_NAMED_PIPE); + assert(pipe->pipe_fname != NULL); + + sockfd = uv__accept(pipe->fd, (struct sockaddr *)&saddr, sizeof saddr); + if (sockfd == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + assert(0 && "EAGAIN on uv__accept(pipefd)"); + } else { + uv_err_new(pipe->loop, errno); + } + } else { + pipe->accepted_fd = sockfd; + pipe->connection_cb((uv_stream_t*)pipe, 0); + if (pipe->accepted_fd == sockfd) { + /* The user hasn't yet accepted called uv_accept() */ + ev_io_stop(pipe->loop->ev, &pipe->read_watcher); + } + } + + errno = saved_errno; +} diff --git a/src/rt/libuv/src/unix/process.c b/src/rt/libuv/src/unix/process.c new file mode 100644 index 00000000000..34f12d1737f --- /dev/null +++ b/src/rt/libuv/src/unix/process.c @@ -0,0 +1,300 @@ + +/* 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 +#include +#include +#include /* O_CLOEXEC, O_NONBLOCK */ +#include +#include +#include + +#ifdef __APPLE__ +# include +# define environ (*_NSGetEnviron()) +#else +extern char **environ; +#endif + + +static void uv__chld(EV_P_ ev_child* watcher, int revents) { + int status = watcher->rstatus; + int exit_status = 0; + int term_signal = 0; + uv_process_t *process = watcher->data; + + assert(&process->child_watcher == watcher); + assert(revents & EV_CHILD); + + ev_child_stop(EV_A_ &process->child_watcher); + + if (WIFEXITED(status)) { + exit_status = WEXITSTATUS(status); + } + + if (WIFSIGNALED(status)) { + term_signal = WTERMSIG(status); + } + + if (process->exit_cb) { + process->exit_cb(process, exit_status, term_signal); + } +} + +#ifndef SPAWN_WAIT_EXEC +# define SPAWN_WAIT_EXEC 1 +#endif + +int uv_spawn(uv_loop_t* loop, uv_process_t* process, + uv_process_options_t options) { + /* + * Save environ in the case that we get it clobbered + * by the child process. + */ + char** save_our_env = environ; + int stdin_pipe[2] = { -1, -1 }; + int stdout_pipe[2] = { -1, -1 }; + int stderr_pipe[2] = { -1, -1 }; +#if SPAWN_WAIT_EXEC + int signal_pipe[2] = { -1, -1 }; + struct pollfd pfd; +#endif + int status; + pid_t pid; + + uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); + loop->counters.process_init++; + + process->exit_cb = options.exit_cb; + + if (options.stdin_stream) { + if (options.stdin_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stdin_pipe) < 0) { + goto error; + } + uv__cloexec(stdin_pipe[0], 1); + uv__cloexec(stdin_pipe[1], 1); + } + + if (options.stdout_stream) { + if (options.stdout_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stdout_pipe) < 0) { + goto error; + } + uv__cloexec(stdout_pipe[0], 1); + uv__cloexec(stdout_pipe[1], 1); + } + + if (options.stderr_stream) { + if (options.stderr_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stderr_pipe) < 0) { + goto error; + } + uv__cloexec(stderr_pipe[0], 1); + uv__cloexec(stderr_pipe[1], 1); + } + + /* 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 sees POLLHUP. + */ +#if SPAWN_WAIT_EXEC +# ifdef HAVE_PIPE2 + if (pipe2(signal_pipe, O_CLOEXEC | O_NONBLOCK) < 0) { + goto error; + } +# else + if (pipe(signal_pipe) < 0) { + goto error; + } + uv__cloexec(signal_pipe[0], 1); + uv__cloexec(signal_pipe[1], 1); + uv__nonblock(signal_pipe[0], 1); + uv__nonblock(signal_pipe[1], 1); +# endif +#endif + + pid = fork(); + + if (pid == -1) { +#if SPAWN_WAIT_EXEC + uv__close(signal_pipe[0]); + uv__close(signal_pipe[1]); +#endif + environ = save_our_env; + goto error; + } + + if (pid == 0) { + if (stdin_pipe[0] >= 0) { + uv__close(stdin_pipe[1]); + dup2(stdin_pipe[0], STDIN_FILENO); + } else { + /* Reset flags that might be set by Node */ + uv__cloexec(STDIN_FILENO, 0); + uv__nonblock(STDIN_FILENO, 0); + } + + if (stdout_pipe[1] >= 0) { + uv__close(stdout_pipe[0]); + dup2(stdout_pipe[1], STDOUT_FILENO); + } else { + /* Reset flags that might be set by Node */ + uv__cloexec(STDOUT_FILENO, 0); + uv__nonblock(STDOUT_FILENO, 0); + } + + if (stderr_pipe[1] >= 0) { + uv__close(stderr_pipe[0]); + dup2(stderr_pipe[1], STDERR_FILENO); + } else { + /* Reset flags that might be set by Node */ + uv__cloexec(STDERR_FILENO, 0); + uv__nonblock(STDERR_FILENO, 0); + } + + if (options.cwd && chdir(options.cwd)) { + perror("chdir()"); + _exit(127); + } + + environ = options.env; + + execvp(options.file, options.args); + perror("execvp()"); + _exit(127); + /* Execution never reaches here. */ + } + + /* Parent. */ + + /* Restore environment. */ + environ = save_our_env; + +#if SPAWN_WAIT_EXEC + /* POLLHUP signals child has exited or execve()'d. */ + uv__close(signal_pipe[1]); + do { + pfd.fd = signal_pipe[0]; + pfd.events = POLLIN|POLLHUP; + pfd.revents = 0; + errno = 0, status = poll(&pfd, 1, -1); + } + while (status == -1 && (errno == EINTR || errno == ENOMEM)); + + uv__close(signal_pipe[0]); + uv__close(signal_pipe[1]); + + assert((status == 1) + && "poll() on pipe read end failed"); + assert((pfd.revents & POLLHUP) == POLLHUP + && "no POLLHUP on pipe read end"); +#endif + + process->pid = pid; + + ev_child_init(&process->child_watcher, uv__chld, pid, 0); + ev_child_start(process->loop->ev, &process->child_watcher); + process->child_watcher.data = process; + + if (stdin_pipe[1] >= 0) { + assert(options.stdin_stream); + assert(stdin_pipe[0] >= 0); + uv__close(stdin_pipe[0]); + uv__nonblock(stdin_pipe[1], 1); + uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1], + UV_WRITABLE); + } + + if (stdout_pipe[0] >= 0) { + assert(options.stdout_stream); + assert(stdout_pipe[1] >= 0); + uv__close(stdout_pipe[1]); + uv__nonblock(stdout_pipe[0], 1); + uv__stream_open((uv_stream_t*)options.stdout_stream, stdout_pipe[0], + UV_READABLE); + } + + if (stderr_pipe[0] >= 0) { + assert(options.stderr_stream); + assert(stderr_pipe[1] >= 0); + uv__close(stderr_pipe[1]); + uv__nonblock(stderr_pipe[0], 1); + uv__stream_open((uv_stream_t*)options.stderr_stream, stderr_pipe[0], + UV_READABLE); + } + + return 0; + +error: + uv_err_new(process->loop, errno); + uv__close(stdin_pipe[0]); + uv__close(stdin_pipe[1]); + uv__close(stdout_pipe[0]); + uv__close(stdout_pipe[1]); + uv__close(stderr_pipe[0]); + uv__close(stderr_pipe[1]); + return -1; +} + + +int uv_process_kill(uv_process_t* process, int signum) { + int r = kill(process->pid, signum); + + if (r) { + uv_err_new(process->loop, errno); + return -1; + } else { + return 0; + } +} diff --git a/src/rt/libuv/src/unix/stream.c b/src/rt/libuv/src/unix/stream.c new file mode 100644 index 00000000000..3983ca23b6d --- /dev/null +++ b/src/rt/libuv/src/unix/stream.c @@ -0,0 +1,783 @@ +/* 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 +#include +#include +#include +#include +#include + + +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 size_t uv__buf_count(uv_buf_t bufs[], int bufcnt) { + size_t total = 0; + int i; + + for (i = 0; i < bufcnt; i++) { + total += bufs[i].len; + } + + return total; +} + + +void uv__stream_init(uv_loop_t* loop, + uv_stream_t* stream, + uv_handle_type type) { + uv__handle_init(loop, (uv_handle_t*)stream, type); + + stream->alloc_cb = NULL; + stream->close_cb = NULL; + stream->connection_cb = NULL; + stream->connect_req = NULL; + stream->accepted_fd = -1; + stream->fd = -1; + stream->delayed_error = 0; + ngx_queue_init(&stream->write_queue); + ngx_queue_init(&stream->write_completed_queue); + stream->write_queue_size = 0; + + ev_init(&stream->read_watcher, uv__stream_io); + stream->read_watcher.data = stream; + + ev_init(&stream->write_watcher, uv__stream_io); + stream->write_watcher.data = stream; + + assert(ngx_queue_empty(&stream->write_queue)); + assert(ngx_queue_empty(&stream->write_completed_queue)); + assert(stream->write_queue_size == 0); +} + + +int uv__stream_open(uv_stream_t* stream, int fd, int flags) { + socklen_t yes; + + assert(fd >= 0); + stream->fd = fd; + + ((uv_handle_t*)stream)->flags |= flags; + + /* Reuse the port address if applicable. */ + yes = 1; + if (stream->type == UV_TCP + && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + uv_err_new(stream->loop, errno); + return -1; + } + + /* Associate the fd with each ev_io watcher. */ + ev_io_set(&stream->read_watcher, fd, EV_READ); + ev_io_set(&stream->write_watcher, fd, EV_WRITE); + + /* These should have been set up by uv_tcp_init or uv_pipe_init. */ + assert(stream->read_watcher.cb == uv__stream_io); + assert(stream->write_watcher.cb == uv__stream_io); + + return 0; +} + + +void uv__stream_destroy(uv_stream_t* stream) { + uv_write_t* req; + ngx_queue_t* q; + + assert(stream->flags & UV_CLOSED); + + while (!ngx_queue_empty(&stream->write_queue)) { + q = ngx_queue_head(&stream->write_queue); + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_write_t, queue); + if (req->bufs != req->bufsml) + free(req->bufs); + + if (req->cb) { + uv_err_new_artificial(req->handle->loop, UV_EINTR); + req->cb(req, -1); + } + } + + while (!ngx_queue_empty(&stream->write_completed_queue)) { + q = ngx_queue_head(&stream->write_completed_queue); + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_write_t, queue); + if (req->cb) { + uv_err_new_artificial(stream->loop, req->error); + req->cb(req, req->error ? -1 : 0); + } + } +} + + +void uv__server_io(EV_P_ ev_io* watcher, int revents) { + int fd; + struct sockaddr_storage addr; + uv_stream_t* stream = watcher->data; + + assert(watcher == &stream->read_watcher || + watcher == &stream->write_watcher); + assert(revents == EV_READ); + + assert(!(stream->flags & UV_CLOSING)); + + if (stream->accepted_fd >= 0) { + ev_io_stop(EV_A, &stream->read_watcher); + return; + } + + /* connection_cb can close the server socket while we're + * in the loop so check it on each iteration. + */ + while (stream->fd != -1) { + assert(stream->accepted_fd < 0); + fd = uv__accept(stream->fd, (struct sockaddr*)&addr, sizeof addr); + + if (fd < 0) { + if (errno == EAGAIN) { + /* No problem. */ + return; + } else if (errno == EMFILE) { + /* TODO special trick. unlock reserved socket, accept, close. */ + return; + } else { + uv_err_new(stream->loop, errno); + stream->connection_cb((uv_stream_t*)stream, -1); + } + } else { + stream->accepted_fd = fd; + stream->connection_cb((uv_stream_t*)stream, 0); + if (stream->accepted_fd >= 0) { + /* The user hasn't yet accepted called uv_accept() */ + ev_io_stop(stream->loop->ev, &stream->read_watcher); + return; + } + } + } +} + + +int uv_accept(uv_stream_t* server, uv_stream_t* client) { + uv_stream_t* streamServer; + uv_stream_t* streamClient; + int saved_errno; + int status; + + /* TODO document this */ + assert(server->loop == client->loop); + + saved_errno = errno; + status = -1; + + streamServer = (uv_stream_t*)server; + streamClient = (uv_stream_t*)client; + + if (streamServer->accepted_fd < 0) { + uv_err_new(server->loop, EAGAIN); + goto out; + } + + if (uv__stream_open(streamClient, streamServer->accepted_fd, + UV_READABLE | UV_WRITABLE)) { + /* TODO handle error */ + streamServer->accepted_fd = -1; + uv__close(streamServer->accepted_fd); + goto out; + } + + ev_io_start(streamServer->loop->ev, &streamServer->read_watcher); + streamServer->accepted_fd = -1; + status = 0; + +out: + errno = saved_errno; + return status; +} + + +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { + switch (stream->type) { + case UV_TCP: + return uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + case UV_NAMED_PIPE: + return uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + default: + assert(0); + return -1; + } +} + + +uv_write_t* uv_write_queue_head(uv_stream_t* stream) { + ngx_queue_t* q; + uv_write_t* req; + + if (ngx_queue_empty(&stream->write_queue)) { + return NULL; + } + + q = ngx_queue_head(&stream->write_queue); + if (!q) { + return NULL; + } + + req = ngx_queue_data(q, struct uv_write_s, queue); + assert(req); + + return req; +} + + +static void uv__drain(uv_stream_t* stream) { + uv_shutdown_t* req; + + assert(!uv_write_queue_head(stream)); + assert(stream->write_queue_size == 0); + + ev_io_stop(stream->loop->ev, &stream->write_watcher); + + /* Shutdown? */ + if ((stream->flags & UV_SHUTTING) && + !(stream->flags & UV_CLOSING) && + !(stream->flags & UV_SHUT)) { + assert(stream->shutdown_req); + + req = stream->shutdown_req; + + if (shutdown(stream->fd, SHUT_WR)) { + /* Error. Report it. User should call uv_close(). */ + uv_err_new(stream->loop, errno); + if (req->cb) { + req->cb(req, -1); + } + } else { + uv_err_new(stream->loop, 0); + ((uv_handle_t*) stream)->flags |= UV_SHUT; + if (req->cb) { + req->cb(req, 0); + } + } + } +} + + +static size_t uv__write_req_size(uv_write_t* req) { + size_t size; + + size = uv__buf_count(req->bufs + req->write_index, + req->bufcnt - 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. */ + ngx_queue_remove(&req->queue); + if (req->bufs != req->bufsml) { + free(req->bufs); + } + req->bufs = NULL; + + /* Add it to the write_completed_queue where it will have its + * callback called in the near future. + */ + ngx_queue_insert_tail(&stream->write_completed_queue, &req->queue); + ev_feed_event(stream->loop->ev, &stream->write_watcher, EV_WRITE); +} + + +/* On success returns NULL. On error returns a pointer to the write request + * which had the error. + */ +static void uv__write(uv_stream_t* stream) { + uv_write_t* req; + struct iovec* iov; + int iovcnt; + ssize_t n; + + assert(stream->fd >= 0); + + /* TODO: should probably while(1) here until EAGAIN */ + + /* Get the request at the head of the queue. */ + req = uv_write_queue_head(stream); + if (!req) { + assert(stream->write_queue_size == 0); + return; + } + + 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->bufcnt - req->write_index; + + /* 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. + */ + + do { + if (iovcnt == 1) { + n = write(stream->fd, iov[0].iov_base, iov[0].iov_len); + } else { + n = writev(stream->fd, iov, iovcnt); + } + } + while (n == -1 && errno == EINTR); + + if (n < 0) { + if (errno != EAGAIN) { + /* Error */ + req->error = errno; + stream->write_queue_size -= uv__write_req_size(req); + uv__write_req_finish(req); + return; + } + } else { + /* Successful write */ + + /* Update the counters. */ + while (n >= 0) { + uv_buf_t* buf = &(req->bufs[req->write_index]); + size_t len = buf->len; + + assert(req->write_index < req->bufcnt); + + if ((size_t)n < len) { + buf->base += n; + buf->len -= n; + stream->write_queue_size -= n; + n = 0; + + /* There is more to write. Break 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->bufcnt) { + /* 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); + + /* We're not done. */ + ev_io_start(stream->loop->ev, &stream->write_watcher); +} + + +static void uv__write_callbacks(uv_stream_t* stream) { + int callbacks_made = 0; + ngx_queue_t* q; + uv_write_t* req; + + while (!ngx_queue_empty(&stream->write_completed_queue)) { + /* Pop a req off write_completed_queue. */ + q = ngx_queue_head(&stream->write_completed_queue); + assert(q); + req = ngx_queue_data(q, struct uv_write_s, queue); + ngx_queue_remove(q); + + /* NOTE: call callback AFTER freeing the request data. */ + if (req->cb) { + uv_err_new_artificial(stream->loop, req->error); + req->cb(req, req->error ? -1 : 0); + } + + callbacks_made++; + } + + assert(ngx_queue_empty(&stream->write_completed_queue)); + + /* Write queue drained. */ + if (!uv_write_queue_head(stream)) { + uv__drain(stream); + } +} + + +static void uv__read(uv_stream_t* stream) { + uv_buf_t buf; + ssize_t nread; + struct ev_loop* ev = stream->loop->ev; + + /* XXX: Maybe instead of having UV_READING we just test if + * tcp->read_cb is NULL or not? + */ + while (stream->read_cb && ((uv_handle_t*)stream)->flags & UV_READING) { + assert(stream->alloc_cb); + buf = stream->alloc_cb((uv_handle_t*)stream, 64 * 1024); + + assert(buf.len > 0); + assert(buf.base); + assert(stream->fd >= 0); + + do { + nread = read(stream->fd, buf.base, buf.len); + } + while (nread < 0 && errno == EINTR); + + if (nread < 0) { + /* Error */ + if (errno == EAGAIN) { + /* Wait for the next one. */ + if (stream->flags & UV_READING) { + ev_io_start(ev, &stream->read_watcher); + } + uv_err_new(stream->loop, EAGAIN); + stream->read_cb(stream, 0, buf); + return; + } else { + /* Error. User should call uv_close(). */ + uv_err_new(stream->loop, errno); + stream->read_cb(stream, -1, buf); + assert(!ev_is_active(&stream->read_watcher)); + return; + } + } else if (nread == 0) { + /* EOF */ + uv_err_new_artificial(stream->loop, UV_EOF); + ev_io_stop(ev, &stream->read_watcher); + stream->read_cb(stream, -1, buf); + return; + } else { + /* Successful read */ + stream->read_cb(stream, nread, buf); + } + } +} + + +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"); + assert(stream->fd >= 0); + + if (!(stream->flags & UV_WRITABLE) || + stream->flags & UV_SHUT || + stream->flags & UV_CLOSED || + stream->flags & UV_CLOSING) { + uv_err_new(stream->loop, EINVAL); + return -1; + } + + /* Initialize request */ + uv__req_init((uv_req_t*)req); + req->handle = stream; + req->cb = cb; + + stream->shutdown_req = req; + req->type = UV_SHUTDOWN; + + ((uv_handle_t*)stream)->flags |= UV_SHUTTING; + + + ev_io_start(stream->loop->ev, &stream->write_watcher); + + return 0; +} + + +void uv__stream_io(EV_P_ ev_io* watcher, int revents) { + uv_stream_t* stream = watcher->data; + + assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || + stream->type == UV_TTY); + assert(watcher == &stream->read_watcher || + watcher == &stream->write_watcher); + assert(!(stream->flags & UV_CLOSING)); + + if (stream->connect_req) { + uv__stream_connect(stream); + } else { + assert(revents & (EV_READ | EV_WRITE)); + assert(stream->fd >= 0); + + if (revents & EV_READ) { + uv__read((uv_stream_t*)stream); + } + + if (revents & EV_WRITE) { + uv__write(stream); + uv__write_callbacks(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. */ + assert(stream->fd >= 0); + getsockopt(stream->fd, SOL_SOCKET, SO_ERROR, &error, &errorsize); + } + + if (!error) { + ev_io_start(stream->loop->ev, &stream->read_watcher); + + /* Successful connection */ + stream->connect_req = NULL; + if (req->cb) { + req->cb(req, 0); + } + + } else if (error == EINPROGRESS) { + /* Still connecting. */ + return; + } else { + /* Error */ + uv_err_new(stream->loop, error); + + stream->connect_req = NULL; + if (req->cb) { + req->cb(req, -1); + } + } +} + + +int uv__connect(uv_connect_t* req, uv_stream_t* stream, struct sockaddr* addr, + socklen_t addrlen, uv_connect_cb cb) { + int sockfd; + int r; + + if (stream->fd <= 0) { + if ((sockfd = uv__socket(addr->sa_family, SOCK_STREAM, 0)) == -1) { + uv_err_new(stream->loop, errno); + return -1; + } + + if (uv__stream_open(stream, sockfd, UV_READABLE | UV_WRITABLE)) { + uv__close(sockfd); + return -2; + } + } + + uv__req_init((uv_req_t*)req); + req->cb = cb; + req->handle = stream; + req->type = UV_CONNECT; + ngx_queue_init(&req->queue); + + if (stream->connect_req) { + uv_err_new(stream->loop, EALREADY); + return -1; + } + + if (stream->type != UV_TCP) { + uv_err_new(stream->loop, ENOTSOCK); + return -1; + } + + stream->connect_req = req; + + do { + r = connect(stream->fd, addr, addrlen); + } + while (r == -1 && errno == EINTR); + + stream->delayed_error = 0; + + if (r != 0 && errno != EINPROGRESS) { + switch (errno) { + /* If we get a ECONNREFUSED wait until the next tick to report the + * error. Solaris wants to report immediately--other unixes want to + * wait. + */ + case ECONNREFUSED: + stream->delayed_error = errno; + break; + + default: + uv_err_new(stream->loop, errno); + return -1; + } + } + + assert(stream->write_watcher.data == stream); + ev_io_start(stream->loop->ev, &stream->write_watcher); + + if (stream->delayed_error) { + ev_feed_event(stream->loop->ev, &stream->write_watcher, EV_WRITE); + } + + 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* stream, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb) { + int empty_queue; + + 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 (stream->fd < 0) { + uv_err_new(stream->loop, EBADF); + return -1; + } + + empty_queue = (stream->write_queue_size == 0); + + /* Initialize the req */ + uv__req_init((uv_req_t*) req); + req->cb = cb; + req->handle = stream; + req->error = 0; + req->type = UV_WRITE; + ngx_queue_init(&req->queue); + + if (bufcnt <= UV_REQ_BUFSML_SIZE) { + req->bufs = req->bufsml; + } + else { + req->bufs = malloc(sizeof(uv_buf_t) * bufcnt); + } + + memcpy(req->bufs, bufs, bufcnt * sizeof(uv_buf_t)); + req->bufcnt = bufcnt; + + /* + * fprintf(stderr, "cnt: %d bufs: %p bufsml: %p\n", bufcnt, req->bufs, req->bufsml); + */ + + req->write_index = 0; + stream->write_queue_size += uv__buf_count(bufs, bufcnt); + + /* Append the request to write_queue. */ + ngx_queue_insert_tail(&stream->write_queue, &req->queue); + + assert(!ngx_queue_empty(&stream->write_queue)); + assert(stream->write_watcher.cb == uv__stream_io); + assert(stream->write_watcher.data == stream); + assert(stream->write_watcher.fd == stream->fd); + + /* 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 (empty_queue) { + uv__write(stream); + } else { + ev_io_start(stream->loop->ev, &stream->write_watcher); + } + + return 0; +} + + +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) { + uv_err_new(stream->loop, EINVAL); + return -1; + } + + /* The UV_READING flag is irrelevant of the state of the tcp - it just + * expresses the desired state of the user. + */ + ((uv_handle_t*)stream)->flags |= UV_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(stream->fd >= 0); + assert(alloc_cb); + + stream->read_cb = read_cb; + stream->alloc_cb = alloc_cb; + + /* These should have been set by uv_tcp_init. */ + assert(stream->read_watcher.cb == uv__stream_io); + + ev_io_start(stream->loop->ev, &stream->read_watcher); + return 0; +} + + +int uv_read_stop(uv_stream_t* stream) { + uv_tcp_t* tcp = (uv_tcp_t*)stream; + + ((uv_handle_t*)tcp)->flags &= ~UV_READING; + + ev_io_stop(tcp->loop->ev, &tcp->read_watcher); + tcp->read_cb = NULL; + tcp->alloc_cb = NULL; + return 0; +} + + diff --git a/src/rt/libuv/src/uv-sunos.c b/src/rt/libuv/src/unix/sunos.c similarity index 84% rename from src/rt/libuv/src/uv-sunos.c rename to src/rt/libuv/src/unix/sunos.c index 4a75461413e..0b5c03b5a69 100644 --- a/src/rt/libuv/src/uv-sunos.c +++ b/src/rt/libuv/src/unix/sunos.c @@ -22,8 +22,11 @@ #include #include -#include +#include +#include + #include +#include uint64_t uv_hrtime() { @@ -37,7 +40,7 @@ uint64_t uv_hrtime() { * we don't want to potentially create a race condition in the use of snprintf. */ int uv_exepath(char* buffer, size_t* size) { - size_t res; + ssize_t res; pid_t pid; char buf[128]; @@ -58,3 +61,17 @@ int uv_exepath(char* buffer, size_t* size) { *size = res; return (0); } + + +int uv_fs_event_init(uv_loop_t* loop, + uv_fs_event_t* handle, + const char* filename, + uv_fs_event_cb cb) { + uv_err_new(loop, ENOSYS); + return -1; +} + + +void uv__fs_event_destroy(uv_fs_event_t* handle) { + assert(0 && "implement me"); +} diff --git a/src/rt/libuv/src/unix/tcp.c b/src/rt/libuv/src/unix/tcp.c new file mode 100644 index 00000000000..9f14c1b6c0f --- /dev/null +++ b/src/rt/libuv/src/unix/tcp.c @@ -0,0 +1,280 @@ +/* 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 +#include + + +int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { + uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); + loop->counters.tcp_init++; + return 0; +} + + +static int uv__tcp_bind(uv_tcp_t* tcp, + int domain, + struct sockaddr* addr, + int addrsize) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (tcp->fd < 0) { + if ((tcp->fd = uv__socket(domain, SOCK_STREAM, 0)) == -1) { + uv_err_new(tcp->loop, errno); + goto out; + } + + if (uv__stream_open((uv_stream_t*)tcp, tcp->fd, UV_READABLE | UV_WRITABLE)) { + uv__close(tcp->fd); + tcp->fd = -1; + status = -2; + goto out; + } + } + + assert(tcp->fd >= 0); + + tcp->delayed_error = 0; + if (bind(tcp->fd, addr, addrsize) == -1) { + if (errno == EADDRINUSE) { + tcp->delayed_error = errno; + } else { + uv_err_new(tcp->loop, errno); + goto out; + } + } + status = 0; + +out: + errno = saved_errno; + return status; +} + + +int uv_tcp_bind(uv_tcp_t* tcp, struct sockaddr_in addr) { + if (addr.sin_family != AF_INET) { + uv_err_new(tcp->loop, EFAULT); + return -1; + } + + return uv__tcp_bind(tcp, + AF_INET, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)); +} + + +int uv_tcp_bind6(uv_tcp_t* tcp, struct sockaddr_in6 addr) { + if (addr.sin6_family != AF_INET6) { + uv_err_new(tcp->loop, EFAULT); + return -1; + } + + return uv__tcp_bind(tcp, + AF_INET6, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in6)); +} + + +int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + socklen_t socklen; + int saved_errno; + int rv = 0; + + /* Don't clobber errno. */ + saved_errno = errno; + + if (handle->delayed_error) { + uv_err_new(handle->loop, handle->delayed_error); + rv = -1; + goto out; + } + + if (handle->fd < 0) { + uv_err_new(handle->loop, EINVAL); + rv = -1; + goto out; + } + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t)*namelen; + + if (getsockname(handle->fd, name, &socklen) == -1) { + uv_err_new(handle->loop, errno); + rv = -1; + } else { + *namelen = (int)socklen; + } + +out: + errno = saved_errno; + return rv; +} + + +int uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + socklen_t socklen; + int saved_errno; + int rv = 0; + + /* Don't clobber errno. */ + saved_errno = errno; + + if (handle->delayed_error) { + uv_err_new(handle->loop, handle->delayed_error); + rv = -1; + goto out; + } + + if (handle->fd < 0) { + uv_err_new(handle->loop, EINVAL); + rv = -1; + goto out; + } + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t)*namelen; + + if (getpeername(handle->fd, name, &socklen) == -1) { + uv_err_new(handle->loop, errno); + rv = -1; + } else { + *namelen = (int)socklen; + } + +out: + errno = saved_errno; + return rv; +} + + +int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { + int r; + + if (tcp->delayed_error) { + uv_err_new(tcp->loop, tcp->delayed_error); + return -1; + } + + if (tcp->fd < 0) { + if ((tcp->fd = uv__socket(AF_INET, SOCK_STREAM, 0)) == -1) { + uv_err_new(tcp->loop, errno); + return -1; + } + + if (uv__stream_open((uv_stream_t*)tcp, tcp->fd, UV_READABLE)) { + uv__close(tcp->fd); + tcp->fd = -1; + return -1; + } + } + + assert(tcp->fd >= 0); + + r = listen(tcp->fd, backlog); + if (r < 0) { + uv_err_new(tcp->loop, errno); + return -1; + } + + tcp->connection_cb = cb; + + /* Start listening for connections. */ + ev_io_set(&tcp->read_watcher, tcp->fd, EV_READ); + ev_set_cb(&tcp->read_watcher, uv__server_io); + ev_io_start(tcp->loop->ev, &tcp->read_watcher); + + return 0; +} + + +int uv_tcp_connect(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr_in address, + uv_connect_cb cb) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->type != UV_TCP) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + if (address.sin_family != AF_INET) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + status = uv__connect(req, + (uv_stream_t*)handle, + (struct sockaddr*)&address, + sizeof address, + cb); + +out: + errno = saved_errno; + return status; +} + + +int uv_tcp_connect6(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr_in6 address, + uv_connect_cb cb) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->type != UV_TCP) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + if (address.sin6_family != AF_INET6) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + status = uv__connect(req, + (uv_stream_t*)handle, + (struct sockaddr*)&address, + sizeof address, + cb); + +out: + errno = saved_errno; + return status; +} diff --git a/src/rt/libuv/src/unix/tty.c b/src/rt/libuv/src/unix/tty.c new file mode 100644 index 00000000000..989c09d1bf1 --- /dev/null +++ b/src/rt/libuv/src/unix/tty.c @@ -0,0 +1,110 @@ +/* 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 +#include +#include +#include +#include + + +int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd) { + uv__nonblock(fd, 1); + uv__stream_init(loop, (uv_stream_t*)tty, UV_TTY); + uv__stream_open((uv_stream_t*)tty, fd, UV_READABLE | UV_WRITABLE); + loop->counters.tty_init++; + return 0; +} + + +int uv_tty_set_mode(uv_tty_t* tty, int mode) { + int fd = tty->fd; + struct termios orig_termios; /* in order to restore at exit */ + struct termios raw; + + if (tcgetattr(fd, &orig_termios) == -1) goto fatal; + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes */ + raw.c_oflag |= (ONLCR); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - echoing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(fd, TCSAFLUSH, &raw) < 0) goto fatal; + return 0; + +fatal: + uv_err_new(tty->loop, ENOTTY); + return -1; +} + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + struct winsize ws; + + if (ioctl(tty->fd, TIOCGWINSZ, &ws) < 0) { + uv_err_new(tty->loop, errno); + return -1; + } + + *width = ws.ws_col; + *height = ws.ws_row; + + return 0; +} + + +uv_handle_type uv_guess_handle(uv_file file) { + struct stat s; + + if (file < 0) { + uv_err_new(NULL, EINVAL); /* XXX Need loop? */ + return -1; + } + + if (isatty(file)) { + return UV_TTY; + } + + if (fstat(file, &s)) { + uv_err_new(NULL, errno); /* XXX Need loop? */ + return -1; + } + + if (!S_ISSOCK(s.st_mode) && !S_ISFIFO(s.st_mode)) { + return UV_FILE; + } + + return UV_NAMED_PIPE; +} diff --git a/src/rt/libuv/src/unix/udp.c b/src/rt/libuv/src/unix/udp.c new file mode 100644 index 00000000000..20e5f3798b9 --- /dev/null +++ b/src/rt/libuv/src/unix/udp.c @@ -0,0 +1,555 @@ +/* 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 +#include +#include +#include + + +static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w); +static void uv__udp_run_completed(uv_udp_t* handle); +static void uv__udp_run_pending(uv_udp_t* handle); +static void uv__udp_recvmsg(uv_udp_t* handle); +static void uv__udp_sendmsg(uv_udp_t* handle); +static void uv__udp_io(EV_P_ ev_io* w, int events); +static int uv__udp_bind(uv_udp_t* handle, int domain, struct sockaddr* addr, + socklen_t len, unsigned flags); +static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain); +static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr* addr, socklen_t addrlen, uv_udp_send_cb send_cb); + + +static void uv__udp_watcher_start(uv_udp_t* handle, ev_io* w) { + int flags; + + assert(w == &handle->read_watcher + || w == &handle->write_watcher); + + flags = (w == &handle->read_watcher ? EV_READ : EV_WRITE); + + w->data = handle; + ev_set_cb(w, uv__udp_io); + ev_io_set(w, handle->fd, flags); + ev_io_start(handle->loop->ev, w); +} + + +void uv__udp_watcher_stop(uv_udp_t* handle, ev_io* w) { + int flags; + + assert(w == &handle->read_watcher + || w == &handle->write_watcher); + + flags = (w == &handle->read_watcher ? EV_READ : EV_WRITE); + + ev_io_stop(handle->loop->ev, w); + ev_io_set(w, -1, flags); + ev_set_cb(w, NULL); + w->data = (void*)0xDEADBABE; +} + + +void uv__udp_destroy(uv_udp_t* handle) { + uv_udp_send_t* req; + ngx_queue_t* q; + + uv__udp_run_completed(handle); + + while (!ngx_queue_empty(&handle->write_queue)) { + q = ngx_queue_head(&handle->write_queue); + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_udp_send_t, queue); + if (req->send_cb) { + /* FIXME proper error code like UV_EABORTED */ + uv_err_new_artificial(handle->loop, UV_EINTR); + req->send_cb(req, -1); + } + } + + /* Now tear down the handle. */ + handle->flags = 0; + handle->recv_cb = NULL; + handle->alloc_cb = NULL; + /* but _do not_ touch close_cb */ + + if (handle->fd != -1) { + uv__close(handle->fd); + handle->fd = -1; + } + + uv__udp_watcher_stop(handle, &handle->read_watcher); + uv__udp_watcher_stop(handle, &handle->write_watcher); +} + + +static void uv__udp_run_pending(uv_udp_t* handle) { + uv_udp_send_t* req; + ngx_queue_t* q; + struct msghdr h; + ssize_t size; + + while (!ngx_queue_empty(&handle->write_queue)) { + q = ngx_queue_head(&handle->write_queue); + assert(q != NULL); + + req = ngx_queue_data(q, uv_udp_send_t, queue); + assert(req != NULL); + + memset(&h, 0, sizeof h); + h.msg_name = &req->addr; + h.msg_namelen = req->addrlen; + h.msg_iov = (struct iovec*)req->bufs; + h.msg_iovlen = req->bufcnt; + + do { + size = sendmsg(handle->fd, &h, 0); + } + while (size == -1 && errno == EINTR); + + /* TODO try to write once or twice more in the + * hope that the socket becomes readable again? + */ + if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + break; + + req->status = (size == -1 ? -errno : size); + +#ifndef NDEBUG + /* Sanity check. */ + if (size != -1) { + ssize_t nbytes; + int i; + + for (nbytes = i = 0; i < req->bufcnt; i++) + nbytes += req->bufs[i].len; + + assert(size == nbytes); + } +#endif + + /* 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. + */ + ngx_queue_remove(&req->queue); + ngx_queue_insert_tail(&handle->write_completed_queue, &req->queue); + } +} + + +static void uv__udp_run_completed(uv_udp_t* handle) { + uv_udp_send_t* req; + ngx_queue_t* q; + + while (!ngx_queue_empty(&handle->write_completed_queue)) { + q = ngx_queue_head(&handle->write_completed_queue); + assert(q != NULL); + + ngx_queue_remove(q); + + req = ngx_queue_data(q, uv_udp_send_t, queue); + assert(req != NULL); + + if (req->bufs != req->bufsml) + free(req->bufs); + + 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 { + uv_err_new(handle->loop, -req->status); + req->send_cb(req, -1); + } + } +} + + +static void uv__udp_recvmsg(uv_udp_t* handle) { + struct sockaddr_storage peer; + struct msghdr h; + ssize_t nread; + uv_buf_t buf; + int flags; + + assert(handle->recv_cb != NULL); + assert(handle->alloc_cb != NULL); + + do { + /* FIXME: hoist alloc_cb out the loop but for now follow uv__read() */ + buf = handle->alloc_cb((uv_handle_t*)handle, 64 * 1024); + assert(buf.len > 0); + assert(buf.base != NULL); + + memset(&h, 0, sizeof h); + h.msg_name = &peer; + h.msg_namelen = sizeof peer; + h.msg_iov = (struct iovec*)&buf; + h.msg_iovlen = 1; + + do { + nread = recvmsg(handle->fd, &h, 0); + } + while (nread == -1 && errno == EINTR); + + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + uv_err_new(handle->loop, EAGAIN); + handle->recv_cb(handle, 0, buf, NULL, 0); + } + else { + uv_err_new(handle->loop, errno); + handle->recv_cb(handle, -1, buf, NULL, 0); + } + } + else { + flags = 0; + + if (h.msg_flags & MSG_TRUNC) + flags |= UV_UDP_PARTIAL; + + handle->recv_cb(handle, + nread, + buf, + (struct sockaddr*)&peer, + flags); + } + } + /* recv_cb callback may decide to pause or close the handle */ + while (nread != -1 + && handle->fd != -1 + && handle->recv_cb != NULL); +} + + +static void uv__udp_sendmsg(uv_udp_t* handle) { + assert(!ngx_queue_empty(&handle->write_queue) + || !ngx_queue_empty(&handle->write_completed_queue)); + + /* Write out pending data first. */ + uv__udp_run_pending(handle); + + /* Drain 'request completed' queue. */ + uv__udp_run_completed(handle); + + if (!ngx_queue_empty(&handle->write_completed_queue)) { + /* Schedule completion callbacks. */ + ev_feed_event(handle->loop->ev, &handle->write_watcher, EV_WRITE); + } + else if (ngx_queue_empty(&handle->write_queue)) { + /* Pending queue and completion queue empty, stop watcher. */ + uv__udp_watcher_stop(handle, &handle->write_watcher); + } +} + + +static void uv__udp_io(EV_P_ ev_io* w, int events) { + uv_udp_t* handle; + + handle = w->data; + assert(handle != NULL); + assert(handle->type == UV_UDP); + assert(handle->fd >= 0); + assert(!(events & ~(EV_READ|EV_WRITE))); + + if (events & EV_READ) + uv__udp_recvmsg(handle); + + if (events & EV_WRITE) + uv__udp_sendmsg(handle); +} + + +static int uv__udp_bind(uv_udp_t* handle, + int domain, + struct sockaddr* addr, + socklen_t len, + unsigned flags) { + int saved_errno; + int status; + int yes; + int fd; + + saved_errno = errno; + status = -1; + + /* Check for bad flags. */ + if (flags & ~UV_UDP_IPV6ONLY) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + /* Cannot set IPv6-only mode on non-IPv6 socket. */ + if ((flags & UV_UDP_IPV6ONLY) && domain != AF_INET6) { + uv_err_new(handle->loop, EINVAL); + goto out; + } + + /* Check for already active socket. */ + if (handle->fd != -1) { + uv_err_new_artificial(handle->loop, UV_EALREADY); + goto out; + } + + if ((fd = uv__socket(domain, SOCK_DGRAM, 0)) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + if (flags & UV_UDP_IPV6ONLY) { +#ifdef IPV6_V6ONLY + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } +#else + uv_err_new((uv_handle_t*)handle, ENOTSUP); + goto out; +#endif + } + + if (bind(fd, addr, len) == -1) { + uv_err_new(handle->loop, errno); + goto out; + } + + handle->fd = fd; + status = 0; + +out: + if (status) + uv__close(fd); + + errno = saved_errno; + return status; +} + + +static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain) { + struct sockaddr_storage taddr; + socklen_t addrlen; + + assert(domain == AF_INET || domain == AF_INET6); + + if (handle->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; + } + 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; + } + default: + assert(0 && "unsupported address family"); + abort(); + } + + return uv__udp_bind(handle, domain, (struct sockaddr*)&taddr, addrlen, 0); +} + + +static int uv__udp_send(uv_udp_send_t* req, + uv_udp_t* handle, + uv_buf_t bufs[], + int bufcnt, + struct sockaddr* addr, + socklen_t addrlen, + uv_udp_send_cb send_cb) { + if (uv__udp_maybe_deferred_bind(handle, addr->sa_family)) + return -1; + + /* Don't use uv__req_init(), it zeroes the data field. */ + handle->loop->counters.req_init++; + + memcpy(&req->addr, addr, addrlen); + req->addrlen = addrlen; + req->send_cb = send_cb; + req->handle = handle; + req->bufcnt = bufcnt; + req->type = UV_UDP_SEND; + + if (bufcnt <= UV_REQ_BUFSML_SIZE) { + req->bufs = req->bufsml; + } + else if ((req->bufs = malloc(bufcnt * sizeof(bufs[0]))) == NULL) { + uv_err_new(handle->loop, ENOMEM); + return -1; + } + memcpy(req->bufs, bufs, bufcnt * sizeof(bufs[0])); + + ngx_queue_insert_tail(&handle->write_queue, &req->queue); + uv__udp_watcher_start(handle, &handle->write_watcher); + + return 0; +} + + +int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { + memset(handle, 0, sizeof *handle); + + uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP); + loop->counters.udp_init++; + + handle->fd = -1; + ngx_queue_init(&handle->write_queue); + ngx_queue_init(&handle->write_completed_queue); + + return 0; +} + + +int uv_udp_bind(uv_udp_t* handle, struct sockaddr_in addr, unsigned flags) { + return uv__udp_bind(handle, + AF_INET, + (struct sockaddr*)&addr, + sizeof addr, + flags); +} + + +int uv_udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags) { + return uv__udp_bind(handle, + AF_INET6, + (struct sockaddr*)&addr, + sizeof addr, + flags); +} + + +int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, + int* namelen) { + socklen_t socklen; + int saved_errno; + int rv = 0; + + /* Don't clobber errno. */ + saved_errno = errno; + + if (handle->fd < 0) { + uv_err_new(handle->loop, EINVAL); + rv = -1; + goto out; + } + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t)*namelen; + + if (getsockname(handle->fd, name, &socklen) == -1) { + uv_err_new(handle->loop, errno); + rv = -1; + } else { + *namelen = (int)socklen; + } + +out: + errno = saved_errno; + return rv; +} + + +int uv_udp_send(uv_udp_send_t* req, + uv_udp_t* handle, + uv_buf_t bufs[], + int bufcnt, + struct sockaddr_in addr, + uv_udp_send_cb send_cb) { + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*)&addr, + sizeof addr, + send_cb); +} + + +int uv_udp_send6(uv_udp_send_t* req, + uv_udp_t* handle, + uv_buf_t bufs[], + int bufcnt, + struct sockaddr_in6 addr, + uv_udp_send_cb send_cb) { + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*)&addr, + sizeof addr, + send_cb); +} + + +int uv_udp_recv_start(uv_udp_t* handle, + uv_alloc_cb alloc_cb, + uv_udp_recv_cb recv_cb) { + if (alloc_cb == NULL || recv_cb == NULL) { + uv_err_new_artificial(handle->loop, UV_EINVAL); + return -1; + } + + if (ev_is_active(&handle->read_watcher)) { + uv_err_new_artificial(handle->loop, UV_EALREADY); + return -1; + } + + if (uv__udp_maybe_deferred_bind(handle, AF_INET)) + return -1; + + handle->alloc_cb = alloc_cb; + handle->recv_cb = recv_cb; + uv__udp_watcher_start(handle, &handle->read_watcher); + + return 0; +} + + +int uv_udp_recv_stop(uv_udp_t* handle) { + uv__udp_watcher_stop(handle, &handle->read_watcher); + handle->alloc_cb = NULL; + handle->recv_cb = NULL; + return 0; +} diff --git a/src/rt/libuv/src/uv-eio.c b/src/rt/libuv/src/unix/uv-eio.c similarity index 59% rename from src/rt/libuv/src/uv-eio.c rename to src/rt/libuv/src/unix/uv-eio.c index b07735cad8a..84afe09b747 100644 --- a/src/rt/libuv/src/uv-eio.c +++ b/src/rt/libuv/src/unix/uv-eio.c @@ -22,51 +22,55 @@ #include "uv.h" #include "eio.h" + #include +#include -static uv_async_t uv_eio_want_poll_notifier; -static uv_async_t uv_eio_done_poll_notifier; -static uv_idle_t uv_eio_poller; -static int uv_eio_init_count; +/* TODO remove me! */ +static uv_loop_t* main_loop; static void uv_eio_do_poll(uv_idle_t* watcher, int status) { - assert(watcher == &uv_eio_poller); + assert(watcher == &(watcher->loop->uv_eio_poller)); /* printf("uv_eio_poller\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &uv_eio_poller)) { + if (eio_poll() != -1 && uv_is_active((uv_handle_t*) watcher)) { /* printf("uv_eio_poller stop\n"); */ - uv_idle_stop(&uv_eio_poller); - uv_unref(); + uv_idle_stop(watcher); + uv_unref(watcher->loop); } } /* Called from the main thread. */ static void uv_eio_want_poll_notifier_cb(uv_async_t* watcher, int status) { - assert(watcher == &uv_eio_want_poll_notifier); + uv_loop_t* loop = watcher->loop; + + assert(watcher == &loop->uv_eio_want_poll_notifier); /* printf("want poll notifier\n"); */ - if (eio_poll() == -1 && !uv_is_active((uv_handle_t*) &uv_eio_poller)) { + if (eio_poll() == -1 && !uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller start\n"); */ - uv_idle_start(&uv_eio_poller, uv_eio_do_poll); - uv_ref(); + uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); + uv_ref(loop); } } static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { - assert(watcher == &uv_eio_done_poll_notifier); + uv_loop_t* loop = watcher->loop; + + assert(watcher == &loop->uv_eio_done_poll_notifier); /* printf("done poll notifier\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &uv_eio_poller)) { + if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller stop\n"); */ - uv_idle_stop(&uv_eio_poller); - uv_unref(); + uv_idle_stop(&loop->uv_eio_poller); + uv_unref(loop); } } @@ -77,7 +81,13 @@ static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { */ static void uv_eio_want_poll(void) { /* Signal the main thread that eio_poll need to be processed. */ - uv_async_send(&uv_eio_want_poll_notifier); + + /* + * TODO need to select the correct uv_loop_t and async_send to + * uv_eio_want_poll_notifier. + */ + + uv_async_send(&main_loop->uv_eio_want_poll_notifier); } @@ -86,22 +96,27 @@ static void uv_eio_done_poll(void) { * Signal the main thread that we should stop calling eio_poll(). * from the idle watcher. */ - uv_async_send(&uv_eio_done_poll_notifier); + uv_async_send(&main_loop->uv_eio_done_poll_notifier); } -void uv_eio_init() { - if (uv_eio_init_count == 0) { - uv_eio_init_count++; +void uv_eio_init(uv_loop_t* loop) { + if (loop->counters.eio_init == 0) { + loop->counters.eio_init++; - uv_idle_init(&uv_eio_poller); - uv_idle_start(&uv_eio_poller, uv_eio_do_poll); + main_loop = loop; - uv_async_init(&uv_eio_want_poll_notifier, uv_eio_want_poll_notifier_cb); - uv_unref(); + uv_idle_init(loop, &loop->uv_eio_poller); + uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); - uv_async_init(&uv_eio_done_poll_notifier, uv_eio_done_poll_notifier_cb); - uv_unref(); + loop->uv_eio_want_poll_notifier.data = loop; + uv_async_init(loop, &loop->uv_eio_want_poll_notifier, + uv_eio_want_poll_notifier_cb); + uv_unref(loop); + + uv_async_init(loop, &loop->uv_eio_done_poll_notifier, + uv_eio_done_poll_notifier_cb); + uv_unref(loop); eio_init(uv_eio_want_poll, uv_eio_done_poll); /* @@ -109,5 +124,11 @@ void uv_eio_init() { * race conditions. See Node's test/simple/test-eio-race.js */ eio_set_max_poll_reqs(10); + } else { + /* + * If this assertion breaks then Ryan hasn't implemented support for + * receiving thread pool requests back to multiple threads. + */ + assert(main_loop == loop); } } diff --git a/src/rt/libuv/src/uv-eio.h b/src/rt/libuv/src/unix/uv-eio.h similarity index 88% rename from src/rt/libuv/src/uv-eio.h rename to src/rt/libuv/src/unix/uv-eio.h index 2f2d3486bf3..711d0cf281c 100644 --- a/src/rt/libuv/src/uv-eio.h +++ b/src/rt/libuv/src/unix/uv-eio.h @@ -9,5 +9,5 @@ * safe to call more than once. * TODO: uv_eio_deinit */ -void uv_eio_init(void); +void uv_eio_init(uv_loop_t*); #endif diff --git a/src/rt/libuv/src/uv-common.c b/src/rt/libuv/src/uv-common.c index 2c4e0cc7659..51188337bd9 100644 --- a/src/rt/libuv/src/uv-common.c +++ b/src/rt/libuv/src/uv-common.c @@ -29,9 +29,7 @@ /* use inet_pton from c-ares if necessary */ #include "ares_config.h" #include "ares/inet_net_pton.h" - -/* list used for ares task handles */ -static uv_ares_task_t* uv_ares_handles_ = NULL; +#include "ares/inet_ntop.h" static uv_counters_t counters; @@ -42,6 +40,14 @@ uv_counters_t* uv_counters() { } +uv_buf_t uv_buf_init(char* base, size_t len) { + uv_buf_t buf; + buf.base = base; + buf.len = len; + return buf; +} + + const char* uv_err_name(uv_err_t err) { switch (err.code) { case UV_UNKNOWN: return "UNKNOWN"; @@ -75,10 +81,13 @@ const char* uv_err_name(uv_err_t err) { case UV_ENOTCONN: return "ENOTCONN"; case UV_ENOTSOCK: return "ENOTSOCK"; case UV_ENOTSUP: return "ENOTSUP"; + case UV_ENOENT: return "ENOENT"; + case UV_EPIPE: return "EPIPE"; case UV_EPROTO: return "EPROTO"; case UV_EPROTONOSUPPORT: return "EPROTONOSUPPORT"; case UV_EPROTOTYPE: return "EPROTOTYPE"; case UV_ETIMEDOUT: return "ETIMEDOUT"; + case UV_EEXIST: return "EEXIST"; default: assert(0); return NULL; @@ -112,21 +121,36 @@ struct sockaddr_in6 uv_ip6_addr(const char* ip, int port) { } +int uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size) { + const char* d = ares_inet_ntop(AF_INET, &src->sin_addr, dst, size); + return d != dst; +} + + +int uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size) { + const char* d = ares_inet_ntop(AF_INET6, &src->sin6_addr, dst, size); + return d != dst; +} + + /* find matching ares handle in list */ -void uv_add_ares_handle(uv_ares_task_t* handle) { - handle->ares_next = uv_ares_handles_; +void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle) { + handle->loop = loop; + handle->ares_next = loop->uv_ares_handles_; handle->ares_prev = NULL; - if (uv_ares_handles_) { - uv_ares_handles_->ares_prev = handle; + if (loop->uv_ares_handles_) { + loop->uv_ares_handles_->ares_prev = handle; } - uv_ares_handles_ = handle; + + loop->uv_ares_handles_ = handle; } /* find matching ares handle in list */ /* TODO: faster lookup */ -uv_ares_task_t* uv_find_ares_handle(ares_socket_t sock) { - uv_ares_task_t* handle = uv_ares_handles_; +uv_ares_task_t* uv_find_ares_handle(uv_loop_t* loop, ares_socket_t sock) { + uv_ares_task_t* handle = loop->uv_ares_handles_; + while (handle != NULL) { if (handle->sock == sock) { break; @@ -139,8 +163,10 @@ uv_ares_task_t* uv_find_ares_handle(ares_socket_t sock) { /* remove ares handle in list */ void uv_remove_ares_handle(uv_ares_task_t* handle) { - if (handle == uv_ares_handles_) { - uv_ares_handles_ = handle->ares_next; + uv_loop_t* loop = handle->loop; + + if (handle == loop->uv_ares_handles_) { + loop->uv_ares_handles_ = handle->ares_next; } if (handle->ares_next) { @@ -154,6 +180,6 @@ void uv_remove_ares_handle(uv_ares_task_t* handle) { /* Returns 1 if the uv_ares_handles_ list is empty. 0 otherwise. */ -int uv_ares_handles_empty() { - return uv_ares_handles_ ? 0 : 1; +int uv_ares_handles_empty(uv_loop_t* loop) { + return loop->uv_ares_handles_ ? 0 : 1; } diff --git a/src/rt/libuv/src/uv-common.h b/src/rt/libuv/src/uv-common.h index 74b4c4e38bb..ae543195569 100644 --- a/src/rt/libuv/src/uv-common.h +++ b/src/rt/libuv/src/uv-common.h @@ -29,10 +29,8 @@ #include "uv.h" -/* - * Subclass of uv_handle_t. Used for integration of c-ares. - */ -typedef struct uv_ares_task_s uv_ares_task_t; +#define COUNTOF(a) (sizeof(a) / sizeof(a[0])) + struct uv_ares_task_s { UV_HANDLE_FIELDS @@ -43,9 +41,12 @@ struct uv_ares_task_s { void uv_remove_ares_handle(uv_ares_task_t* handle); -uv_ares_task_t* uv_find_ares_handle(ares_socket_t sock); -void uv_add_ares_handle(uv_ares_task_t* handle); -int uv_ares_handles_empty(); +uv_ares_task_t* uv_find_ares_handle(uv_loop_t*, ares_socket_t sock); + +/* TODO Rename to uv_ares_task_init? */ +void uv_add_ares_handle(uv_loop_t* loop, uv_ares_task_t* handle); + +int uv_ares_handles_empty(uv_loop_t* loop); #endif /* UV_COMMON_H_ */ diff --git a/src/rt/libuv/src/uv-unix.c b/src/rt/libuv/src/uv-unix.c deleted file mode 100644 index e29c842bfae..00000000000 --- a/src/rt/libuv/src/uv-unix.c +++ /dev/null @@ -1,1596 +0,0 @@ -/* 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 "uv-eio.h" - -#include /* NULL */ -#include /* printf */ -#include -#include /* strerror */ -#include -#include -#include -#include -#include -#include -#include -#include /* PATH_MAX */ - -#if defined(__APPLE__) -#include /* _NSGetExecutablePath */ -#endif - -#if defined(__FreeBSD__) -#include -#endif - - -static uv_err_t last_err; - -struct uv_ares_data_s { - ares_channel channel; - /* - * While the channel is active this timer is called once per second to be sure - * that we're always calling ares_process. See the warning above the - * definition of ares_timeout(). - */ - ev_timer timer; -}; - -static struct uv_ares_data_s ares_data; - - -void uv__tcp_io(EV_P_ ev_io* watcher, int revents); -void uv__next(EV_P_ ev_idle* watcher, int revents); -static void uv__tcp_connect(uv_tcp_t*); -int uv_tcp_open(uv_tcp_t*, int fd); -static void uv__finish_close(uv_handle_t* handle); - -/* flags */ -enum { - UV_CLOSING = 0x00000001, /* uv_close() called but not finished. */ - UV_CLOSED = 0x00000002, /* close(2) finished. */ - UV_READING = 0x00000004, /* uv_read_start() called. */ - UV_SHUTTING = 0x00000008, /* uv_shutdown() called but not complete. */ - UV_SHUT = 0x00000010 /* Write side closed. */ -}; - - -void uv_flag_set(uv_handle_t* handle, int flag) { - handle->flags |= flag; -} - - -/* TODO Share this code with Windows. */ -/* TODO Expose callback to user to handle fatal error like V8 does. */ -static void uv_fatal_error(const int errorno, const char* syscall) { - char* buf = NULL; - const char* errmsg; - - if (buf) { - errmsg = buf; - } else { - errmsg = "Unknown error"; - } - - if (syscall) { - fprintf(stderr, "\nlibuv fatal error. %s: (%d) %s\n", syscall, errorno, - errmsg); - } else { - fprintf(stderr, "\nlibuv fatal error. (%d) %s\n", errorno, errmsg); - } - - abort(); -} - - -uv_err_t uv_last_error() { - return last_err; -} - - -char* uv_strerror(uv_err_t err) { - return strerror(err.sys_errno_); -} - - -void uv_flag_unset(uv_handle_t* handle, int flag) { - handle->flags = handle->flags & ~flag; -} - - -int uv_flag_is_set(uv_handle_t* handle, int flag) { - return (handle->flags & flag) != 0; -} - - -static uv_err_code uv_translate_sys_error(int sys_errno) { - switch (sys_errno) { - case 0: return UV_OK; - case EACCES: return UV_EACCESS; - case EAGAIN: return UV_EAGAIN; - case ECONNRESET: return UV_ECONNRESET; - case EFAULT: return UV_EFAULT; - case EMFILE: return UV_EMFILE; - case EINVAL: return UV_EINVAL; - case ECONNREFUSED: return UV_ECONNREFUSED; - case EADDRINUSE: return UV_EADDRINUSE; - case EADDRNOTAVAIL: return UV_EADDRNOTAVAIL; - default: return UV_UNKNOWN; - } -} - - -static uv_err_t uv_err_new_artificial(uv_handle_t* handle, int code) { - uv_err_t err; - err.sys_errno_ = 0; - err.code = code; - last_err = err; - return err; -} - - -static uv_err_t uv_err_new(uv_handle_t* handle, int sys_error) { - uv_err_t err; - err.sys_errno_ = sys_error; - err.code = uv_translate_sys_error(sys_error); - last_err = err; - return err; -} - - -int uv_close(uv_handle_t* handle, uv_close_cb close_cb) { - uv_tcp_t* tcp; - uv_async_t* async; - uv_timer_t* timer; - - handle->close_cb = close_cb; - - switch (handle->type) { - case UV_TCP: - tcp = (uv_tcp_t*) handle; - ev_io_stop(EV_DEFAULT_ &tcp->write_watcher); - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - break; - - case UV_PREPARE: - uv_prepare_stop((uv_prepare_t*) handle); - break; - - case UV_CHECK: - uv_check_stop((uv_check_t*) handle); - break; - - case UV_IDLE: - uv_idle_stop((uv_idle_t*) handle); - break; - - case UV_ASYNC: - async = (uv_async_t*)handle; - ev_async_stop(EV_DEFAULT_ &async->async_watcher); - ev_ref(EV_DEFAULT_UC); - break; - - case UV_TIMER: - timer = (uv_timer_t*)handle; - if (ev_is_active(&timer->timer_watcher)) { - ev_ref(EV_DEFAULT_UC); - } - ev_timer_stop(EV_DEFAULT_ &timer->timer_watcher); - break; - - default: - assert(0); - return -1; - } - - uv_flag_set(handle, UV_CLOSING); - - /* This is used to call the on_close callback in the next loop. */ - ev_idle_start(EV_DEFAULT_ &handle->next_watcher); - ev_feed_event(EV_DEFAULT_ &handle->next_watcher, EV_IDLE); - assert(ev_is_pending(&handle->next_watcher)); - - return 0; -} - - -void uv_init() { - /* Initialize the default ev loop. */ -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 - ev_default_loop(EVBACKEND_KQUEUE); -#else - ev_default_loop(EVFLAG_AUTO); -#endif -} - - -int uv_run() { - ev_run(EV_DEFAULT_ 0); - return 0; -} - - -static void uv__handle_init(uv_handle_t* handle, uv_handle_type type) { - uv_counters()->handle_init++; - - handle->type = type; - handle->flags = 0; - - ev_init(&handle->next_watcher, uv__next); - handle->next_watcher.data = handle; - - /* Ref the loop until this handle is closed. See uv__finish_close. */ - ev_ref(EV_DEFAULT_UC); -} - - -int uv_tcp_init(uv_tcp_t* tcp) { - uv__handle_init((uv_handle_t*)tcp, UV_TCP); - uv_counters()->tcp_init++; - - tcp->alloc_cb = NULL; - tcp->connect_req = NULL; - tcp->accepted_fd = -1; - tcp->fd = -1; - tcp->delayed_error = 0; - ngx_queue_init(&tcp->write_queue); - ngx_queue_init(&tcp->write_completed_queue); - tcp->write_queue_size = 0; - - ev_init(&tcp->read_watcher, uv__tcp_io); - tcp->read_watcher.data = tcp; - - ev_init(&tcp->write_watcher, uv__tcp_io); - tcp->write_watcher.data = tcp; - - assert(ngx_queue_empty(&tcp->write_queue)); - assert(ngx_queue_empty(&tcp->write_completed_queue)); - assert(tcp->write_queue_size == 0); - - return 0; -} - - -int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, int addrsize) { - int r; - - if (tcp->fd <= 0) { - int fd = socket(domain, SOCK_STREAM, 0); - - if (fd < 0) { - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - - if (uv_tcp_open(tcp, fd)) { - close(fd); - return -2; - } - } - - assert(tcp->fd >= 0); - - r = bind(tcp->fd, addr, addrsize); - tcp->delayed_error = 0; - - if (r) { - switch (errno) { - case EADDRINUSE: - tcp->delayed_error = errno; - return 0; - - default: - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - } - - return 0; -} - - -int uv_tcp_bind(uv_tcp_t* tcp, struct sockaddr_in addr) { - if (addr.sin_family != AF_INET) { - uv_err_new((uv_handle_t*)tcp, EFAULT); - return -1; - } - - return uv__bind(tcp, AF_INET, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); -} - - -int uv_tcp_bind6(uv_tcp_t* tcp, struct sockaddr_in6 addr) { - if (addr.sin6_family != AF_INET6) { - uv_err_new((uv_handle_t*)tcp, EFAULT); - return -1; - } - - return uv__bind(tcp, AF_INET6, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)); -} - - -int uv_tcp_open(uv_tcp_t* tcp, int fd) { - int yes; - int r; - - assert(fd >= 0); - tcp->fd = fd; - - /* Set non-blocking. */ - yes = 1; - r = fcntl(fd, F_SETFL, O_NONBLOCK); - assert(r == 0); - - /* Reuse the port address. */ - r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - assert(r == 0); - - /* Associate the fd with each ev_io watcher. */ - ev_io_set(&tcp->read_watcher, fd, EV_READ); - ev_io_set(&tcp->write_watcher, fd, EV_WRITE); - - /* These should have been set up by uv_tcp_init. */ - assert(tcp->next_watcher.data == tcp); - assert(tcp->write_watcher.data == tcp); - assert(tcp->read_watcher.data == tcp); - assert(tcp->read_watcher.cb == uv__tcp_io); - assert(tcp->write_watcher.cb == uv__tcp_io); - - return 0; -} - - -void uv__server_io(EV_P_ ev_io* watcher, int revents) { - int fd; - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(struct sockaddr_storage); - uv_tcp_t* tcp = watcher->data; - - assert(watcher == &tcp->read_watcher || - watcher == &tcp->write_watcher); - assert(revents == EV_READ); - - assert(!uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING)); - - if (tcp->accepted_fd >= 0) { - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - return; - } - - while (1) { - assert(tcp->accepted_fd < 0); - fd = accept(tcp->fd, (struct sockaddr*)&addr, &addrlen); - - if (fd < 0) { - if (errno == EAGAIN) { - /* No problem. */ - return; - } else if (errno == EMFILE) { - /* TODO special trick. unlock reserved socket, accept, close. */ - return; - } else { - uv_err_new((uv_handle_t*)tcp, errno); - tcp->connection_cb((uv_handle_t*)tcp, -1); - } - - } else { - tcp->accepted_fd = fd; - tcp->connection_cb((uv_handle_t*)tcp, 0); - if (tcp->accepted_fd >= 0) { - /* The user hasn't yet accepted called uv_accept() */ - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - return; - } - } - } -} - - -int uv_accept(uv_handle_t* server, uv_stream_t* client) { - uv_tcp_t* tcpServer = (uv_tcp_t*)server; - uv_tcp_t* tcpClient = (uv_tcp_t*)client; - - if (tcpServer->accepted_fd < 0) { - uv_err_new(server, EAGAIN); - return -1; - } - - if (uv_tcp_open(tcpClient, tcpServer->accepted_fd)) { - /* Ignore error for now */ - tcpServer->accepted_fd = -1; - close(tcpServer->accepted_fd); - return -1; - } else { - tcpServer->accepted_fd = -1; - ev_io_start(EV_DEFAULT_ &tcpServer->read_watcher); - return 0; - } -} - - -int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - int r; - - assert(tcp->fd >= 0); - - if (tcp->delayed_error) { - uv_err_new((uv_handle_t*)tcp, tcp->delayed_error); - return -1; - } - - r = listen(tcp->fd, backlog); - if (r < 0) { - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - - tcp->connection_cb = cb; - - /* Start listening for connections. */ - ev_io_set(&tcp->read_watcher, tcp->fd, EV_READ); - ev_set_cb(&tcp->read_watcher, uv__server_io); - ev_io_start(EV_DEFAULT_ &tcp->read_watcher); - - return 0; -} - - -void uv__finish_close(uv_handle_t* handle) { - uv_tcp_t* tcp; - - assert(uv_flag_is_set(handle, UV_CLOSING)); - assert(!uv_flag_is_set(handle, UV_CLOSED)); - uv_flag_set(handle, UV_CLOSED); - - switch (handle->type) { - case UV_TCP: - /* XXX Is it necessary to stop these watchers here? weren't they - * supposed to be stopped in uv_close()? - */ - tcp = (uv_tcp_t*)handle; - ev_io_stop(EV_DEFAULT_ &tcp->write_watcher); - ev_io_stop(EV_DEFAULT_ &tcp->read_watcher); - - assert(!ev_is_active(&tcp->read_watcher)); - assert(!ev_is_active(&tcp->write_watcher)); - - close(tcp->fd); - tcp->fd = -1; - - if (tcp->accepted_fd >= 0) { - close(tcp->accepted_fd); - tcp->accepted_fd = -1; - } - break; - - case UV_PREPARE: - assert(!ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher)); - break; - - case UV_CHECK: - assert(!ev_is_active(&((uv_check_t*)handle)->check_watcher)); - break; - - case UV_IDLE: - assert(!ev_is_active(&((uv_idle_t*)handle)->idle_watcher)); - break; - - case UV_ASYNC: - assert(!ev_is_active(&((uv_async_t*)handle)->async_watcher)); - break; - - case UV_TIMER: - assert(!ev_is_active(&((uv_timer_t*)handle)->timer_watcher)); - break; - - default: - assert(0); - break; - } - - ev_idle_stop(EV_DEFAULT_ &handle->next_watcher); - - if (handle->close_cb) { - handle->close_cb(handle); - } - - ev_unref(EV_DEFAULT_UC); -} - - -uv_req_t* uv_write_queue_head(uv_tcp_t* tcp) { - ngx_queue_t* q; - uv_req_t* req; - - if (ngx_queue_empty(&tcp->write_queue)) { - return NULL; - } - - q = ngx_queue_head(&tcp->write_queue); - if (!q) { - return NULL; - } - - req = ngx_queue_data(q, struct uv_req_s, queue); - assert(req); - - return req; -} - - -void uv__next(EV_P_ ev_idle* watcher, int revents) { - uv_handle_t* handle = watcher->data; - assert(watcher == &handle->next_watcher); - assert(revents == EV_IDLE); - - /* For now this function is only to handle the closing event, but we might - * put more stuff here later. - */ - assert(uv_flag_is_set(handle, UV_CLOSING)); - uv__finish_close(handle); -} - - -static void uv__drain(uv_tcp_t* tcp) { - uv_req_t* req; - uv_shutdown_cb cb; - - assert(!uv_write_queue_head(tcp)); - assert(tcp->write_queue_size == 0); - - ev_io_stop(EV_DEFAULT_ &tcp->write_watcher); - - /* Shutdown? */ - if (uv_flag_is_set((uv_handle_t*)tcp, UV_SHUTTING) && - !uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING) && - !uv_flag_is_set((uv_handle_t*)tcp, UV_SHUT)) { - assert(tcp->shutdown_req); - - req = tcp->shutdown_req; - cb = (uv_shutdown_cb)req->cb; - - if (shutdown(tcp->fd, SHUT_WR)) { - /* Error. Report it. User should call uv_close(). */ - uv_err_new((uv_handle_t*)tcp, errno); - if (cb) cb(req, -1); - } else { - uv_err_new((uv_handle_t*)tcp, 0); - uv_flag_set((uv_handle_t*)tcp, UV_SHUT); - if (cb) cb(req, 0); - } - } -} - - -/* On success returns NULL. On error returns a pointer to the write request - * which had the error. - */ -static uv_req_t* uv__write(uv_tcp_t* tcp) { - uv_req_t* req; - struct iovec* iov; - int iovcnt; - ssize_t n; - - assert(tcp->fd >= 0); - - /* TODO: should probably while(1) here until EAGAIN */ - - /* Get the request at the head of the queue. */ - req = uv_write_queue_head(tcp); - if (!req) { - assert(tcp->write_queue_size == 0); - return NULL; - } - - assert(req->handle == (uv_handle_t*)tcp); - - /* 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->bufcnt - req->write_index; - - /* 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 (iovcnt == 1) { - n = write(tcp->fd, iov[0].iov_base, iov[0].iov_len); - } - else { - n = writev(tcp->fd, iov, iovcnt); - } - - if (n < 0) { - if (errno != EAGAIN) { - /* Error */ - uv_err_new((uv_handle_t*)tcp, errno); - return req; - } - } else { - /* Successful write */ - - /* Update the counters. */ - while (n > 0) { - uv_buf_t* buf = &(req->bufs[req->write_index]); - size_t len = buf->len; - - assert(req->write_index < req->bufcnt); - - if (n < len) { - buf->base += n; - buf->len -= n; - tcp->write_queue_size -= n; - n = 0; - - /* There is more to write. Break and ensure the watcher is pending. */ - break; - - } else { - /* Finished writing the buf at index req->write_index. */ - req->write_index++; - - assert(n >= len); - n -= len; - - assert(tcp->write_queue_size >= len); - tcp->write_queue_size -= len; - - if (req->write_index == req->bufcnt) { - /* Then we're done! */ - assert(n == 0); - - /* Pop the req off tcp->write_queue. */ - ngx_queue_remove(&req->queue); - if (req->bufs != req->bufsml) { - free(req->bufs); - } - req->bufs = NULL; - - /* Add it to the write_completed_queue where it will have its - * callback called in the near future. - * TODO: start trying to write the next request. - */ - ngx_queue_insert_tail(&tcp->write_completed_queue, &req->queue); - ev_feed_event(EV_DEFAULT_ &tcp->write_watcher, EV_WRITE); - return NULL; - } - } - } - } - - /* Either we've counted n down to zero or we've got EAGAIN. */ - assert(n == 0 || n == -1); - - /* We're not done. */ - ev_io_start(EV_DEFAULT_ &tcp->write_watcher); - - return NULL; -} - - -static void uv__write_callbacks(uv_tcp_t* tcp) { - uv_write_cb cb; - int callbacks_made = 0; - ngx_queue_t* q; - uv_req_t* req; - - while (!ngx_queue_empty(&tcp->write_completed_queue)) { - /* Pop a req off write_completed_queue. */ - q = ngx_queue_head(&tcp->write_completed_queue); - assert(q); - req = ngx_queue_data(q, struct uv_req_s, queue); - ngx_queue_remove(q); - - cb = (uv_write_cb) req->cb; - - /* NOTE: call callback AFTER freeing the request data. */ - if (cb) { - cb(req, 0); - } - - callbacks_made++; - } - - assert(ngx_queue_empty(&tcp->write_completed_queue)); - - /* Write queue drained. */ - if (!uv_write_queue_head(tcp)) { - uv__drain(tcp); - } -} - - -void uv__read(uv_tcp_t* tcp) { - uv_buf_t buf; - struct iovec* iov; - ssize_t nread; - - /* XXX: Maybe instead of having UV_READING we just test if - * tcp->read_cb is NULL or not? - */ - while (tcp->read_cb && uv_flag_is_set((uv_handle_t*)tcp, UV_READING)) { - assert(tcp->alloc_cb); - buf = tcp->alloc_cb((uv_stream_t*)tcp, 64 * 1024); - - assert(buf.len > 0); - assert(buf.base); - - iov = (struct iovec*) &buf; - - nread = read(tcp->fd, buf.base, buf.len); - - if (nread < 0) { - /* Error */ - if (errno == EAGAIN) { - /* Wait for the next one. */ - if (uv_flag_is_set((uv_handle_t*)tcp, UV_READING)) { - ev_io_start(EV_DEFAULT_UC_ &tcp->read_watcher); - } - uv_err_new((uv_handle_t*)tcp, EAGAIN); - tcp->read_cb((uv_stream_t*)tcp, 0, buf); - return; - } else { - /* Error. User should call uv_close(). */ - uv_err_new((uv_handle_t*)tcp, errno); - tcp->read_cb((uv_stream_t*)tcp, -1, buf); - assert(!ev_is_active(&tcp->read_watcher)); - return; - } - } else if (nread == 0) { - /* EOF */ - uv_err_new_artificial((uv_handle_t*)tcp, UV_EOF); - ev_io_stop(EV_DEFAULT_UC_ &tcp->read_watcher); - tcp->read_cb((uv_stream_t*)tcp, -1, buf); - return; - } else { - /* Successful read */ - tcp->read_cb((uv_stream_t*)tcp, nread, buf); - } - } -} - - -int uv_shutdown(uv_req_t* req) { - uv_tcp_t* tcp = (uv_tcp_t*)req->handle; - assert(tcp->fd >= 0); - assert(tcp->type == UV_TCP); - - if (uv_flag_is_set((uv_handle_t*)tcp, UV_SHUT) || - uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSED) || - uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING)) { - return -1; - } - - tcp->shutdown_req = req; - req->type = UV_SHUTDOWN; - - uv_flag_set((uv_handle_t*)tcp, UV_SHUTTING); - - ev_io_start(EV_DEFAULT_UC_ &tcp->write_watcher); - - return 0; -} - - -void uv__tcp_io(EV_P_ ev_io* watcher, int revents) { - uv_tcp_t* tcp = watcher->data; - assert(watcher == &tcp->read_watcher || - watcher == &tcp->write_watcher); - - assert(tcp->fd >= 0); - - assert(!uv_flag_is_set((uv_handle_t*)tcp, UV_CLOSING)); - - if (tcp->connect_req) { - uv__tcp_connect(tcp); - } else { - if (revents & EV_READ) { - uv__read(tcp); - } - - if (revents & EV_WRITE) { - uv_req_t* req = uv__write(tcp); - if (req) { - /* Error. Notify the user. */ - uv_write_cb cb = (uv_write_cb) req->cb; - - if (cb) { - cb(req, -1); - } - } else { - uv__write_callbacks(tcp); - } - } - } -} - - -/** - * 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__tcp_connect(uv_tcp_t* tcp) { - int error; - uv_req_t* req; - uv_connect_cb connect_cb; - socklen_t errorsize = sizeof(int); - - assert(tcp->fd >= 0); - - req = tcp->connect_req; - assert(req); - - if (tcp->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 = tcp->delayed_error; - tcp->delayed_error = 0; - } else { - /* Normal situation: we need to get the socket error from the kernel. */ - getsockopt(tcp->fd, SOL_SOCKET, SO_ERROR, &error, &errorsize); - } - - if (!error) { - ev_io_start(EV_DEFAULT_ &tcp->read_watcher); - - /* Successful connection */ - tcp->connect_req = NULL; - connect_cb = (uv_connect_cb) req->cb; - if (connect_cb) { - connect_cb(req, 0); - } - - } else if (error == EINPROGRESS) { - /* Still connecting. */ - return; - } else { - /* Error */ - uv_err_t err = uv_err_new((uv_handle_t*)tcp, error); - - tcp->connect_req = NULL; - - connect_cb = (uv_connect_cb) req->cb; - if (connect_cb) { - connect_cb(req, -1); - } - } -} - - -static int uv__connect(uv_req_t* req, struct sockaddr* addr, - socklen_t addrlen) { - uv_tcp_t* tcp = (uv_tcp_t*)req->handle; - int r; - - if (tcp->fd <= 0) { - int fd = socket(addr->sa_family, SOCK_STREAM, 0); - - if (fd < 0) { - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - - if (uv_tcp_open(tcp, fd)) { - close(fd); - return -2; - } - } - - req->type = UV_CONNECT; - ngx_queue_init(&req->queue); - - if (tcp->connect_req) { - uv_err_new((uv_handle_t*)tcp, EALREADY); - return -1; - } - - if (tcp->type != UV_TCP) { - uv_err_new((uv_handle_t*)tcp, ENOTSOCK); - return -1; - } - - tcp->connect_req = req; - - r = connect(tcp->fd, addr, addrlen); - - tcp->delayed_error = 0; - - if (r != 0 && errno != EINPROGRESS) { - switch (errno) { - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. - */ - case ECONNREFUSED: - tcp->delayed_error = errno; - break; - - default: - uv_err_new((uv_handle_t*)tcp, errno); - return -1; - } - } - - assert(tcp->write_watcher.data == tcp); - ev_io_start(EV_DEFAULT_ &tcp->write_watcher); - - if (tcp->delayed_error) { - ev_feed_event(EV_DEFAULT_ &tcp->write_watcher, EV_WRITE); - } - - return 0; -} - - -int uv_tcp_connect(uv_req_t* req, struct sockaddr_in addr) { - assert(addr.sin_family == AF_INET); - return uv__connect(req, (struct sockaddr*) &addr, - sizeof(struct sockaddr_in)); -} - - -int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6 addr) { - assert(addr.sin6_family == AF_INET6); - return uv__connect(req, (struct sockaddr*) &addr, - sizeof(struct sockaddr_in6)); -} - - -static size_t uv__buf_count(uv_buf_t bufs[], int bufcnt) { - size_t total = 0; - int i; - - for (i = 0; i < bufcnt; i++) { - total += bufs[i].len; - } - - return total; -} - - -/* 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_req_t* req, uv_buf_t bufs[], int bufcnt) { - uv_tcp_t* tcp = (uv_tcp_t*)req->handle; - int empty_queue = (tcp->write_queue_size == 0); - assert(tcp->fd >= 0); - - ngx_queue_init(&req->queue); - req->type = UV_WRITE; - - - if (bufcnt < UV_REQ_BUFSML_SIZE) { - req->bufs = req->bufsml; - } - else { - req->bufs = malloc(sizeof(uv_buf_t) * bufcnt); - } - - memcpy(req->bufs, bufs, bufcnt * sizeof(uv_buf_t)); - req->bufcnt = bufcnt; - - /* - * fprintf(stderr, "cnt: %d bufs: %p bufsml: %p\n", bufcnt, req->bufs, req->bufsml); - */ - - req->write_index = 0; - tcp->write_queue_size += uv__buf_count(bufs, bufcnt); - - /* Append the request to write_queue. */ - ngx_queue_insert_tail(&tcp->write_queue, &req->queue); - - assert(!ngx_queue_empty(&tcp->write_queue)); - assert(tcp->write_watcher.cb == uv__tcp_io); - assert(tcp->write_watcher.data == tcp); - assert(tcp->write_watcher.fd == tcp->fd); - - /* 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 (empty_queue) { - if (uv__write(tcp)) { - /* Error. uv_last_error has been set. */ - return -1; - } - } - - /* If the queue is now empty - we've flushed the request already. That - * means we need to make the callback. The callback can only be done on a - * fresh stack so we feed the event loop in order to service it. - */ - if (ngx_queue_empty(&tcp->write_queue)) { - ev_feed_event(EV_DEFAULT_ &tcp->write_watcher, EV_WRITE); - } else { - /* Otherwise there is data to write - so we should wait for the file - * descriptor to become writable. - */ - ev_io_start(EV_DEFAULT_ &tcp->write_watcher); - } - - return 0; -} - - -void uv_ref() { - ev_ref(EV_DEFAULT_UC); -} - - -void uv_unref() { - ev_unref(EV_DEFAULT_UC); -} - - -void uv_update_time() { - ev_now_update(EV_DEFAULT_UC); -} - - -int64_t uv_now() { - return (int64_t)(ev_now(EV_DEFAULT_UC) * 1000); -} - - -int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { - uv_tcp_t* tcp = (uv_tcp_t*)stream; - - /* The UV_READING flag is irrelevant of the state of the tcp - it just - * expresses the desired state of the user. - */ - uv_flag_set((uv_handle_t*)tcp, UV_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(tcp->fd >= 0); - assert(alloc_cb); - - tcp->read_cb = read_cb; - tcp->alloc_cb = alloc_cb; - - /* These should have been set by uv_tcp_init. */ - assert(tcp->read_watcher.data == tcp); - assert(tcp->read_watcher.cb == uv__tcp_io); - - ev_io_start(EV_DEFAULT_UC_ &tcp->read_watcher); - return 0; -} - - -int uv_read_stop(uv_stream_t* stream) { - uv_tcp_t* tcp = (uv_tcp_t*)stream; - - uv_flag_unset((uv_handle_t*)tcp, UV_READING); - - ev_io_stop(EV_DEFAULT_UC_ &tcp->read_watcher); - tcp->read_cb = NULL; - tcp->alloc_cb = NULL; - return 0; -} - - -void uv_req_init(uv_req_t* req, uv_handle_t* handle, void *(*cb)(void *)) { - uv_counters()->req_init++; - req->type = UV_UNKNOWN_REQ; - req->cb = cb; - req->handle = handle; - ngx_queue_init(&req->queue); -} - - -static void uv__prepare(EV_P_ ev_prepare* w, int revents) { - uv_prepare_t* prepare = w->data; - - if (prepare->prepare_cb) { - prepare->prepare_cb(prepare, 0); - } -} - - -int uv_prepare_init(uv_prepare_t* prepare) { - uv__handle_init((uv_handle_t*)prepare, UV_PREPARE); - uv_counters()->prepare_init++; - - ev_prepare_init(&prepare->prepare_watcher, uv__prepare); - prepare->prepare_watcher.data = prepare; - - prepare->prepare_cb = NULL; - - return 0; -} - - -int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) { - int was_active = ev_is_active(&prepare->prepare_watcher); - - prepare->prepare_cb = cb; - - ev_prepare_start(EV_DEFAULT_UC_ &prepare->prepare_watcher); - - if (!was_active) { - ev_unref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_prepare_stop(uv_prepare_t* prepare) { - int was_active = ev_is_active(&prepare->prepare_watcher); - - ev_prepare_stop(EV_DEFAULT_UC_ &prepare->prepare_watcher); - - if (was_active) { - ev_ref(EV_DEFAULT_UC); - } - return 0; -} - - - -static void uv__check(EV_P_ ev_check* w, int revents) { - uv_check_t* check = w->data; - - if (check->check_cb) { - check->check_cb(check, 0); - } -} - - -int uv_check_init(uv_check_t* check) { - uv__handle_init((uv_handle_t*)check, UV_CHECK); - uv_counters()->check_init; - - ev_check_init(&check->check_watcher, uv__check); - check->check_watcher.data = check; - - check->check_cb = NULL; - - return 0; -} - - -int uv_check_start(uv_check_t* check, uv_check_cb cb) { - int was_active = ev_is_active(&check->check_watcher); - - check->check_cb = cb; - - ev_check_start(EV_DEFAULT_UC_ &check->check_watcher); - - if (!was_active) { - ev_unref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_check_stop(uv_check_t* check) { - int was_active = ev_is_active(&check->check_watcher); - - ev_check_stop(EV_DEFAULT_UC_ &check->check_watcher); - - if (was_active) { - ev_ref(EV_DEFAULT_UC); - } - - return 0; -} - - -static void uv__idle(EV_P_ ev_idle* w, int revents) { - uv_idle_t* idle = (uv_idle_t*)(w->data); - - if (idle->idle_cb) { - idle->idle_cb(idle, 0); - } -} - - - -int uv_idle_init(uv_idle_t* idle) { - uv__handle_init((uv_handle_t*)idle, UV_IDLE); - uv_counters()->idle_init++; - - ev_idle_init(&idle->idle_watcher, uv__idle); - idle->idle_watcher.data = idle; - - idle->idle_cb = NULL; - - return 0; -} - - -int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) { - int was_active = ev_is_active(&idle->idle_watcher); - - idle->idle_cb = cb; - ev_idle_start(EV_DEFAULT_UC_ &idle->idle_watcher); - - if (!was_active) { - ev_unref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_idle_stop(uv_idle_t* idle) { - int was_active = ev_is_active(&idle->idle_watcher); - - ev_idle_stop(EV_DEFAULT_UC_ &idle->idle_watcher); - - if (was_active) { - ev_ref(EV_DEFAULT_UC); - } - - return 0; -} - - -int uv_is_active(uv_handle_t* handle) { - switch (handle->type) { - case UV_TIMER: - return ev_is_active(&((uv_timer_t*)handle)->timer_watcher); - - case UV_PREPARE: - return ev_is_active(&((uv_prepare_t*)handle)->prepare_watcher); - - case UV_CHECK: - return ev_is_active(&((uv_check_t*)handle)->check_watcher); - - case UV_IDLE: - return ev_is_active(&((uv_idle_t*)handle)->idle_watcher); - - default: - return 1; - } -} - - -static void uv__async(EV_P_ ev_async* w, int revents) { - uv_async_t* async = w->data; - - if (async->async_cb) { - async->async_cb(async, 0); - } -} - - -int uv_async_init(uv_async_t* async, uv_async_cb async_cb) { - uv__handle_init((uv_handle_t*)async, UV_ASYNC); - uv_counters()->async_init++; - - ev_async_init(&async->async_watcher, uv__async); - async->async_watcher.data = async; - - async->async_cb = async_cb; - - /* Note: This does not have symmetry with the other libev wrappers. */ - ev_async_start(EV_DEFAULT_UC_ &async->async_watcher); - ev_unref(EV_DEFAULT_UC); - - return 0; -} - - -int uv_async_send(uv_async_t* async) { - ev_async_send(EV_DEFAULT_UC_ &async->async_watcher); -} - - -static void uv__timer_cb(EV_P_ ev_timer* w, int revents) { - uv_timer_t* timer = w->data; - - if (!ev_is_active(w)) { - ev_ref(EV_DEFAULT_UC); - } - - if (timer->timer_cb) { - timer->timer_cb(timer, 0); - } -} - - -int uv_timer_init(uv_timer_t* timer) { - uv__handle_init((uv_handle_t*)timer, UV_TIMER); - uv_counters()->timer_init++; - - ev_init(&timer->timer_watcher, uv__timer_cb); - timer->timer_watcher.data = timer; - - return 0; -} - - -int uv_timer_start(uv_timer_t* timer, uv_timer_cb cb, int64_t timeout, - int64_t repeat) { - if (ev_is_active(&timer->timer_watcher)) { - return -1; - } - - timer->timer_cb = cb; - ev_timer_set(&timer->timer_watcher, timeout / 1000.0, repeat / 1000.0); - ev_timer_start(EV_DEFAULT_UC_ &timer->timer_watcher); - ev_unref(EV_DEFAULT_UC); - return 0; -} - - -int uv_timer_stop(uv_timer_t* timer) { - if (ev_is_active(&timer->timer_watcher)) { - ev_ref(EV_DEFAULT_UC); - } - - ev_timer_stop(EV_DEFAULT_UC_ &timer->timer_watcher); - return 0; -} - - -int uv_timer_again(uv_timer_t* timer) { - if (!ev_is_active(&timer->timer_watcher)) { - uv_err_new((uv_handle_t*)timer, EINVAL); - return -1; - } - - ev_timer_again(EV_DEFAULT_UC_ &timer->timer_watcher); - return 0; -} - -void uv_timer_set_repeat(uv_timer_t* timer, int64_t repeat) { - assert(timer->type == UV_TIMER); - timer->timer_watcher.repeat = repeat / 1000.0; -} - -int64_t uv_timer_get_repeat(uv_timer_t* timer) { - assert(timer->type == UV_TIMER); - return (int64_t)(1000 * timer->timer_watcher.repeat); -} - - -/* - * This is called once per second by ares_data.timer. It is used to - * constantly callback into c-ares for possibly processing timeouts. - */ -static void uv__ares_timeout(EV_P_ struct ev_timer* watcher, int revents) { - assert(watcher == &ares_data.timer); - assert(revents == EV_TIMER); - assert(!uv_ares_handles_empty()); - ares_process_fd(ares_data.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); -} - - -static void uv__ares_io(EV_P_ struct ev_io* watcher, int revents) { - /* Reset the idle timer */ - ev_timer_again(EV_A_ &ares_data.timer); - - /* Process DNS responses */ - ares_process_fd(ares_data.channel, - revents & EV_READ ? watcher->fd : ARES_SOCKET_BAD, - revents & EV_WRITE ? watcher->fd : ARES_SOCKET_BAD); -} - - -/* Allocates and returns a new uv_ares_task_t */ -static uv_ares_task_t* uv__ares_task_create(int fd) { - uv_ares_task_t* h = malloc(sizeof(uv_ares_task_t)); - - if (h == NULL) { - uv_fatal_error(ENOMEM, "malloc"); - } - - h->sock = fd; - - ev_io_init(&h->read_watcher, uv__ares_io, fd, EV_READ); - ev_io_init(&h->write_watcher, uv__ares_io, fd, EV_WRITE); - - h->read_watcher.data = h; - h->write_watcher.data = h; -} - - -/* Callback from ares when socket operation is started */ -static void uv__ares_sockstate_cb(void* data, ares_socket_t sock, - int read, int write) { - uv_ares_task_t* h = uv_find_ares_handle(sock); - - if (read || write) { - if (!h) { - /* New socket */ - - /* If this is the first socket then start the timer. */ - if (!ev_is_active(&ares_data.timer)) { - assert(uv_ares_handles_empty()); - ev_timer_again(EV_DEFAULT_UC_ &ares_data.timer); - } - - h = uv__ares_task_create(sock); - uv_add_ares_handle(h); - } - - if (read) { - ev_io_start(EV_DEFAULT_UC_ &h->read_watcher); - } else { - ev_io_stop(EV_DEFAULT_UC_ &h->read_watcher); - } - - if (write) { - ev_io_start(EV_DEFAULT_UC_ &h->write_watcher); - } else { - ev_io_stop(EV_DEFAULT_UC_ &h->write_watcher); - } - - } else { - /* - * read == 0 and write == 0 this is c-ares's way of notifying us that - * the socket is now closed. We must free the data associated with - * socket. - */ - assert(h && "When an ares socket is closed we should have a handle for it"); - - ev_io_stop(EV_DEFAULT_UC_ &h->read_watcher); - ev_io_stop(EV_DEFAULT_UC_ &h->write_watcher); - - uv_remove_ares_handle(h); - free(h); - - if (uv_ares_handles_empty()) { - ev_timer_stop(EV_DEFAULT_UC_ &ares_data.timer); - } - } -} - - -/* c-ares integration initialize and terminate */ -/* TODO: share this with windows? */ -int uv_ares_init_options(ares_channel *channelptr, - struct ares_options *options, - int optmask) { - int rc; - - /* only allow single init at a time */ - if (ares_data.channel != NULL) { - uv_err_new_artificial(NULL, UV_EALREADY); - return -1; - } - - /* set our callback as an option */ - options->sock_state_cb = uv__ares_sockstate_cb; - options->sock_state_cb_data = &ares_data; - optmask |= ARES_OPT_SOCK_STATE_CB; - - /* We do the call to ares_init_option for caller. */ - rc = ares_init_options(channelptr, options, optmask); - - /* if success, save channel */ - if (rc == ARES_SUCCESS) { - ares_data.channel = *channelptr; - } - - /* - * Initialize the timeout timer. The timer won't be started until the - * first socket is opened. - */ - ev_init(&ares_data.timer, uv__ares_timeout); - ares_data.timer.repeat = 1.0; - - return rc; -} - - -/* TODO share this with windows? */ -void uv_ares_destroy(ares_channel channel) { - /* only allow destroy if did init */ - if (ares_data.channel != NULL) { - ev_timer_stop(EV_DEFAULT_UC_ &ares_data.timer); - ares_destroy(channel); - ares_data.channel = NULL; - } -} - - -static int uv_getaddrinfo_done(eio_req* req) { - uv_getaddrinfo_t* handle = req->data; - - uv_unref(); - - free(handle->hints); - free(handle->service); - free(handle->hostname); - - if (handle->retcode != 0) { - /* TODO how to display gai error strings? */ - uv_err_new(NULL, handle->retcode); - } - - handle->cb(handle, handle->retcode, handle->res); - - freeaddrinfo(handle->res); - handle->res = NULL; - - return 0; -} - - -static int getaddrinfo_thread_proc(eio_req *req) { - uv_getaddrinfo_t* handle = req->data; - - handle->retcode = getaddrinfo(handle->hostname, - handle->service, - handle->hints, - &handle->res); - return 0; -} - - -/* stub implementation of uv_getaddrinfo */ -int uv_getaddrinfo(uv_getaddrinfo_t* handle, - uv_getaddrinfo_cb cb, - const char* hostname, - const char* service, - const struct addrinfo* hints) { - eio_req* req; - uv_eio_init(); - - if (handle == NULL || cb == NULL || - (hostname == NULL && service == NULL)) { - uv_err_new_artificial(NULL, UV_EINVAL); - return -1; - } - - memset(handle, 0, sizeof(uv_getaddrinfo_t)); - - /* TODO don't alloc so much. */ - - if (hints) { - handle->hints = malloc(sizeof(struct addrinfo)); - memcpy(&handle->hints, hints, sizeof(struct addrinfo)); - } - - /* TODO security! check lengths, check return values. */ - - handle->cb = cb; - handle->hostname = hostname ? strdup(hostname) : NULL; - handle->service = service ? strdup(service) : NULL; - - /* TODO check handle->hostname == NULL */ - /* TODO check handle->service == NULL */ - - uv_ref(); - - req = eio_custom(getaddrinfo_thread_proc, EIO_PRI_DEFAULT, - uv_getaddrinfo_done, handle); - assert(req); - assert(req->data == handle); - - return 0; -} - diff --git a/src/rt/libuv/src/uv-win.c b/src/rt/libuv/src/uv-win.c deleted file mode 100644 index 93bf64d193c..00000000000 --- a/src/rt/libuv/src/uv-win.c +++ /dev/null @@ -1,2455 +0,0 @@ -/* 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 -#include -#include -#include -#include - -#include "uv.h" -#include "uv-common.h" -#include "tree.h" - -/* - * 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); -#endif - -/* - * MinGW is missing this too - */ -#ifndef SO_UPDATE_CONNECT_CONTEXT -# define SO_UPDATE_CONNECT_CONTEXT 0x7010 -#endif - - -/* - * MinGW is missing this too - */ -#ifndef _MSC_VER - typedef struct addrinfoW { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - wchar_t* ai_canonname; - struct sockaddr* ai_addr; - struct addrinfoW* ai_next; - } ADDRINFOW, *PADDRINFOW; - - DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const wchar_t* node, - const wchar_t* service, - const ADDRINFOW* hints, - PADDRINFOW* result); - - DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo); -#endif - - -/* - * Pointers to winsock extension functions to be retrieved dynamically - */ -static LPFN_CONNECTEX pConnectEx; -static LPFN_ACCEPTEX pAcceptEx; -static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; -static LPFN_DISCONNECTEX pDisconnectEx; -static LPFN_TRANSMITFILE pTransmitFile; -/* need IPv6 versions of winsock extension functions */ -static LPFN_CONNECTEX pConnectEx6; -static LPFN_ACCEPTEX pAcceptEx6; -static LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; -static LPFN_DISCONNECTEX pDisconnectEx6; -static LPFN_TRANSMITFILE pTransmitFile6; - - -/* - * Private uv_handle flags - */ -#define UV_HANDLE_CLOSING 0x0001 -#define UV_HANDLE_CLOSED 0x0002 -#define UV_HANDLE_BOUND 0x0004 -#define UV_HANDLE_LISTENING 0x0008 -#define UV_HANDLE_CONNECTION 0x0010 -#define UV_HANDLE_CONNECTED 0x0020 -#define UV_HANDLE_READING 0x0040 -#define UV_HANDLE_ACTIVE 0x0040 -#define UV_HANDLE_EOF 0x0080 -#define UV_HANDLE_SHUTTING 0x0100 -#define UV_HANDLE_SHUT 0x0200 -#define UV_HANDLE_ENDGAME_QUEUED 0x0400 -#define UV_HANDLE_BIND_ERROR 0x1000 -#define UV_HANDLE_IPV6 0x2000 - -/* - * Private uv_req flags. - */ -/* The request is currently queued. */ -#define UV_REQ_PENDING 0x01 - - -/* Binary tree used to keep the list of timers sorted. */ -static int uv_timer_compare(uv_timer_t* handle1, uv_timer_t* handle2); -RB_HEAD(uv_timer_tree_s, uv_timer_s); -RB_PROTOTYPE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare) - -/* The head of the timers tree */ -static struct uv_timer_tree_s uv_timers_ = RB_INITIALIZER(uv_timers_); - - -/* Head of a single-linked list of closed handles */ -static uv_handle_t* uv_endgame_handles_ = NULL; - - -/* 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_ */ -static uv_req_t* uv_pending_reqs_tail_ = NULL; - - -/* The current time according to the event loop. in msecs. */ -static int64_t uv_now_ = 0; -static int64_t uv_ticks_per_msec_ = 0; - - -/* - * Global I/O completion port - */ -static HANDLE uv_iocp_; - - -/* Global error code */ -static const uv_err_t uv_ok_ = { UV_OK, ERROR_SUCCESS }; -static uv_err_t uv_last_error_ = { UV_OK, ERROR_SUCCESS }; - -/* Error message string */ -static char* uv_err_str_ = NULL; - - -/* Reference count that keeps the event loop alive */ -static int uv_refs_ = 0; - - -/* Ip address used to bind to any port at any interface */ -static struct sockaddr_in uv_addr_ip4_any_; -static struct sockaddr_in6 uv_addr_ip6_any_; - - -/* A zero-size buffer for use by uv_read */ -static char uv_zero_[] = ""; - - -/* mark if IPv6 sockets are supported */ -static BOOL uv_allow_ipv6 = FALSE; - -/* - * Subclass of uv_handle_t. Used for integration of c-ares. - */ -typedef struct uv_ares_action_s uv_ares_action_t; - -struct uv_ares_action_s { - UV_HANDLE_FIELDS - struct uv_req_s ares_req; - SOCKET sock; - int read; - int write; -}; - -void uv_ares_process(uv_ares_action_t* handle, uv_req_t* req); -void uv_ares_task_cleanup(uv_ares_task_t* handle, uv_req_t* req); -void uv_ares_poll(uv_timer_t* handle, int status); - -/* memory used per ares_channel */ -struct uv_ares_channel_s { - ares_channel channel; - int activesockets; - uv_timer_t pollingtimer; -}; - -typedef struct uv_ares_channel_s uv_ares_channel_t; - -/* static data to hold single ares_channel */ -static uv_ares_channel_t uv_ares_data = { NULL, 0 }; - -/* default timeout per socket request if ares does not specify value */ -/* use 20 sec */ -#define ARES_TIMEOUT_MS 20000 - - -/* getaddrinfo integration */ -static void uv_getaddrinfo_done(uv_getaddrinfo_t* handle, uv_req_t* req); -/* 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) - - -/* 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 - - -/* - * Display an error message and abort the event loop. - */ -static 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(); -} - - -uv_err_t uv_last_error() { - return uv_last_error_; -} - - -char* uv_strerror(uv_err_t err) { - if (uv_err_str_ != NULL) { - LocalFree((void*) uv_err_str_); - } - - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err.sys_errno_, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&uv_err_str_, 0, NULL); - - if (uv_err_str_) { - return uv_err_str_; - } else { - return "Unknown error"; - } -} - - -static uv_err_code uv_translate_sys_error(int sys_errno) { - switch (sys_errno) { - case ERROR_SUCCESS: return UV_OK; - case ERROR_NOACCESS: return UV_EACCESS; - case WSAEACCES: return UV_EACCESS; - case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; - case WSAEADDRINUSE: return UV_EADDRINUSE; - case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; - case WSAEWOULDBLOCK: return UV_EAGAIN; - case WSAEALREADY: return UV_EALREADY; - case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED; - case WSAECONNREFUSED: return UV_ECONNREFUSED; - case WSAEFAULT: return UV_EFAULT; - case ERROR_INVALID_DATA: return UV_EINVAL; - case WSAEINVAL: return UV_EINVAL; - case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; - case WSAEMFILE: return UV_EMFILE; - case ERROR_OUTOFMEMORY: return UV_ENOMEM; - case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL; - case ERROR_INVALID_FLAGS: return UV_EBADF; - case ERROR_INVALID_PARAMETER: return UV_EINVAL; - case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; - default: return UV_UNKNOWN; - } -} - - -static uv_err_t uv_new_sys_error(int sys_errno) { - uv_err_t e; - e.code = uv_translate_sys_error(sys_errno); - e.sys_errno_ = sys_errno; - return e; -} - - -static void uv_set_sys_error(int sys_errno) { - uv_last_error_.code = uv_translate_sys_error(sys_errno); - uv_last_error_.sys_errno_ = sys_errno; -} - - -/* - * Retrieves the pointer to a winsock extension function. - */ -static BOOL uv_get_extension_function(SOCKET socket, GUID guid, - void **target) { - DWORD result, 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; - } -} - - -void uv_init() { - const GUID wsaid_connectex = WSAID_CONNECTEX; - const GUID wsaid_acceptex = WSAID_ACCEPTEX; - const GUID wsaid_getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; - const GUID wsaid_disconnectex = WSAID_DISCONNECTEX; - const GUID wsaid_transmitfile = WSAID_TRANSMITFILE; - - WSADATA wsa_data; - int errorno; - LARGE_INTEGER timer_frequency; - SOCKET dummy; - SOCKET dummy6; - - /* Initialize winsock */ - errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (errorno != 0) { - uv_fatal_error(errorno, "WSAStartup"); - } - - /* Set implicit binding address used by connectEx */ - uv_addr_ip4_any_ = uv_ip4_addr("0.0.0.0", 0); - uv_addr_ip6_any_ = uv_ip6_addr("::1", 0); - - /* Retrieve the needed winsock extension function pointers. */ - dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - if (dummy == INVALID_SOCKET) { - uv_fatal_error(WSAGetLastError(), "socket"); - } - - if (!uv_get_extension_function(dummy, - wsaid_connectex, - (void**)&pConnectEx) || - !uv_get_extension_function(dummy, - wsaid_acceptex, - (void**)&pAcceptEx) || - !uv_get_extension_function(dummy, - wsaid_getacceptexsockaddrs, - (void**)&pGetAcceptExSockAddrs) || - !uv_get_extension_function(dummy, - wsaid_disconnectex, - (void**)&pDisconnectEx) || - !uv_get_extension_function(dummy, - wsaid_transmitfile, - (void**)&pTransmitFile)) { - uv_fatal_error(WSAGetLastError(), - "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); - } - - if (closesocket(dummy) == SOCKET_ERROR) { - uv_fatal_error(WSAGetLastError(), "closesocket"); - } - -/* optional IPv6 versions of winsock extension functions */ - dummy6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); - if (dummy6 != INVALID_SOCKET) { - uv_allow_ipv6 = TRUE; - - if (!uv_get_extension_function(dummy6, - wsaid_connectex, - (void**)&pConnectEx6) || - !uv_get_extension_function(dummy6, - wsaid_acceptex, - (void**)&pAcceptEx6) || - !uv_get_extension_function(dummy6, - wsaid_getacceptexsockaddrs, - (void**)&pGetAcceptExSockAddrs6) || - !uv_get_extension_function(dummy6, - wsaid_disconnectex, - (void**)&pDisconnectEx6) || - !uv_get_extension_function(dummy6, - wsaid_transmitfile, - (void**)&pTransmitFile6)) { - uv_allow_ipv6 = FALSE; - } - - if (closesocket(dummy6) == SOCKET_ERROR) { - uv_fatal_error(WSAGetLastError(), "closesocket"); - } - } - - /* Create an I/O completion port */ - uv_iocp_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); - if (uv_iocp_ == NULL) { - uv_fatal_error(GetLastError(), "CreateIoCompletionPort"); - } - - /* Initialize the event loop time */ - if (!QueryPerformanceFrequency(&timer_frequency)) - uv_fatal_error(GetLastError(), "QueryPerformanceFrequency"); - uv_ticks_per_msec_ = timer_frequency.QuadPart / 1000; - - uv_update_time(); -} - - -void uv_req_init(uv_req_t* req, uv_handle_t* handle, void *(*cb)(void *)) { - uv_counters()->req_init++; - req->type = UV_UNKNOWN_REQ; - req->flags = 0; - req->handle = handle; - req->cb = cb; -} - - -static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { - return CONTAINING_RECORD(overlapped, uv_req_t, overlapped); -} - - -static void uv_insert_pending_req(uv_req_t* req) { - req->next_req = NULL; - if (uv_pending_reqs_tail_) { - req->next_req = uv_pending_reqs_tail_->next_req; - uv_pending_reqs_tail_ = req; - } else { - req->next_req = req; - uv_pending_reqs_tail_ = req; - } -} - - -static uv_req_t* uv_remove_pending_req() { - uv_req_t* req; - - if (uv_pending_reqs_tail_) { - req = uv_pending_reqs_tail_->next_req; - - if (req == uv_pending_reqs_tail_) { - uv_pending_reqs_tail_ = NULL; - } else { - uv_pending_reqs_tail_->next_req = req->next_req; - } - - return req; - - } else { - /* queue empty */ - return NULL; - } -} - - -static int uv_tcp_set_socket(uv_tcp_t* handle, SOCKET socket) { - DWORD yes = 1; - - assert(handle->socket == INVALID_SOCKET); - - /* Set the socket to nonblocking mode */ - if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - /* Make the socket non-inheritable */ - if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { - uv_set_sys_error(GetLastError()); - return -1; - } - - /* Associate it with the I/O completion port. */ - /* Use uv_handle_t pointer as completion key. */ - if (CreateIoCompletionPort((HANDLE)socket, - uv_iocp_, - (ULONG_PTR)socket, - 0) == NULL) { - uv_set_sys_error(GetLastError()); - return -1; - } - - handle->socket = socket; - - return 0; -} - - -static void uv_tcp_init_connection(uv_tcp_t* handle) { - handle->flags |= UV_HANDLE_CONNECTION; - handle->write_reqs_pending = 0; - uv_req_init(&(handle->read_req), (uv_handle_t*)handle, NULL); -} - - -int uv_tcp_init(uv_tcp_t* handle) { - handle->socket = INVALID_SOCKET; - handle->write_queue_size = 0; - handle->type = UV_TCP; - handle->flags = 0; - handle->reqs_pending = 0; - handle->error = uv_ok_; - handle->accept_socket = INVALID_SOCKET; - - uv_counters()->handle_init++; - uv_counters()->tcp_init++; - - uv_refs_++; - - return 0; -} - - -static void uv_tcp_endgame(uv_tcp_t* handle) { - uv_err_t err; - int status; - - if (handle->flags & UV_HANDLE_SHUTTING && - !(handle->flags & UV_HANDLE_SHUT) && - handle->write_reqs_pending == 0) { - - if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { - status = 0; - handle->flags |= UV_HANDLE_SHUT; - } else { - status = -1; - err = uv_new_sys_error(WSAGetLastError()); - } - if (handle->shutdown_req->cb) { - handle->shutdown_req->flags &= ~UV_REQ_PENDING; - if (status == -1) { - uv_last_error_ = err; - } - ((uv_shutdown_cb)handle->shutdown_req->cb)(handle->shutdown_req, status); - } - handle->reqs_pending--; - } - - if (handle->flags & UV_HANDLE_CLOSING && - handle->reqs_pending == 0) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb((uv_handle_t*)handle); - } - - uv_refs_--; - } -} - - -static void uv_timer_endgame(uv_timer_t* handle) { - if (handle->flags & UV_HANDLE_CLOSING) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb((uv_handle_t*)handle); - } - - uv_refs_--; - } -} - - -static void uv_loop_watcher_endgame(uv_handle_t* handle) { - if (handle->flags & UV_HANDLE_CLOSING) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb(handle); - } - - uv_refs_--; - } -} - - -static void uv_async_endgame(uv_async_t* handle) { - if (handle->flags & UV_HANDLE_CLOSING && - !handle->async_sent) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - handle->flags |= UV_HANDLE_CLOSED; - - if (handle->close_cb) { - handle->close_cb((uv_handle_t*)handle); - } - - uv_refs_--; - } -} - - -static void uv_process_endgames() { - uv_handle_t* handle; - - while (uv_endgame_handles_) { - handle = uv_endgame_handles_; - uv_endgame_handles_ = handle->endgame_next; - - handle->flags &= ~UV_HANDLE_ENDGAME_QUEUED; - - switch (handle->type) { - case UV_TCP: - uv_tcp_endgame((uv_tcp_t*)handle); - break; - - case UV_TIMER: - uv_timer_endgame((uv_timer_t*)handle); - break; - - case UV_PREPARE: - case UV_CHECK: - case UV_IDLE: - uv_loop_watcher_endgame(handle); - break; - - case UV_ASYNC: - uv_async_endgame((uv_async_t*)handle); - break; - - default: - assert(0); - break; - } - } -} - - -static void uv_want_endgame(uv_handle_t* handle) { - if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) { - handle->flags |= UV_HANDLE_ENDGAME_QUEUED; - - handle->endgame_next = uv_endgame_handles_; - uv_endgame_handles_ = handle; - } -} - - -static int uv_close_error(uv_handle_t* handle, uv_err_t e) { - uv_tcp_t* tcp; - - if (handle->flags & UV_HANDLE_CLOSING) { - return 0; - } - - handle->error = e; - handle->flags |= UV_HANDLE_CLOSING; - - /* Handle-specific close actions */ - switch (handle->type) { - case UV_TCP: - tcp = (uv_tcp_t*)handle; - tcp->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); - closesocket(tcp->socket); - if (tcp->reqs_pending == 0) { - uv_want_endgame(handle); - } - return 0; - - case UV_TIMER: - uv_timer_stop((uv_timer_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_PREPARE: - uv_prepare_stop((uv_prepare_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_CHECK: - uv_check_stop((uv_check_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_IDLE: - uv_idle_stop((uv_idle_t*)handle); - uv_want_endgame(handle); - return 0; - - case UV_ASYNC: - if (!((uv_async_t*)handle)->async_sent) { - uv_want_endgame(handle); - } - return 0; - - default: - /* Not supported */ - assert(0); - return -1; - } -} - - -int uv_close(uv_handle_t* handle, uv_close_cb close_cb) { - handle->close_cb = close_cb; - return uv_close_error(handle, uv_ok_); -} - - -int uv__bind(uv_tcp_t* handle, int domain, struct sockaddr* addr, int addrsize) { - DWORD err; - int r; - SOCKET sock; - - if (handle->socket == INVALID_SOCKET) { - sock = socket(domain, SOCK_STREAM, 0); - if (sock == INVALID_SOCKET) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - if (uv_tcp_set_socket(handle, sock) == -1) { - closesocket(sock); - return -1; - } - } - - r = bind(handle->socket, addr, addrsize); - - if (r == SOCKET_ERROR) { - err = WSAGetLastError(); - if (err == WSAEADDRINUSE) { - /* Some errors are not to be reported until connect() or listen() */ - handle->error = uv_new_sys_error(err); - handle->flags |= UV_HANDLE_BIND_ERROR; - } else { - uv_set_sys_error(err); - return -1; - } - } - - handle->flags |= UV_HANDLE_BOUND; - - return 0; -} - - -int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { - if (addr.sin_family != AF_INET) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - - return uv__bind(handle, AF_INET, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); -} - - -int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { - if (addr.sin6_family != AF_INET6) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - if (uv_allow_ipv6) { - handle->flags |= UV_HANDLE_IPV6; - return uv__bind(handle, AF_INET6, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)); - } else { - uv_new_sys_error(UV_EAFNOSUPPORT); - return -1; - } -} - - -static void uv_queue_accept(uv_tcp_t* handle) { - uv_req_t* req; - BOOL success; - DWORD bytes; - SOCKET accept_socket; - short family; - LPFN_ACCEPTEX pAcceptExFamily; - - assert(handle->flags & UV_HANDLE_LISTENING); - assert(handle->accept_socket == INVALID_SOCKET); - - /* Prepare the uv_req structure. */ - req = &handle->accept_req; - assert(!(req->flags & UV_REQ_PENDING)); - req->type = UV_ACCEPT; - req->flags |= UV_REQ_PENDING; - - /* choose family and extension function */ - if ((handle->flags & UV_HANDLE_IPV6) != 0) { - family = AF_INET6; - pAcceptExFamily = pAcceptEx6; - } else { - family = AF_INET; - pAcceptExFamily = pAcceptEx; - } - - /* Open a socket for the accepted connection. */ - accept_socket = socket(family, SOCK_STREAM, 0); - if (accept_socket == INVALID_SOCKET) { - req->error = uv_new_sys_error(WSAGetLastError()); - uv_insert_pending_req(req); - return; - } - - /* Prepare the overlapped structure. */ - memset(&(req->overlapped), 0, sizeof(req->overlapped)); - - success = pAcceptExFamily(handle->socket, - accept_socket, - (void*)&handle->accept_buffer, - 0, - sizeof(struct sockaddr_storage), - sizeof(struct sockaddr_storage), - &bytes, - &req->overlapped); - - if (!success && WSAGetLastError() != ERROR_IO_PENDING) { - /* Make this req pending reporting an error. */ - req->error = uv_new_sys_error(WSAGetLastError()); - uv_insert_pending_req(req); - /* Destroy the preallocated client socket. */ - closesocket(accept_socket); - return; - } - - handle->accept_socket = accept_socket; - - handle->reqs_pending++; - req->flags |= UV_REQ_PENDING; -} - - -static void uv_queue_read(uv_tcp_t* handle) { - uv_req_t* req; - uv_buf_t buf; - int result; - DWORD bytes, flags; - - assert(handle->flags & UV_HANDLE_READING); - - req = &handle->read_req; - assert(!(req->flags & UV_REQ_PENDING)); - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_READ; - - buf.base = (char*) &uv_zero_; - buf.len = 0; - - flags = 0; - result = WSARecv(handle->socket, - (WSABUF*)&buf, - 1, - &bytes, - &flags, - &req->overlapped, - NULL); - if (result != 0 && WSAGetLastError() != ERROR_IO_PENDING) { - /* Make this req pending reporting an error. */ - req->error = uv_new_sys_error(WSAGetLastError()); - uv_insert_pending_req(req); - return; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; -} - - -int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { - assert(backlog > 0); - - if (handle->flags & UV_HANDLE_BIND_ERROR) { - uv_last_error_ = handle->error; - return -1; - } - - if (handle->flags & UV_HANDLE_LISTENING || - handle->flags & UV_HANDLE_READING) { - /* Already listening. */ - uv_set_sys_error(WSAEALREADY); - return -1; - } - - if (listen(handle->socket, backlog) == SOCKET_ERROR) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - handle->flags |= UV_HANDLE_LISTENING; - handle->connection_cb = cb; - - uv_req_init(&(handle->accept_req), (uv_handle_t*)handle, NULL); - uv_queue_accept(handle); - - return 0; -} - - -int uv_accept(uv_handle_t* server, uv_stream_t* client) { - int rv = 0; - uv_tcp_t* tcpServer = (uv_tcp_t*)server; - uv_tcp_t* tcpClient = (uv_tcp_t*)client; - - if (tcpServer->accept_socket == INVALID_SOCKET) { - uv_set_sys_error(WSAENOTCONN); - return -1; - } - - if (uv_tcp_set_socket(tcpClient, tcpServer->accept_socket) == -1) { - closesocket(tcpServer->accept_socket); - rv = -1; - } else { - uv_tcp_init_connection(tcpClient); - } - - tcpServer->accept_socket = INVALID_SOCKET; - - if (!(tcpServer->flags & UV_HANDLE_CLOSING)) { - uv_queue_accept(tcpServer); - } - - return rv; -} - - -int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - uv_set_sys_error(WSAEINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_READING) { - uv_set_sys_error(WSAEALREADY); - return -1; - } - - if (handle->flags & UV_HANDLE_EOF) { - uv_set_sys_error(WSAESHUTDOWN); - return -1; - } - - handle->flags |= UV_HANDLE_READING; - handle->read_cb = read_cb; - handle->alloc_cb = alloc_cb; - - /* If reading was stopped and then started again, there could stell be a */ - /* read request pending. */ - if (!(handle->read_req.flags & UV_REQ_PENDING)) - uv_queue_read((uv_tcp_t*)handle); - - return 0; -} - - -int uv_read_stop(uv_stream_t* handle) { - handle->flags &= ~UV_HANDLE_READING; - - return 0; -} - - -int uv_tcp_connect(uv_req_t* req, struct sockaddr_in addr) { - int addrsize = sizeof(struct sockaddr_in); - BOOL success; - DWORD bytes; - uv_tcp_t* handle = (uv_tcp_t*)req->handle; - - assert(!(req->flags & UV_REQ_PENDING)); - - if (handle->flags & UV_HANDLE_BIND_ERROR) { - uv_last_error_ = handle->error; - return -1; - } - - if (addr.sin_family != AF_INET) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - - if (!(handle->flags & UV_HANDLE_BOUND) && - uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) - return -1; - - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_CONNECT; - - success = pConnectEx(handle->socket, - (struct sockaddr*)&addr, - addrsize, - NULL, - 0, - &bytes, - &req->overlapped); - - if (!success && WSAGetLastError() != ERROR_IO_PENDING) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; - - return 0; -} - - -int uv_tcp_connect6(uv_req_t* req, struct sockaddr_in6 addr) { - int addrsize = sizeof(struct sockaddr_in6); - BOOL success; - DWORD bytes; - uv_tcp_t* handle = (uv_tcp_t*)req->handle; - - if (!uv_allow_ipv6) { - uv_new_sys_error(UV_EAFNOSUPPORT); - return -1; - } - - assert(!(req->flags & UV_REQ_PENDING)); - - if (handle->flags & UV_HANDLE_BIND_ERROR) { - uv_last_error_ = handle->error; - return -1; - } - - if (addr.sin6_family != AF_INET6) { - uv_set_sys_error(WSAEFAULT); - return -1; - } - - if (!(handle->flags & UV_HANDLE_BOUND) && - uv_tcp_bind6(handle, uv_addr_ip6_any_) < 0) - return -1; - - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_CONNECT; - - success = pConnectEx6(handle->socket, - (struct sockaddr*)&addr, - addrsize, - NULL, - 0, - &bytes, - &req->overlapped); - - if (!success && WSAGetLastError() != ERROR_IO_PENDING) { - uv_set_sys_error(WSAGetLastError()); - return -1; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; - - return 0; -} - - -static size_t uv_count_bufs(uv_buf_t bufs[], int count) { - size_t bytes = 0; - int i; - - for (i = 0; i < count; i++) { - bytes += (size_t)bufs[i].len; - } - - return bytes; -} - - -int uv_write(uv_req_t* req, uv_buf_t bufs[], int bufcnt) { - int result; - DWORD bytes, err; - uv_tcp_t* handle = (uv_tcp_t*) req->handle; - - assert(!(req->flags & UV_REQ_PENDING)); - - if (!(req->handle->flags & UV_HANDLE_CONNECTION)) { - uv_set_sys_error(WSAEINVAL); - return -1; - } - - if (req->handle->flags & UV_HANDLE_SHUTTING) { - uv_set_sys_error(WSAESHUTDOWN); - return -1; - } - - memset(&req->overlapped, 0, sizeof(req->overlapped)); - req->type = UV_WRITE; - - result = WSASend(handle->socket, - (WSABUF*)bufs, - bufcnt, - &bytes, - 0, - &req->overlapped, - NULL); - if (result != 0) { - err = WSAGetLastError(); - if (err != WSA_IO_PENDING) { - /* Send failed due to an error. */ - uv_set_sys_error(WSAGetLastError()); - return -1; - } - } - - if (result == 0) { - /* Request completed immediately. */ - req->queued_bytes = 0; - } else { - /* Request queued by the kernel. */ - req->queued_bytes = uv_count_bufs(bufs, bufcnt); - handle->write_queue_size += req->queued_bytes; - } - - req->flags |= UV_REQ_PENDING; - handle->reqs_pending++; - handle->write_reqs_pending++; - - return 0; -} - - -int uv_shutdown(uv_req_t* req) { - uv_tcp_t* handle = (uv_tcp_t*) req->handle; - int status = 0; - - if (!(req->handle->flags & UV_HANDLE_CONNECTION)) { - uv_set_sys_error(WSAEINVAL); - return -1; - } - - if (handle->flags & UV_HANDLE_SHUTTING) { - uv_set_sys_error(WSAESHUTDOWN); - return -1; - } - - req->type = UV_SHUTDOWN; - req->flags |= UV_REQ_PENDING; - - handle->flags |= UV_HANDLE_SHUTTING; - handle->shutdown_req = req; - handle->reqs_pending++; - - uv_want_endgame((uv_handle_t*)handle); - - return 0; -} - - -static void uv_tcp_return_req(uv_tcp_t* handle, uv_req_t* req) { - DWORD bytes, flags, err; - uv_buf_t buf; - - assert(handle->type == UV_TCP); - - /* Mark the request non-pending */ - req->flags &= ~UV_REQ_PENDING; - - switch (req->type) { - case UV_WRITE: - handle->write_queue_size -= req->queued_bytes; - if (req->cb) { - uv_last_error_ = req->error; - ((uv_write_cb)req->cb)(req, uv_last_error_.code == UV_OK ? 0 : -1); - } - handle->write_reqs_pending--; - if (handle->write_reqs_pending == 0 && - handle->flags & UV_HANDLE_SHUTTING) { - uv_want_endgame((uv_handle_t*)handle); - } - break; - - case UV_READ: - if (req->error.code != UV_OK) { - /* An error occurred doing the 0-read. */ - if (!(handle->flags & UV_HANDLE_READING)) { - break; - } - - /* Stop reading and report error. */ - handle->flags &= ~UV_HANDLE_READING; - uv_last_error_ = req->error; - buf.base = 0; - buf.len = 0; - handle->read_cb((uv_stream_t*)handle, -1, buf); - break; - } - - /* Do nonblocking reads until the buffer is empty */ - while (handle->flags & UV_HANDLE_READING) { - buf = handle->alloc_cb((uv_stream_t*)handle, 65536); - assert(buf.len > 0); - 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; - handle->flags |= UV_HANDLE_EOF; - uv_last_error_.code = UV_EOF; - uv_last_error_.sys_errno_ = ERROR_SUCCESS; - handle->read_cb((uv_stream_t*)handle, -1, buf); - break; - } - } else { - err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK) { - /* Read buffer was completely empty, report a 0-byte read. */ - uv_set_sys_error(WSAEWOULDBLOCK); - handle->read_cb((uv_stream_t*)handle, 0, buf); - } else { - /* Ouch! serious error. */ - uv_set_sys_error(err); - handle->read_cb((uv_stream_t*)handle, -1, buf); - } - break; - } - } - /* Post another 0-read if still reading and not closing. */ - if (handle->flags & UV_HANDLE_READING) { - uv_queue_read(handle); - } - break; - - case UV_ACCEPT: - /* 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 (handle->accept_socket == INVALID_SOCKET) { - if (!(handle->flags & UV_HANDLE_LISTENING)) { - break; - } - handle->flags &= ~UV_HANDLE_LISTENING; - if (handle->connection_cb) { - uv_last_error_ = req->error; - handle->connection_cb((uv_handle_t*)handle, -1); - } - break; - } - - if (req->error.code == UV_OK && - setsockopt(handle->accept_socket, - SOL_SOCKET, - SO_UPDATE_ACCEPT_CONTEXT, - (char*)&handle->socket, - sizeof(handle->socket)) == 0) { - /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */ - if (handle->connection_cb) { - handle->connection_cb((uv_handle_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(handle->accept_socket); - if (handle->flags & UV_HANDLE_LISTENING) { - uv_queue_accept(handle); - } - } - break; - - case UV_CONNECT: - if (req->cb) { - if (req->error.code == UV_OK) { - if (setsockopt(handle->socket, - SOL_SOCKET, - SO_UPDATE_CONNECT_CONTEXT, - NULL, - 0) == 0) { - uv_tcp_init_connection(handle); - ((uv_connect_cb)req->cb)(req, 0); - } else { - uv_set_sys_error(WSAGetLastError()); - ((uv_connect_cb)req->cb)(req, -1); - } - } else { - uv_last_error_ = req->error; - ((uv_connect_cb)req->cb)(req, -1); - } - } - break; - - default: - assert(0); - } - - /* The number of pending requests is now down by one */ - handle->reqs_pending--; - - /* Queue the handle's close callback if it is closing and there are no */ - /* more pending requests. */ - if (handle->flags & UV_HANDLE_CLOSING && - handle->reqs_pending == 0) { - uv_want_endgame((uv_handle_t*)handle); - } -} - - -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; - if ((intptr_t)a < (intptr_t)b) - return -1; - if ((intptr_t)a > (intptr_t)b) - return 1; - return 0; -} - - -RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare); - - -int uv_timer_init(uv_timer_t* handle) { - uv_counters()->handle_init++; - uv_counters()->timer_init++; - - handle->type = UV_TIMER; - handle->flags = 0; - handle->error = uv_ok_; - handle->timer_cb = NULL; - handle->repeat = 0; - - uv_refs_++; - - return 0; -} - - -int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, int64_t timeout, int64_t repeat) { - if (handle->flags & UV_HANDLE_ACTIVE) { - RB_REMOVE(uv_timer_tree_s, &uv_timers_, handle); - } - - handle->timer_cb = (void*) timer_cb; - handle->due = uv_now_ + timeout; - handle->repeat = repeat; - handle->flags |= UV_HANDLE_ACTIVE; - - if (RB_INSERT(uv_timer_tree_s, &uv_timers_, handle) != NULL) { - uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); - } - - return 0; -} - - -int uv_timer_stop(uv_timer_t* handle) { - if (!(handle->flags & UV_HANDLE_ACTIVE)) - return 0; - - RB_REMOVE(uv_timer_tree_s, &uv_timers_, handle); - - handle->flags &= ~UV_HANDLE_ACTIVE; - - 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) { - uv_set_sys_error(ERROR_INVALID_DATA); - return -1; - } - - if (handle->flags & UV_HANDLE_ACTIVE) { - RB_REMOVE(uv_timer_tree_s, &uv_timers_, handle); - handle->flags &= ~UV_HANDLE_ACTIVE; - } - - if (handle->repeat) { - handle->due = uv_now_ + handle->repeat; - - if (RB_INSERT(uv_timer_tree_s, &uv_timers_, handle) != NULL) { - uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); - } - - handle->flags |= UV_HANDLE_ACTIVE; - } - - return 0; -} - - -void uv_timer_set_repeat(uv_timer_t* handle, int64_t repeat) { - assert(handle->type == UV_TIMER); - handle->repeat = repeat; -} - - -int64_t uv_timer_get_repeat(uv_timer_t* handle) { - assert(handle->type == UV_TIMER); - return handle->repeat; -} - - -void uv_update_time() { - LARGE_INTEGER counter; - - if (!QueryPerformanceCounter(&counter)) - uv_fatal_error(GetLastError(), "QueryPerformanceCounter"); - - uv_now_ = counter.QuadPart / uv_ticks_per_msec_; -} - - -int64_t uv_now() { - return uv_now_; -} - - -#define UV_LOOP_WATCHER_DEFINE(name, NAME) \ - /* Lists of active loop (prepare / check / idle) watchers */ \ - static uv_##name##_t* uv_##name##_handles_ = NULL; \ - \ - /* 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. */ \ - static uv_##name##_t* uv_next_##name##_handle_ = NULL; \ - \ - \ - int uv_##name##_init(uv_##name##_t* handle) { \ - handle->type = UV_##NAME; \ - handle->flags = 0; \ - handle->error = uv_ok_; \ - \ - uv_refs_++; \ - \ - uv_counters()->handle_init++; \ - uv_counters()->prepare_init++; \ - \ - return 0; \ - } \ - \ - \ - int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ - uv_##name##_t* old_head; \ - \ - assert(handle->type == UV_##NAME); \ - \ - if (handle->flags & UV_HANDLE_ACTIVE) \ - return 0; \ - \ - old_head = uv_##name##_handles_; \ - \ - handle->name##_next = old_head; \ - handle->name##_prev = NULL; \ - \ - if (old_head) { \ - old_head->name##_prev = handle; \ - } \ - \ - uv_##name##_handles_ = handle; \ - \ - handle->name##_cb = cb; \ - handle->flags |= UV_HANDLE_ACTIVE; \ - \ - return 0; \ - } \ - \ - \ - int uv_##name##_stop(uv_##name##_t* handle) { \ - assert(handle->type == UV_##NAME); \ - \ - if (!(handle->flags & UV_HANDLE_ACTIVE)) \ - return 0; \ - \ - /* Update loop head if needed */ \ - if (uv_##name##_handles_ == handle) { \ - uv_##name##_handles_ = handle->name##_next; \ - } \ - \ - /* Update the iterator-next pointer of needed */ \ - if (uv_next_##name##_handle_ == handle) { \ - uv_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; \ - } \ - \ - handle->flags &= ~UV_HANDLE_ACTIVE; \ - \ - return 0; \ - } \ - \ - \ - static void uv_##name##_invoke() { \ - uv_##name##_t* handle; \ - \ - uv_next_##name##_handle_ = uv_##name##_handles_; \ - \ - while (uv_next_##name##_handle_ != NULL) { \ - handle = uv_next_##name##_handle_; \ - uv_next_##name##_handle_ = handle->name##_next; \ - \ - handle->name##_cb(handle, 0); \ - } \ - } - - -UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) -UV_LOOP_WATCHER_DEFINE(check, CHECK) -UV_LOOP_WATCHER_DEFINE(idle, IDLE) - - -int uv_is_active(uv_handle_t* handle) { - switch (handle->type) { - case UV_TIMER: - case UV_IDLE: - case UV_PREPARE: - case UV_CHECK: - return (handle->flags & UV_HANDLE_ACTIVE) ? 1 : 0; - - default: - return 1; - } -} - - -int uv_async_init(uv_async_t* handle, uv_async_cb async_cb) { - uv_req_t* req; - - uv_counters()->handle_init++; - uv_counters()->async_init++; - - handle->type = UV_ASYNC; - handle->flags = 0; - handle->async_sent = 0; - handle->error = uv_ok_; - - req = &handle->async_req; - uv_req_init(req, (uv_handle_t*)handle, async_cb); - req->type = UV_WAKEUP; - - uv_refs_++; - - return 0; -} - - -int uv_async_send(uv_async_t* handle) { - 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)) { - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &handle->async_req.overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } - - return 0; -} - - -static void uv_async_return_req(uv_async_t* handle, uv_req_t* req) { - assert(handle->type == UV_ASYNC); - assert(req->type == UV_WAKEUP); - - handle->async_sent = 0; - if (req->cb) { - ((uv_async_cb)req->cb)((uv_async_t*) handle, 0); - } - if (handle->flags & UV_HANDLE_CLOSING) { - uv_want_endgame((uv_handle_t*)handle); - } -} - - -static void uv_process_reqs() { - uv_req_t* req; - uv_handle_t* handle; - - while (req = uv_remove_pending_req()) { - handle = req->handle; - - switch (handle->type) { - case UV_TCP: - uv_tcp_return_req((uv_tcp_t*)handle, req); - break; - - case UV_ASYNC: - uv_async_return_req((uv_async_t*)handle, req); - break; - - case UV_ARES: - uv_ares_process((uv_ares_action_t*)handle, req); - break; - - case UV_ARES_TASK: - uv_ares_task_cleanup((uv_ares_task_t*)handle, req); - break; - - case UV_GETADDRINFO: - uv_getaddrinfo_done((uv_getaddrinfo_t*)handle, req); - break; - - default: - assert(0); - } - } -} - - -static void uv_process_timers() { - uv_timer_t* timer; - - /* Call timer callbacks */ - for (timer = RB_MIN(uv_timer_tree_s, &uv_timers_); - timer != NULL && timer->due <= uv_now_; - timer = RB_MIN(uv_timer_tree_s, &uv_timers_)) { - RB_REMOVE(uv_timer_tree_s, &uv_timers_, timer); - - if (timer->repeat != 0) { - /* If it is a repeating timer, reschedule with repeat timeout. */ - timer->due += timer->repeat; - if (timer->due < uv_now_) { - timer->due = uv_now_; - } - if (RB_INSERT(uv_timer_tree_s, &uv_timers_, timer) != NULL) { - uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); - } - } else { - /* If non-repeating, mark the timer as inactive. */ - timer->flags &= ~UV_HANDLE_ACTIVE; - } - - timer->timer_cb((uv_timer_t*) timer, 0); - } -} - - -static DWORD uv_get_poll_timeout() { - uv_timer_t* timer; - int64_t delta; - - /* Check if there are any running timers */ - timer = RB_MIN(uv_timer_tree_s, &uv_timers_); - if (timer) { - uv_update_time(); - - delta = timer->due - uv_now_; - if (delta >= UINT_MAX) { - /* Can't have a timeout greater than UINT_MAX, and a timeout value of */ - /* UINT_MAX means infinite, so that's no good either. */ - 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; - } -} - - -static void uv_poll() { - BOOL success; - DWORD bytes; - ULONG_PTR key; - OVERLAPPED* overlapped; - uv_req_t* req; - - success = GetQueuedCompletionStatus(uv_iocp_, - &bytes, - &key, - &overlapped, - uv_get_poll_timeout()); - - uv_update_time(); - - if (overlapped) { - /* Package was dequeued */ - req = uv_overlapped_to_req(overlapped); - - if (success) { - req->error = uv_ok_; - } else { - req->error = uv_new_sys_error(GetLastError()); - } - - uv_insert_pending_req(req); - - } else if (GetLastError() != WAIT_TIMEOUT) { - /* Serious error */ - uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); - } -} - - -int uv_run() { - while (1) { - uv_update_time(); - uv_process_timers(); - - /* Terrible: please fix me! */ - while (uv_refs_ > 0 && - (uv_idle_handles_ || uv_pending_reqs_tail_ || uv_endgame_handles_)) { - /* Terrible: please fix me! */ - while (uv_pending_reqs_tail_ || uv_endgame_handles_) { - uv_process_endgames(); - uv_process_reqs(); - } - - /* Call idle callbacks */ - uv_idle_invoke(); - } - - if (uv_refs_ <= 0) { - break; - } - - uv_prepare_invoke(); - - uv_poll(); - - uv_check_invoke(); - } - - assert(uv_refs_ == 0); - return 0; -} - - -void uv_ref() { - uv_refs_++; -} - - -void uv_unref() { - uv_refs_--; -} - - -int uv_utf16_to_utf8(wchar_t* utf16Buffer, size_t utf16Size, char* utf8Buffer, size_t utf8Size) { - return WideCharToMultiByte(CP_UTF8, 0, utf16Buffer, utf16Size, utf8Buffer, utf8Size, NULL, NULL); -} - -int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, size_t utf16Size) { - return MultiByteToWideChar(CP_UTF8, 0, utf8Buffer, -1, utf16Buffer, utf16Size); -} - - -int uv_exepath(char* buffer, size_t* size) { - int retVal; - size_t utf16Size; - wchar_t* utf16Buffer; - - if (!buffer || !size) { - return -1; - } - - utf16Buffer = (wchar_t*)malloc(sizeof(wchar_t) * *size); - if (!utf16Buffer) { - retVal = -1; - goto done; - } - - /* Get the path as UTF-16 */ - utf16Size = GetModuleFileNameW(NULL, utf16Buffer, *size - 1); - if (utf16Size <= 0) { - uv_set_sys_error(GetLastError()); - retVal = -1; - goto done; - } - - utf16Buffer[utf16Size] = L'\0'; - - /* Convert to UTF-8 */ - *size = uv_utf16_to_utf8(utf16Buffer, utf16Size, buffer, *size); - if (!*size) { - uv_set_sys_error(GetLastError()); - retVal = -1; - goto done; - } - - buffer[*size] = '\0'; - retVal = 0; - -done: - if (utf16Buffer) { - free(utf16Buffer); - } - - return retVal; -} - - -uint64_t uv_hrtime(void) { - assert(0 && "implement me"); -} - - -/* thread pool callback when socket is signalled */ -VOID CALLBACK uv_ares_socksignal_tp(void* parameter, BOOLEAN timerfired) { - WSANETWORKEVENTS network_events; - uv_ares_task_t* sockhandle; - uv_ares_action_t* selhandle; - uv_req_t* uv_ares_req; - - assert(parameter != NULL); - - if (parameter != NULL) { - sockhandle = (uv_ares_task_t*)parameter; - - /* clear socket status for this event */ - /* do not fail if error, thread may run after socket close */ - /* The code assumes that c-ares will write all pending data in the callback, - unless the socket would block. We can clear the state here to avoid unecessary - signals. */ - WSAEnumNetworkEvents(sockhandle->sock, sockhandle->h_event, &network_events); - - /* setup new handle */ - selhandle = (uv_ares_action_t*)malloc(sizeof(uv_ares_action_t)); - if (selhandle == NULL) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - } - selhandle->type = UV_ARES; - selhandle->close_cb = NULL; - selhandle->data = sockhandle->data; - selhandle->sock = sockhandle->sock; - selhandle->read = (network_events.lNetworkEvents & (FD_READ | FD_CONNECT)) ? 1 : 0; - selhandle->write = (network_events.lNetworkEvents & (FD_WRITE | FD_CONNECT)) ? 1 : 0; - - uv_ares_req = &selhandle->ares_req; - uv_req_init(uv_ares_req, (uv_handle_t*)selhandle, NULL); - uv_ares_req->type = UV_WAKEUP; - - /* post ares needs to called */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &uv_ares_req->overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } -} - -/* callback from ares when socket operation is started */ -void uv_ares_sockstate_cb(void *data, ares_socket_t sock, int read, int write) { - /* look to see if we have a handle for this socket in our list */ - uv_ares_task_t* uv_handle_ares = uv_find_ares_handle(sock); - uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)data; - - struct timeval tv; - struct timeval* tvptr; - int timeoutms = 0; - - if (read == 0 && write == 0) { - /* if read and write are 0, cleanup existing data */ - /* The code assumes that c-ares does a callback with read = 0 and write = 0 - when the socket is closed. After we recieve this we stop monitoring the socket. */ - if (uv_handle_ares != NULL) { - uv_req_t* uv_ares_req; - - uv_handle_ares->h_close_event = CreateEvent(NULL, FALSE, FALSE, NULL); - /* remove Wait */ - if (uv_handle_ares->h_wait) { - UnregisterWaitEx(uv_handle_ares->h_wait, uv_handle_ares->h_close_event); - uv_handle_ares->h_wait = NULL; - } - - /* detach socket from the event */ - WSAEventSelect(sock, NULL, 0); - if (uv_handle_ares->h_event != WSA_INVALID_EVENT) { - WSACloseEvent(uv_handle_ares->h_event); - uv_handle_ares->h_event = WSA_INVALID_EVENT; - } - /* remove handle from list */ - uv_remove_ares_handle(uv_handle_ares); - - /* Post request to cleanup the Task */ - uv_ares_req = &uv_handle_ares->ares_req; - uv_req_init(uv_ares_req, (uv_handle_t*)uv_handle_ares, NULL); - uv_ares_req->type = UV_WAKEUP; - - /* post ares done with socket - finish cleanup when all threads done. */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &uv_ares_req->overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } else { - assert(0); - uv_fatal_error(ERROR_INVALID_DATA, "ares_SockStateCB"); - } - } else { - if (uv_handle_ares == NULL) { - /* setup new handle */ - /* The code assumes that c-ares will call us when it has an open socket. - We need to call into c-ares when there is something to read, - or when it becomes writable. */ - uv_handle_ares = (uv_ares_task_t*)malloc(sizeof(uv_ares_task_t)); - if (uv_handle_ares == NULL) { - uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); - } - uv_handle_ares->type = UV_ARES_TASK; - uv_handle_ares->close_cb = NULL; - uv_handle_ares->data = uv_ares_data_ptr; - uv_handle_ares->sock = sock; - uv_handle_ares->h_wait = NULL; - uv_handle_ares->flags = 0; - - /* create an event to wait on socket signal */ - uv_handle_ares->h_event = WSACreateEvent(); - if (uv_handle_ares->h_event == WSA_INVALID_EVENT) { - uv_fatal_error(WSAGetLastError(), "WSACreateEvent"); - } - - /* tie event to socket */ - if (SOCKET_ERROR == WSAEventSelect(sock, uv_handle_ares->h_event, FD_READ | FD_WRITE | FD_CONNECT)) { - uv_fatal_error(WSAGetLastError(), "WSAEventSelect"); - } - - /* add handle to list */ - uv_add_ares_handle(uv_handle_ares); - uv_refs_++; - - /* - * we have a single polling timer for all ares sockets. - * This is preferred to using ares_timeout. See ares_timeout.c warning. - * if timer is not running start it, and keep socket count - */ - if (uv_ares_data_ptr->activesockets == 0) { - uv_timer_init(&uv_ares_data_ptr->pollingtimer); - uv_timer_start(&uv_ares_data_ptr->pollingtimer, uv_ares_poll, 1000L, 1000L); - } - uv_ares_data_ptr->activesockets++; - - /* specify thread pool function to call when event is signaled */ - if (RegisterWaitForSingleObject(&uv_handle_ares->h_wait, - uv_handle_ares->h_event, - uv_ares_socksignal_tp, - (void*)uv_handle_ares, - INFINITE, - WT_EXECUTEINWAITTHREAD) == 0) { - uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); - } - } else { - /* found existing handle. */ - assert(uv_handle_ares->type == UV_ARES_TASK); - assert(uv_handle_ares->data != NULL); - assert(uv_handle_ares->h_event != WSA_INVALID_EVENT); - } - } -} - -/* called via uv_poll when ares completion port signaled */ -void uv_ares_process(uv_ares_action_t* handle, uv_req_t* req) { - uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)handle->data; - - ares_process_fd(uv_ares_data_ptr->channel, - handle->read ? handle->sock : INVALID_SOCKET, - handle->write ? handle->sock : INVALID_SOCKET); - - /* release handle for select here */ - free(handle); -} - -/* called via uv_poll when ares is finished with socket */ -void uv_ares_task_cleanup(uv_ares_task_t* handle, uv_req_t* req) { - /* check for event complete without waiting */ - unsigned int signaled = WaitForSingleObject(handle->h_close_event, 0); - - if (signaled != WAIT_TIMEOUT) { - uv_ares_channel_t* uv_ares_data_ptr = (uv_ares_channel_t*)handle->data; - - uv_refs_--; - - /* close event handle and free uv handle memory */ - CloseHandle(handle->h_close_event); - free(handle); - - /* decrement active count. if it becomes 0 stop polling */ - if (uv_ares_data_ptr->activesockets > 0) { - uv_ares_data_ptr->activesockets--; - if (uv_ares_data_ptr->activesockets == 0) { - uv_close((uv_handle_t*)&uv_ares_data_ptr->pollingtimer, NULL); - } - } - } else { - /* stil busy - repost and try again */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &req->overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } -} - -/* periodically call ares to check for timeouts */ -void uv_ares_poll(uv_timer_t* handle, int status) { - if (uv_ares_data.channel != NULL && uv_ares_data.activesockets > 0) { - ares_process_fd(uv_ares_data.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); - } -} - - -/* set ares SOCK_STATE callback to our handler */ -int uv_ares_init_options(ares_channel *channelptr, - struct ares_options *options, - int optmask) { - int rc; - - /* only allow single init at a time */ - if (uv_ares_data.channel != NULL) { - return UV_EALREADY; - } - - /* set our callback as an option */ - options->sock_state_cb = uv_ares_sockstate_cb; - options->sock_state_cb_data = &uv_ares_data; - optmask |= ARES_OPT_SOCK_STATE_CB; - - /* We do the call to ares_init_option for caller. */ - rc = ares_init_options(channelptr, options, optmask); - - /* if success, save channel */ - if (rc == ARES_SUCCESS) { - uv_ares_data.channel = *channelptr; - } - - return rc; -} - - -/* release memory */ -void uv_ares_destroy(ares_channel channel) { - /* only allow destroy if did init */ - if (uv_ares_data.channel != NULL) { - ares_destroy(channel); - uv_ares_data.channel = NULL; - } -} - - -/* - * getaddrinfo error code mapping - * Falls back to uv_translate_sys_error if no match - */ - -static uv_err_code uv_translate_eai_error(int eai_errno) { - switch (eai_errno) { - case ERROR_SUCCESS: return UV_OK; - case EAI_BADFLAGS: return UV_EBADF; - case EAI_FAIL: return UV_EFAULT; - case EAI_FAMILY: return UV_EAIFAMNOSUPPORT; - case EAI_MEMORY: return UV_ENOMEM; - case EAI_NONAME: return UV_EAINONAME; - case EAI_AGAIN: return UV_EAGAIN; - case EAI_SERVICE: return UV_EAISERVICE; - case EAI_SOCKTYPE: return UV_EAISOCKTYPE; - default: return uv_translate_sys_error(eai_errno); - } -} - - -/* getaddrinfo worker thread implementation */ -static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) { - uv_getaddrinfo_t* handle = (uv_getaddrinfo_t*)parameter; - int ret; - - assert(handle != NULL); - - if (handle != NULL) { - /* call OS function on this thread */ - ret = GetAddrInfoW(handle->node, handle->service, handle->hints, &handle->res); - handle->retcode = ret; - - /* post getaddrinfo completed */ - if (!PostQueuedCompletionStatus(uv_iocp_, - 0, - 0, - &handle->getadddrinfo_req.overlapped)) { - uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); - } - } - - return 0; -} - - -/* - * 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(uv_getaddrinfo_t* handle, uv_req_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; - uv_err_code uv_ret; - - /* release input parameter memory */ - if (handle->alloc != NULL) { - free(handle->alloc); - handle->alloc = NULL; - } - - uv_ret = uv_translate_eai_error(handle->retcode); - if (handle->retcode == 0) { - /* convert addrinfoW to addrinfo */ - /* first calculate required length */ - addrinfow_ptr = handle->res; - while (addrinfow_ptr != NULL) { - addrinfo_len += addrinfo_struct_len + ALIGNED_SIZE(addrinfow_ptr->ai_addrlen); - if (addrinfow_ptr->ai_canonname != NULL) { - name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); - if (name_len == 0) { - uv_ret = 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*)malloc(addrinfo_len); - - /* do conversions */ - if (alloc_ptr != NULL) { - cur_ptr = alloc_ptr; - addrinfow_ptr = handle->res; - - 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 = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); - assert(name_len > 0); - assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); - name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, cur_ptr, name_len); - 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; - } - } - } else { - uv_ret = UV_ENOMEM; - } - - } - - /* return memory to system */ - if (handle->res != NULL) { - FreeAddrInfoW(handle->res); - handle->res = NULL; - } - -complete: - /* finally do callback with converted result */ - handle->getaddrinfo_cb(handle, uv_ret, (struct addrinfo*)alloc_ptr); - - /* release copied result memory */ - if (alloc_ptr != NULL) { - free(alloc_ptr); - } - - uv_refs_--; -} - - -/* - * Entry point for getaddrinfo - * we convert the UTF-8 strings to UNICODE - * and save the UNICODE string pointers in the handle - * We also copy hints so that caller does not need to keep memory until the callback. - * return UV_OK 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_getaddrinfo_t* handle, - 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; - - if (handle == NULL || getaddrinfo_cb == NULL || - (node == NULL && service == NULL)) { - uv_set_sys_error(WSAEINVAL); - goto error; - } - - handle->getaddrinfo_cb = getaddrinfo_cb; - handle->res = NULL; - handle->type = UV_GETADDRINFO; - - /* calculate required memory size for all input values */ - if (node != NULL) { - nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(wchar_t)); - if (nodesize == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - } - - if (service != NULL) { - servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) * sizeof(wchar_t)); - if (servicesize == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - } - if (hints != NULL) { - hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW)); - } - - /* allocate memory for inputs, and partition it as needed */ - alloc_ptr = (char*)malloc(nodesize + servicesize + hintssize); - if (!alloc_ptr) { - uv_set_sys_error(WSAENOBUFS); - goto error; - } - - /* save alloc_ptr now so we can free if error */ - handle->alloc = (void*)alloc_ptr; - - /* convert node string to UTF16 into allocated memory and save pointer in handle */ - if (node != NULL) { - handle->node = (wchar_t*)alloc_ptr; - if (uv_utf8_to_utf16(node, (wchar_t*)alloc_ptr, nodesize / sizeof(wchar_t)) == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - alloc_ptr += nodesize; - } else { - handle->node = NULL; - } - - /* convert service string to UTF16 into allocated memory and save pointer in handle */ - if (service != NULL) { - handle->service = (wchar_t*)alloc_ptr; - if (uv_utf8_to_utf16(service, (wchar_t*)alloc_ptr, servicesize / sizeof(wchar_t)) == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - alloc_ptr += servicesize; - } else { - handle->service = NULL; - } - - /* copy hints to allocated memory and save pointer in handle */ - if (hints != NULL) { - handle->hints = (struct addrinfoW*)alloc_ptr; - handle->hints->ai_family = hints->ai_family; - handle->hints->ai_socktype = hints->ai_socktype; - handle->hints->ai_protocol = hints->ai_protocol; - handle->hints->ai_flags = hints->ai_flags; - handle->hints->ai_addrlen = 0; - handle->hints->ai_canonname = NULL; - handle->hints->ai_addr = NULL; - handle->hints->ai_next = NULL; - } else { - handle->hints = NULL; - } - - /* init request for Post handling */ - uv_req_init(&handle->getadddrinfo_req, (uv_handle_t*)handle, NULL); - handle->getadddrinfo_req.type = UV_WAKEUP; - - /* Ask thread to run. Treat this as a long operation */ - if (QueueUserWorkItem(&getaddrinfo_thread_proc, handle, WT_EXECUTELONGFUNCTION) == 0) { - uv_set_sys_error(GetLastError()); - goto error; - } - - uv_refs_++; - - return 0; - -error: - if (handle != NULL && handle->alloc != NULL) { - free(handle->alloc); - } - return -1; -} - diff --git a/src/rt/libuv/src/win/async.c b/src/rt/libuv/src/win/async.c new file mode 100644 index 00000000000..ee5faea272c --- /dev/null +++ b/src/rt/libuv/src/win/async.c @@ -0,0 +1,127 @@ +/* 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 + +#include "uv.h" +#include "internal.h" + + +/* Atomic set operation on char */ +#ifdef _MSC_VER /* MSVC */ + +/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */ +/* efficient than InterlockedExchange, but InterlockedExchange8 does not */ +/* exist, and interlocked operations on larger targets might require the */ +/* target to be aligned. */ +#pragma intrinsic(_InterlockedOr8) + +static char __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 + + +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)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { + uv_req_t* req; + + loop->counters.handle_init++; + loop->counters.async_init++; + + handle->type = UV_ASYNC; + handle->loop = loop; + handle->flags = 0; + 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_ref(loop); + + return 0; +} + + +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->async_cb) { + handle->async_cb((uv_async_t*) handle, 0); + } + if (handle->flags & UV_HANDLE_CLOSING) { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} diff --git a/src/rt/libuv/src/win/cares.c b/src/rt/libuv/src/win/cares.c new file mode 100644 index 00000000000..f146c31a56a --- /dev/null +++ b/src/rt/libuv/src/win/cares.c @@ -0,0 +1,289 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* + * Subclass of uv_handle_t. Used for integration of c-ares. + */ +struct uv_ares_action_s { + UV_HANDLE_FIELDS + struct uv_req_s ares_req; + SOCKET sock; + int read; + int write; +}; + + +/* default timeout per socket request if ares does not specify value */ +/* use 20 sec */ +#define ARES_TIMEOUT_MS 20000 + + +/* thread pool callback when socket is signalled */ +static void CALLBACK uv_ares_socksignal_tp(void* parameter, + BOOLEAN timerfired) { + WSANETWORKEVENTS network_events; + uv_ares_task_t* sockhandle; + uv_ares_action_t* selhandle; + uv_req_t* uv_ares_req; + uv_loop_t* loop; + + assert(parameter != NULL); + + if (parameter != NULL) { + sockhandle = (uv_ares_task_t*) parameter; + loop = sockhandle->loop; + + /* clear socket status for this event */ + /* do not fail if error, thread may run after socket close */ + /* The code assumes that c-ares will write all pending data in the */ + /* callback, unless the socket would block. We can clear the state here */ + /* to avoid unecessary signals. */ + WSAEnumNetworkEvents(sockhandle->sock, + sockhandle->h_event, + &network_events); + + /* setup new handle */ + selhandle = (uv_ares_action_t*)malloc(sizeof(uv_ares_action_t)); + if (selhandle == NULL) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + selhandle->type = UV_ARES_EVENT; + selhandle->close_cb = NULL; + selhandle->data = sockhandle->data; + selhandle->sock = sockhandle->sock; + selhandle->read = + (network_events.lNetworkEvents & (FD_READ | FD_CONNECT)) ? 1 : 0; + selhandle->write = + (network_events.lNetworkEvents & (FD_WRITE | FD_CONNECT)) ? 1 : 0; + + uv_ares_req = &selhandle->ares_req; + uv_req_init(loop, uv_ares_req); + uv_ares_req->type = UV_ARES_EVENT_REQ; + uv_ares_req->data = selhandle; + + /* post ares needs to called */ + POST_COMPLETION_FOR_REQ(loop, uv_ares_req); + } +} + + +/* periodically call ares to check for timeouts */ +static void uv_ares_poll(uv_timer_t* handle, int status) { + uv_loop_t* loop = handle->loop; + if (loop->ares_chan != NULL && loop->ares_active_sockets > 0) { + ares_process_fd(loop->ares_chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + } +} + + +/* callback from ares when socket operation is started */ +static void uv_ares_sockstate_cb(void *data, ares_socket_t sock, int read, + int write) { + /* look to see if we have a handle for this socket in our list */ + uv_loop_t* loop = (uv_loop_t*) data; + uv_ares_task_t* uv_handle_ares = uv_find_ares_handle(loop, sock); + + int timeoutms = 0; + + if (read == 0 && write == 0) { + /* if read and write are 0, cleanup existing data */ + /* The code assumes that c-ares does a callback with read = 0 and */ + /* write = 0 when the socket is closed. After we recieve this we stop */ + /* monitoring the socket. */ + if (uv_handle_ares != NULL) { + uv_req_t* uv_ares_req; + + uv_handle_ares->h_close_event = CreateEvent(NULL, FALSE, FALSE, NULL); + /* remove Wait */ + if (uv_handle_ares->h_wait) { + UnregisterWaitEx(uv_handle_ares->h_wait, + uv_handle_ares->h_close_event); + uv_handle_ares->h_wait = NULL; + } + + /* detach socket from the event */ + WSAEventSelect(sock, NULL, 0); + if (uv_handle_ares->h_event != WSA_INVALID_EVENT) { + WSACloseEvent(uv_handle_ares->h_event); + uv_handle_ares->h_event = WSA_INVALID_EVENT; + } + /* remove handle from list */ + uv_remove_ares_handle(uv_handle_ares); + + /* Post request to cleanup the Task */ + uv_ares_req = &uv_handle_ares->ares_req; + uv_req_init(loop, uv_ares_req); + uv_ares_req->type = UV_ARES_CLEANUP_REQ; + uv_ares_req->data = uv_handle_ares; + + /* post ares done with socket - finish cleanup when all threads done. */ + POST_COMPLETION_FOR_REQ(loop, uv_ares_req); + } else { + assert(0); + uv_fatal_error(ERROR_INVALID_DATA, "ares_SockStateCB"); + } + } else { + if (uv_handle_ares == NULL) { + /* setup new handle */ + /* The code assumes that c-ares will call us when it has an open socket. + We need to call into c-ares when there is something to read, + or when it becomes writable. */ + uv_handle_ares = (uv_ares_task_t*)malloc(sizeof(uv_ares_task_t)); + if (uv_handle_ares == NULL) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + uv_handle_ares->type = UV_ARES_TASK; + uv_handle_ares->close_cb = NULL; + uv_handle_ares->data = loop; + uv_handle_ares->sock = sock; + uv_handle_ares->h_wait = NULL; + uv_handle_ares->flags = 0; + + /* create an event to wait on socket signal */ + uv_handle_ares->h_event = WSACreateEvent(); + if (uv_handle_ares->h_event == WSA_INVALID_EVENT) { + uv_fatal_error(WSAGetLastError(), "WSACreateEvent"); + } + + /* tie event to socket */ + if (SOCKET_ERROR == WSAEventSelect(sock, + uv_handle_ares->h_event, + FD_READ | FD_WRITE | FD_CONNECT)) { + uv_fatal_error(WSAGetLastError(), "WSAEventSelect"); + } + + /* add handle to list */ + uv_add_ares_handle(loop, uv_handle_ares); + uv_ref(loop); + + /* + * we have a single polling timer for all ares sockets. + * This is preferred to using ares_timeout. See ares_timeout.c warning. + * if timer is not running start it, and keep socket count + */ + if (loop->ares_active_sockets == 0) { + uv_timer_init(loop, &loop->ares_polling_timer); + uv_timer_start(&loop->ares_polling_timer, uv_ares_poll, 1000L, 1000L); + } + loop->ares_active_sockets++; + + /* specify thread pool function to call when event is signaled */ + if (RegisterWaitForSingleObject(&uv_handle_ares->h_wait, + uv_handle_ares->h_event, + uv_ares_socksignal_tp, + (void*)uv_handle_ares, + INFINITE, + WT_EXECUTEINWAITTHREAD) == 0) { + uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject"); + } + } else { + /* found existing handle. */ + assert(uv_handle_ares->type == UV_ARES_TASK); + assert(uv_handle_ares->data != NULL); + assert(uv_handle_ares->h_event != WSA_INVALID_EVENT); + } + } +} + + +/* called via uv_poll when ares completion port signaled */ +void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle, + uv_req_t* req) { + ares_process_fd(loop->ares_chan, + handle->read ? handle->sock : INVALID_SOCKET, + handle->write ? handle->sock : INVALID_SOCKET); + + /* release handle for select here */ + free(handle); +} + + +/* called via uv_poll when ares is finished with socket */ +void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle, + uv_req_t* req) { + /* check for event complete without waiting */ + unsigned int signaled = WaitForSingleObject(handle->h_close_event, 0); + + if (signaled != WAIT_TIMEOUT) { + uv_unref(loop); + + /* close event handle and free uv handle memory */ + CloseHandle(handle->h_close_event); + free(handle); + + /* decrement active count. if it becomes 0 stop polling */ + if (loop->ares_active_sockets > 0) { + loop->ares_active_sockets--; + if (loop->ares_active_sockets == 0) { + uv_close((uv_handle_t*) &loop->ares_polling_timer, NULL); + } + } + } else { + /* stil busy - repost and try again */ + POST_COMPLETION_FOR_REQ(loop, req); + } +} + + +/* set ares SOCK_STATE callback to our handler */ +int uv_ares_init_options(uv_loop_t* loop, + ares_channel *channelptr, + struct ares_options *options, + int optmask) { + int rc; + + /* only allow single init at a time */ + if (loop->ares_chan != NULL) { + return UV_EALREADY; + } + + /* set our callback as an option */ + options->sock_state_cb = uv_ares_sockstate_cb; + options->sock_state_cb_data = loop; + optmask |= ARES_OPT_SOCK_STATE_CB; + + /* We do the call to ares_init_option for caller. */ + rc = ares_init_options(channelptr, options, optmask); + + /* if success, save channel */ + if (rc == ARES_SUCCESS) { + loop->ares_chan = *channelptr; + } + + return rc; +} + + +/* release memory */ +void uv_ares_destroy(uv_loop_t* loop, ares_channel channel) { + /* only allow destroy if did init */ + if (loop->ares_chan != NULL) { + ares_destroy(channel); + loop->ares_chan = NULL; + } +} diff --git a/src/rt/libuv/src/win/core.c b/src/rt/libuv/src/win/core.c new file mode 100644 index 00000000000..3211bbf2afd --- /dev/null +++ b/src/rt/libuv/src/win/core.c @@ -0,0 +1,218 @@ +/* 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 +#include +#include +#include +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* The only event loop we support right now */ +static uv_loop_t uv_default_loop_; + +/* uv_once intialization guards */ +static uv_once_t uv_init_guard_ = UV_ONCE_INIT; +static uv_once_t uv_default_loop_init_guard_ = UV_ONCE_INIT; + + +static void uv_init(void) { + /* Initialize winsock */ + uv_winsock_init(); + + /* Fetch winapi function pointers */ + uv_winapi_init(); + + /* Initialize FS */ + uv_fs_init(); +} + + +static void uv_loop_init(uv_loop_t* loop) { + /* Create an I/O completion port */ + loop->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1); + if (loop->iocp == NULL) { + uv_fatal_error(GetLastError(), "CreateIoCompletionPort"); + } + + loop->refs = 0; + + uv_update_time(loop); + + 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; + + loop->ares_active_sockets = 0; + loop->ares_chan = NULL; + + loop->last_error = uv_ok_; +} + + +static void uv_default_loop_init(void) { + /* Intialize libuv itself first */ + uv_once(&uv_init_guard_, uv_init); + + /* Initialize the main loop */ + uv_loop_init(&uv_default_loop_); +} + + +uv_loop_t* uv_default_loop() { + uv_once(&uv_default_loop_init_guard_, uv_default_loop_init); + return &uv_default_loop_; +} + + +void uv_ref(uv_loop_t* loop) { + loop->refs++; +} + + +void uv_unref(uv_loop_t* loop) { + loop->refs--; +} + + +static void uv_poll(uv_loop_t* loop, int block) { + BOOL success; + DWORD bytes, timeout; + ULONG_PTR key; + OVERLAPPED* overlapped; + uv_req_t* req; + + if (block) { + timeout = uv_get_poll_timeout(loop); + } else { + timeout = 0; + } + + success = GetQueuedCompletionStatus(loop->iocp, + &bytes, + &key, + &overlapped, + timeout); + + if (overlapped) { + /* Package was dequeued */ + req = uv_overlapped_to_req(overlapped); + + uv_insert_pending_req(loop, req); + + } else if (GetLastError() != WAIT_TIMEOUT) { + /* Serious error */ + uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus"); + } +} + + +static void uv_poll_ex(uv_loop_t* loop, int block) { + BOOL success; + DWORD timeout; + uv_req_t* req; + OVERLAPPED_ENTRY overlappeds[64]; + ULONG count; + ULONG i; + + if (block) { + timeout = uv_get_poll_timeout(loop); + } else { + timeout = 0; + } + + assert(pGetQueuedCompletionStatusEx); + + success = pGetQueuedCompletionStatusEx(loop->iocp, + overlappeds, + COUNTOF(overlappeds), + &count, + timeout, + FALSE); + if (success) { + for (i = 0; i < count; i++) { + /* Package was dequeued */ + req = uv_overlapped_to_req(overlappeds[i].lpOverlapped); + uv_insert_pending_req(loop, req); + } + } else if (GetLastError() != WAIT_TIMEOUT) { + /* Serious error */ + uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx"); + } +} + + +#define UV_LOOP(loop, poll) \ + while ((loop)->refs > 0) { \ + uv_update_time((loop)); \ + uv_process_timers((loop)); \ + \ + /* Call idle callbacks if nothing to do. */ \ + if ((loop)->pending_reqs_tail == NULL && \ + (loop)->endgame_handles == NULL) { \ + uv_idle_invoke((loop)); \ + } \ + \ + /* Completely flush all pending reqs and endgames. */ \ + /* We do even when we just called the idle callbacks because those may */ \ + /* have closed handles or started requests that short-circuited. */ \ + while ((loop)->pending_reqs_tail || (loop)->endgame_handles) { \ + uv_process_endgames((loop)); \ + uv_process_reqs((loop)); \ + } \ + \ + if ((loop)->refs <= 0) { \ + break; \ + } \ + \ + uv_prepare_invoke((loop)); \ + \ + poll((loop), (loop)->idle_handles == NULL && (loop)->refs > 0); \ + \ + uv_check_invoke((loop)); \ + } + + +int uv_run(uv_loop_t* loop) { + if (pGetQueuedCompletionStatusEx) { + UV_LOOP(loop, uv_poll_ex); + } else { + UV_LOOP(loop, uv_poll); + } + + assert(loop->refs == 0); + return 0; +} diff --git a/src/rt/libuv/src/win/error.c b/src/rt/libuv/src/win/error.c new file mode 100644 index 00000000000..0ed2e16abe2 --- /dev/null +++ b/src/rt/libuv/src/win/error.c @@ -0,0 +1,157 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +const uv_err_t uv_ok_ = { UV_OK, ERROR_SUCCESS }; + + +/* + * 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(); +} + + +uv_err_t uv_last_error(uv_loop_t* loop) { + return loop->last_error; +} + + +/* TODO: thread safety */ +static char* last_err_str_ = NULL; + +char* uv_strerror(uv_err_t err) { + if (last_err_str_ != NULL) { + LocalFree(last_err_str_); + } + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err.sys_errno_, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &last_err_str_, 0, + NULL); + + if (last_err_str_) { + return last_err_str_; + } else { + return "Unknown error"; + } +} + + +uv_err_code uv_translate_sys_error(int sys_errno) { + switch (sys_errno) { + case ERROR_SUCCESS: return UV_OK; + case ERROR_FILE_NOT_FOUND: return UV_ENOENT; + case ERROR_PATH_NOT_FOUND: return UV_ENOENT; + case ERROR_NOACCESS: return UV_EACCESS; + case WSAEACCES: return UV_EACCESS; + 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_CONNECTION_ABORTED: return UV_ECONNABORTED; + case WSAECONNABORTED: return UV_ECONNABORTED; + case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED; + case WSAECONNREFUSED: return UV_ECONNREFUSED; + case WSAEFAULT: return UV_EFAULT; + case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH; + case WSAEHOSTUNREACH: return UV_EHOSTUNREACH; + case ERROR_INVALID_DATA: return UV_EINVAL; + case WSAEINVAL: return UV_EINVAL; + case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; + case WSAEMFILE: return UV_EMFILE; + case WSAEMSGSIZE: return UV_EMSGSIZE; + case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH; + case WSAENETUNREACH: return UV_ENETUNREACH; + case ERROR_OUTOFMEMORY: return UV_ENOMEM; + case ERROR_NOT_CONNECTED: return UV_ENOTCONN; + case WSAENOTCONN: return UV_ENOTCONN; + case ERROR_NOT_SUPPORTED: return UV_ENOTSUP; + case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL; + case ERROR_INVALID_FLAGS: return UV_EBADF; + case ERROR_INVALID_PARAMETER: return UV_EINVAL; + case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; + case ERROR_BROKEN_PIPE: return UV_EOF; + case ERROR_PIPE_BUSY: return UV_EBUSY; + case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT; + case ERROR_ALREADY_EXISTS: return UV_EEXIST; + default: return UV_UNKNOWN; + } +} + + +uv_err_t uv_new_sys_error(int sys_errno) { + uv_err_t e; + e.code = uv_translate_sys_error(sys_errno); + e.sys_errno_ = sys_errno; + return e; +} + + +void uv_set_sys_error(uv_loop_t* loop, int sys_errno) { + loop->last_error.code = uv_translate_sys_error(sys_errno); + loop->last_error.sys_errno_ = sys_errno; +} + + +void uv_set_error(uv_loop_t* loop, uv_err_code code, int sys_errno) { + loop->last_error.code = code; + loop->last_error.sys_errno_ = sys_errno; +} diff --git a/src/rt/libuv/src/win/fs-event.c b/src/rt/libuv/src/win/fs-event.c new file mode 100644 index 00000000000..a68dfe00def --- /dev/null +++ b/src/rt/libuv/src/win/fs-event.c @@ -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 +#include +#include +#include +#include "uv.h" +#include "internal.h" + + +const unsigned int uv_directory_watcher_buffer_size = 4096; + + +static void uv_fs_event_init_handle(uv_loop_t* loop, uv_fs_event_t* handle, + const char* filename, uv_fs_event_cb cb) { + handle->type = UV_FS_EVENT; + handle->loop = loop; + handle->flags = 0; + handle->cb = cb; + handle->is_path_dir = 0; + handle->dir_handle = INVALID_HANDLE_VALUE; + handle->buffer = NULL; + handle->req_pending = 0; + handle->filew = NULL; + + uv_req_init(loop, (uv_req_t*)&handle->req); + handle->req.type = UV_FS_EVENT_REQ; + handle->req.data = (void*)handle; + + handle->filename = strdup(filename); + if (!handle->filename) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + loop->counters.handle_init++; + loop->counters.fs_event_init++; + + uv_ref(loop); +} + + +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.overlapped), 0, sizeof(handle->req.overlapped)); + if (!ReadDirectoryChangesW(handle->dir_handle, + handle->buffer, + uv_directory_watcher_buffer_size, + 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.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 int uv_split_path(const wchar_t* filename, wchar_t** dir, + wchar_t** file) { + int len = wcslen(filename); + int i = len; + while (i > 0 && filename[--i] != '\\' && filename[i] != '/'); + + if (i == 0) { + *dir = (wchar_t*)malloc((MAX_PATH + 1) * sizeof(wchar_t)); + if (!*dir) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!GetCurrentDirectoryW(MAX_PATH, *dir)) { + free(*dir); + *dir = NULL; + return -1; + } + + *file = wcsdup(filename); + } else { + *dir = (wchar_t*)malloc((i + 1) * sizeof(wchar_t)); + if (!*dir) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + wcsncpy(*dir, filename, i); + (*dir)[i] = L'\0'; + + *file = (wchar_t*)malloc((len - i) * sizeof(wchar_t)); + if (!*file) { + uv_fatal_error(ERROR_OUTOFMEMORY, "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, + const char* filename, uv_fs_event_cb cb) { + int name_size; + DWORD attr, last_error; + wchar_t* dir = NULL, *dir_to_watch, *filenamew; + + uv_fs_event_init_handle(loop, handle, filename, cb); + + /* Convert name to UTF16. */ + name_size = uv_utf8_to_utf16(filename, NULL, 0) * sizeof(wchar_t); + filenamew = (wchar_t*)malloc(name_size); + if (!filenamew) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf8_to_utf16(filename, filenamew, + name_size / sizeof(wchar_t))) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* Determine whether filename is a file or a directory. */ + attr = GetFileAttributesW(filenamew); + if (attr == INVALID_FILE_ATTRIBUTES) { + last_error = GetLastError(); + goto error; + } + + handle->is_path_dir = (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0; + + if (handle->is_path_dir) { + /* filename is a directory, so that's the directory that we will watch. */ + dir_to_watch = filenamew; + } else { + /* + * filename is a file. So we split filename into dir & file parts, and + * watch the dir directory. + */ + if (uv_split_path(filenamew, &dir, &handle->filew) != 0) { + last_error = GetLastError(); + goto error; + } + + dir_to_watch = dir; + } + + 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) { + free(dir); + dir = NULL; + } + + if (handle->dir_handle == INVALID_HANDLE_VALUE) { + last_error = GetLastError(); + goto error; + } + + if (CreateIoCompletionPort(handle->dir_handle, + loop->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + last_error = GetLastError(); + goto error; + } + + handle->buffer = (char*)_aligned_malloc(uv_directory_watcher_buffer_size, + sizeof(DWORD)); + if (!handle->buffer) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + memset(&(handle->req.overlapped), 0, sizeof(handle->req.overlapped)); + + if (!ReadDirectoryChangesW(handle->dir_handle, + handle->buffer, + uv_directory_watcher_buffer_size, + 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.overlapped, + NULL)) { + last_error = GetLastError(); + goto error; + } + + handle->req_pending = 1; + return 0; + +error: + if (handle->filename) { + free(handle->filename); + handle->filename = NULL; + } + + if (handle->filew) { + free(handle->filew); + handle->filew = NULL; + } + + if (handle->dir_handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->dir_handle); + handle->dir_handle = INVALID_HANDLE_VALUE; + } + + if (handle->buffer) { + _aligned_free(handle->buffer); + handle->buffer = NULL; + } + + uv_set_sys_error(loop, last_error); + return -1; +} + + +void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, + uv_fs_event_t* handle) { + FILE_NOTIFY_INFORMATION* file_info; + char* filename = NULL; + int utf8size; + DWORD offset = 0; + + assert(req->type == UV_FS_EVENT_REQ); + assert(handle->req_pending); + handle->req_pending = 0; + + file_info = (FILE_NOTIFY_INFORMATION*)(handle->buffer + offset); + + if (REQ_SUCCESS(req)) { + if (req->overlapped.InternalHigh > 0) { + do { + file_info = (FILE_NOTIFY_INFORMATION*)((char*)file_info + offset); + + /* + * Fire the event only if we were asked to watch a directory, + * or if the filename filter matches. + */ + if (handle->is_path_dir || _wcsnicmp(handle->filew, file_info->FileName, + file_info->FileNameLength / sizeof(wchar_t)) == 0) { + + /* Convert the filename to utf8. */ + utf8size = uv_utf16_to_utf8(file_info->FileName, + file_info->FileNameLength / + sizeof(wchar_t), + NULL, + 0); + if (utf8size) { + filename = (char*)malloc(utf8size + 1); + if (!filename) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + utf8size = uv_utf16_to_utf8(file_info->FileName, + file_info->FileNameLength / + sizeof(wchar_t), + filename, + utf8size); + if (utf8size) { + filename[utf8size] = L'\0'; + } else { + free(filename); + filename = NULL; + } + } + + 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; + } + + free(filename); + filename = NULL; + } + + offset = file_info->NextEntryOffset; + } while(offset); + } else { + handle->cb(handle, NULL, UV_CHANGE, 0); + } + } else { + loop->last_error = GET_REQ_UV_ERROR(req); + handle->cb(handle, NULL, 0, -1); + } + + 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) { + if (handle->dir_handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->dir_handle); + handle->dir_handle = INVALID_HANDLE_VALUE; + } + + 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)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->buffer) { + _aligned_free(handle->buffer); + handle->buffer = NULL; + } + + if (handle->filew) { + free(handle->filew); + handle->filew = NULL; + } + + if (handle->filename) { + free(handle->filename); + handle->filename = NULL; + } + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} diff --git a/src/rt/libuv/src/win/fs.c b/src/rt/libuv/src/win/fs.c new file mode 100644 index 00000000000..93fa5eb36b5 --- /dev/null +++ b/src/rt/libuv/src/win/fs.c @@ -0,0 +1,1302 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uv.h" +#include "internal.h" + +#define UV_FS_ASYNC_QUEUED 0x0001 +#define UV_FS_FREE_ARG0 0x0002 +#define UV_FS_FREE_ARG1 0x0004 +#define UV_FS_FREE_PTR 0x0008 +#define UV_FS_CLEANEDUP 0x0010 +#define UV_FS_LAST_ERROR_SET 0x0020 + +#define STRDUP_ARG(req, i) \ + req->arg##i = (void*)strdup((const char*)req->arg##i); \ + if (!req->arg##i) { \ + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); \ + } \ + req->flags |= UV_FS_FREE_ARG##i; + +#define WRAP_REQ_ARGS1(req, a0) \ + req->arg0 = (void*)a0; + +#define WRAP_REQ_ARGS2(req, a0, a1) \ + WRAP_REQ_ARGS1(req, a0) \ + req->arg1 = (void*)a1; + +#define WRAP_REQ_ARGS3(req, a0, a1, a2) \ + WRAP_REQ_ARGS2(req, a0, a1) \ + req->arg2 = (void*)a2; + +#define WRAP_REQ_ARGS4(req, a0, a1, a2, a3) \ + WRAP_REQ_ARGS3(req, a0, a1, a2) \ + req->arg3 = (void*)a3; + +#define QUEUE_FS_TP_JOB(loop, req) \ + if (!QueueUserWorkItem(&uv_fs_thread_proc, \ + req, \ + WT_EXECUTELONGFUNCTION)) { \ + uv_set_sys_error((loop), GetLastError()); \ + return -1; \ + } \ + req->flags |= UV_FS_ASYNC_QUEUED; \ + uv_ref((loop)); + + +#define SET_UV_LAST_ERROR_FROM_REQ(req) \ + if (req->flags & UV_FS_LAST_ERROR_SET) { \ + uv_set_sys_error(req->loop, req->last_error); \ + } + +#define SET_REQ_LAST_ERROR(req, error) \ + req->last_error = error; \ + req->flags |= UV_FS_LAST_ERROR_SET; + +#define SET_REQ_RESULT(req, result_value) \ + req->result = (result_value); \ + if (req->result == -1) { \ + req->errorno = uv_translate_sys_error(_doserrno); \ + } + +#define SET_REQ_RESULT_WIN32_ERROR(req, sys_errno) \ + req->result = -1; \ + req->errorno = uv_translate_sys_error(sys_errno); \ + SET_REQ_LAST_ERROR(req, sys_errno); + + +void uv_fs_init() { + _fmode = _O_BINARY; +} + + +static void uv_fs_req_init_async(uv_loop_t* loop, uv_fs_t* req, + uv_fs_type fs_type, const char* path, 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->cb = cb; + req->result = 0; + req->ptr = NULL; + req->path = path ? strdup(path) : NULL; + req->errorno = 0; + req->last_error = 0; + memset(&req->overlapped, 0, sizeof(req->overlapped)); +} + + +static void uv_fs_req_init_sync(uv_loop_t* loop, uv_fs_t* req, + uv_fs_type fs_type) { + 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->errorno = 0; +} + + +void fs__open(uv_fs_t* req, const char* path, int flags, int mode) { + DWORD access; + DWORD share; + DWORD disposition; + DWORD attributes; + HANDLE file; + int result, current_umask; + + /* 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 = GENERIC_READ; + break; + case _O_WRONLY: + access = GENERIC_WRITE; + break; + case _O_RDWR: + access = GENERIC_READ | GENERIC_WRITE; + break; + default: + result = -1; + goto end; + } + + /* + * 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: + result = -1; + goto end; + } + + attributes = FILE_ATTRIBUTE_NORMAL; + if (flags & _O_CREAT) { + if (!((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: + result = -1; + goto end; + } + + file = CreateFileA(path, + access, + share, + NULL, + disposition, + attributes, + NULL); + if (file == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + return; + } + result = _open_osfhandle((intptr_t)file, flags); +end: + SET_REQ_RESULT(req, result); +} + +void fs__close(uv_fs_t* req, uv_file file) { + int result = _close(file); + SET_REQ_RESULT(req, result); +} + + +void fs__read(uv_fs_t* req, uv_file file, void *buf, size_t length, + off_t offset) { + HANDLE handle; + OVERLAPPED overlapped, *overlapped_ptr; + LARGE_INTEGER offset_; + DWORD bytes; + + handle = (HANDLE) _get_osfhandle(file); + if (handle == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT(req, -1); + return; + } + + if (length > INT_MAX) { + SET_REQ_ERROR(req, ERROR_INSUFFICIENT_BUFFER); + return; + } + + if (offset != -1) { + memset(&overlapped, 0, sizeof overlapped); + + offset_.QuadPart = offset; + overlapped.Offset = offset_.LowPart; + overlapped.OffsetHigh = offset_.HighPart; + + overlapped_ptr = &overlapped; + } else { + overlapped_ptr = NULL; + } + + if (ReadFile(handle, buf, length, &bytes, overlapped_ptr)) { + SET_REQ_RESULT(req, bytes); + } else { + SET_REQ_ERROR(req, GetLastError()); + } +} + + +void fs__write(uv_fs_t* req, uv_file file, void *buf, size_t length, + off_t offset) { + HANDLE handle; + OVERLAPPED overlapped, *overlapped_ptr; + LARGE_INTEGER offset_; + DWORD bytes; + + handle = (HANDLE) _get_osfhandle(file); + if (handle == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT(req, -1); + return; + } + + if (length > INT_MAX) { + SET_REQ_ERROR(req, ERROR_INSUFFICIENT_BUFFER); + return; + } + + if (offset != -1) { + memset(&overlapped, 0, sizeof overlapped); + + offset_.QuadPart = offset; + overlapped.Offset = offset_.LowPart; + overlapped.OffsetHigh = offset_.HighPart; + + overlapped_ptr = &overlapped; + } else { + overlapped_ptr = NULL; + } + + if (WriteFile(handle, buf, length, &bytes, overlapped_ptr)) { + SET_REQ_RESULT(req, bytes); + } else { + SET_REQ_ERROR(req, GetLastError()); + } +} + + +void fs__unlink(uv_fs_t* req, const char* path) { + int result = _unlink(path); + SET_REQ_RESULT(req, result); +} + + +void fs__mkdir(uv_fs_t* req, const char* path, int mode) { + int result = _mkdir(path); + SET_REQ_RESULT(req, result); +} + + +void fs__rmdir(uv_fs_t* req, const char* path) { + int result = _rmdir(path); + SET_REQ_RESULT(req, result); +} + + +void fs__readdir(uv_fs_t* req, const char* path, int flags) { + int result; + char* buf, *ptr, *name; + HANDLE dir; + WIN32_FIND_DATAA ent = {0}; + size_t len = strlen(path); + size_t buf_size = 4096; + const char* fmt = !len ? "./*" + : (path[len - 1] == '/' || path[len - 1] == '\\') ? "%s*" + : "%s\\*"; + + char* path2 = (char*)malloc(len + 4); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + sprintf(path2, fmt, path); + dir = FindFirstFileA(path2, &ent); + free(path2); + + if(dir == INVALID_HANDLE_VALUE) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + return; + } + + buf = (char*)malloc(buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + ptr = buf; + result = 0; + + do { + name = ent.cFileName; + + if (name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))) { + len = strlen(name); + + while ((ptr - buf) + len + 1 > buf_size) { + buf_size *= 2; + path2 = buf; + buf = (char*)realloc(buf, buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "realloc"); + } + + ptr = buf + (ptr - path2); + } + + strcpy(ptr, name); + ptr += len + 1; + result++; + } + } while(FindNextFileA(dir, &ent)); + + FindClose(dir); + + req->ptr = buf; + req->flags |= UV_FS_FREE_PTR; + + SET_REQ_RESULT(req, result); +} + + +void fs__stat(uv_fs_t* req, const char* path) { + int result; + + result = _stati64(path, &req->stat); + if (result == -1) { + req->ptr = NULL; + } else { + req->ptr = &req->stat; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__fstat(uv_fs_t* req, uv_file file) { + int result; + + result = _fstati64(file, &req->stat); + if (result == -1) { + req->ptr = NULL; + } else { + req->ptr = &req->stat; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__rename(uv_fs_t* req, const char* path, const char* new_path) { + int result = rename(path, new_path); + SET_REQ_RESULT(req, result); +} + + +void fs__fsync(uv_fs_t* req, uv_file file) { + int result = FlushFileBuffers((HANDLE)_get_osfhandle(file)) ? 0 : -1; + if (result == -1) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + } else { + SET_REQ_RESULT(req, result); + } +} + + +void fs__ftruncate(uv_fs_t* req, uv_file file, off_t offset) { + int result = _chsize(file, offset); + SET_REQ_RESULT(req, result); +} + + +void fs__sendfile(uv_fs_t* req, uv_file out_file, uv_file in_file, + off_t in_offset, size_t length) { + const size_t max_buf_size = 65536; + size_t buf_size = length < max_buf_size ? length : max_buf_size; + int n, result = 0; + char* buf = (char*)malloc(buf_size); + if (!buf) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (in_offset != -1) { + result = _lseek(in_file, in_offset, SEEK_SET); + } + + if (result != -1) { + while (length > 0) { + n = _read(in_file, buf, length < buf_size ? length : buf_size); + if (n == 0) { + break; + } else if (n == -1) { + result = -1; + break; + } + + length -= n; + + n = _write(out_file, buf, n); + if (n == -1) { + result = -1; + break; + } + + result += n; + } + } + + SET_REQ_RESULT(req, result); +} + + +void fs__chmod(uv_fs_t* req, const char* path, int mode) { + int result = _chmod(path, mode); + SET_REQ_RESULT(req, result); +} + + +void fs__fchmod(uv_fs_t* req, uv_file file, int mode) { + int result; + HANDLE handle; + NTSTATUS nt_status; + IO_STATUS_BLOCK io_status; + FILE_BASIC_INFORMATION file_info; + + handle = (HANDLE)_get_osfhandle(file); + + nt_status = pNtQueryInformationFile(handle, + &io_status, + &file_info, + sizeof file_info, + FileBasicInformation); + + if (nt_status != STATUS_SUCCESS) { + result = -1; + goto done; + } + + if (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_status != STATUS_SUCCESS) { + result = -1; + goto done; + } + + result = 0; + +done: + SET_REQ_RESULT(req, result); +} + + +void fs__utime(uv_fs_t* req, const char* path, double atime, double mtime) { + int result; + struct _utimbuf b = {(time_t)atime, (time_t)mtime}; + result = _utime(path, &b); + SET_REQ_RESULT(req, result); +} + + +void fs__futime(uv_fs_t* req, uv_file file, double atime, double mtime) { + int result; + struct _utimbuf b = {(time_t)atime, (time_t)mtime}; + result = _futime(file, &b); + SET_REQ_RESULT(req, result); +} + + +void fs__link(uv_fs_t* req, const char* path, const char* new_path) { + int result = CreateHardLinkA(new_path, path, NULL) ? 0 : -1; + if (result == -1) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + } else { + SET_REQ_RESULT(req, result); + } +} + + +void fs__symlink(uv_fs_t* req, const char* path, const char* new_path, + int flags) { + int result; + if (pCreateSymbolicLinkA) { + result = pCreateSymbolicLinkA(new_path, + path, + flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1; + if (result == -1) { + SET_REQ_LAST_ERROR(req, GetLastError()); + } + } else { + result = -1; + errno = ENOSYS; + } + + SET_REQ_RESULT(req, result); +} + + +void fs__readlink(uv_fs_t* req, const char* path) { + int result = -1; + BOOL rv; + HANDLE symlink; + void* buffer = NULL; + DWORD bytes_returned; + REPARSE_DATA_BUFFER* reparse_data; + int utf8size; + wchar_t* substitute_name; + int substitute_name_length; + + symlink = CreateFileA(path, + 0, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (INVALID_HANDLE_VALUE == symlink) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (!buffer) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + rv = DeviceIoControl(symlink, + FSCTL_GET_REPARSE_POINT, + NULL, + 0, + buffer, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, + &bytes_returned, + NULL); + + if (!rv) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + reparse_data = buffer; + if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK) { + result = -1; + /* something is seriously wrong */ + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + substitute_name = reparse_data->SymbolicLinkReparseBuffer.PathBuffer + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)); + substitute_name_length = reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + + /* Strip off the leading \??\ from the substitute name buffer.*/ + if (memcmp(substitute_name, L"\\??\\", 8) == 0) { + substitute_name += 4; + substitute_name_length -= 4; + } + + utf8size = uv_utf16_to_utf8(substitute_name, + substitute_name_length, + NULL, + 0); + if (!utf8size) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + req->ptr = malloc(utf8size + 1); + if (!req->ptr) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + utf8size = uv_utf16_to_utf8(substitute_name, + substitute_name_length, + req->ptr, + utf8size); + if (!utf8size) { + result = -1; + SET_REQ_LAST_ERROR(req, GetLastError()); + goto done; + } + + req->flags |= UV_FS_FREE_PTR; + ((char*)req->ptr)[utf8size] = '\0'; + result = 0; + +done: + if (buffer) { + free(buffer); + } + + if (symlink != INVALID_HANDLE_VALUE) { + CloseHandle(symlink); + } + + SET_REQ_RESULT(req, result); +} + + +void fs__nop(uv_fs_t* req) { + req->result = 0; +} + + +static DWORD WINAPI uv_fs_thread_proc(void* parameter) { + uv_fs_t* req = (uv_fs_t*) parameter; + uv_loop_t* loop = req->loop; + + assert(req != NULL); + assert(req->type == UV_FS); + + switch (req->fs_type) { + case UV_FS_OPEN: + fs__open(req, req->path, (int)req->arg0, (int)req->arg1); + break; + case UV_FS_CLOSE: + fs__close(req, (uv_file)req->arg0); + break; + case UV_FS_READ: + fs__read(req, + (uv_file) req->arg0, + req->arg1, + (size_t) req->arg2, + (off_t) req->arg3); + break; + case UV_FS_WRITE: + fs__write(req, + (uv_file)req->arg0, + req->arg1, + (size_t) req->arg2, + (off_t) req->arg3); + break; + case UV_FS_UNLINK: + fs__unlink(req, req->path); + break; + case UV_FS_MKDIR: + fs__mkdir(req, req->path, (int)req->arg0); + break; + case UV_FS_RMDIR: + fs__rmdir(req, req->path); + break; + case UV_FS_READDIR: + fs__readdir(req, req->path, (int)req->arg0); + break; + case UV_FS_STAT: + case UV_FS_LSTAT: + fs__stat(req, req->path); + break; + case UV_FS_FSTAT: + fs__fstat(req, (uv_file)req->arg0); + break; + case UV_FS_RENAME: + fs__rename(req, req->path, (const char*)req->arg0); + break; + case UV_FS_FSYNC: + case UV_FS_FDATASYNC: + fs__fsync(req, (uv_file)req->arg0); + break; + case UV_FS_FTRUNCATE: + fs__ftruncate(req, (uv_file)req->arg0, (off_t)req->arg1); + break; + case UV_FS_SENDFILE: + fs__sendfile(req, + (uv_file) req->arg0, + (uv_file) req->arg1, + (off_t) req->arg2, + (size_t) req->arg3); + break; + case UV_FS_CHMOD: + fs__chmod(req, req->path, (int)req->arg0); + break; + case UV_FS_FCHMOD: + fs__fchmod(req, (uv_file)req->arg0, (int)req->arg1); + break; + case UV_FS_UTIME: + fs__utime(req, req->path, req->arg4, req->arg5); + break; + case UV_FS_FUTIME: + fs__futime(req, (uv_file)req->arg0, req->arg4, req->arg5); + break; + case UV_FS_LINK: + fs__link(req, req->path, (const char*)req->arg0); + break; + case UV_FS_SYMLINK: + fs__symlink(req, req->path, (const char*)req->arg0, (int)req->arg1); + break; + case UV_FS_READLINK: + fs__readlink(req, req->path); + break; + case UV_FS_CHOWN: + case UV_FS_FCHOWN: + fs__nop(req); + break; + default: + assert(!"bad uv_fs_type"); + } + + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + int mode, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_OPEN, path, cb); + WRAP_REQ_ARGS2(req, flags, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_OPEN); + fs__open(req, path, flags, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_CLOSE, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_CLOSE); + fs__close(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READ, NULL, cb); + WRAP_REQ_ARGS4(req, file, buf, length, offset); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READ); + fs__read(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, + size_t length, off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_WRITE, NULL, cb); + WRAP_REQ_ARGS4(req, file, buf, length, offset); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_WRITE); + fs__write(req, file, buf, length, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_UNLINK, path, cb); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_UNLINK); + fs__unlink(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_MKDIR, path, cb); + WRAP_REQ_ARGS1(req, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_MKDIR); + fs__mkdir(req, path, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_RMDIR, path, cb); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_RMDIR); + fs__rmdir(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READDIR, path, cb); + WRAP_REQ_ARGS1(req, flags); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READDIR); + fs__readdir(req, path, flags); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_LINK, path, cb); + WRAP_REQ_ARGS1(req, new_path); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_LINK); + fs__link(req, path, new_path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +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) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_SYMLINK, path, cb); + WRAP_REQ_ARGS2(req, new_path, flags); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_SYMLINK); + fs__symlink(req, path, new_path, flags); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_READLINK, path, cb); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_READLINK); + fs__readlink(req, path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid, + int gid, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_CHOWN, path, cb); + WRAP_REQ_ARGS2(req, uid, gid); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_CHOWN); + fs__nop(req); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid, + int gid, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FCHOWN, NULL, cb); + WRAP_REQ_ARGS3(req, file, uid, gid); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FCHOWN); + fs__nop(req); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + int len = strlen(path); + char* path2 = NULL; + int has_backslash = (path[len - 1] == '\\' || path[len - 1] == '/'); + + if (path[len - 1] == '\\' || path[len - 1] == '/') { + path2 = strdup(path); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + path2[len - 1] = '\0'; + } + + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_STAT, NULL, cb); + if (path2) { + req->path = path2; + } else { + req->path = strdup(path); + } + + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_STAT); + fs__stat(req, path2 ? path2 : path); + if (path2) { + free(path2); + } + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +/* TODO: add support for links. */ +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + int len = strlen(path); + char* path2 = NULL; + int has_backslash = (path[len - 1] == '\\' || path[len - 1] == '/'); + + if (path[len - 1] == '\\' || path[len - 1] == '/') { + path2 = strdup(path); + if (!path2) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + path2[len - 1] = '\0'; + } + + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_LSTAT, NULL, cb); + if (path2) { + req->path = path2; + } else { + req->path = strdup(path); + } + + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_LSTAT); + fs__stat(req, path2 ? path2 : path); + if (path2) { + free(path2); + } + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FSTAT, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FSTAT); + fs__fstat(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, + const char* new_path, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_RENAME, path, cb); + WRAP_REQ_ARGS1(req, new_path); + STRDUP_ARG(req, 0); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_RENAME); + fs__rename(req, path, new_path); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FDATASYNC, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FDATASYNC); + fs__fsync(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FSYNC, NULL, cb); + WRAP_REQ_ARGS1(req, file); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FSYNC); + fs__fsync(req, file); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, + off_t offset, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FTRUNCATE, NULL, cb); + WRAP_REQ_ARGS2(req, file, offset); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FTRUNCATE); + fs__ftruncate(req, file, offset); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, + uv_file in_fd, off_t in_offset, size_t length, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_SENDFILE, NULL, cb); + WRAP_REQ_ARGS4(req, out_fd, in_fd, in_offset, length); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_SENDFILE); + fs__sendfile(req, out_fd, in_fd, in_offset, length); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_CHMOD, path, cb); + WRAP_REQ_ARGS1(req, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_CHMOD); + fs__chmod(req, path, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, + uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FCHMOD, NULL, cb); + WRAP_REQ_ARGS2(req, file, mode); + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FCHMOD); + fs__fchmod(req, file, mode); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_UTIME, path, cb); + req->arg4 = (ssize_t)atime; + req->arg5 = (ssize_t)mtime; + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_UTIME); + fs__utime(req, path, atime, mtime); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, + double mtime, uv_fs_cb cb) { + if (cb) { + uv_fs_req_init_async(loop, req, UV_FS_FUTIME, NULL, cb); + WRAP_REQ_ARGS1(req, file); + req->arg4 = (ssize_t)atime; + req->arg5 = (ssize_t)mtime; + QUEUE_FS_TP_JOB(loop, req); + } else { + uv_fs_req_init_sync(loop, req, UV_FS_FUTIME); + fs__futime(req, file, atime, mtime); + SET_UV_LAST_ERROR_FROM_REQ(req); + return req->result; + } + + return 0; +} + + +void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req) { + assert(req->cb); + SET_UV_LAST_ERROR_FROM_REQ(req); + req->cb(req); +} + + +void uv_fs_req_cleanup(uv_fs_t* req) { + uv_loop_t* loop = req->loop; + + if (req->flags & UV_FS_CLEANEDUP) { + return; + } + + if (req->flags & UV_FS_FREE_ARG0 && req->arg0) { + free(req->arg0); + req->arg0 = NULL; + } + + if (req->flags & UV_FS_FREE_ARG1 && req->arg1) { + free(req->arg1); + req->arg1 = NULL; + } + + if (req->flags & UV_FS_FREE_PTR && req->ptr) { + free(req->ptr); + } + + req->ptr = NULL; + + if (req->path) { + free(req->path); + req->path = NULL; + } + + if (req->flags & UV_FS_ASYNC_QUEUED) { + uv_unref(loop); + } + + req->flags |= UV_FS_CLEANEDUP; +} diff --git a/src/rt/libuv/src/win/getaddrinfo.c b/src/rt/libuv/src/win/getaddrinfo.c new file mode 100644 index 00000000000..1416f04d62e --- /dev/null +++ b/src/rt/libuv/src/win/getaddrinfo.c @@ -0,0 +1,368 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include "uv.h" +#include "internal.h" + + +/* + * 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_t* ai_canonname; + struct sockaddr* ai_addr; + struct addrinfoW* ai_next; + } ADDRINFOW, *PADDRINFOW; + + DECLSPEC_IMPORT int WSAAPI GetAddrInfoW(const wchar_t* node, + const wchar_t* 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) + + +/* + * getaddrinfo error code mapping + * Falls back to uv_translate_sys_error if no match + */ +static uv_err_code uv_translate_eai_error(int eai_errno) { + switch (eai_errno) { + case ERROR_SUCCESS: return UV_OK; + case EAI_BADFLAGS: return UV_EBADF; + case EAI_FAIL: return UV_EFAULT; + case EAI_FAMILY: return UV_EAIFAMNOSUPPORT; + case EAI_MEMORY: return UV_ENOMEM; + case EAI_NONAME: return UV_EAINONAME; + case EAI_AGAIN: return UV_EAGAIN; + case EAI_SERVICE: return UV_EAISERVICE; + case EAI_SOCKTYPE: return UV_EAISOCKTYPE; + default: return uv_translate_sys_error(eai_errno); + } +} + + +/* getaddrinfo worker thread implementation */ +static DWORD WINAPI getaddrinfo_thread_proc(void* parameter) { + uv_getaddrinfo_t* handle = (uv_getaddrinfo_t*) parameter; + uv_loop_t* loop = handle->loop; + int ret; + + assert(handle != NULL); + + if (handle != NULL) { + /* call OS function on this thread */ + ret = GetAddrInfoW(handle->node, + handle->service, + handle->hints, + &handle->res); + handle->retcode = ret; + + /* post getaddrinfo completed */ + POST_COMPLETION_FOR_REQ(loop, &handle->getadddrinfo_req); + } + + return 0; +} + + +/* + * 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. + */ +void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* handle, + uv_req_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; + uv_err_code uv_ret; + + /* release input parameter memory */ + if (handle->alloc != NULL) { + free(handle->alloc); + handle->alloc = NULL; + } + + uv_ret = uv_translate_eai_error(handle->retcode); + if (handle->retcode == 0) { + /* convert addrinfoW to addrinfo */ + /* first calculate required length */ + addrinfow_ptr = handle->res; + while (addrinfow_ptr != NULL) { + addrinfo_len += addrinfo_struct_len + + ALIGNED_SIZE(addrinfow_ptr->ai_addrlen); + if (addrinfow_ptr->ai_canonname != NULL) { + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, -1, NULL, 0); + if (name_len == 0) { + uv_ret = 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*)malloc(addrinfo_len); + + /* do conversions */ + if (alloc_ptr != NULL) { + cur_ptr = alloc_ptr; + addrinfow_ptr = handle->res; + + 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 = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, + -1, + NULL, + 0); + assert(name_len > 0); + assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); + name_len = uv_utf16_to_utf8(addrinfow_ptr->ai_canonname, + -1, + cur_ptr, + name_len); + 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; + } + } + } else { + uv_ret = UV_ENOMEM; + } + + } + + /* return memory to system */ + if (handle->res != NULL) { + FreeAddrInfoW(handle->res); + handle->res = NULL; + } + +complete: + /* finally do callback with converted result */ + handle->getaddrinfo_cb(handle, uv_ret, (struct addrinfo*)alloc_ptr); + + uv_unref(loop); +} + + +void uv_freeaddrinfo(struct addrinfo* ai) { + char* alloc_ptr = (char*)ai; + + /* release copied result memory */ + if (alloc_ptr != NULL) { + free(alloc_ptr); + } +} + + +/* + * Entry point for getaddrinfo + * we convert the UTF-8 strings to UNICODE + * and save the UNICODE string pointers in the handle + * We also copy hints so that caller does not need to keep memory until the + * callback. + * return UV_OK 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* handle, + 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; + + if (handle == NULL || getaddrinfo_cb == NULL || + (node == NULL && service == NULL)) { + uv_set_sys_error(loop, WSAEINVAL); + goto error; + } + + uv_req_init(loop, (uv_req_t*)handle); + + handle->getaddrinfo_cb = getaddrinfo_cb; + handle->res = NULL; + handle->type = UV_GETADDRINFO; + handle->loop = loop; + + /* calculate required memory size for all input values */ + if (node != NULL) { + nodesize = ALIGNED_SIZE(uv_utf8_to_utf16(node, NULL, 0) * sizeof(wchar_t)); + if (nodesize == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + } + + if (service != NULL) { + servicesize = ALIGNED_SIZE(uv_utf8_to_utf16(service, NULL, 0) * + sizeof(wchar_t)); + if (servicesize == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + } + if (hints != NULL) { + hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW)); + } + + /* allocate memory for inputs, and partition it as needed */ + alloc_ptr = (char*)malloc(nodesize + servicesize + hintssize); + if (!alloc_ptr) { + uv_set_sys_error(loop, WSAENOBUFS); + goto error; + } + + /* save alloc_ptr now so we can free if error */ + handle->alloc = (void*)alloc_ptr; + + /* convert node string to UTF16 into allocated memory and save pointer in */ + /* handle */ + if (node != NULL) { + handle->node = (wchar_t*)alloc_ptr; + if (uv_utf8_to_utf16(node, + (wchar_t*) alloc_ptr, + nodesize / sizeof(wchar_t)) == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + alloc_ptr += nodesize; + } else { + handle->node = NULL; + } + + /* convert service string to UTF16 into allocated memory and save pointer */ + /* in handle */ + if (service != NULL) { + handle->service = (wchar_t*)alloc_ptr; + if (uv_utf8_to_utf16(service, + (wchar_t*) alloc_ptr, + servicesize / sizeof(wchar_t)) == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + alloc_ptr += servicesize; + } else { + handle->service = NULL; + } + + /* copy hints to allocated memory and save pointer in handle */ + if (hints != NULL) { + handle->hints = (struct addrinfoW*)alloc_ptr; + handle->hints->ai_family = hints->ai_family; + handle->hints->ai_socktype = hints->ai_socktype; + handle->hints->ai_protocol = hints->ai_protocol; + handle->hints->ai_flags = hints->ai_flags; + handle->hints->ai_addrlen = 0; + handle->hints->ai_canonname = NULL; + handle->hints->ai_addr = NULL; + handle->hints->ai_next = NULL; + } else { + handle->hints = NULL; + } + + /* init request for Post handling */ + uv_req_init(loop, &handle->getadddrinfo_req); + handle->getadddrinfo_req.data = handle; + handle->getadddrinfo_req.type = UV_GETADDRINFO_REQ; + + /* Ask thread to run. Treat this as a long operation */ + if (QueueUserWorkItem(&getaddrinfo_thread_proc, + handle, + WT_EXECUTELONGFUNCTION) == 0) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + + uv_ref(loop); + + return 0; + +error: + if (handle != NULL && handle->alloc != NULL) { + free(handle->alloc); + } + return -1; +} diff --git a/src/rt/libuv/src/win/handle.c b/src/rt/libuv/src/win/handle.c new file mode 100644 index 00000000000..ab4f64bc5ae --- /dev/null +++ b/src/rt/libuv/src/win/handle.c @@ -0,0 +1,193 @@ +/* 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 + +#include "uv.h" +#include "internal.h" + + +int uv_is_active(uv_handle_t* handle) { + switch (handle->type) { + case UV_TIMER: + case UV_IDLE: + case UV_PREPARE: + case UV_CHECK: + return (handle->flags & UV_HANDLE_ACTIVE) ? 1 : 0; + + default: + return 1; + } +} + + +void uv_close(uv_handle_t* handle, uv_close_cb cb) { + uv_tcp_t* tcp; + uv_pipe_t* pipe; + uv_udp_t* udp; + uv_process_t* process; + + uv_loop_t* loop = handle->loop; + + if (handle->flags & UV_HANDLE_CLOSING) { + return; + } + + handle->flags |= UV_HANDLE_CLOSING; + handle->close_cb = cb; + + /* Handle-specific close actions */ + switch (handle->type) { + case UV_TCP: + tcp = (uv_tcp_t*)handle; + /* If we don't shutdown before calling closesocket, windows will */ + /* silently discard the kernel send buffer and reset the connection. */ + if (!(tcp->flags & UV_HANDLE_SHUT)) { + shutdown(tcp->socket, SD_SEND); + tcp->flags |= UV_HANDLE_SHUT; + } + tcp->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); + closesocket(tcp->socket); + if (tcp->reqs_pending == 0) { + uv_want_endgame(loop, handle); + } + return; + + case UV_NAMED_PIPE: + pipe = (uv_pipe_t*)handle; + pipe->flags &= ~(UV_HANDLE_READING | UV_HANDLE_LISTENING); + close_pipe(pipe, NULL, NULL); + if (pipe->reqs_pending == 0) { + uv_want_endgame(loop, handle); + } + return; + + case UV_UDP: + udp = (uv_udp_t*) handle; + uv_udp_recv_stop(udp); + closesocket(udp->socket); + if (udp->reqs_pending == 0) { + uv_want_endgame(loop, handle); + } + return; + + case UV_TIMER: + uv_timer_stop((uv_timer_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_PREPARE: + uv_prepare_stop((uv_prepare_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_CHECK: + uv_check_stop((uv_check_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_IDLE: + uv_idle_stop((uv_idle_t*)handle); + uv_want_endgame(loop, handle); + return; + + case UV_ASYNC: + if (!((uv_async_t*)handle)->async_sent) { + uv_want_endgame(loop, handle); + } + return; + + case UV_PROCESS: + process = (uv_process_t*)handle; + uv_process_close(loop, process); + return; + + case UV_FS_EVENT: + uv_fs_event_close(loop, (uv_fs_event_t*)handle); + return; + + default: + /* Not supported */ + abort(); + } +} + + +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; + } +} + + +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_UDP: + uv_udp_endgame(loop, (uv_udp_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_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; + + default: + assert(0); + break; + } + } +} diff --git a/src/rt/libuv/src/win/internal.h b/src/rt/libuv/src/win/internal.h new file mode 100644 index 00000000000..87a64eda1b2 --- /dev/null +++ b/src/rt/libuv/src/win/internal.h @@ -0,0 +1,305 @@ +/* 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" + + +/* + * Timers + */ +void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle); + +DWORD uv_get_poll_timeout(uv_loop_t* loop); +void uv_process_timers(uv_loop_t* loop); + + +/* + * Handles + */ + +/* Private uv_handle flags */ +#define UV_HANDLE_CLOSING 0x0001 +#define UV_HANDLE_CLOSED 0x0002 +#define UV_HANDLE_BOUND 0x0004 +#define UV_HANDLE_LISTENING 0x0008 +#define UV_HANDLE_CONNECTION 0x0010 +#define UV_HANDLE_CONNECTED 0x0020 +#define UV_HANDLE_READING 0x0040 +#define UV_HANDLE_ACTIVE 0x0040 +#define UV_HANDLE_EOF 0x0080 +#define UV_HANDLE_SHUTTING 0x0100 +#define UV_HANDLE_SHUT 0x0200 +#define UV_HANDLE_ENDGAME_QUEUED 0x0400 +#define UV_HANDLE_BIND_ERROR 0x1000 +#define UV_HANDLE_IPV6 0x2000 +#define UV_HANDLE_PIPESERVER 0x4000 +#define UV_HANDLE_READ_PENDING 0x8000 +#define UV_HANDLE_GIVEN_OS_HANDLE 0x10000 +#define UV_HANDLE_UV_ALLOCED 0x20000 +#define UV_HANDLE_SYNC_BYPASS_IOCP 0x40000 +#define UV_HANDLE_ZERO_READ 0x80000 + +void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle); +void uv_process_endgames(uv_loop_t* loop); + +#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_SUCCEEDED_WITHOUT_IOCP(result) \ + ((result) && (handle->flags & UV_HANDLE_SYNC_BYPASS_IOCP)) + +#define UV_SUCCEEDED_WITH_IOCP(result) \ + ((result) || (GetLastError() == ERROR_IO_PENDING)) + + +/* + * Requests + */ +void uv_req_init(uv_loop_t* loop, uv_req_t* req); + +uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped); + +void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req); +void uv_process_reqs(uv_loop_t* loop); + +#define POST_COMPLETION_FOR_REQ(loop, req) \ + if (!PostQueuedCompletionStatus((loop)->iocp, \ + 0, \ + 0, \ + &((req)->overlapped))) { \ + uv_fatal_error(GetLastError(), "PostQueuedCompletionStatus"); \ + } + + +/* + * Streams + */ +void uv_stream_init(uv_loop_t* loop, uv_stream_t* handle); +void uv_connection_init(uv_stream_t* handle); + +size_t uv_count_bufs(uv_buf_t bufs[], int count); + + +/* + * TCP + */ +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, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb); + +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_endgame(uv_loop_t* loop, uv_tcp_t* handle); + + +/* + * 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_endgame(uv_loop_t* loop, uv_udp_t* handle); + + +/* + * Pipes + */ +int uv_pipe_init_with_handle(uv_loop_t* loop, uv_pipe_t* handle, + HANDLE pipeHandle); +int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, + char* name, size_t nameSize); +void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err); +void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle); + +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); +int uv_pipe_accept(uv_pipe_t* server, uv_pipe_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, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb); + +void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_req_t* req); +void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_write_t* req); +void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_req_t* raw_req); +void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_connect_t* req); +void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_shutdown_t* req); + +/* + * 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); + + +/* + * Async watcher + */ +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); + + +/* + * Spawn + */ +void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle); +void uv_process_proc_close(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); + + +/* + * C-ares integration + */ +typedef struct uv_ares_action_s uv_ares_action_t; + +void uv_process_ares_event_req(uv_loop_t* loop, uv_ares_action_t* handle, + uv_req_t* req); +void uv_process_ares_cleanup_req(uv_loop_t* loop, uv_ares_task_t* handle, + uv_req_t* req); + +/* + * Getaddrinfo + */ +void uv_process_getaddrinfo_req(uv_loop_t* loop, uv_getaddrinfo_t* handle, + uv_req_t* req); + + +/* + * FS + */ +void uv_fs_init(); +void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req); + + +/* + * Threadpool + */ +void uv_process_work_req(uv_loop_t* loop, uv_work_t* req); + + +/* + * 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); + + +/* + * Error handling + */ +extern const uv_err_t uv_ok_; + +void uv_fatal_error(const int errorno, const char* syscall); + +uv_err_code uv_translate_sys_error(int sys_errno); +uv_err_t uv_new_sys_error(int sys_errno); +void uv_set_sys_error(uv_loop_t* loop, int sys_errno); +void uv_set_error(uv_loop_t* loop, uv_err_code code, int sys_errno); + +#define SET_REQ_STATUS(req, status) \ + (req)->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) \ + ((req)->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 GET_REQ_UV_ERROR(req) \ + (uv_new_sys_error(GET_REQ_ERROR((req)))) + +#define GET_REQ_UV_SOCK_ERROR(req) \ + (uv_new_sys_error(GET_REQ_SOCK_ERROR((req)))) + + +/* + * Initialization for the windows and winsock api + */ +void uv_winapi_init(); +void uv_winsock_init(); +int uv_ntstatus_to_winsock_error(NTSTATUS status); + + +/* Threads and synchronization */ +typedef struct uv_once_s { + unsigned char ran; + /* The actual event handle must be aligned to sizeof(HANDLE), so in */ + /* practice it might overlap padding a little. */ + HANDLE event; + HANDLE padding; +} uv_once_t; + +#define UV_ONCE_INIT \ + { 0, NULL, NULL } + +void uv_once(uv_once_t* guard, void (*callback)(void)); + + +#endif /* UV_WIN_INTERNAL_H_ */ diff --git a/src/rt/libuv/src/win/loop-watcher.c b/src/rt/libuv/src/win/loop-watcher.c new file mode 100644 index 00000000000..c597cd99b12 --- /dev/null +++ b/src/rt/libuv/src/win/loop-watcher.c @@ -0,0 +1,131 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.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; + + if (handle->close_cb) { + handle->close_cb(handle); + } + + uv_unref(loop); + } +} + + +#define UV_LOOP_WATCHER_DEFINE(name, NAME) \ + int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \ + handle->type = UV_##NAME; \ + handle->loop = loop; \ + handle->flags = 0; \ + \ + uv_ref(loop); \ + \ + loop->counters.handle_init++; \ + loop->counters.name##_init++; \ + \ + 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 (handle->flags & UV_HANDLE_ACTIVE) \ + return 0; \ + \ + 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; \ + handle->flags |= UV_HANDLE_ACTIVE; \ + \ + return 0; \ + } \ + \ + \ + int uv_##name##_stop(uv_##name##_t* handle) { \ + uv_loop_t* loop = handle->loop; \ + \ + assert(handle->type == UV_##NAME); \ + \ + if (!(handle->flags & UV_HANDLE_ACTIVE)) \ + 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; \ + } \ + \ + handle->flags &= ~UV_HANDLE_ACTIVE; \ + \ + 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, 0); \ + } \ + } + +UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) +UV_LOOP_WATCHER_DEFINE(check, CHECK) +UV_LOOP_WATCHER_DEFINE(idle, IDLE) diff --git a/src/rt/libuv/src/win/pipe.c b/src/rt/libuv/src/win/pipe.c new file mode 100644 index 00000000000..7832c6a41af --- /dev/null +++ b/src/rt/libuv/src/win/pipe.c @@ -0,0 +1,1067 @@ +/* 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 +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* A zero-size buffer for use by uv_pipe_read */ +static char uv_zero_[] = ""; + +/* Null uv_buf_t */ +static const uv_buf_t uv_null_buf_ = { 0, NULL }; + +/* The timeout that the pipe will wait for the remote end to write data */ +/* when the local ends wants to shut it down. */ +static const int64_t eof_timeout = 50; /* ms */ + +static 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, int status); +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-%d", ptr, GetCurrentProcessId()); +} + + +int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle) { + uv_stream_init(loop, (uv_stream_t*)handle); + + handle->type = UV_NAMED_PIPE; + handle->reqs_pending = 0; + handle->handle = INVALID_HANDLE_VALUE; + handle->name = NULL; + + loop->counters.pipe_init++; + + return 0; +} + + +int uv_pipe_init_with_handle(uv_loop_t* loop, uv_pipe_t* handle, + HANDLE pipeHandle) { + int err = uv_pipe_init(loop, handle); + + if (!err) { + /* + * At this point we don't know whether the pipe will be used as a client + * or a server. So, we assume that it will be a client until + * uv_listen is called. + */ + handle->handle = pipeHandle; + handle->flags |= UV_HANDLE_GIVEN_OS_HANDLE; + } + + return err; +} + + +static void uv_pipe_connection_init(uv_pipe_t* handle) { + uv_connection_init((uv_stream_t*) handle); + handle->eof_timer = NULL; +} + + +int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, + char* name, size_t nameSize) { + HANDLE pipeHandle; + int errno; + int err; + char* ptr = (char*)handle; + + while (TRUE) { + 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; + } + + errno = GetLastError(); + if (errno != ERROR_PIPE_BUSY && errno != ERROR_ACCESS_DENIED) { + uv_set_sys_error(loop, errno); + err = -1; + goto done; + } + + /* Pipe name collision. Increment the pointer and try again. */ + ptr++; + } + + if (CreateIoCompletionPort(pipeHandle, + loop->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + + uv_pipe_connection_init(handle); + handle->handle = pipeHandle; + handle->flags |= UV_HANDLE_GIVEN_OS_HANDLE; + err = 0; + +done: + if (err && pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + } + + return err; +} + + +static int uv_set_pipe_handle(uv_loop_t* loop, uv_pipe_t* handle, + HANDLE pipeHandle) { + DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + + if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) { + return -1; + } + + if (CreateIoCompletionPort(pipeHandle, + loop->iocp, + (ULONG_PTR)handle, + 0) == NULL) { + return -1; + } + + return 0; +} + + +static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) { + int errno; + 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) { + unsigned int uv_alloced; + DWORD result; + uv_shutdown_t* req; + NTSTATUS nt_status; + IO_STATUS_BLOCK io_status; + FILE_PIPE_LOCAL_INFORMATION pipe_info; + + + if (handle->flags & UV_HANDLE_SHUTTING && + !(handle->flags & UV_HANDLE_SHUT) && + handle->write_reqs_pending == 0) { + req = handle->shutdown_req; + + /* 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 */ + handle->flags &= ~UV_HANDLE_SHUTTING; + if (req->cb) { + uv_set_sys_error(loop, pRtlNtStatusToDosError(nt_status)); + req->cb(req, -1); + } + DECREASE_PENDING_REQ_COUNT(handle); + return; + } + + if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { + handle->flags |= UV_HANDLE_SHUT; + + /* Short-circuit, no need to call FlushFileBuffers. */ + uv_insert_pending_req(loop, (uv_req_t*) req); + return; + } + + /* Run FlushFileBuffers in the thhead pool. */ + result = QueueUserWorkItem(pipe_shutdown_thread_proc, + req, + WT_EXECUTELONGFUNCTION); + if (result) { + /* Mark the handle as shut now to avoid going through this again. */ + handle->flags |= UV_HANDLE_SHUT; + + } else { + /* Failure. */ + handle->flags &= ~UV_HANDLE_SHUTTING; + if (req->cb) { + uv_set_sys_error(loop, GetLastError()); + req->cb(req, -1); + } + DECREASE_PENDING_REQ_COUNT(handle); + return; + } + } + + if (handle->flags & UV_HANDLE_CLOSING && + handle->reqs_pending == 0) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + /* Remember the state of this flag because the close callback is */ + /* allowed to clobber or free the handle's memory */ + uv_alloced = handle->flags & UV_HANDLE_UV_ALLOCED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + if (uv_alloced) { + free(handle); + } + + uv_unref(loop); + } +} + + +/* Creates a pipe server. */ +int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + uv_loop_t* loop = handle->loop; + int i, errno, nameSize; + uv_pipe_accept_t* req; + + if (handle->flags & UV_HANDLE_BOUND) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (!name) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + req = &handle->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 = uv_utf8_to_utf16(name, NULL, 0) * sizeof(wchar_t); + handle->name = (wchar_t*)malloc(nameSize); + if (!handle->name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(wchar_t))) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* + * 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->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->accept_reqs[0].pipeHandle == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_ACCESS_DENIED) { + uv_set_error(loop, UV_EADDRINUSE, errno); + } else if (errno == ERROR_PATH_NOT_FOUND || errno == ERROR_INVALID_NAME) { + uv_set_error(loop, UV_EACCESS, errno); + } else { + uv_set_sys_error(loop, errno); + } + goto error; + } + + if (uv_set_pipe_handle(loop, handle, handle->accept_reqs[0].pipeHandle)) { + uv_set_sys_error(loop, GetLastError()); + goto error; + } + + handle->pending_accepts = NULL; + handle->flags |= UV_HANDLE_PIPESERVER; + handle->flags |= UV_HANDLE_BOUND; + + return 0; + +error: + if (handle->name) { + free(handle->name); + handle->name = NULL; + } + + if (handle->accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->accept_reqs[0].pipeHandle); + handle->accept_reqs[0].pipeHandle = INVALID_HANDLE_VALUE; + } + + return -1; +} + + +static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { + HANDLE pipeHandle = INVALID_HANDLE_VALUE; + int errno; + uv_loop_t* loop; + uv_pipe_t* handle; + uv_connect_t* req; + + 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 = CreateFileW(handle->name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + if (pipeHandle != INVALID_HANDLE_VALUE) { + break; + } + + SwitchToThread(); + } + + if (pipeHandle != INVALID_HANDLE_VALUE && + !uv_set_pipe_handle(loop, handle, pipeHandle)) { + handle->handle = pipeHandle; + SET_REQ_SUCCESS(req); + } else { + SET_REQ_ERROR(req, GetLastError()); + } + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +int 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 errno, nameSize; + HANDLE pipeHandle; + + handle->handle = INVALID_HANDLE_VALUE; + + 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 = uv_utf8_to_utf16(name, NULL, 0) * sizeof(wchar_t); + handle->name = (wchar_t*)malloc(nameSize); + if (!handle->name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf8_to_utf16(name, handle->name, nameSize / sizeof(wchar_t))) { + errno = GetLastError(); + goto error; + } + + pipeHandle = CreateFileW(handle->name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL); + + 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)) { + errno = GetLastError(); + goto error; + } + + handle->reqs_pending++; + + return 0; + } + + errno = GetLastError(); + goto error; + } + + if (uv_set_pipe_handle(loop, (uv_pipe_t*)req->handle, pipeHandle)) { + errno = GetLastError(); + goto error; + } + + handle->handle = pipeHandle; + + SET_REQ_SUCCESS(req); + uv_insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + return 0; + +error: + if (handle->name) { + free(handle->name); + handle->name = NULL; + } + + if (pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + } + uv_set_sys_error(loop, errno); + return -1; +} + + +/* Cleans up uv_pipe_t (server or connection) and all resources associated */ +/* with it. */ +void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err) { + int i; + HANDLE pipeHandle; + + if (handle->name) { + free(handle->name); + handle->name = NULL; + } + + if (handle->flags & UV_HANDLE_PIPESERVER) { + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + pipeHandle = handle->accept_reqs[i].pipeHandle; + if (pipeHandle != INVALID_HANDLE_VALUE) { + CloseHandle(pipeHandle); + handle->accept_reqs[i].pipeHandle = INVALID_HANDLE_VALUE; + } + } + + } + + if (handle->flags & UV_HANDLE_CONNECTION) { + eof_timer_destroy(handle); + } + + if ((handle->flags & UV_HANDLE_CONNECTION) + && handle->handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle->handle); + handle->handle = INVALID_HANDLE_VALUE; + } + + handle->flags |= UV_HANDLE_SHUT; +} + + +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)) { + 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->overlapped), 0, sizeof(req->overlapped)); + + if (!ConnectNamedPipe(req->pipeHandle, &req->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_pipe_t* client) { + uv_loop_t* loop = server->loop; + /* Find a connection instance that has been connected, but not yet */ + /* accepted. */ + uv_pipe_accept_t* req = server->pending_accepts; + + if (!req) { + /* No valid connections found, so we error out. */ + uv_set_sys_error(loop, WSAEWOULDBLOCK); + return -1; + } + + /* Initialize the client handle and copy the pipeHandle to the client */ + uv_pipe_connection_init(client); + client->handle = req->pipeHandle; + + /* Prepare the req to pick up a new connection */ + server->pending_accepts = req->next_pending; + req->next_pending = NULL; + req->pipeHandle = INVALID_HANDLE_VALUE; + + if (!(server->flags & UV_HANDLE_CLOSING) && + !(server->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + 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, errno; + uv_pipe_accept_t* req; + HANDLE pipeHandle; + + if (!(handle->flags & UV_HANDLE_BOUND) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_LISTENING || + handle->flags & UV_HANDLE_READING) { + uv_set_error(loop, UV_EALREADY, 0); + return -1; + } + + if (!(handle->flags & UV_HANDLE_PIPESERVER) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + uv_set_error(loop, UV_ENOTSUP, 0); + return -1; + } + + handle->flags |= UV_HANDLE_LISTENING; + handle->connection_cb = cb; + + if (handle->flags & UV_HANDLE_GIVEN_OS_HANDLE) { + handle->flags |= UV_HANDLE_PIPESERVER; + pipeHandle = handle->handle; + assert(pipeHandle != INVALID_HANDLE_VALUE); + req = &handle->accept_reqs[0]; + uv_req_init(loop, (uv_req_t*) req); + req->pipeHandle = pipeHandle; + req->type = UV_ACCEPT; + req->data = handle; + req->next_pending = NULL; + + if (uv_set_pipe_handle(loop, handle, pipeHandle)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + uv_pipe_queue_accept(loop, handle, req, TRUE); + } else { + /* First pipe handle should have already been created in uv_pipe_bind */ + assert(handle->accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE); + + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + uv_pipe_queue_accept(loop, handle, &handle->accept_reqs[i], i == 0); + } + } + + return 0; +} + + +static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { + uv_req_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; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + /* Do 0-read */ + result = ReadFile(handle->handle, + &uv_zero_, + 0, + NULL, + &req->overlapped); + + if (!result && GetLastError() != ERROR_IO_PENDING) { + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv_insert_pending_req(loop, req); + handle->reqs_pending++; + return; + } + + /* Start the eof timer if there is one */ + eof_timer_start(handle); + + 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; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_READING) { + uv_set_error(loop, UV_EALREADY, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_EOF) { + uv_set_error(loop, UV_EOF, 0); + return -1; + } + + handle->flags |= UV_HANDLE_READING; + handle->read_cb = read_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell be a */ + /* read request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + uv_pipe_queue_read(loop, handle); + + return 0; +} + + +int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { + int result; + + if (bufcnt != 1) { + uv_set_error(loop, UV_ENOTSUP, 0); + return -1; + } + + assert(handle->handle != INVALID_HANDLE_VALUE); + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_error(loop, UV_EOF, 0); + return -1; + } + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_WRITE; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + result = WriteFile(handle->handle, + bufs[0].base, + bufs[0].len, + NULL, + &req->overlapped); + + if (!result && GetLastError() != ERROR_IO_PENDING) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (result) { + /* Request completed immediately. */ + req->queued_bytes = 0; + } else { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->write_queue_size += req->queued_bytes; + } + + handle->reqs_pending++; + handle->write_reqs_pending++; + + return 0; +} + + +static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, + uv_buf_t buf) { + /* If there is an eof timer running, we don't need it any more, */ + /* so discard it. */ + eof_timer_destroy(handle); + + handle->flags |= UV_HANDLE_EOF; + uv_read_stop((uv_stream_t*) handle); + + uv_set_error(loop, UV_EOF, 0); + handle->read_cb((uv_stream_t*) handle, -1, uv_null_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); + + uv_set_sys_error(loop, error); + handle->read_cb((uv_stream_t*)handle, -1, 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_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, + uv_req_t* req) { + DWORD bytes, avail; + uv_buf_t buf; + + 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; + } + + buf = handle->alloc_cb((uv_handle_t*) handle, avail); + assert(buf.len > 0); + + if (ReadFile(handle->handle, + buf.base, + buf.len, + &bytes, + NULL)) { + /* Successful read */ + 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(), uv_null_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) { + assert(handle->type == UV_NAMED_PIPE); + + handle->write_queue_size -= req->queued_bytes; + + if (req->cb) { + if (!REQ_SUCCESS(req)) { + loop->last_error = GET_REQ_UV_ERROR(req); + ((uv_write_cb)req->cb)(req, -1); + } else { + ((uv_write_cb)req->cb)(req, 0); + } + } + + handle->write_reqs_pending--; + if (handle->write_reqs_pending == 0 && + handle->flags & UV_HANDLE_SHUTTING) { + 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 (REQ_SUCCESS(req)) { + assert(req->pipeHandle != INVALID_HANDLE_VALUE); + req->next_pending = handle->pending_accepts; + handle->pending_accepts = req; + + if (handle->connection_cb) { + handle->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) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { + 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) { + assert(handle->type == UV_NAMED_PIPE); + + if (req->cb) { + if (REQ_SUCCESS(req)) { + uv_pipe_connection_init(handle); + ((uv_connect_cb)req->cb)(req, 0); + } else { + loop->last_error = GET_REQ_UV_ERROR(req); + ((uv_connect_cb)req->cb)(req, -1); + } + } + + 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); + + /* Initialize and optionally start the eof timer. */ + /* This makes no sense if we've already seen EOF. */ + if (!(handle->flags & UV_HANDLE_EOF)) { + 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); + } + } + + 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->eof_timer == NULL); + assert(pipe->flags & UV_HANDLE_CONNECTION); + + pipe->eof_timer = (uv_timer_t*) malloc(sizeof *pipe->eof_timer); + + r = uv_timer_init(pipe->loop, pipe->eof_timer); + assert(r == 0); /* timers can't fail */ + pipe->eof_timer->data = pipe; +} + + +static void eof_timer_start(uv_pipe_t* pipe) { + assert(pipe->flags & UV_HANDLE_CONNECTION); + + if (pipe->eof_timer != NULL) { + uv_timer_start(pipe->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->eof_timer != NULL) { + uv_timer_stop(pipe->eof_timer); + } +} + + +static void eof_timer_cb(uv_timer_t* timer, int status) { + uv_pipe_t* pipe = (uv_pipe_t*) timer->data; + uv_loop_t* loop = timer->loop; + + assert(status == 0); /* timers can't fail */ + 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.overlapped)) { + return; + } + + /* Force both ends off the pipe. */ + CloseHandle(pipe->handle); + pipe->handle = INVALID_HANDLE_VALUE; + + /* 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->eof_timer) { + uv_close((uv_handle_t*) pipe->eof_timer, eof_timer_close_cb); + pipe->eof_timer = NULL; + } +} + + +static void eof_timer_close_cb(uv_handle_t* handle) { + assert(handle->type == UV_TIMER); + free(handle); +} + + +void uv_pipe_open(uv_pipe_t* pipe, uv_file file) { + assert(0 && "implement me"); +} + diff --git a/src/rt/libuv/src/win/process.c b/src/rt/libuv/src/win/process.c new file mode 100644 index 00000000000..ed8015e0ef6 --- /dev/null +++ b/src/rt/libuv/src/win/process.c @@ -0,0 +1,1045 @@ +/* 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 "internal.h" + +#include +#include +#include +#include + +typedef struct env_var { + const char* narrow; + const wchar_t* wide; + int len; /* including null or '=' */ + int supplied; + int value_len; +} env_var_t; + +#define E_V(str) { str "=", L##str, sizeof(str), 0, 0 } + +#define UTF8_TO_UTF16(s, t) \ + size = uv_utf8_to_utf16(s, NULL, 0) * sizeof(wchar_t); \ + t = (wchar_t*)malloc(size); \ + if (!t) { \ + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); \ + } \ + if (!uv_utf8_to_utf16(s, t, size / sizeof(wchar_t))) { \ + uv_set_sys_error(loop, GetLastError()); \ + err = -1; \ + goto done; \ + } + + +static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { + handle->type = UV_PROCESS; + handle->loop = loop; + handle->flags = 0; + handle->exit_cb = NULL; + handle->pid = 0; + handle->exit_signal = 0; + handle->wait_handle = INVALID_HANDLE_VALUE; + handle->process_handle = INVALID_HANDLE_VALUE; + handle->close_handle = INVALID_HANDLE_VALUE; + handle->child_stdio[0] = INVALID_HANDLE_VALUE; + handle->child_stdio[1] = INVALID_HANDLE_VALUE; + handle->child_stdio[2] = INVALID_HANDLE_VALUE; + + uv_req_init(loop, (uv_req_t*)&handle->exit_req); + handle->exit_req.type = UV_PROCESS_EXIT; + handle->exit_req.data = handle; + uv_req_init(loop, (uv_req_t*)&handle->close_req); + handle->close_req.type = UV_PROCESS_CLOSE; + handle->close_req.data = handle; + + loop->counters.handle_init++; + loop->counters.process_init++; + + uv_ref(loop); +} + + +/* + * Path search functions + */ + +/* + * Helper function for search_path + */ +static wchar_t* search_path_join_test(const wchar_t* dir, + int dir_len, + const wchar_t* name, + int name_len, + const wchar_t* ext, + int ext_len, + const wchar_t* cwd, + int cwd_len) { + wchar_t *result, *result_pos; + DWORD attrs; + + 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_t*)malloc(sizeof(wchar_t) * + (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 | FILE_ATTRIBUTE_REPARSE_POINT))) { + return result; + } + + free(result); + return NULL; +} + + +/* + * Helper function for search_path + */ +static wchar_t* path_search_walk_ext(const wchar_t *dir, + int dir_len, + const wchar_t *name, + int name_len, + wchar_t *cwd, + int cwd_len, + int name_has_ext) { + wchar_t* 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 wil 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. + * + * TODO: correctly interpret UNC paths + */ +static wchar_t* search_path(const wchar_t *file, + wchar_t *cwd, + const wchar_t *path) { + int file_has_dir; + wchar_t* result = NULL; + wchar_t *file_name_start; + wchar_t *dot; + int name_has_ext; + + int file_len = wcslen(file); + int 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_t*)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 { + const wchar_t *dir_start, + *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) { + 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; + } + + result = path_search_walk_ext(dir_start, dir_end - dir_start, + 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_t* quote_cmd_arg(const wchar_t *source, wchar_t *target) { + int len = wcslen(source), + i, quote_hit; + wchar_t* start; + + /* + * Check if the string must be quoted; + * if unnecessary, don't do it, it may only confuse older programs. + */ + if (len == 0) { + 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 intput/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; +} + + +wchar_t* make_program_args(char** args, int verbatim_arguments) { + wchar_t* dst; + wchar_t* ptr; + char** arg; + size_t size = 0; + size_t len; + int arg_count = 0; + wchar_t* buffer; + int arg_size; + int buffer_size = 0; + + /* Count the required size. */ + for (arg = args; *arg; arg++) { + arg_size = uv_utf8_to_utf16(*arg, NULL, 0) * sizeof(wchar_t); + size += arg_size; + buffer_size = arg_size > buffer_size ? arg_size : buffer_size; + arg_count++; + } + + /* Adjust for potential quotes. Also assume the worst-case scenario + /* that every character needs escaping, so we need twice as much space. */ + size = size * 2 + arg_count * 2; + + dst = (wchar_t*)malloc(size); + if (!dst) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + buffer = (wchar_t*)malloc(buffer_size); + if (!buffer) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + ptr = dst; + for (arg = args; *arg; arg++) { + len = uv_utf8_to_utf16(*arg, buffer, (size_t)(size - (ptr - dst))); + if (!len) { + goto error; + } + if (verbatim_arguments) { + wcscpy(ptr, buffer); + ptr += len - 1; + } else { + ptr = quote_cmd_arg(buffer, ptr); + } + *ptr++ = *(arg + 1) ? L' ' : L'\0'; + } + + free(buffer); + return dst; + +error: + free(dst); + free(buffer); + return NULL; +} + + +/* + * If we learn that people are passing in huge environment blocks + * then we should probably qsort() the array and then bsearch() + * to see if it contains this variable. But there are ownership + * issues associated with that solution; this is the caller's + * char**, and modifying it is rude. + */ +static void check_required_vars_contains_var(env_var_t* required, int size, + const char* var) { + int i; + for (i = 0; i < size; ++i) { + if (_strnicmp(required[i].narrow, var, required[i].len) == 0) { + required[i].supplied = 1; + return; + } + } +} + + +/* + * 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. + */ +wchar_t* make_program_env(char** env_block) { + wchar_t* dst; + wchar_t* ptr; + char** env; + int env_len = 1 * sizeof(wchar_t); /* room for closing null */ + int len; + int i; + DWORD var_size; + + env_var_t required_vars[] = { + E_V("SYSTEMROOT"), + E_V("SYSTEMDRIVE"), + E_V("TEMP"), + }; + + for (env = env_block; *env; env++) { + check_required_vars_contains_var(required_vars, + COUNTOF(required_vars), + *env); + env_len += (uv_utf8_to_utf16(*env, NULL, 0) * sizeof(wchar_t)); + } + + for (i = 0; i < COUNTOF(required_vars); ++i) { + if (!required_vars[i].supplied) { + env_len += required_vars[i].len * sizeof(wchar_t); + var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0); + if (var_size == 0) { + uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); + } + required_vars[i].value_len = (int)var_size; + env_len += (int)var_size * sizeof(wchar_t); + } + } + + dst = malloc(env_len); + if (!dst) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + ptr = dst; + + for (env = env_block; *env; env++, ptr += len) { + len = uv_utf8_to_utf16(*env, ptr, (size_t)(env_len - (ptr - dst))); + if (!len) { + free(dst); + return NULL; + } + } + + for (i = 0; i < COUNTOF(required_vars); ++i) { + if (!required_vars[i].supplied) { + wcscpy(ptr, required_vars[i].wide); + ptr += required_vars[i].len - 1; + *ptr++ = L'='; + var_size = GetEnvironmentVariableW(required_vars[i].wide, + ptr, + required_vars[i].value_len); + if (var_size == 0) { + uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); + } + ptr += required_vars[i].value_len; + } + } + + *ptr = L'\0'; + return dst; +} + + +/* + * 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); + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, &process->exit_req); +} + + +/* + * Called on Windows thread-pool thread to indicate that + * UnregisterWaitEx has completed. + */ +static void CALLBACK close_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); + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, &process->close_req); +} + + +/* + * Called on windows thread pool when CreateProcess failed. It writes an error + * message to the process' intended stderr and then posts a PROCESS_EXIT + * packet to the completion port. + */ +static DWORD WINAPI spawn_failure(void* data) { + char syscall[] = "CreateProcessW: "; + char unknown[] = "unknown error\n"; + uv_process_t* process = (uv_process_t*) data; + uv_loop_t* loop = process->loop; + HANDLE child_stderr = process->child_stdio[2]; + char* buf = NULL; + DWORD count, written; + + WriteFile(child_stderr, syscall, sizeof(syscall) - 1, &written, NULL); + + count = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + process->spawn_errno, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &buf, + 0, + NULL); + + if (buf != NULL && count > 0) { + WriteFile(child_stderr, buf, count, &written, NULL); + LocalFree(buf); + } else { + WriteFile(child_stderr, unknown, sizeof(unknown) - 1, &written, NULL); + } + + FlushFileBuffers(child_stderr); + + /* Post completed */ + POST_COMPLETION_FOR_REQ(loop, &process->exit_req); + + return 0; +} + + +static void close_child_stdio(uv_process_t* process) { + int i; + HANDLE handle; + + for (i = 0; i < COUNTOF(process->child_stdio); i++) { + handle = process->child_stdio[i]; + if (handle != NULL && handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + process->child_stdio[i] = INVALID_HANDLE_VALUE; + } + } +} + + +/* Called on main thread after a child process has exited. */ +void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { + DWORD exit_code; + + /* Unregister from process notification. */ + if (handle->wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(handle->wait_handle); + handle->wait_handle = INVALID_HANDLE_VALUE; + } + + if (handle->process_handle != INVALID_HANDLE_VALUE) { + /* Get the exit code. */ + if (!GetExitCodeProcess(handle->process_handle, &exit_code)) { + exit_code = 127; + } + + /* Clean-up the process handle. */ + CloseHandle(handle->process_handle); + handle->process_handle = INVALID_HANDLE_VALUE; + } else { + /* We probably left the child stdio handles open to report the error */ + /* asynchronously, so close them now. */ + close_child_stdio(handle); + + /* The process never even started in the first place. */ + exit_code = 127; + } + + /* Fire the exit callback. */ + if (handle->exit_cb) { + handle->exit_cb(handle, exit_code, handle->exit_signal); + } +} + + +/* Called on main thread after UnregisterWaitEx finishes. */ +void uv_process_proc_close(uv_loop_t* loop, uv_process_t* handle) { + uv_want_endgame(loop, (uv_handle_t*)handle); +} + + +void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { + if (handle->flags & UV_HANDLE_CLOSING) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { + if (handle->wait_handle != INVALID_HANDLE_VALUE) { + handle->close_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + UnregisterWaitEx(handle->wait_handle, handle->close_handle); + handle->wait_handle = NULL; + + RegisterWaitForSingleObject(&handle->wait_handle, handle->close_handle, + close_wait_callback, (void*)handle, INFINITE, + WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE); + } else { + uv_want_endgame(loop, (uv_handle_t*)handle); + } +} + + +static int uv_create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* server_pipe, + HANDLE* child_pipe, DWORD server_access, DWORD child_access) { + int err; + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + char pipe_name[64]; + DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; + + if (server_pipe->type != UV_NAMED_PIPE) { + uv_set_error(loop, UV_EINVAL, 0); + err = -1; + goto done; + } + + /* Create server pipe handle. */ + err = uv_stdio_pipe_server(loop, + server_pipe, + server_access, + pipe_name, + sizeof(pipe_name)); + if (err) { + goto done; + } + + /* Create child pipe handle. */ + *child_pipe = CreateFileA(pipe_name, + child_access, + 0, + &sa, + OPEN_EXISTING, + 0, + NULL); + + if (*child_pipe == INVALID_HANDLE_VALUE) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + + if (!SetNamedPipeHandleState(*child_pipe, &mode, NULL, NULL)) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + + /* 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) { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + } + + err = 0; + +done: + if (err) { + if (server_pipe->handle != INVALID_HANDLE_VALUE) { + close_pipe(server_pipe, NULL, NULL); + } + + if (*child_pipe != INVALID_HANDLE_VALUE) { + CloseHandle(*child_pipe); + *child_pipe = INVALID_HANDLE_VALUE; + } + } + + return err; +} + + +static int duplicate_std_handle(uv_loop_t* loop, DWORD id, HANDLE* dup) { + HANDLE handle; + HANDLE current_process = GetCurrentProcess(); + + handle = GetStdHandle(id); + + if (handle == NULL) { + *dup = NULL; + return 0; + } else if (handle == INVALID_HANDLE_VALUE) { + *dup = INVALID_HANDLE_VALUE; + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (!DuplicateHandle(current_process, + handle, + current_process, + dup, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) { + *dup = INVALID_HANDLE_VALUE; + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + return 0; +} + + +int uv_spawn(uv_loop_t* loop, uv_process_t* process, + uv_process_options_t options) { + int err = 0, keep_child_stdio_open = 0; + wchar_t* path = NULL; + int size; + BOOL result; + wchar_t* application_path = NULL, *application = NULL, *arguments = NULL, *env = NULL, *cwd = NULL; + HANDLE* child_stdio = process->child_stdio; + STARTUPINFOW startup; + PROCESS_INFORMATION info; + + if (!options.file) { + uv_set_error(loop, UV_EINVAL, 0); + return -1; + } + + uv_process_init(loop, process); + + process->exit_cb = options.exit_cb; + UTF8_TO_UTF16(options.file, application); + arguments = options.args ? make_program_args(options.args, + options.windows_verbatim_arguments) : NULL; + env = options.env ? make_program_env(options.env) : NULL; + + if (options.cwd) { + UTF8_TO_UTF16(options.cwd, cwd); + } else { + size = GetCurrentDirectoryW(0, NULL) * sizeof(wchar_t); + if (size) { + cwd = (wchar_t*)malloc(size); + if (!cwd) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + GetCurrentDirectoryW(size, cwd); + } else { + uv_set_sys_error(loop, GetLastError()); + err = -1; + goto done; + } + } + + /* Get PATH env. variable. */ + size = GetEnvironmentVariableW(L"PATH", NULL, 0) + 1; + path = (wchar_t*)malloc(size * sizeof(wchar_t)); + if (!path) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + GetEnvironmentVariableW(L"PATH", path, size * sizeof(wchar_t)); + path[size - 1] = L'\0'; + + application_path = search_path(application, + cwd, + path); + + if (!application_path) { + /* CreateProcess will fail, but this allows us to pass this error to */ + /* the user asynchronously. */ + application_path = application; + } + + /* Create stdio pipes. */ + if (options.stdin_stream) { + err = uv_create_stdio_pipe_pair( + loop, + options.stdin_stream, + &child_stdio[0], + PIPE_ACCESS_OUTBOUND, + GENERIC_READ | FILE_WRITE_ATTRIBUTES); + } else { + err = duplicate_std_handle(loop, STD_INPUT_HANDLE, &child_stdio[0]); + } + if (err) { + goto done; + } + + if (options.stdout_stream) { + err = uv_create_stdio_pipe_pair( + loop, options.stdout_stream, + &child_stdio[1], + PIPE_ACCESS_INBOUND, + GENERIC_WRITE); + } else { + err = duplicate_std_handle(loop, STD_OUTPUT_HANDLE, &child_stdio[1]); + } + if (err) { + goto done; + } + + if (options.stderr_stream) { + err = uv_create_stdio_pipe_pair( + loop, + options.stderr_stream, + &child_stdio[2], + PIPE_ACCESS_INBOUND, + GENERIC_WRITE); + } else { + err = duplicate_std_handle(loop, STD_ERROR_HANDLE, &child_stdio[2]); + } + if (err) { + goto done; + } + + startup.cb = sizeof(startup); + startup.lpReserved = NULL; + startup.lpDesktop = NULL; + startup.lpTitle = NULL; + startup.dwFlags = STARTF_USESTDHANDLES; + startup.cbReserved2 = 0; + startup.lpReserved2 = NULL; + startup.hStdInput = child_stdio[0]; + startup.hStdOutput = child_stdio[1]; + startup.hStdError = child_stdio[2]; + + if (CreateProcessW(application_path, + arguments, + NULL, + NULL, + 1, + CREATE_UNICODE_ENVIRONMENT, + env, + cwd, + &startup, + &info)) { + /* Spawn succeeded */ + process->process_handle = info.hProcess; + process->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); + + } else { + /* CreateProcessW failed, but this failure should be delivered */ + /* asynchronously to retain unix compatibility. So pretent spawn */ + /* succeeded, and start a thread instead that prints an error */ + /* to the child's intended stderr. */ + process->spawn_errno = GetLastError(); + keep_child_stdio_open = 1; + if (!QueueUserWorkItem(spawn_failure, process, WT_EXECUTEDEFAULT)) { + uv_fatal_error(GetLastError(), "QueueUserWorkItem"); + } + } + +done: + free(application); + if (application_path != application) { + free(application_path); + } + free(arguments); + free(cwd); + free(env); + free(path); + + /* Under normal circumstances we should close the stdio handles now - */ + /* the child now has its own duplicates, or something went horribly wrong. */ + /* The only exception is when CreateProcess has failed, then we actually */ + /* need to keep the stdio handles to report the error asynchronously. */ + if (!keep_child_stdio_open) { + close_child_stdio(process); + } else { + /* We're keeping the handles open, the thread pool is going to have */ + /* it's way with them. But at least make them noninheritable. */ + int i; + for (i = 0; i < COUNTOF(process->child_stdio); i++) { + SetHandleInformation(child_stdio[i], HANDLE_FLAG_INHERIT, 0); + } + } + + if (err) { + if (process->wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(process->wait_handle); + process->wait_handle = INVALID_HANDLE_VALUE; + } + + if (process->process_handle != INVALID_HANDLE_VALUE) { + CloseHandle(process->process_handle); + process->process_handle = INVALID_HANDLE_VALUE; + } + } + + return err; +} + + +int uv_process_kill(uv_process_t* process, int signum) { + process->exit_signal = signum; + + /* On windows killed processes normally return 1 */ + if (process->process_handle != INVALID_HANDLE_VALUE && + TerminateProcess(process->process_handle, 1)) { + return 0; + } + + return -1; +} diff --git a/src/rt/libuv/src/win/req.c b/src/rt/libuv/src/win/req.c new file mode 100644 index 00000000000..a0a6e03dc60 --- /dev/null +++ b/src/rt/libuv/src/win/req.c @@ -0,0 +1,176 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_req_init(uv_loop_t* loop, uv_req_t* req) { + loop->counters.req_init++; + req->type = UV_UNKNOWN_REQ; + SET_REQ_SUCCESS(req); +} + + +uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { + return CONTAINING_RECORD(overlapped, uv_req_t, overlapped); +} + + +void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) { + req->next_req = NULL; + if (loop->pending_reqs_tail) { + 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; + } +} + + +static uv_req_t* uv_remove_pending_req(uv_loop_t* loop) { + uv_req_t* req; + + if (loop->pending_reqs_tail) { + req = loop->pending_reqs_tail->next_req; + + if (req == loop->pending_reqs_tail) { + loop->pending_reqs_tail = NULL; + } else { + loop->pending_reqs_tail->next_req = req->next_req; + } + + return req; + + } else { + /* queue empty */ + return NULL; + } +} + + +#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; \ + \ + default: \ + assert(0); \ + } \ + } while (0) + + +void uv_process_reqs(uv_loop_t* loop) { + uv_req_t* req; + + while (req = uv_remove_pending_req(loop)) { + 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_ARES_EVENT_REQ: + uv_process_ares_event_req(loop, (uv_ares_action_t*) req->data, req); + break; + + case UV_ARES_CLEANUP_REQ: + uv_process_ares_cleanup_req(loop, (uv_ares_task_t*) req->data, req); + break; + + case UV_GETADDRINFO_REQ: + uv_process_getaddrinfo_req(loop, (uv_getaddrinfo_t*) req->data, req); + break; + + case UV_PROCESS_EXIT: + uv_process_proc_exit(loop, (uv_process_t*) req->data); + break; + + case UV_PROCESS_CLOSE: + uv_process_proc_close(loop, (uv_process_t*) req->data); + break; + + case UV_FS: + uv_process_fs_req(loop, (uv_fs_t*) req); + break; + + case UV_WORK: + uv_process_work_req(loop, (uv_work_t*) req); + break; + + case UV_FS_EVENT_REQ: + uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); + break; + + default: + assert(0); + } + } +} diff --git a/src/rt/libuv/src/win/stdio.c b/src/rt/libuv/src/win/stdio.c new file mode 100644 index 00000000000..30fe2a1d6e1 --- /dev/null +++ b/src/rt/libuv/src/win/stdio.c @@ -0,0 +1,75 @@ +/* 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 +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +static uv_pipe_t* uv_make_pipe_for_std_handle(uv_loop_t* loop, HANDLE handle) { + uv_pipe_t* pipe = NULL; + + pipe = (uv_pipe_t*)malloc(sizeof(uv_pipe_t)); + if (!pipe) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (uv_pipe_init_with_handle(loop, pipe, handle)) { + free(pipe); + return NULL; + } + + pipe->flags |= UV_HANDLE_UV_ALLOCED; + return pipe; +} + + +uv_stream_t* uv_std_handle(uv_loop_t* loop, uv_std_type type) { + HANDLE handle; + + switch (type) { + case UV_STDIN: + handle = GetStdHandle(STD_INPUT_HANDLE); + if (handle == INVALID_HANDLE_VALUE) { + return NULL; + } + + /* Assume only named pipes for now. */ + return (uv_stream_t*)uv_make_pipe_for_std_handle(loop, handle); + break; + + case UV_STDOUT: + return NULL; + break; + + case UV_STDERR: + return NULL; + break; + + default: + assert(0); + uv_set_error(loop, UV_EINVAL, 0); + return NULL; + } +} diff --git a/src/rt/libuv/src/win/stream.c b/src/rt/libuv/src/win/stream.c new file mode 100644 index 00000000000..26964110fce --- /dev/null +++ b/src/rt/libuv/src/win/stream.c @@ -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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +void uv_stream_init(uv_loop_t* loop, uv_stream_t* handle) { + handle->write_queue_size = 0; + handle->loop = loop; + handle->flags = 0; + + loop->counters.handle_init++; + loop->counters.stream_init++; + + uv_ref(loop); +} + + +void uv_connection_init(uv_stream_t* handle) { + handle->flags |= UV_HANDLE_CONNECTION; + handle->write_reqs_pending = 0; + + uv_req_init(handle->loop, (uv_req_t*) &(handle->read_req)); + handle->read_req.type = UV_READ; + handle->read_req.data = handle; +} + + +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { + switch (stream->type) { + case UV_TCP: + return uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + case UV_NAMED_PIPE: + return uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + default: + assert(0); + return -1; + } +} + + +int uv_accept(uv_stream_t* server, uv_stream_t* client) { + assert(client->type == server->type); + + switch (server->type) { + case UV_TCP: + return uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); + case UV_NAMED_PIPE: + return uv_pipe_accept((uv_pipe_t*)server, (uv_pipe_t*)client); + default: + assert(0); + return -1; + } +} + + +int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + switch (handle->type) { + case UV_TCP: + return uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); + case UV_NAMED_PIPE: + return uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); + default: + assert(0); + return -1; + } +} + + +int uv_read_stop(uv_stream_t* handle) { + handle->flags &= ~UV_HANDLE_READING; + + return 0; +} + + +int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, + uv_write_cb cb) { + uv_loop_t* loop = handle->loop; + + switch (handle->type) { + case UV_TCP: + return uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, bufcnt, cb); + case UV_NAMED_PIPE: + return uv_pipe_write(loop, req, (uv_pipe_t*) handle, bufs, bufcnt, cb); + default: + assert(0); + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } +} + + +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_CONNECTION)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(loop, WSAESHUTDOWN); + return -1; + } + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_SHUTDOWN; + req->handle = handle; + req->cb = cb; + + handle->flags |= UV_HANDLE_SHUTTING; + handle->shutdown_req = req; + handle->reqs_pending++; + + uv_want_endgame(loop, (uv_handle_t*)handle); + + return 0; +} + + +size_t uv_count_bufs(uv_buf_t bufs[], int count) { + size_t bytes = 0; + int i; + + for (i = 0; i < count; i++) { + bytes += (size_t)bufs[i].len; + } + + return bytes; +} diff --git a/src/rt/libuv/src/win/tcp.c b/src/rt/libuv/src/win/tcp.c new file mode 100644 index 00000000000..ebd83534983 --- /dev/null +++ b/src/rt/libuv/src/win/tcp.c @@ -0,0 +1,870 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.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_[] = ""; + +/* Counter to keep track of active tcp streams */ +static unsigned int active_tcp_streams = 0; + + +static int uv_tcp_set_socket(uv_loop_t* loop, uv_tcp_t* handle, + SOCKET socket) { + DWORD yes = 1; + + assert(handle->socket == INVALID_SOCKET); + + /* Set the socket to nonblocking mode */ + if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + /* Make the socket non-inheritable */ + if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* 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) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (pSetFileCompletionNotificationModes) { + 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) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + } + + handle->socket = socket; + + return 0; +} + + +int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) { + uv_stream_init(loop, (uv_stream_t*)handle); + + handle->accept_reqs = NULL; + handle->pending_accepts = NULL; + handle->socket = INVALID_SOCKET; + handle->type = UV_TCP; + handle->reqs_pending = 0; + + loop->counters.tcp_init++; + + return 0; +} + + +void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { + uv_err_t err; + int status; + + if (handle->flags & UV_HANDLE_CONNECTION && + handle->flags & UV_HANDLE_SHUTTING && + !(handle->flags & UV_HANDLE_SHUT) && + handle->write_reqs_pending == 0) { + + if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) { + status = 0; + handle->flags |= UV_HANDLE_SHUT; + } else { + status = -1; + err = uv_new_sys_error(WSAGetLastError()); + } + if (handle->shutdown_req->cb) { + if (status == -1) { + loop->last_error = err; + } + handle->shutdown_req->cb(handle->shutdown_req, status); + } + handle->reqs_pending--; + } + + if (handle->flags & UV_HANDLE_CLOSING && + handle->reqs_pending == 0) { + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->accept_reqs) { + free(handle->accept_reqs); + handle->accept_reqs = NULL; + } + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + active_tcp_streams--; + + uv_unref(loop); + } +} + + +static int uv__bind(uv_loop_t* loop, uv_tcp_t* handle, int domain, + struct sockaddr* addr, int addrsize) { + DWORD err; + int r; + SOCKET sock; + + if (handle->socket == INVALID_SOCKET) { + sock = socket(domain, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + if (uv_tcp_set_socket(loop, handle, sock) == -1) { + closesocket(sock); + return -1; + } + } + + r = bind(handle->socket, addr, addrsize); + + if (r == SOCKET_ERROR) { + err = WSAGetLastError(); + if (err == WSAEADDRINUSE) { + /* Some errors are not to be reported until connect() or listen() */ + handle->bind_error = uv_new_sys_error(err); + handle->flags |= UV_HANDLE_BIND_ERROR; + } else { + uv_set_sys_error(loop, err); + return -1; + } + } + + handle->flags |= UV_HANDLE_BOUND; + + return 0; +} + + +int uv_tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { + uv_loop_t* loop = handle->loop; + + if (addr.sin_family != AF_INET) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + return uv__bind(loop, + handle, + AF_INET, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)); +} + + +int uv_tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { + uv_loop_t* loop = handle->loop; + + if (addr.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (uv_allow_ipv6) { + handle->flags |= UV_HANDLE_IPV6; + return uv__bind(loop, + handle, + AF_INET6, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in6)); + + } else { + uv_set_sys_error(loop, WSAEAFNOSUPPORT); + return -1; + } +} + + +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; + LPFN_ACCEPTEX pAcceptExFamily; + + assert(handle->flags & UV_HANDLE_LISTENING); + assert(req->accept_socket == INVALID_SOCKET); + + /* choose family and extension function */ + if ((handle->flags & UV_HANDLE_IPV6) != 0) { + family = AF_INET6; + pAcceptExFamily = pAcceptEx6; + } else { + family = AF_INET; + pAcceptExFamily = pAcceptEx; + } + + /* 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; + } + + /* Prepare the overlapped structure. */ + memset(&(req->overlapped), 0, sizeof(req->overlapped)); + + success = pAcceptExFamily(handle->socket, + accept_socket, + (void*)req->accept_buffer, + 0, + sizeof(struct sockaddr_storage), + sizeof(struct sockaddr_storage), + &bytes, + &req->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++; + } 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); + } +} + + +static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { + uv_req_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->overlapped, 0, sizeof(req->overlapped)); + + /* + * Preallocate a read buffer if the number of active streams is below + * the threshold. + */ + if (active_tcp_streams < uv_active_tcp_streams_threshold) { + handle->flags &= ~UV_HANDLE_ZERO_READ; + handle->read_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(handle->read_buffer.len > 0); + buf = handle->read_buffer; + } else { + handle->flags |= UV_HANDLE_ZERO_READ; + buf.base = (char*) &uv_zero_; + buf.len = 0; + } + + flags = 0; + result = WSARecv(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->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_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { + uv_loop_t* loop = handle->loop; + unsigned int i; + uv_tcp_accept_t* req; + + assert(backlog > 0); + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + if (handle->flags & UV_HANDLE_LISTENING || + handle->flags & UV_HANDLE_READING) { + /* Already listening. */ + uv_set_sys_error(loop, WSAEALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) + return -1; + + if (listen(handle->socket, backlog) == SOCKET_ERROR) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + handle->flags |= UV_HANDLE_LISTENING; + handle->connection_cb = cb; + + assert(!handle->accept_reqs); + handle->accept_reqs = (uv_tcp_accept_t*) + malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t)); + if (!handle->accept_reqs) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + for (i = 0; i < uv_simultaneous_server_accepts; i++) { + req = &handle->accept_reqs[i]; + uv_req_init(loop, (uv_req_t*)req); + req->type = UV_ACCEPT; + req->accept_socket = INVALID_SOCKET; + req->data = handle; + uv_tcp_queue_accept(handle, req); + } + + return 0; +} + + +int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { + uv_loop_t* loop = server->loop; + int rv = 0; + + uv_tcp_accept_t* req = server->pending_accepts; + + if (!req) { + /* No valid connections found, so we error out. */ + uv_set_sys_error(loop, WSAEWOULDBLOCK); + return -1; + } + + if (req->accept_socket == INVALID_SOCKET) { + uv_set_sys_error(loop, WSAENOTCONN); + return -1; + } + + if (uv_tcp_set_socket(client->loop, client, req->accept_socket) == -1) { + closesocket(req->accept_socket); + rv = -1; + } else { + uv_connection_init((uv_stream_t*) client); + /* AcceptEx() implicitly binds the accepted socket. */ + client->flags |= UV_HANDLE_BOUND; + } + + /* Prepare the req to pick up a new connection */ + server->pending_accepts = req->next_pending; + req->next_pending = NULL; + req->accept_socket = INVALID_SOCKET; + + if (!(server->flags & UV_HANDLE_CLOSING)) { + uv_tcp_queue_accept(server, req); + } + + active_tcp_streams++; + + return rv; +} + + +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; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_READING) { + uv_set_sys_error(loop, WSAEALREADY); + return -1; + } + + if (handle->flags & UV_HANDLE_EOF) { + uv_set_sys_error(loop, WSAESHUTDOWN); + return -1; + } + + handle->flags |= UV_HANDLE_READING; + handle->read_cb = read_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell be a */ + /* read request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + uv_tcp_queue_read(loop, handle); + + return 0; +} + + +int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in address, uv_connect_cb cb) { + uv_loop_t* loop = handle->loop; + int addrsize = sizeof(struct sockaddr_in); + BOOL success; + DWORD bytes; + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + if (address.sin_family != AF_INET) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind(handle, uv_addr_ip4_any_) < 0) + return -1; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { + /* Process the req without IOCP. */ + 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. */ + handle->reqs_pending++; + } else { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_connect6(uv_connect_t* req, uv_tcp_t* handle, + struct sockaddr_in6 address, uv_connect_cb cb) { + uv_loop_t* loop = handle->loop; + int addrsize = sizeof(struct sockaddr_in6); + BOOL success; + DWORD bytes; + + if (!uv_allow_ipv6) { + uv_new_sys_error(WSAEAFNOSUPPORT); + return -1; + } + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + if (address.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_tcp_bind6(handle, uv_addr_ip6_any_) < 0) + return -1; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx6(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(success)) { + handle->reqs_pending++; + } else { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + uv_loop_t* loop = handle->loop; + int result; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + result = getsockname(handle->socket, name, namelen); + if (result != 0) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_getpeername(uv_tcp_t* handle, struct sockaddr* name, + int* namelen) { + uv_loop_t* loop = handle->loop; + int result; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_BIND_ERROR) { + loop->last_error = handle->bind_error; + return -1; + } + + result = getpeername(handle->socket, name, namelen); + if (result != 0) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, + uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { + int result; + DWORD bytes; + + if (!(handle->flags & UV_HANDLE_CONNECTION)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + if (handle->flags & UV_HANDLE_SHUTTING) { + uv_set_sys_error(loop, WSAESHUTDOWN); + return -1; + } + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_WRITE; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + result = WSASend(handle->socket, + (WSABUF*)bufs, + bufcnt, + &bytes, + 0, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Request completed immediately. */ + req->queued_bytes = 0; + handle->reqs_pending++; + handle->write_reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*) req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->reqs_pending++; + handle->write_reqs_pending++; + handle->write_queue_size += req->queued_bytes; + } else { + /* Send failed due to an error. */ + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +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_READING; + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + buf = (handle->flags & UV_HANDLE_ZERO_READ) ? + uv_buf_init(NULL, 0) : handle->read_buffer; + handle->read_cb((uv_stream_t*)handle, -1, buf); + } + } else { + if (!(handle->flags & UV_HANDLE_ZERO_READ)) { + /* The read was done with a non-zero buffer length. */ + if (req->overlapped.InternalHigh > 0) { + /* Successful read */ + handle->read_cb((uv_stream_t*)handle, + req->overlapped.InternalHigh, + handle->read_buffer); + /* Read again only if bytes == buf.len */ + if (req->overlapped.InternalHigh < handle->read_buffer.len) { + goto done; + } + } else { + /* Connection closed */ + handle->flags &= ~UV_HANDLE_READING; + handle->flags |= UV_HANDLE_EOF; + loop->last_error.code = UV_EOF; + loop->last_error.sys_errno_ = ERROR_SUCCESS; + buf.base = 0; + buf.len = 0; + handle->read_cb((uv_stream_t*)handle, -1, handle->read_buffer); + goto done; + } + } + + /* Do nonblocking reads until the buffer is empty */ + while (handle->flags & UV_HANDLE_READING) { + buf = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(buf.len > 0); + 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; + handle->flags |= UV_HANDLE_EOF; + loop->last_error.code = UV_EOF; + loop->last_error.sys_errno_ = ERROR_SUCCESS; + handle->read_cb((uv_stream_t*)handle, -1, buf); + break; + } + } else { + err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { + /* Read buffer was completely empty, report a 0-byte read. */ + uv_set_sys_error(loop, WSAEWOULDBLOCK); + handle->read_cb((uv_stream_t*)handle, 0, buf); + } else { + /* Ouch! serious error. */ + uv_set_sys_error(loop, err); + handle->flags &= ~UV_HANDLE_READING; + handle->read_cb((uv_stream_t*)handle, -1, 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) { + assert(handle->type == UV_TCP); + + handle->write_queue_size -= req->queued_bytes; + + if (req->cb) { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + ((uv_write_cb)req->cb)(req, loop->last_error.code == UV_OK ? 0 : -1); + } + + handle->write_reqs_pending--; + if (handle->flags & UV_HANDLE_SHUTTING && + handle->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; + + 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; + if (handle->connection_cb) { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + handle->connection_cb((uv_stream_t*)handle, -1); + } + } + } 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->pending_accepts; + handle->pending_accepts = req; + + /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */ + if (handle->connection_cb) { + handle->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) { + assert(handle->type == UV_TCP); + + if (req->cb) { + if (REQ_SUCCESS(req)) { + if (setsockopt(handle->socket, + SOL_SOCKET, + SO_UPDATE_CONNECT_CONTEXT, + NULL, + 0) == 0) { + uv_connection_init((uv_stream_t*)handle); + active_tcp_streams++; + ((uv_connect_cb)req->cb)(req, 0); + } else { + uv_set_sys_error(loop, WSAGetLastError()); + ((uv_connect_cb)req->cb)(req, -1); + } + } else { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + ((uv_connect_cb)req->cb)(req, -1); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} diff --git a/src/rt/libuv/src/win/threadpool.c b/src/rt/libuv/src/win/threadpool.c new file mode 100644 index 00000000000..c40613e1f5c --- /dev/null +++ b/src/rt/libuv/src/win/threadpool.c @@ -0,0 +1,73 @@ +/* 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 + +#include "uv.h" +#include "internal.h" + + +static void uv_work_req_init(uv_loop_t* loop, uv_work_t* req, + uv_work_cb work_cb, uv_after_work_cb after_work_cb) { + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_WORK; + req->loop = loop; + req->work_cb = work_cb; + req->after_work_cb = after_work_cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); +} + + +static DWORD WINAPI uv_work_thread_proc(void* parameter) { + uv_work_t* req = (uv_work_t*)parameter; + uv_loop_t* loop = req->loop; + + assert(req != NULL); + assert(req->type == UV_WORK); + assert(req->work_cb); + + req->work_cb(req); + + POST_COMPLETION_FOR_REQ(loop, req); + + return 0; +} + + +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_work_req_init(loop, req, work_cb, after_work_cb); + + if (!QueueUserWorkItem(&uv_work_thread_proc, req, WT_EXECUTELONGFUNCTION)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + uv_ref(loop); + return 0; +} + + +void uv_process_work_req(uv_loop_t* loop, uv_work_t* req) { + assert(req->after_work_cb); + req->after_work_cb(req); + uv_unref(loop); +} diff --git a/src/rt/libuv/src/win/threads.c b/src/rt/libuv/src/win/threads.c new file mode 100644 index 00000000000..1fc6b73f870 --- /dev/null +++ b/src/rt/libuv/src/win/threads.c @@ -0,0 +1,81 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +#ifdef _MSC_VER /* msvc */ +# define NOINLINE __declspec (noinline) +#else /* gcc */ +# define NOINLINE __attribute__ ((noinline)) +#endif + + +static NOINLINE void uv__once_inner(uv_once_t* guard, + void (*callback)(void)) { + DWORD result; + HANDLE existing_event, created_event; + HANDLE* event_ptr; + + /* Fetch and align event_ptr */ + event_ptr = (HANDLE*) (((uintptr_t) &guard->event + (sizeof(HANDLE) - 1)) & + ~(sizeof(HANDLE) - 1)); + + 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(event_ptr, + 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); +} diff --git a/src/rt/libuv/src/win/timer.c b/src/rt/libuv/src/win/timer.c new file mode 100644 index 00000000000..ac20925293f --- /dev/null +++ b/src/rt/libuv/src/win/timer.c @@ -0,0 +1,276 @@ +/* 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 +#include + +#include "uv.h" +#include "internal.h" +#include "tree.h" + +#undef NANOSEC +#define NANOSEC 1000000000 + + +/* The resolution of the high-resolution clock. */ +static int64_t uv_ticks_per_msec_ = 0; +static uint64_t uv_hrtime_frequency_ = 0; +static uv_once_t uv_hrtime_init_guard_ = UV_ONCE_INIT; + + +void uv_update_time(uv_loop_t* loop) { + DWORD ticks = GetTickCount(); + + /* The assumption is made that LARGE_INTEGER.QuadPart has the same type */ + /* loop->time, which happens to be. Is there any way to assert this? */ + LARGE_INTEGER* time = (LARGE_INTEGER*) &loop->time; + + /* If the timer has wrapped, add 1 to it's high-order dword. */ + /* uv_poll must make sure that the timer can never overflow more than */ + /* once between two subsequent uv_update_time calls. */ + if (ticks < time->LowPart) { + time->HighPart += 1; + } + time->LowPart = ticks; +} + + +int64_t uv_now(uv_loop_t* loop) { + return loop->time; +} + + +static void uv_hrtime_init(void) { + LARGE_INTEGER frequency; + + if (!QueryPerformanceFrequency(&frequency)) { + uv_hrtime_frequency_ = 0; + return; + } + + uv_hrtime_frequency_ = frequency.QuadPart; +} + + +uint64_t uv_hrtime(void) { + LARGE_INTEGER counter; + + uv_once(&uv_hrtime_init_guard_, uv_hrtime_init); + + /* If the performance frequency is zero, there's no support. */ + if (!uv_hrtime_frequency_) { + /* uv_set_sys_error(loop, ERROR_NOT_SUPPORTED); */ + return 0; + } + + if (!QueryPerformanceCounter(&counter)) { + /* uv_set_sys_error(loop, GetLastError()); */ + return 0; + } + + /* Because we have no guarantee about the order of magniture of the */ + /* performance counter frequency, and there may not be much headroom to */ + /* multiply by NANOSEC without overflowing, we use 128-bit math instead. */ + return ((uint64_t) counter.LowPart * NANOSEC / uv_hrtime_frequency_) + + (((uint64_t) counter.HighPart * NANOSEC / uv_hrtime_frequency_) + << 32); +} + + +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; + if ((intptr_t)a < (intptr_t)b) + return -1; + if ((intptr_t)a > (intptr_t)b) + 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) { + loop->counters.handle_init++; + loop->counters.timer_init++; + + handle->type = UV_TIMER; + handle->loop = loop; + handle->flags = 0; + handle->timer_cb = NULL; + handle->repeat = 0; + + uv_ref(loop); + + 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)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, int64_t timeout, + int64_t repeat) { + uv_loop_t* loop = handle->loop; + + if (handle->flags & UV_HANDLE_ACTIVE) { + RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); + } + + handle->timer_cb = timer_cb; + handle->due = loop->time + timeout; + handle->repeat = repeat; + handle->flags |= UV_HANDLE_ACTIVE; + + if (RB_INSERT(uv_timer_tree_s, &loop->timers, handle) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + + return 0; +} + + +int uv_timer_stop(uv_timer_t* handle) { + uv_loop_t* loop = handle->loop; + + if (!(handle->flags & UV_HANDLE_ACTIVE)) + return 0; + + RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); + + handle->flags &= ~UV_HANDLE_ACTIVE; + + return 0; +} + + +int uv_timer_again(uv_timer_t* handle) { + uv_loop_t* loop = handle->loop; + + /* If timer_cb is NULL that means that the timer was never started. */ + if (!handle->timer_cb) { + uv_set_sys_error(loop, ERROR_INVALID_DATA); + return -1; + } + + if (handle->flags & UV_HANDLE_ACTIVE) { + RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); + handle->flags &= ~UV_HANDLE_ACTIVE; + } + + if (handle->repeat) { + handle->due = loop->time + handle->repeat; + + if (RB_INSERT(uv_timer_tree_s, &loop->timers, handle) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + + handle->flags |= UV_HANDLE_ACTIVE; + } + + return 0; +} + + +void uv_timer_set_repeat(uv_timer_t* handle, int64_t repeat) { + assert(handle->type == UV_TIMER); + handle->repeat = repeat; +} + + +int64_t uv_timer_get_repeat(uv_timer_t* handle) { + assert(handle->type == UV_TIMER); + return handle->repeat; +} + + +DWORD uv_get_poll_timeout(uv_loop_t* loop) { + uv_timer_t* timer; + int64_t delta; + + /* Check if there are any running timers */ + timer = RB_MIN(uv_timer_tree_s, &loop->timers); + if (timer) { + uv_update_time(loop); + + delta = timer->due - loop->time; + if (delta >= UINT_MAX >> 1) { + /* A timeout value of UINT_MAX means infinite, so that's no good. But */ + /* more importantly, there's always the risk that GetTickCount wraps. */ + /* uv_update_time can detect this, but we must make sure that the */ + /* tick counter never overflows twice between two subsequent */ + /* uv_update_time calls. We do this by never sleeping more than half */ + /* the time it takes to wrap the counter - which is huge overkill, */ + /* but hey, it's not so bad to wake up every 25 days. */ + 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)) { + RB_REMOVE(uv_timer_tree_s, &loop->timers, timer); + + if (timer->repeat != 0) { + /* If it is a repeating timer, reschedule with repeat timeout. */ + timer->due += timer->repeat; + if (timer->due < loop->time) { + timer->due = loop->time; + } + if (RB_INSERT(uv_timer_tree_s, &loop->timers, timer) != NULL) { + uv_fatal_error(ERROR_INVALID_DATA, "RB_INSERT"); + } + } else { + /* If non-repeating, mark the timer as inactive. */ + timer->flags &= ~UV_HANDLE_ACTIVE; + } + + timer->timer_cb((uv_timer_t*) timer, 0); + } +} diff --git a/src/rt/libuv/src/win/tty.c b/src/rt/libuv/src/win/tty.c new file mode 100644 index 00000000000..a4a46829643 --- /dev/null +++ b/src/rt/libuv/src/win/tty.c @@ -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. + */ + +#include "uv.h" +#include "internal.h" + +#include + + +int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd) { + assert(0 && "implement me"); + return -1; +} + + +int uv_tty_set_mode(uv_tty_t* tty, int mode) { + assert(0 && "implement me"); + return -1; +} + + +int uv_is_tty(uv_file file) { +} + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + assert(0 && "implement me"); + return -1; +} + + +uv_handle_type uv_guess_handle(uv_file file) { + DWORD result; + int r = GetConsoleMode((HANDLE)_get_osfhandle(file), &result); + + if (r) { + return UV_TTY; + } + + assert(0 && "implement me"); + + return UV_UNKNOWN_HANDLE; +} diff --git a/src/rt/libuv/src/win/udp.c b/src/rt/libuv/src/win/udp.c new file mode 100644 index 00000000000..3a2ab6bc1b4 --- /dev/null +++ b/src/rt/libuv/src/win/udp.c @@ -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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" +#include + +#if 0 +/* + * 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_[] = ""; +#endif + +/* Counter to keep track of active udp streams */ +static unsigned int active_udp_streams = 0; + + +int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, + int* namelen) { + uv_loop_t* loop = handle->loop; + int result; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + uv_set_sys_error(loop, WSAEINVAL); + return -1; + } + + result = getsockname(handle->socket, name, namelen); + if (result != 0) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, + SOCKET socket) { + DWORD yes = 1; + + assert(handle->socket == INVALID_SOCKET); + + /* Set the socket to nonblocking mode */ + if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + /* Make the socket non-inheritable */ + if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + /* 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) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + if (pSetFileCompletionNotificationModes) { + if (!pSetFileCompletionNotificationModes((HANDLE)socket, + FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { + uv_set_sys_error(loop, GetLastError()); + return -1; + } + + handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; + } + + handle->socket = socket; + + return 0; +} + + +int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { + handle->type = UV_UDP; + handle->socket = INVALID_SOCKET; + handle->reqs_pending = 0; + handle->loop = loop; + handle->flags = 0; + + uv_req_init(loop, (uv_req_t*) &(handle->recv_req)); + handle->recv_req.type = UV_UDP_RECV; + handle->recv_req.data = handle; + + uv_ref(loop); + + loop->counters.handle_init++; + loop->counters.udp_init++; + + return 0; +} + + +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)); + handle->flags |= UV_HANDLE_CLOSED; + + if (handle->close_cb) { + handle->close_cb((uv_handle_t*)handle); + } + + uv_unref(loop); + } +} + + +static int uv__bind(uv_udp_t* handle, int domain, struct sockaddr* addr, + int addrsize, unsigned int flags) { + uv_loop_t* loop = handle->loop; + DWORD err; + int r; + SOCKET sock; + + if ((flags & UV_UDP_IPV6ONLY) && domain != AF_INET6) { + /* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */ + uv_set_sys_error(loop, UV_EINVAL); + } + + if (handle->socket == INVALID_SOCKET) { + sock = socket(domain, SOCK_DGRAM, 0); + if (sock == INVALID_SOCKET) { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + if (uv_udp_set_socket(loop, handle, sock) == -1) { + closesocket(sock); + return -1; + } + } + + if (domain == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) { + DWORD off = 0; + /* 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(sock, + IPPROTO_IPV6, + IPV6_V6ONLY, + (const char*) &off, + sizeof off); + } + + r = bind(handle->socket, addr, addrsize); + + if (r == SOCKET_ERROR) { + err = WSAGetLastError(); + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + handle->flags |= UV_HANDLE_BOUND; + + return 0; +} + + +int uv_udp_bind(uv_udp_t* handle, struct sockaddr_in addr, + unsigned int flags) { + uv_loop_t* loop = handle->loop; + + if (addr.sin_family != AF_INET) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + return uv__bind(handle, + AF_INET, + (struct sockaddr*) &addr, + sizeof(struct sockaddr_in), + flags); +} + + +int uv_udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, + unsigned int flags) { + uv_loop_t* loop = handle->loop; + + if (addr.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (uv_allow_ipv6) { + handle->flags |= UV_HANDLE_IPV6; + return uv__bind(handle, + AF_INET6, + (struct sockaddr*) &addr, + sizeof(struct sockaddr_in6), + flags); + } else { + uv_new_sys_error(WSAEAFNOSUPPORT); + return -1; + } +} + + +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->overlapped, 0, sizeof(req->overlapped)); + + /* + * Preallocate a read buffer if the number of active streams is below + * the threshold. + */ +#if 0 + if (active_udp_streams < uv_active_udp_streams_threshold) { + handle->flags &= ~UV_HANDLE_ZERO_READ; +#endif + handle->recv_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(handle->recv_buffer.len > 0); + + buf = handle->recv_buffer; + memset(&handle->recv_from, 0, sizeof handle->recv_from); + handle->recv_from_len = sizeof handle->recv_from; + flags = 0; + + result = WSARecvFrom(handle->socket, + (WSABUF*) &buf, + 1, + &bytes, + &flags, + (struct sockaddr*) &handle->recv_from, + &handle->recv_from_len, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->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++; + } +#if 0 + } else { + handle->flags |= UV_HANDLE_ZERO_READ; + + buf.base = (char*) uv_zero_; + buf.len = 0; + flags = MSG_PARTIAL; + + result = WSARecv(handle->socket, + (WSABUF*) &buf, + 1, + &bytes, + &flags, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->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++; + } + } +#endif +} + + +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; + + if (handle->flags & UV_HANDLE_READING) { + uv_set_sys_error(loop, WSAEALREADY); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind(handle, uv_addr_ip4_any_, 0) < 0) { + return -1; + } + + handle->flags |= UV_HANDLE_READING; + active_udp_streams++; + + handle->recv_cb = recv_cb; + handle->alloc_cb = alloc_cb; + + /* If reading was stopped and then started again, there could stell 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; + active_udp_streams--; + } + + return 0; +} + + +int uv_udp_connect6(uv_connect_t* req, uv_udp_t* handle, + struct sockaddr_in6 address, uv_connect_cb cb) { + uv_loop_t* loop = handle->loop; + int addrsize = sizeof(struct sockaddr_in6); + BOOL success; + DWORD bytes; + + if (!uv_allow_ipv6) { + uv_new_sys_error(WSAEAFNOSUPPORT); + return -1; + } + + if (address.sin6_family != AF_INET6) { + uv_set_sys_error(loop, WSAEFAULT); + return -1; + } + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) + return -1; + + uv_req_init(loop, (uv_req_t*) req); + req->type = UV_CONNECT; + req->handle = (uv_stream_t*) handle; + req->cb = cb; + memset(&req->overlapped, 0, sizeof(req->overlapped)); + + success = pConnectEx6(handle->socket, + (struct sockaddr*) &address, + addrsize, + NULL, + 0, + &bytes, + &req->overlapped); + + if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(success)) { + handle->reqs_pending++; + } else { + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr* addr, int addr_len, 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->overlapped, 0, sizeof(req->overlapped)); + + result = WSASendTo(handle->socket, + (WSABUF*)bufs, + bufcnt, + &bytes, + 0, + addr, + addr_len, + &req->overlapped, + NULL); + + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Request completed immediately. */ + req->queued_bytes = 0; + handle->reqs_pending++; + uv_insert_pending_req(loop, (uv_req_t*)req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* Request queued by the kernel. */ + req->queued_bytes = uv_count_bufs(bufs, bufcnt); + handle->reqs_pending++; + } else { + /* Send failed due to an error. */ + uv_set_sys_error(loop, WSAGetLastError()); + return -1; + } + + return 0; +} + + +int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in addr, uv_udp_send_cb cb) { + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind(handle, uv_addr_ip4_any_, 0) < 0) { + return -1; + } + + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*) &addr, + sizeof addr, + cb); +} + + +int uv_udp_send6(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], + int bufcnt, struct sockaddr_in6 addr, uv_udp_send_cb cb) { + + if (!(handle->flags & UV_HANDLE_BOUND) && + uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) { + return -1; + } + + return uv__udp_send(req, + handle, + bufs, + bufcnt, + (struct sockaddr*) &addr, + sizeof addr, + cb); +} + + +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) && + GET_REQ_STATUS(req) != STATUS_RECEIVE_EXPEDITED) { + /* An error occurred doing the read. */ + if ((handle->flags & UV_HANDLE_READING)) { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + uv_udp_recv_stop(handle); +#if 0 + buf = (handle->flags & UV_HANDLE_ZERO_READ) ? + uv_buf_init(NULL, 0) : handle->recv_buffer; +#else + buf = handle->recv_buffer; +#endif + handle->recv_cb(handle, -1, buf, NULL, 0); + } + goto done; + } + +#if 0 + if (!(handle->flags & UV_HANDLE_ZERO_READ)) { +#endif + /* Successful read */ + partial = (GET_REQ_STATUS(req) == STATUS_RECEIVE_EXPEDITED); + handle->recv_cb(handle, + req->overlapped.InternalHigh, + handle->recv_buffer, + (struct sockaddr*) &handle->recv_from, + partial ? UV_UDP_PARTIAL : 0); +#if 0 + } else { + 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 = handle->alloc_cb((uv_handle_t*) handle, 65536); + assert(buf.len > 0); + + memset(&from, 0, sizeof from); + from_len = sizeof from; + flags = MSG_PARTIAL; + + 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, + (struct sockaddr*) &from, + (flags & MSG_PARTIAL) ? UV_UDP_PARTIAL : 0); + } else { + err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) { + uv_set_sys_error(loop, WSAEWOULDBLOCK); + handle->recv_cb(handle, 0, buf, NULL, 0); + } else { + /* Ouch! serious error. */ + uv_set_sys_error(loop, err); + handle->recv_cb(handle, -1, buf, NULL, 0); + } + } + } +#endif + +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) { + assert(handle->type == UV_UDP); + + if (req->cb) { + if (REQ_SUCCESS(req)) { + req->cb(req, 0); + } else { + loop->last_error = GET_REQ_UV_SOCK_ERROR(req); + req->cb(req, -1); + } + } + + DECREASE_PENDING_REQ_COUNT(handle); +} + diff --git a/src/rt/libuv/src/win/util.c b/src/rt/libuv/src/win/util.c new file mode 100644 index 00000000000..67de53e5c04 --- /dev/null +++ b/src/rt/libuv/src/win/util.c @@ -0,0 +1,96 @@ +/* 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 +#include +#include + +#include "uv.h" +#include "internal.h" + + +int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, + char* utf8Buffer, size_t utf8Size) { + return WideCharToMultiByte(CP_UTF8, + 0, + utf16Buffer, + utf16Size, + utf8Buffer, + utf8Size, + NULL, + NULL); +} + + +int uv_utf8_to_utf16(const char* utf8Buffer, wchar_t* utf16Buffer, + size_t utf16Size) { + return MultiByteToWideChar(CP_UTF8, + 0, + utf8Buffer, + -1, + utf16Buffer, + utf16Size); +} + + +int uv_exepath(char* buffer, size_t* size) { + int retVal; + size_t utf16Size; + wchar_t* utf16Buffer; + + if (!buffer || !size) { + return -1; + } + + utf16Buffer = (wchar_t*)malloc(sizeof(wchar_t) * *size); + if (!utf16Buffer) { + retVal = -1; + goto done; + } + + /* Get the path as UTF-16 */ + utf16Size = GetModuleFileNameW(NULL, utf16Buffer, *size - 1); + if (utf16Size <= 0) { + /* uv_set_sys_error(loop, GetLastError()); */ + retVal = -1; + goto done; + } + + utf16Buffer[utf16Size] = L'\0'; + + /* Convert to UTF-8 */ + *size = uv_utf16_to_utf8(utf16Buffer, utf16Size, buffer, *size); + if (!*size) { + /* uv_set_sys_error(loop, GetLastError()); */ + retVal = -1; + goto done; + } + + buffer[*size] = '\0'; + retVal = 0; + +done: + if (utf16Buffer) { + free(utf16Buffer); + } + + return retVal; +} diff --git a/src/rt/libuv/src/win/winapi.c b/src/rt/libuv/src/win/winapi.c new file mode 100644 index 00000000000..4a58c14574f --- /dev/null +++ b/src/rt/libuv/src/win/winapi.c @@ -0,0 +1,81 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +sRtlNtStatusToDosError pRtlNtStatusToDosError; +sNtQueryInformationFile pNtQueryInformationFile; +sNtSetInformationFile pNtSetInformationFile; +sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; +sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; +sCreateSymbolicLinkA pCreateSymbolicLinkA; + + +void uv_winapi_init() { + HMODULE ntdll_module; + HMODULE kernel32_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"); + } + + 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"); + } + + 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"); + + pCreateSymbolicLinkA = (sCreateSymbolicLinkA) + GetProcAddress(kernel32_module, "CreateSymbolicLinkA"); +} diff --git a/src/rt/libuv/src/win/winapi.h b/src/rt/libuv/src/win/winapi.h new file mode 100644 index 00000000000..760281e8619 --- /dev/null +++ b/src/rt/libuv/src/win/winapi.h @@ -0,0 +1,4337 @@ +/* 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 + + +/* + * Ntdll headers + */ +#ifndef _NTDEF_ + typedef LONG NTSTATUS; + typedef NTSTATUS *PNTSTATUS; +#endif + +#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 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))) + +/* from ntifs.h */ +/* MinGW already has it */ +#ifndef __MINGW32__ + 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; + } DUMMYUNIONNAME; + } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +#endif + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +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; + +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 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; + +#ifndef DEVICE_TYPE +# define DEVICE_TYPE DWORD +#endif + +#ifndef FILE_DEVICE_FILE_SYSTEM +# define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#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 + +typedef ULONG (NTAPI *sRtlNtStatusToDosError) + (NTSTATUS Status); + +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); + + +/* + * Kernel32 headers + */ +#define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 +#define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 + +#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1 + +#ifdef __MINGW32__ + typedef struct _OVERLAPPED_ENTRY { + ULONG_PTR lpCompletionKey; + LPOVERLAPPED lpOverlapped; + ULONG_PTR Internal; + DWORD dwNumberOfBytesTransferred; + } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY; +#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* sCreateSymbolicLinkA) + (LPCSTR lpSymlinkFileName, + LPCSTR lpTargetFileName, + DWORD dwFlags); + + +/* Ntapi function pointers */ +extern sRtlNtStatusToDosError pRtlNtStatusToDosError; +extern sNtQueryInformationFile pNtQueryInformationFile; +extern sNtSetInformationFile pNtSetInformationFile; + + +/* Kernel32 function pointers */ +extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; +extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; +extern sCreateSymbolicLinkA pCreateSymbolicLinkA; + +#endif /* UV_WIN_WINAPI_H_ */ diff --git a/src/rt/libuv/src/win/winsock.c b/src/rt/libuv/src/win/winsock.c new file mode 100644 index 00000000000..1f56b3d75bd --- /dev/null +++ b/src/rt/libuv/src/win/winsock.c @@ -0,0 +1,270 @@ +/* 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 + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +/* Winsock extension functions (ipv4) */ +LPFN_CONNECTEX pConnectEx; +LPFN_ACCEPTEX pAcceptEx; +LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; +LPFN_DISCONNECTEX pDisconnectEx; +LPFN_TRANSMITFILE pTransmitFile; + +/* Winsock extension functions (ipv6) */ +LPFN_CONNECTEX pConnectEx6; +LPFN_ACCEPTEX pAcceptEx6; +LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; +LPFN_DISCONNECTEX pDisconnectEx6; +LPFN_TRANSMITFILE pTransmitFile6; + +/* Whether ipv6 is supported */ +int uv_allow_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) { + DWORD result, 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; + } +} + + +void uv_winsock_init() { + const GUID wsaid_connectex = WSAID_CONNECTEX; + const GUID wsaid_acceptex = WSAID_ACCEPTEX; + const GUID wsaid_getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; + const GUID wsaid_disconnectex = WSAID_DISCONNECTEX; + const GUID wsaid_transmitfile = WSAID_TRANSMITFILE; + + WSADATA wsa_data; + int errorno; + SOCKET dummy; + SOCKET dummy6; + + /* Initialize winsock */ + errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (errorno != 0) { + uv_fatal_error(errorno, "WSAStartup"); + } + + /* Set implicit binding address used by connectEx */ + uv_addr_ip4_any_ = uv_ip4_addr("0.0.0.0", 0); + uv_addr_ip6_any_ = uv_ip6_addr("::", 0); + + /* Retrieve the needed winsock extension function pointers. */ + dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (dummy == INVALID_SOCKET) { + uv_fatal_error(WSAGetLastError(), "socket"); + } + + if (!uv_get_extension_function(dummy, + wsaid_connectex, + (void**)&pConnectEx) || + !uv_get_extension_function(dummy, + wsaid_acceptex, + (void**)&pAcceptEx) || + !uv_get_extension_function(dummy, + wsaid_getacceptexsockaddrs, + (void**)&pGetAcceptExSockAddrs) || + !uv_get_extension_function(dummy, + wsaid_disconnectex, + (void**)&pDisconnectEx) || + !uv_get_extension_function(dummy, + wsaid_transmitfile, + (void**)&pTransmitFile)) { + uv_fatal_error(WSAGetLastError(), + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); + } + + if (closesocket(dummy) == SOCKET_ERROR) { + uv_fatal_error(WSAGetLastError(), "closesocket"); + } + + /* optional IPv6 versions of winsock extension functions */ + dummy6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); + if (dummy6 != INVALID_SOCKET) { + uv_allow_ipv6 = TRUE; + + if (!uv_get_extension_function(dummy6, + wsaid_connectex, + (void**)&pConnectEx6) || + !uv_get_extension_function(dummy6, + wsaid_acceptex, + (void**)&pAcceptEx6) || + !uv_get_extension_function(dummy6, + wsaid_getacceptexsockaddrs, + (void**)&pGetAcceptExSockAddrs6) || + !uv_get_extension_function(dummy6, + wsaid_disconnectex, + (void**)&pDisconnectEx6) || + !uv_get_extension_function(dummy6, + wsaid_transmitfile, + (void**)&pTransmitFile6)) { + uv_allow_ipv6 = FALSE; + } + + if (closesocket(dummy6) == SOCKET_ERROR) { + uv_fatal_error(WSAGetLastError(), "closesocket"); + } + } +} + + +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_CONFLICTING_ADDRESSES: + case STATUS_QUOTA_EXCEEDED: + case STATUS_TOO_MANY_PAGING_FILES: + case STATUS_REMOTE_RESOURCES: + case STATUS_TOO_MANY_ADDRESSES: + return WSAENOBUFS; + + 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: + 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_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) | ERROR_SEVERITY_ERROR)) { + /* 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; + } + } +} diff --git a/src/rt/libuv/src/win/winsock.h b/src/rt/libuv/src/win/winsock.h new file mode 100644 index 00000000000..2c9fb92db1b --- /dev/null +++ b/src/rt/libuv/src/win/winsock.h @@ -0,0 +1,134 @@ +/* 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 +#include +#include +#include + + +/* + * 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); +#endif + +/* + * MinGW is missing these too + */ +#ifndef SO_UPDATE_CONNECT_CONTEXT +# define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + +#ifndef IPV6_V6ONLY + #define IPV6_V6ONLY 27 +#endif + + +/* Winsock extension functions (ipv4) */ +extern LPFN_CONNECTEX pConnectEx; +extern LPFN_ACCEPTEX pAcceptEx; +extern LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs; +extern LPFN_DISCONNECTEX pDisconnectEx; +extern LPFN_TRANSMITFILE pTransmitFile; + +/* Winsock extension functions (ipv6) */ +extern LPFN_CONNECTEX pConnectEx6; +extern LPFN_ACCEPTEX pAcceptEx6; +extern LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockAddrs6; +extern LPFN_DISCONNECTEX pDisconnectEx6; +extern LPFN_TRANSMITFILE pTransmitFile6; + +/* Whether ipv6 is supported */ +extern int uv_allow_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_; + +#endif /* UV_WIN_WINSOCK_H_ */ diff --git a/src/rt/libuv/test/benchmark-ares.c b/src/rt/libuv/test/benchmark-ares.c index c3f538bff95..bfd081deff5 100644 --- a/src/rt/libuv/test/benchmark-ares.c +++ b/src/rt/libuv/test/benchmark-ares.c @@ -26,6 +26,8 @@ #include #include /* strlen */ +static uv_loop_t* loop; + ares_channel channel; struct ares_options options; int optmask; @@ -67,7 +69,7 @@ static void prep_tcploopback() options.tcp_port = htons(TEST_PORT_2); options.flags = ARES_FLAG_USEVC; - rc = uv_ares_init_options(&channel, &options, optmask); + rc = uv_ares_init_options(loop, &channel, &options, optmask); ASSERT(rc == ARES_SUCCESS); } @@ -84,12 +86,13 @@ BENCHMARK_IMPL(gethostbyname) { return 1; } - uv_init(); + loop = uv_default_loop(); + ares_callbacks = 0; ares_errors = 0; - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); prep_tcploopback(); @@ -101,16 +104,17 @@ BENCHMARK_IMPL(gethostbyname) { &argument); } - uv_run(); + uv_run(loop); - uv_ares_destroy(channel); + uv_ares_destroy(loop, channel); - end_time = uv_now(); + end_time = uv_now(loop); if (ares_errors > 0) { printf("There were %d failures\n", ares_errors); } - LOGF("ares_gethostbyname: %d calls in %d ms \n", ares_callbacks, (int) (end_time - start_time)); + LOGF("ares_gethostbyname: %.0f req/s\n", + 1000.0 * ares_callbacks / (double)(end_time - start_time)); return 0; } diff --git a/src/rt/libuv/test/benchmark-getaddrinfo.c b/src/rt/libuv/test/benchmark-getaddrinfo.c index ba859c3c82e..892c14d1e95 100644 --- a/src/rt/libuv/test/benchmark-getaddrinfo.c +++ b/src/rt/libuv/test/benchmark-getaddrinfo.c @@ -32,6 +32,8 @@ const char* name = "localhost"; +static uv_loop_t* loop; + static uv_getaddrinfo_t handles[CONCURRENT_CALLS]; static int calls_initiated = 0; @@ -50,6 +52,8 @@ static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, if (calls_initiated < TOTAL_CALLS) { getaddrinfo_initiate(handle); } + + uv_freeaddrinfo(res); } @@ -58,7 +62,7 @@ static void getaddrinfo_initiate(uv_getaddrinfo_t* handle) { calls_initiated++; - r = uv_getaddrinfo(handle, &getaddrinfo_cb, name, NULL, NULL); + r = uv_getaddrinfo(loop, handle, &getaddrinfo_cb, name, NULL, NULL); ASSERT(r == 0); } @@ -66,26 +70,24 @@ static void getaddrinfo_initiate(uv_getaddrinfo_t* handle) { BENCHMARK_IMPL(getaddrinfo) { int i; - uv_init(); + loop = uv_default_loop(); - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); for (i = 0; i < CONCURRENT_CALLS; i++) { getaddrinfo_initiate(&handles[i]); } - uv_run(); + uv_run(loop); - uv_update_time(); - end_time = uv_now(); + uv_update_time(loop); + end_time = uv_now(loop); ASSERT(calls_initiated == TOTAL_CALLS); ASSERT(calls_completed == TOTAL_CALLS); - LOGF("getaddrinfo: %d calls in %d ms (%.0f requests/second)\n", - calls_completed, - (int) (end_time - start_time), + LOGF("getaddrinfo: %.0f req/s\n", (double) calls_completed / (double) (end_time - start_time) * 1000.0); return 0; diff --git a/src/rt/libuv/test/benchmark-list.h b/src/rt/libuv/test/benchmark-list.h index 6040e90a326..0e5467c7a1b 100644 --- a/src/rt/libuv/test/benchmark-list.h +++ b/src/rt/libuv/test/benchmark-list.h @@ -21,28 +21,78 @@ BENCHMARK_DECLARE (sizes) BENCHMARK_DECLARE (ping_pongs) -BENCHMARK_DECLARE (pump100_client) -BENCHMARK_DECLARE (pump1_client) +BENCHMARK_DECLARE (tcp4_pound_100) +BENCHMARK_DECLARE (tcp4_pound_1000) +BENCHMARK_DECLARE (pipe_pound_100) +BENCHMARK_DECLARE (pipe_pound_1000) +BENCHMARK_DECLARE (tcp_pump100_client) +BENCHMARK_DECLARE (tcp_pump1_client) +BENCHMARK_DECLARE (pipe_pump100_client) +BENCHMARK_DECLARE (pipe_pump1_client) +BENCHMARK_DECLARE (udp_packet_storm_1v1) +BENCHMARK_DECLARE (udp_packet_storm_1v10) +BENCHMARK_DECLARE (udp_packet_storm_1v100) +BENCHMARK_DECLARE (udp_packet_storm_1v1000) +BENCHMARK_DECLARE (udp_packet_storm_10v10) +BENCHMARK_DECLARE (udp_packet_storm_10v100) +BENCHMARK_DECLARE (udp_packet_storm_10v1000) +BENCHMARK_DECLARE (udp_packet_storm_100v100) +BENCHMARK_DECLARE (udp_packet_storm_100v1000) +BENCHMARK_DECLARE (udp_packet_storm_1000v1000) BENCHMARK_DECLARE (gethostbyname) BENCHMARK_DECLARE (getaddrinfo) -HELPER_DECLARE (pump_server) -HELPER_DECLARE (echo_server) +BENCHMARK_DECLARE (spawn) +HELPER_DECLARE (tcp_pump_server) +HELPER_DECLARE (pipe_pump_server) +HELPER_DECLARE (tcp4_echo_server) +HELPER_DECLARE (pipe_echo_server) HELPER_DECLARE (dns_server) TASK_LIST_START BENCHMARK_ENTRY (sizes) BENCHMARK_ENTRY (ping_pongs) - BENCHMARK_HELPER (ping_pongs, echo_server) + BENCHMARK_HELPER (ping_pongs, tcp4_echo_server) - BENCHMARK_ENTRY (pump100_client) - BENCHMARK_HELPER (pump100_client, pump_server) + BENCHMARK_ENTRY (tcp_pump100_client) + BENCHMARK_HELPER (tcp_pump100_client, tcp_pump_server) - BENCHMARK_ENTRY (pump1_client) - BENCHMARK_HELPER (pump1_client, pump_server) + BENCHMARK_ENTRY (tcp_pump1_client) + BENCHMARK_HELPER (tcp_pump1_client, tcp_pump_server) + + BENCHMARK_ENTRY (tcp4_pound_100) + BENCHMARK_HELPER (tcp4_pound_100, tcp4_echo_server) + + BENCHMARK_ENTRY (tcp4_pound_1000) + BENCHMARK_HELPER (tcp4_pound_1000, tcp4_echo_server) + + BENCHMARK_ENTRY (pipe_pump100_client) + BENCHMARK_HELPER (pipe_pump100_client, pipe_pump_server) + + BENCHMARK_ENTRY (pipe_pump1_client) + BENCHMARK_HELPER (pipe_pump1_client, pipe_pump_server) + + BENCHMARK_ENTRY (pipe_pound_100) + BENCHMARK_HELPER (pipe_pound_100, pipe_echo_server) + + BENCHMARK_ENTRY (pipe_pound_1000) + BENCHMARK_HELPER (pipe_pound_1000, pipe_echo_server) + + BENCHMARK_ENTRY (udp_packet_storm_1v1) + BENCHMARK_ENTRY (udp_packet_storm_1v10) + BENCHMARK_ENTRY (udp_packet_storm_1v100) + BENCHMARK_ENTRY (udp_packet_storm_1v1000) + BENCHMARK_ENTRY (udp_packet_storm_10v10) + BENCHMARK_ENTRY (udp_packet_storm_10v100) + BENCHMARK_ENTRY (udp_packet_storm_10v1000) + BENCHMARK_ENTRY (udp_packet_storm_100v100) + BENCHMARK_ENTRY (udp_packet_storm_100v1000) + BENCHMARK_ENTRY (udp_packet_storm_1000v1000) BENCHMARK_ENTRY (gethostbyname) BENCHMARK_HELPER (gethostbyname, dns_server) BENCHMARK_ENTRY (getaddrinfo) + + BENCHMARK_ENTRY (spawn) TASK_LIST_END diff --git a/src/rt/libuv/test/benchmark-ping-pongs.c b/src/rt/libuv/test/benchmark-ping-pongs.c index 7124a3614a4..c36215c1deb 100644 --- a/src/rt/libuv/test/benchmark-ping-pongs.c +++ b/src/rt/libuv/test/benchmark-ping-pongs.c @@ -34,8 +34,8 @@ typedef struct { int pongs; int state; uv_tcp_t tcp; - uv_req_t connect_req; - uv_req_t shutdown_req; + uv_connect_t connect_req; + uv_shutdown_t shutdown_req; } pinger_t; typedef struct buf_s { @@ -46,13 +46,15 @@ typedef struct buf_s { static char PING[] = "PING\n"; +static uv_loop_t* loop; + static buf_t* buf_freelist = NULL; static int pinger_shutdown_cb_called; static int completed_pingers = 0; static int64_t start_time; -static uv_buf_t buf_alloc(uv_stream_t* tcp, size_t size) { +static uv_buf_t buf_alloc(uv_handle_t* tcp, size_t size) { buf_t* ab; ab = buf_freelist; @@ -90,7 +92,7 @@ static void pinger_close_cb(uv_handle_t* handle) { } -static void pinger_write_cb(uv_req_t *req, int status) { +static void pinger_write_cb(uv_write_t* req, int status) { ASSERT(status == 0); free(req); @@ -98,22 +100,20 @@ static void pinger_write_cb(uv_req_t *req, int status) { static void pinger_write_ping(pinger_t* pinger) { - uv_req_t *req; + uv_write_t* req; uv_buf_t buf; buf.base = (char*)&PING; buf.len = strlen(PING); - req = (uv_req_t*)malloc(sizeof(*req)); - uv_req_init(req, (uv_handle_t*)(&pinger->tcp), pinger_write_cb); - - if (uv_write(req, &buf, 1)) { + req = malloc(sizeof *req); + if (uv_write(req, (uv_stream_t*) &pinger->tcp, &buf, 1, pinger_write_cb)) { FATAL("uv_write failed"); } } -static void pinger_shutdown_cb(uv_handle_t* handle, int status) { +static void pinger_shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(status == 0); pinger_shutdown_cb_called++; @@ -126,13 +126,13 @@ static void pinger_shutdown_cb(uv_handle_t* handle, int status) { static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { - unsigned int i; + ssize_t i; pinger_t* pinger; pinger = (pinger_t*)tcp->data; if (nread < 0) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(loop).code == UV_EOF); if (buf.base) { buf_free(buf); @@ -150,9 +150,8 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); if (pinger->state == 0) { pinger->pongs++; - if (uv_now() - start_time > TIME) { - uv_req_init(&pinger->shutdown_req, (uv_handle_t*)tcp, pinger_shutdown_cb); - uv_shutdown(&pinger->shutdown_req); + if (uv_now(loop) - start_time > TIME) { + uv_shutdown(&pinger->shutdown_req, (uv_stream_t*) tcp, pinger_shutdown_cb); break; } else { pinger_write_ping(pinger); @@ -164,14 +163,14 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { } -static void pinger_connect_cb(uv_req_t *req, int status) { +static void pinger_connect_cb(uv_connect_t* req, int status) { pinger_t *pinger = (pinger_t*)req->handle->data; ASSERT(status == 0); pinger_write_ping(pinger); - if (uv_read_start((uv_stream_t*)(req->handle), buf_alloc, pinger_read_cb)) { + if (uv_read_start(req->handle, buf_alloc, pinger_read_cb)) { FATAL("uv_read_start failed"); } } @@ -188,28 +187,25 @@ static void pinger_new() { pinger->pongs = 0; /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&pinger->tcp); + r = uv_tcp_init(loop, &pinger->tcp); ASSERT(!r); pinger->tcp.data = pinger; - /* We are never doing multiple reads/connects at a time anyway. */ - /* so these handles can be pre-initialized. */ - uv_req_init(&pinger->connect_req, (uv_handle_t*)&pinger->tcp, - pinger_connect_cb); - uv_tcp_bind(&pinger->tcp, client_addr); - r = uv_tcp_connect(&pinger->connect_req, server_addr); + + r = uv_tcp_connect(&pinger->connect_req, &pinger->tcp, server_addr, pinger_connect_cb); ASSERT(!r); } BENCHMARK_IMPL(ping_pongs) { - uv_init(); - start_time = uv_now(); + loop = uv_default_loop(); + + start_time = uv_now(loop); pinger_new(); - uv_run(); + uv_run(loop); ASSERT(completed_pingers == 1); diff --git a/src/rt/libuv/test/benchmark-pound.c b/src/rt/libuv/test/benchmark-pound.c new file mode 100644 index 00000000000..1f56e27f69d --- /dev/null +++ b/src/rt/libuv/test/benchmark-pound.c @@ -0,0 +1,327 @@ +/* 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 "task.h" +#include "uv.h" + +/* Update this is you're going to run > 1000 concurrent requests. */ +#define MAX_CONNS 1000 + +#undef NANOSEC +#define NANOSEC ((uint64_t)10e8) + +#define DEBUG 0 + +struct conn_rec_s; + +typedef void (*setup_fn)(int num, void* arg); +typedef void (*make_connect_fn)(struct conn_rec_s* conn); +typedef int (*connect_fn)(int num, make_connect_fn make_connect, void* arg); + +/* Base class for tcp_conn_rec and pipe_conn_rec. + * The ordering of fields matters! + */ +typedef struct conn_rec_s { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_stream_t stream; +} conn_rec; + +typedef struct { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_tcp_t stream; +} tcp_conn_rec; + +typedef struct { + int i; + uv_connect_t conn_req; + uv_write_t write_req; + make_connect_fn make_connect; + uv_pipe_t stream; +} pipe_conn_rec; + +static char buffer[] = "QS"; + +static uv_loop_t* loop; + +static tcp_conn_rec tcp_conns[MAX_CONNS]; +static pipe_conn_rec pipe_conns[MAX_CONNS]; + +static uint64_t start; /* in ms */ +static int closed_streams; +static int conns_failed; + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size); +static void connect_cb(uv_connect_t* conn_req, int status); +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); +static void close_cb(uv_handle_t* handle); + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + uv_buf_t buf; + buf.base = slab; + buf.len = sizeof(slab); + return buf; +} + + +static void after_write(uv_write_t* req, int status) { + if (status != 0) { + fprintf(stderr, "write error %s\n", uv_err_name(uv_last_error(loop))); + uv_close((uv_handle_t*)req->handle, close_cb); + conns_failed++; + return; + } +} + + +static void connect_cb(uv_connect_t* req, int status) { + conn_rec* conn; + uv_buf_t buf; + int r; + + if (status != 0) { +#if DEBUG + fprintf(stderr, "connect error %s\n", uv_err_name(uv_last_error())); +#endif + uv_close((uv_handle_t*)req->handle, close_cb); + conns_failed++; + return; + } + + ASSERT(req != NULL); + ASSERT(status == 0); + + conn = (conn_rec*)req->data; + ASSERT(conn != NULL); + +#if DEBUG + printf("connect_cb %d\n", conn->i); +#endif + + r = uv_read_start(&conn->stream, alloc_cb, read_cb); + ASSERT(r == 0); + + buf.base = buffer; + buf.len = sizeof(buffer) - 1; + + r = uv_write(&conn->write_req, &conn->stream, &buf, 1, after_write); + ASSERT(r == 0); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { + conn_rec* p = (conn_rec*)stream->data; + uv_err_t err = uv_last_error(loop); + + ASSERT(stream != NULL); + +#if DEBUG + printf("read_cb %d\n", p->i); +#endif + + uv_close((uv_handle_t*)stream, close_cb); + + if (nread == -1) { + if (err.code == UV_EOF) { + ; + } else if (err.code == UV_ECONNRESET) { + conns_failed++; + } else { + fprintf(stderr, "read error %s\n", uv_err_name(uv_last_error(loop))); + ASSERT(0); + } + } +} + + +static void close_cb(uv_handle_t* handle) { + conn_rec* p = (conn_rec*)handle->data; + + ASSERT(handle != NULL); + closed_streams++; + +#if DEBUG + printf("close_cb %d\n", p->i); +#endif + + if (uv_now(loop) - start < 10000) { + p->make_connect(p); + } +} + + +static void tcp_do_setup(int num, void* arg) { + int i; + + for (i = 0; i < num; i++) { + tcp_conns[i].i = i; + } +} + + +static void pipe_do_setup(int num, void* arg) { + int i; + + for (i = 0; i < num; i++) { + pipe_conns[i].i = i; + } +} + + +static void tcp_make_connect(conn_rec* p) { + struct sockaddr_in addr; + int r; + + r = uv_tcp_init(loop, (uv_tcp_t*)&p->stream); + ASSERT(r == 0); + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_tcp_connect(&((tcp_conn_rec*)p)->conn_req, (uv_tcp_t*)&p->stream, addr, connect_cb); + if (r) { + fprintf(stderr, "uv_tcp_connect error %s\n", + uv_err_name(uv_last_error(loop))); + ASSERT(0); + } + +#if DEBUG + printf("make connect %d\n", p->i); +#endif + + p->conn_req.data = p; + p->write_req.data = p; + p->stream.data = p; +} + + +static void pipe_make_connect(conn_rec* p) { + int r; + + r = uv_pipe_init(loop, (uv_pipe_t*)&p->stream); + ASSERT(r == 0); + + r = uv_pipe_connect(&((pipe_conn_rec*)p)->conn_req, (uv_pipe_t*)&p->stream, TEST_PIPENAME, connect_cb); + if (r) { + fprintf(stderr, "uv_tcp_connect error %s\n", + uv_err_name(uv_last_error(loop))); + ASSERT(0); + } + +#if DEBUG + printf("make connect %d\n", p->i); +#endif + + p->conn_req.data = p; + p->write_req.data = p; + p->stream.data = p; +} + + +static int tcp_do_connect(int num, make_connect_fn make_connect, void* arg) { + int i; + + for (i = 0; i < num; i++) { + tcp_make_connect((conn_rec*)&tcp_conns[i]); + tcp_conns[i].make_connect = make_connect; + } + + return 0; +} + + +static int pipe_do_connect(int num, make_connect_fn make_connect, void* arg) { + int i; + + for (i = 0; i < num; i++) { + pipe_make_connect((conn_rec*)&pipe_conns[i]); + pipe_conns[i].make_connect = make_connect; + } + + return 0; +} + + +static int pound_it(int concurrency, + const char* type, + setup_fn do_setup, + connect_fn do_connect, + make_connect_fn make_connect, + void* arg) { + double secs; + int r; + uint64_t start_time; /* in ns */ + uint64_t end_time; + + loop = uv_default_loop(); + + uv_update_time(loop); + start = uv_now(loop); + + /* Run benchmark for at least five seconds. */ + start_time = uv_hrtime(); + + do_setup(concurrency, arg); + + r = do_connect(concurrency, make_connect, arg); + ASSERT(!r); + + uv_run(loop); + + end_time = uv_hrtime(); + + /* Number of fractional seconds it took to run the benchmark. */ + secs = (double)(end_time - start_time) / NANOSEC; + + LOGF("%s-conn-pound-%d: %.0f accepts/s (%d failed)\n", + type, + concurrency, + closed_streams / secs, + conns_failed); + + return 0; +} + + +BENCHMARK_IMPL(tcp4_pound_100) { + return pound_it(100, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL); +} + + +BENCHMARK_IMPL(tcp4_pound_1000) { + return pound_it(1000, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL); +} + + +BENCHMARK_IMPL(pipe_pound_100) { + return pound_it(100, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL); +} + + +BENCHMARK_IMPL(pipe_pound_1000) { + return pound_it(1000, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL); +} diff --git a/src/rt/libuv/test/benchmark-pump.c b/src/rt/libuv/test/benchmark-pump.c index cd9c7d99679..d0b09301570 100644 --- a/src/rt/libuv/test/benchmark-pump.c +++ b/src/rt/libuv/test/benchmark-pump.c @@ -41,11 +41,14 @@ static void maybe_connect_some(); static uv_req_t* req_alloc(); static void req_free(uv_req_t* uv_req); -static uv_buf_t buf_alloc(uv_stream_t*, size_t size); +static uv_buf_t buf_alloc(uv_handle_t*, size_t size); static void buf_free(uv_buf_t uv_buf_t); +static uv_loop_t* loop; -static uv_tcp_t server; +static uv_tcp_t tcpServer; +static uv_pipe_t pipeServer; +static uv_stream_t* server; static struct sockaddr_in listen_addr; static struct sockaddr_in connect_addr; @@ -68,7 +71,10 @@ static char write_buffer[WRITE_BUFFER_SIZE]; /* Make this as large as you need. */ #define MAX_WRITE_HANDLES 1000 -static uv_tcp_t write_handles[MAX_WRITE_HANDLES]; +static stream_type type; + +static uv_tcp_t tcp_write_handles[MAX_WRITE_HANDLES]; +static uv_pipe_t pipe_write_handles[MAX_WRITE_HANDLES]; static uv_timer_t timer_handle; @@ -81,6 +87,7 @@ static double gbit(int64_t bytes, int64_t passed_ms) { static void show_stats(uv_timer_t* handle, int status) { int64_t diff; + int i; #if PRINT_STATS LOGF("connections: %d, write: %.1f gbit/s\n", @@ -91,12 +98,16 @@ static void show_stats(uv_timer_t* handle, int status) { /* Exit if the show is over */ if (!--stats_left) { - uv_update_time(); - diff = uv_now() - start_time; + uv_update_time(loop); + diff = uv_now(loop) - start_time; - LOGF("pump%d_client: %.1f gbit/s\n", write_sockets, + LOGF("%s_pump%d_client: %.1f gbit/s\n", type == TCP ? "tcp" : "pipe", write_sockets, gbit(nsent_total, diff)); + for (i = 0; i < write_sockets; i++) { + uv_close(type == TCP ? (uv_handle_t*)&tcp_write_handles[i] : (uv_handle_t*)&pipe_write_handles[i], NULL); + } + exit(0); } @@ -109,10 +120,10 @@ static void show_stats(uv_timer_t* handle, int status) { static void read_show_stats() { int64_t diff; - uv_update_time(); - diff = uv_now() - start_time; + uv_update_time(loop); + diff = uv_now(loop) - start_time; - LOGF("pump%d_server: %.1f gbit/s\n", max_read_sockets, + LOGF("%s_pump%d_server: %.1f gbit/s\n", type == TCP ? "tcp" : "pipe", max_read_sockets, gbit(nrecv_total, diff)); } @@ -131,38 +142,37 @@ void read_sockets_close_cb(uv_handle_t* handle) { /* If it's past the first second and everyone has closed their connection * Then print stats. */ - if (uv_now() - start_time > 1000 && read_sockets == 0) { + if (uv_now(loop) - start_time > 1000 && read_sockets == 0) { read_show_stats(); - uv_close((uv_handle_t*)&server, NULL); + uv_close((uv_handle_t*)server, NULL); } } static void start_stats_collection() { - uv_req_t* req = req_alloc(); int r; /* Show-stats timer */ stats_left = STATS_COUNT; - r = uv_timer_init(&timer_handle); + r = uv_timer_init(loop, &timer_handle); ASSERT(r == 0); r = uv_timer_start(&timer_handle, show_stats, STATS_INTERVAL, STATS_INTERVAL); ASSERT(r == 0); - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); } -static void read_cb(uv_stream_t* tcp, ssize_t bytes, uv_buf_t buf) { +static void read_cb(uv_stream_t* stream, ssize_t bytes, uv_buf_t buf) { if (nrecv_total == 0) { ASSERT(start_time == 0); - uv_update_time(); - start_time = uv_now(); + uv_update_time(loop); + start_time = uv_now(loop); } if (bytes < 0) { - uv_close((uv_handle_t*)tcp, read_sockets_close_cb); + uv_close((uv_handle_t*)stream, read_sockets_close_cb); return; } @@ -173,46 +183,42 @@ static void read_cb(uv_stream_t* tcp, ssize_t bytes, uv_buf_t buf) { } -static void write_cb(uv_req_t *req, int status) { - uv_buf_t* buf = (uv_buf_t*) req->data; - +static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); - req_free(req); + req_free((uv_req_t*) req); nsent += sizeof write_buffer; nsent_total += sizeof write_buffer; - do_write((uv_stream_t*)req->handle); + do_write((uv_stream_t*) req->handle); } -static void do_write(uv_stream_t* tcp) { - uv_req_t* req; +static void do_write(uv_stream_t* stream) { + uv_write_t* req; uv_buf_t buf; int r; buf.base = (char*) &write_buffer; buf.len = sizeof write_buffer; - while (tcp->write_queue_size == 0) { - req = req_alloc(); - uv_req_init(req, (uv_handle_t*)tcp, write_cb); - - r = uv_write(req, &buf, 1); + while (stream->write_queue_size == 0) { + req = (uv_write_t*) req_alloc(); + r = uv_write(req, stream, &buf, 1, write_cb); ASSERT(r == 0); } } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { int i; - if (status) LOG(uv_strerror(uv_last_error())); + if (status) LOG(uv_strerror(uv_last_error(loop))); ASSERT(status == 0); write_sockets++; - req_free(req); + req_free((uv_req_t*) req); maybe_connect_some(); @@ -221,47 +227,64 @@ static void connect_cb(uv_req_t* req, int status) { /* Yay! start writing */ for (i = 0; i < write_sockets; i++) { - do_write((uv_stream_t*)&write_handles[i]); + do_write(type == TCP ? (uv_stream_t*)&tcp_write_handles[i] : (uv_stream_t*)&pipe_write_handles[i]); } } } static void maybe_connect_some() { - uv_req_t* req; + uv_connect_t* req; uv_tcp_t* tcp; + uv_pipe_t* pipe; int r; while (max_connect_socket < TARGET_CONNECTIONS && max_connect_socket < write_sockets + MAX_SIMULTANEOUS_CONNECTS) { - tcp = &write_handles[max_connect_socket++]; + if (type == TCP) { + tcp = &tcp_write_handles[max_connect_socket++]; - r = uv_tcp_init(tcp); - ASSERT(r == 0); + r = uv_tcp_init(loop, tcp); + ASSERT(r == 0); - req = req_alloc(); - uv_req_init(req, (uv_handle_t*)tcp, connect_cb); - r = uv_tcp_connect(req, connect_addr); - ASSERT(r == 0); + req = (uv_connect_t*) req_alloc(); + r = uv_tcp_connect(req, tcp, connect_addr, connect_cb); + ASSERT(r == 0); + } else { + pipe = &pipe_write_handles[max_connect_socket++]; + + r = uv_pipe_init(loop, pipe); + ASSERT(r == 0); + + req = (uv_connect_t*) req_alloc(); + r = uv_pipe_connect(req, pipe, TEST_PIPENAME, connect_cb); + ASSERT(r == 0); + } } } -static void connection_cb(uv_handle_t* s, int status) { - uv_tcp_t* tcp; +static void connection_cb(uv_stream_t* s, int status) { + uv_stream_t* stream; int r; - ASSERT(&server == (uv_tcp_t*)s); + ASSERT(server == s); ASSERT(status == 0); - tcp = malloc(sizeof(uv_tcp_t)); + if (type == TCP) { + stream = (uv_stream_t*)malloc(sizeof(uv_tcp_t)); + r = uv_tcp_init(loop, (uv_tcp_t*)stream); + ASSERT(r == 0); + } else { + stream = (uv_stream_t*)malloc(sizeof(uv_pipe_t)); + r = uv_pipe_init(loop, (uv_pipe_t*)stream); + ASSERT(r == 0); + } - uv_tcp_init(tcp); - - r = uv_accept(s, (uv_stream_t*)tcp); + r = uv_accept(s, stream); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)tcp, buf_alloc, read_cb); + r = uv_read_start(stream, buf_alloc, read_cb); ASSERT(r == 0); read_sockets++; @@ -274,7 +297,7 @@ static void connection_cb(uv_handle_t* s, int status) { */ typedef struct req_list_s { - uv_req_t uv_req; + union uv_any_req uv_req; struct req_list_s* next; } req_list_t; @@ -317,7 +340,7 @@ typedef struct buf_list_s { static buf_list_t* buf_freelist = NULL; -static uv_buf_t buf_alloc(uv_stream_t* tcp, size_t size) { +static uv_buf_t buf_alloc(uv_handle_t* handle, size_t size) { buf_list_t* buf; buf = buf_freelist; @@ -342,48 +365,99 @@ static void buf_free(uv_buf_t uv_buf_t) { } -HELPER_IMPL(pump_server) { +HELPER_IMPL(tcp_pump_server) { int r; - uv_init(); + type = TCP; + loop = uv_default_loop(); + listen_addr = uv_ip4_addr("0.0.0.0", TEST_PORT); /* Server */ - r = uv_tcp_init(&server); + server = (uv_stream_t*)&tcpServer; + r = uv_tcp_init(loop, &tcpServer); ASSERT(r == 0); - r = uv_tcp_bind(&server, listen_addr); + r = uv_tcp_bind(&tcpServer, listen_addr); ASSERT(r == 0); - r = uv_tcp_listen(&server, MAX_WRITE_HANDLES, connection_cb); + r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); - uv_run(); + uv_run(loop); return 0; } -void pump(int n) { +HELPER_IMPL(pipe_pump_server) { + int r; + type = PIPE; + + loop = uv_default_loop(); + + /* Server */ + server = (uv_stream_t*)&pipeServer; + r = uv_pipe_init(loop, &pipeServer); + ASSERT(r == 0); + r = uv_pipe_bind(&pipeServer, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); + ASSERT(r == 0); + + uv_run(loop); + + return 0; +} + + +void tcp_pump(int n) { ASSERT(n <= MAX_WRITE_HANDLES); TARGET_CONNECTIONS = n; + type = TCP; - uv_init(); + loop = uv_default_loop(); connect_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); /* Start making connections */ maybe_connect_some(); - uv_run(); + uv_run(loop); } -BENCHMARK_IMPL(pump100_client) { - pump(100); +void pipe_pump(int n) { + ASSERT(n <= MAX_WRITE_HANDLES); + TARGET_CONNECTIONS = n; + type = PIPE; + + loop = uv_default_loop(); + + /* Start making connections */ + maybe_connect_some(); + + uv_run(loop); +} + + +BENCHMARK_IMPL(tcp_pump100_client) { + tcp_pump(100); return 0; } -BENCHMARK_IMPL(pump1_client) { - pump(1); +BENCHMARK_IMPL(tcp_pump1_client) { + tcp_pump(1); + return 0; +} + + +BENCHMARK_IMPL(pipe_pump100_client) { + pipe_pump(100); + return 0; +} + + +BENCHMARK_IMPL(pipe_pump1_client) { + pipe_pump(1); return 0; } diff --git a/src/rt/libuv/test/benchmark-sizes.c b/src/rt/libuv/test/benchmark-sizes.c index 830de3a007c..3967f301f9f 100644 --- a/src/rt/libuv/test/benchmark-sizes.c +++ b/src/rt/libuv/test/benchmark-sizes.c @@ -24,12 +24,16 @@ BENCHMARK_IMPL(sizes) { - LOGF("uv_req_t: %u bytes\n", (unsigned int) sizeof(uv_req_t)); + LOGF("uv_shutdown_t: %u bytes\n", (unsigned int) sizeof(uv_shutdown_t)); + LOGF("uv_write_t: %u bytes\n", (unsigned int) sizeof(uv_write_t)); + LOGF("uv_connect_t: %u bytes\n", (unsigned int) sizeof(uv_connect_t)); LOGF("uv_tcp_t: %u bytes\n", (unsigned int) sizeof(uv_tcp_t)); + LOGF("uv_pipe_t: %u bytes\n", (unsigned int) sizeof(uv_pipe_t)); LOGF("uv_prepare_t: %u bytes\n", (unsigned int) sizeof(uv_prepare_t)); LOGF("uv_check_t: %u bytes\n", (unsigned int) sizeof(uv_check_t)); LOGF("uv_idle_t: %u bytes\n", (unsigned int) sizeof(uv_idle_t)); LOGF("uv_async_t: %u bytes\n", (unsigned int) sizeof(uv_async_t)); LOGF("uv_timer_t: %u bytes\n", (unsigned int) sizeof(uv_timer_t)); + LOGF("uv_process_t: %u bytes\n", (unsigned int) sizeof(uv_process_t)); return 0; } diff --git a/src/rt/libuv/test/benchmark-spawn.c b/src/rt/libuv/test/benchmark-spawn.c new file mode 100644 index 00000000000..6e5493d529b --- /dev/null +++ b/src/rt/libuv/test/benchmark-spawn.c @@ -0,0 +1,156 @@ +/* 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 benchmark spawns itself 1000 times. */ + +#include "task.h" +#include "uv.h" + +static uv_loop_t* loop; + +static int N = 1000; +static int done; + +static uv_process_t process; +static uv_process_options_t options = { 0 }; +static char exepath[1024]; +static size_t exepath_size = 1024; +static char* args[3]; +static uv_pipe_t out; + +#define OUTPUT_SIZE 1024 +static char output[OUTPUT_SIZE]; +static int output_used; + +static int process_open; +static int pipe_open; + + +static void spawn(); + + +void maybe_spawn() { + if (process_open == 0 && pipe_open == 0) { + done++; + if (done < N) { + spawn(); + } + } +} + + +static void process_close_cb(uv_handle_t* handle) { + ASSERT(process_open == 1); + process_open = 0; + maybe_spawn(); +} + + +static void exit_cb(uv_process_t* process, int exit_status, int term_signal) { + ASSERT(exit_status == 42); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, process_close_cb); +} + + +uv_buf_t 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; + return buf; +} + + +void pipe_close_cb(uv_handle_t* pipe) { + ASSERT(pipe_open == 1); + pipe_open = 0; + maybe_spawn(); +} + + +void on_read(uv_stream_t* pipe, ssize_t nread, uv_buf_t buf) { + uv_err_t err = uv_last_error(loop); + + if (nread > 0) { + ASSERT(pipe_open == 1); + output_used += nread; + } else if (nread < 0) { + if (err.code == UV_EOF) { + uv_close((uv_handle_t*)pipe, pipe_close_cb); + } + } +} + + +static void spawn() { + int r; + + ASSERT(process_open == 0); + ASSERT(pipe_open == 0); + + args[0] = exepath; + args[1] = "spawn_helper"; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; + + uv_pipe_init(loop, &out); + options.stdout_stream = &out; + + r = uv_spawn(loop, &process, options); + ASSERT(r == 0); + + process_open = 1; + pipe_open = 1; + output_used = 0; + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); +} + + +BENCHMARK_IMPL(spawn) { + int r; + static int64_t start_time, end_time; + + loop = uv_default_loop(); + + r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + + uv_update_time(loop); + start_time = uv_now(loop); + + spawn(); + + r = uv_run(loop); + ASSERT(r == 0); + + uv_update_time(loop); + end_time = uv_now(loop); + + LOGF("spawn: %.0f spawns/s\n", + (double) N / (double) (end_time - start_time) * 1000.0); + + return 0; +} diff --git a/src/rt/libuv/test/benchmark-udp-packet-storm.c b/src/rt/libuv/test/benchmark-udp-packet-storm.c new file mode 100644 index 00000000000..24a9e1b920d --- /dev/null +++ b/src/rt/libuv/test/benchmark-udp-packet-storm.c @@ -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 "task.h" +#include "uv.h" + +#include +#include +#include + +#define EXPECTED "RANG TANG DING DONG I AM THE JAPANESE SANDMAN" /* "Take eight!" */ + +#define TEST_DURATION 5000 /* ms */ + +#define MAX_SENDERS 1000 +#define MAX_RECEIVERS 1000 + +#define BASE_PORT 12345 + +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) + +static uv_loop_t* loop; + +static int n_senders_; +static int n_receivers_; +static uv_udp_t senders[MAX_SENDERS]; +static uv_udp_t receivers[MAX_RECEIVERS]; +static uv_buf_t bufs[5]; + +static int send_cb_called; +static int recv_cb_called; +static int close_cb_called; +static int stopping = 0; + +typedef struct { + struct sockaddr_in addr; +} sender_state_t; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + ASSERT(suggested_size <= sizeof slab); + return uv_buf_init(slab, sizeof slab); +} + + +static void send_cb(uv_udp_send_t* req, int status) { + sender_state_t* ss; + int r; + + if (stopping) { + return; + } + + ASSERT(req != NULL); + ASSERT(status == 0); + + ss = req->data; + + r = uv_udp_send(req, req->handle, bufs, ARRAY_SIZE(bufs), ss->addr, send_cb); + ASSERT(r == 0); + + req->data = ss; + + send_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + if (nread == 0) + return; + + if (nread == -1) { + ASSERT(uv_last_error(loop).code == UV_EINTR); /* FIXME change error code */ + return; + } + + ASSERT(addr->sa_family == AF_INET); + ASSERT(!memcmp(buf.base, EXPECTED, nread)); + + recv_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +static void timeout_cb(uv_timer_t* timer, int status) { + int i; + + stopping = 1; + + for (i = 0; i < n_senders_; i++) + uv_close((uv_handle_t*)&senders[i], close_cb); + + for (i = 0; i < n_receivers_; i++) + uv_close((uv_handle_t*)&receivers[i], close_cb); +} + + +static int do_packet_storm(int n_senders, int n_receivers) { + uv_timer_t timeout; + sender_state_t *ss; + uv_udp_send_t* req; + uv_udp_t* handle; + int i; + int r; + + ASSERT(n_senders <= MAX_SENDERS); + ASSERT(n_receivers <= MAX_RECEIVERS); + + loop = uv_default_loop(); + + n_senders_ = n_senders; + n_receivers_ = n_receivers; + + r = uv_timer_init(loop, &timeout); + ASSERT(r == 0); + + r = uv_timer_start(&timeout, timeout_cb, TEST_DURATION, 0); + ASSERT(r == 0); + + /* Timer should not keep loop alive. */ + uv_unref(loop); + + for (i = 0; i < n_receivers; i++) { + struct sockaddr_in addr; + handle = &receivers[i]; + + r = uv_udp_init(loop, handle); + ASSERT(r == 0); + + addr = uv_ip4_addr("0.0.0.0", BASE_PORT + i); + + r = uv_udp_bind(handle, addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(handle, alloc_cb, recv_cb); + ASSERT(r == 0); + } + + bufs[0] = uv_buf_init(EXPECTED + 0, 10); + bufs[1] = uv_buf_init(EXPECTED + 10, 10); + bufs[2] = uv_buf_init(EXPECTED + 20, 10); + bufs[3] = uv_buf_init(EXPECTED + 30, 10); + bufs[4] = uv_buf_init(EXPECTED + 40, 5); + + for (i = 0; i < n_senders; i++) { + handle = &senders[i]; + + r = uv_udp_init(loop, handle); + ASSERT(r == 0); + + req = malloc(sizeof(*req) + sizeof(*ss)); + + ss = (void*)(req + 1); + ss->addr = uv_ip4_addr("127.0.0.1", BASE_PORT + (i % n_receivers)); + + r = uv_udp_send(req, handle, bufs, ARRAY_SIZE(bufs), ss->addr, send_cb); + ASSERT(r == 0); + + req->data = ss; + } + + uv_run(loop); + + printf("udp_packet_storm_%dv%d: %.0f/s received, %.0f/s sent\n", + n_receivers, + n_senders, + recv_cb_called / (TEST_DURATION / 1000.0), + send_cb_called / (TEST_DURATION / 1000.0)); + + return 0; +} + + +BENCHMARK_IMPL(udp_packet_storm_1v1) { + return do_packet_storm(1, 1); +} + + +BENCHMARK_IMPL(udp_packet_storm_1v10) { + return do_packet_storm(1, 10); +} + + +BENCHMARK_IMPL(udp_packet_storm_1v100) { + return do_packet_storm(1, 100); +} + + +BENCHMARK_IMPL(udp_packet_storm_1v1000) { + return do_packet_storm(1, 1000); +} + + +BENCHMARK_IMPL(udp_packet_storm_10v10) { + return do_packet_storm(10, 10); +} + + +BENCHMARK_IMPL(udp_packet_storm_10v100) { + return do_packet_storm(10, 100); +} + + +BENCHMARK_IMPL(udp_packet_storm_10v1000) { + return do_packet_storm(10, 1000); +} + + +BENCHMARK_IMPL(udp_packet_storm_100v100) { + return do_packet_storm(100, 100); +} + + +BENCHMARK_IMPL(udp_packet_storm_100v1000) { + return do_packet_storm(100, 1000); +} + + +BENCHMARK_IMPL(udp_packet_storm_1000v1000) { + return do_packet_storm(1000, 1000); +} diff --git a/src/rt/libuv/test/dns-server.c b/src/rt/libuv/test/dns-server.c index 361c9d080ba..086b52d1a36 100644 --- a/src/rt/libuv/test/dns-server.c +++ b/src/rt/libuv/test/dns-server.c @@ -27,7 +27,7 @@ typedef struct { - uv_req_t req; + uv_write_t req; uv_buf_t buf; } write_req_t; @@ -47,15 +47,18 @@ typedef struct { } dnshandle; +static uv_loop_t* loop; + + static int server_closed; static uv_tcp_t server; -static void after_write(uv_req_t* req, int status); +static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, uv_buf_t buf); static void on_close(uv_handle_t* peer); static void on_server_close(uv_handle_t* handle); -static void on_connection(uv_handle_t*, int status); +static void on_connection(uv_stream_t*, int status); #define WRITE_BUF_LEN (64*1024) #define DNSREC_LEN (4) @@ -67,11 +70,11 @@ unsigned char qrecord[] = {5, 'e', 'c', 'h', 'o', 's', 3, 's', 'r', 'v', 0, 0, 1 unsigned char arecord[] = {0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 5, 0xbd, 0, 4, 10, 0, 1, 1 }; -static void after_write(uv_req_t* req, int status) { +static void after_write(uv_write_t* req, int status) { write_req_t* wr; if (status) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(loop); fprintf(stderr, "uv_write error: %s\n", uv_strerror(err)); ASSERT(0); } @@ -84,8 +87,8 @@ static void after_write(uv_req_t* req, int status) { } -static void after_shutdown(uv_req_t* req, int status) { - uv_close(req->handle, on_close); +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*) req->handle, on_close); free(req); } @@ -116,7 +119,7 @@ static void addrsp(write_req_t* wr, char* hdr) { } static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { - write_req_t *wr; + write_req_t* wr; dnshandle* dns = (dnshandle*)handle; char hdrbuf[DNSREC_LEN]; int hdrbuf_remaining = DNSREC_LEN; @@ -127,7 +130,6 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { int usingprev = 0; wr = (write_req_t*) malloc(sizeof *wr); - uv_req_init(&wr->req, (uv_handle_t*)handle, after_write); wr->buf.base = (char*)malloc(WRITE_BUF_LEN); wr->buf.len = 0; @@ -165,7 +167,7 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { rec_remaining = ntohs(reclen_n) - (DNSREC_LEN - 2); } } - + if (rec_remaining <= readbuf_remaining) { /* prepare reply */ addrsp(wr, hdrbuf); @@ -197,7 +199,7 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { /* send write buffer */ if (wr->buf.len > 0) { - if (uv_write(&wr->req, &wr->buf, 1)) { + if (uv_write((uv_write_t*) &wr->req, handle, &wr->buf, 1, after_write)) { FATAL("uv_write failed"); } } @@ -217,19 +219,18 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { } static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { - uv_req_t* req; + uv_shutdown_t* req; if (nread < 0) { /* Error or EOF */ - ASSERT (uv_last_error().code == UV_EOF); + ASSERT (uv_last_error(loop).code == UV_EOF); if (buf.base) { free(buf.base); } - req = (uv_req_t*) malloc(sizeof *req); - uv_req_init(req, (uv_handle_t*)handle, after_shutdown); - uv_shutdown(req); + req = malloc(sizeof *req); + uv_shutdown(req, handle, after_shutdown); return; } @@ -249,7 +250,7 @@ static void on_close(uv_handle_t* peer) { } -static uv_buf_t buf_alloc(uv_stream_t* handle, size_t suggested_size) { +static uv_buf_t buf_alloc(uv_handle_t* handle, size_t suggested_size) { uv_buf_t buf; buf.base = (char*) malloc(suggested_size); buf.len = suggested_size; @@ -257,7 +258,7 @@ static uv_buf_t buf_alloc(uv_stream_t* handle, size_t suggested_size) { } -static void on_connection(uv_handle_t* server, int status) { +static void on_connection(uv_stream_t* server, int status) { dnshandle* handle; int r; @@ -271,7 +272,8 @@ static void on_connection(uv_handle_t* server, int status) { handle->state.prevbuf_pos = 0; handle->state.prevbuf_rem = 0; - uv_tcp_init((uv_tcp_t*)handle); + r = uv_tcp_init(loop, (uv_tcp_t*)handle); + ASSERT(r == 0); r = uv_accept(server, (uv_stream_t*)handle); ASSERT(r == 0); @@ -290,7 +292,7 @@ static int dns_start(int port) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", port); int r; - r = uv_tcp_init(&server); + r = uv_tcp_init(loop, &server); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); @@ -304,7 +306,7 @@ static int dns_start(int port) { return 1; } - r = uv_tcp_listen(&server, 128, on_connection); + r = uv_listen((uv_stream_t*)&server, 128, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error\n"); @@ -316,10 +318,11 @@ static int dns_start(int port) { HELPER_IMPL(dns_server) { - uv_init(); + loop = uv_default_loop(); + if (dns_start(TEST_PORT_2)) return 1; - uv_run(); + uv_run(loop); return 0; } diff --git a/src/rt/libuv/test/echo-server.c b/src/rt/libuv/test/echo-server.c index 9addc546c1e..453ada66d24 100644 --- a/src/rt/libuv/test/echo-server.c +++ b/src/rt/libuv/test/echo-server.c @@ -24,32 +24,31 @@ #include #include - typedef struct { - uv_req_t req; + uv_write_t req; uv_buf_t buf; } write_req_t; +static uv_loop_t* loop; static int server_closed; -static uv_tcp_t server; +static stream_type serverType; +static uv_tcp_t tcpServer; +static uv_pipe_t pipeServer; +static uv_handle_t* server; -static int server6_closed; -static uv_tcp_t server6; - - -static void after_write(uv_req_t* req, int status); +static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, uv_buf_t buf); static void on_close(uv_handle_t* peer); static void on_server_close(uv_handle_t* handle); -static void on_connection(uv_handle_t*, int status); +static void on_connection(uv_stream_t*, int status); -static void after_write(uv_req_t* req, int status) { +static void after_write(uv_write_t* req, int status) { write_req_t* wr; if (status) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(loop); fprintf(stderr, "uv_write error: %s\n", uv_strerror(err)); ASSERT(0); } @@ -62,8 +61,8 @@ static void after_write(uv_req_t* req, int status) { } -static void after_shutdown(uv_req_t* req, int status) { - uv_close(req->handle, on_close); +static void after_shutdown(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*)req->handle, on_close); free(req); } @@ -71,19 +70,18 @@ static void after_shutdown(uv_req_t* req, int status) { static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { int i; write_req_t *wr; - uv_req_t* req; + uv_shutdown_t* req; if (nread < 0) { /* Error or EOF */ - ASSERT (uv_last_error().code == UV_EOF); + ASSERT (uv_last_error(loop).code == UV_EOF); if (buf.base) { free(buf.base); } - req = (uv_req_t*) malloc(sizeof *req); - uv_req_init(req, (uv_handle_t*)handle, (void *(*)(void *))after_shutdown); - uv_shutdown(req); + req = (uv_shutdown_t*) malloc(sizeof *req); + uv_shutdown(req, handle, after_shutdown); return; } @@ -94,24 +92,29 @@ static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { return; } - /* Scan for the letter Q which signals that we should quit. */ + /* + * 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') { - uv_close((uv_handle_t*)&server, on_server_close); - server_closed = 1; - uv_close((uv_handle_t*)&server6, on_server_close); - server6_closed = 1; + if (i + 1 < nread && buf.base[i + 1] == 'S') { + free(buf.base); + uv_close((uv_handle_t*)handle, on_close); + return; + } else { + uv_close(server, on_server_close); + server_closed = 1; + } } } } wr = (write_req_t*) malloc(sizeof *wr); - uv_req_init(&wr->req, (uv_handle_t*)handle, (void *(*)(void *))after_write); - wr->buf.base = buf.base; - wr->buf.len = nread; - if (uv_write(&wr->req, &wr->buf, 1)) { + wr->buf = uv_buf_init(buf.base, nread); + if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) { FATAL("uv_write failed"); } } @@ -122,71 +125,98 @@ static void on_close(uv_handle_t* peer) { } -static uv_buf_t echo_alloc(uv_stream_t* handle, size_t suggested_size) { - uv_buf_t buf; - buf.base = (char*) malloc(suggested_size); - buf.len = suggested_size; - return buf; +static uv_buf_t echo_alloc(uv_handle_t* handle, size_t suggested_size) { + return uv_buf_init(malloc(suggested_size), suggested_size); } -static void on_connection(uv_handle_t* server, int status) { - uv_tcp_t* handle; +static void on_connection(uv_stream_t* server, int status) { + uv_stream_t* stream; int r; if (status != 0) { - fprintf(stderr, "Connect error %d\n", uv_last_error().code); + fprintf(stderr, "Connect error %d\n", + uv_last_error(loop).code); } ASSERT(status == 0); - handle = (uv_tcp_t*) malloc(sizeof *handle); - ASSERT(handle != NULL); + switch (serverType) { + case TCP: + stream = malloc(sizeof(uv_tcp_t)); + ASSERT(stream != NULL); + r = uv_tcp_init(loop, (uv_tcp_t*)stream); + ASSERT(r == 0); + break; - uv_tcp_init(handle); + case PIPE: + stream = malloc(sizeof(uv_pipe_t)); + ASSERT(stream != NULL); + r = uv_pipe_init(loop, (uv_pipe_t*)stream); + ASSERT(r == 0); + break; + + default: + ASSERT(0 && "Bad serverType"); + abort(); + } /* associate server with stream */ - handle->data = server; + stream->data = server; - r = uv_accept(server, (uv_stream_t*)handle); + r = uv_accept(server, stream); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)handle, echo_alloc, after_read); + r = uv_read_start(stream, echo_alloc, after_read); ASSERT(r == 0); } static void on_server_close(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*)&server || handle == (uv_handle_t*)&server6); + ASSERT(handle == server); } -static int echo_start(int port) { +static int tcp4_echo_start(int port) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", port); - struct sockaddr_in6 addr6 = uv_ip6_addr("::1", port); int r; - r = uv_tcp_init(&server); + server = (uv_handle_t*)&tcpServer; + serverType = TCP; + + r = uv_tcp_init(loop, &tcpServer); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); return 1; } - r = uv_tcp_bind(&server, addr); + r = uv_tcp_bind(&tcpServer, addr); if (r) { /* TODO: Error codes */ fprintf(stderr, "Bind error\n"); return 1; } - r = uv_tcp_listen(&server, 128, on_connection); + r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); if (r) { /* TODO: Error codes */ - fprintf(stderr, "Listen error\n"); + fprintf(stderr, "Listen error %s\n", + uv_err_name(uv_last_error(loop))); return 1; } - r = uv_tcp_init(&server6); + return 0; +} + + +static int tcp6_echo_start(int port) { + struct sockaddr_in6 addr6 = uv_ip6_addr("::1", port); + int r; + + server = (uv_handle_t*)&tcpServer; + serverType = TCP; + + r = uv_tcp_init(loop, &tcpServer); if (r) { /* TODO: Error codes */ fprintf(stderr, "Socket creation error\n"); @@ -194,17 +224,17 @@ static int echo_start(int port) { } /* IPv6 is optional as not all platforms support it */ - r = uv_tcp_bind6(&server6, addr6); + r = uv_tcp_bind6(&tcpServer, addr6); if (r) { /* show message but return OK */ fprintf(stderr, "IPv6 not supported\n"); return 0; } - r = uv_tcp_listen(&server6, 128, on_connection); + r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection); if (r) { /* TODO: Error codes */ - fprintf(stderr, "Listen error on IPv6\n"); + fprintf(stderr, "Listen error\n"); return 1; } @@ -212,11 +242,65 @@ static int echo_start(int port) { } -HELPER_IMPL(echo_server) { - uv_init(); - if (echo_start(TEST_PORT)) - return 1; +static int pipe_echo_start(char* pipeName) { + int r; + + server = (uv_handle_t*)&pipeServer; + serverType = PIPE; + + r = uv_pipe_init(loop, &pipeServer); + if (r) { + fprintf(stderr, "uv_pipe_init: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } + + r = uv_pipe_bind(&pipeServer, pipeName); + if (r) { + fprintf(stderr, "uv_pipe_bind: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } + + r = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, on_connection); + if (r) { + fprintf(stderr, "uv_pipe_listen: %s\n", + uv_strerror(uv_last_error(loop))); + return 1; + } - uv_run(); + return 0; +} + + +HELPER_IMPL(tcp4_echo_server) { + loop = uv_default_loop(); + + if (tcp4_echo_start(TEST_PORT)) + return 1; + + uv_run(loop); + return 0; +} + + +HELPER_IMPL(tcp6_echo_server) { + loop = uv_default_loop(); + + if (tcp6_echo_start(TEST_PORT)) + return 1; + + uv_run(loop); + return 0; +} + + +HELPER_IMPL(pipe_echo_server) { + loop = uv_default_loop(); + + if (pipe_echo_start(TEST_PIPENAME)) + return 1; + + uv_run(loop); return 0; } diff --git a/src/rt/libuv/test/run-benchmarks.c b/src/rt/libuv/test/run-benchmarks.c index 43d25a63bbd..af11beb9b4c 100644 --- a/src/rt/libuv/test/run-benchmarks.c +++ b/src/rt/libuv/test/run-benchmarks.c @@ -32,28 +32,33 @@ /* The time in milliseconds after which a single benchmark times out. */ #define BENCHMARK_TIMEOUT 60000 +static int maybe_run_test(int argc, char **argv); + int main(int argc, char **argv) { - task_entry_t *task; - platform_init(argc, argv); - if (argc > 1) { - /* A specific process was requested. */ - return run_process(argv[1]); - - } else { - /* Run all benchmarks. */ - task = (task_entry_t*)&TASKS; - for (task = (task_entry_t*)&TASKS; task->main; task++) { - if (task->is_helper) { - continue; - } - - run_task(task, BENCHMARK_TIMEOUT, 1); - } - LOG("Done.\n"); - - return 0; + switch (argc) { + case 1: return run_tests(BENCHMARK_TIMEOUT, 1); + case 2: return maybe_run_test(argc, argv); + case 3: return run_test_part(argv[1], argv[2]); + default: + LOGF("Too many arguments.\n"); + return 1; } } + + +static int maybe_run_test(int argc, char **argv) { + if (strcmp(argv[1], "--list") == 0) { + print_tests(stdout); + return 0; + } + + if (strcmp(argv[1], "spawn_helper") == 0) { + printf("hello world\n"); + return 42; + } + + return run_test(argv[1], BENCHMARK_TIMEOUT, 1); +} diff --git a/src/rt/libuv/test/run-tests.c b/src/rt/libuv/test/run-tests.c index f83006bdd81..a187869110a 100644 --- a/src/rt/libuv/test/run-tests.c +++ b/src/rt/libuv/test/run-tests.c @@ -28,59 +28,53 @@ /* Actual tests and helpers are defined in test-list.h */ #include "test-list.h" - /* The time in milliseconds after which a single test times out. */ #define TEST_TIMEOUT 5000 - -static void log_progress(int total, int passed, int failed, char* name) { - LOGF("[%% %3d|+ %3d|- %3d]: %s", (passed + failed) / total * 100, - passed, failed, name); -} +static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { - int total, passed, failed; - task_entry_t* task; - platform_init(argc, argv); - if (argc > 1) { - /* A specific process was requested. */ - return run_process(argv[1]); - - } else { - /* Count the number of tests. */ - total = 0; - task = (task_entry_t*)&TASKS; - for (task = (task_entry_t*)&TASKS; task->main; task++) { - if (!task->is_helper) { - total++; - } - } - - /* Run all tests. */ - passed = 0; - failed = 0; - task = (task_entry_t*)&TASKS; - for (task = (task_entry_t*)&TASKS; task->main; task++) { - if (task->is_helper) { - continue; - } - - rewind_cursor(); - log_progress(total, passed, failed, task->task_name); - - if (run_task(task, TEST_TIMEOUT, 0)) { - passed++; - } else { - failed++; - } - } - - rewind_cursor(); - log_progress(total, passed, failed, "Done.\n"); - - return 0; + switch (argc) { + case 1: return run_tests(TEST_TIMEOUT, 0); + case 2: return maybe_run_test(argc, argv); + case 3: return run_test_part(argv[1], argv[2]); + default: + LOGF("Too many arguments.\n"); + return 1; } } + + +static int maybe_run_test(int argc, char **argv) { + if (strcmp(argv[1], "--list") == 0) { + print_tests(stdout); + return 0; + } + + 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]; + 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); + } + + return run_test(argv[1], TEST_TIMEOUT, 0); +} diff --git a/src/rt/libuv/test/runner-unix.c b/src/rt/libuv/test/runner-unix.c index fa4c63bb208..6033d64294d 100644 --- a/src/rt/libuv/test/runner-unix.c +++ b/src/rt/libuv/test/runner-unix.c @@ -65,12 +65,14 @@ void platform_init(int argc, char **argv) { #else strcpy(executable_path, argv[0]); #endif + + signal(SIGPIPE, SIG_IGN); } -/* Invoke "arv[0] test-name". Store process info in *p. */ +/* 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(char* name, process_info_t* p) { +int process_start(char* name, char* part, process_info_t* p) { FILE* stdout_file = tmpfile(); if (!stdout_file) { perror("tmpfile"); @@ -92,7 +94,7 @@ int process_start(char* name, process_info_t* p) { dup2(fileno(stdout_file), STDOUT_FILENO); dup2(fileno(stdout_file), STDERR_FILENO); - char* args[3] = { executable_path, name, NULL }; + char* args[] = { executable_path, name, part, NULL }; execvp(executable_path, args); perror("execvp()"); _exit(127); diff --git a/src/rt/libuv/test/runner-win.c b/src/rt/libuv/test/runner-win.c index 09458a6b5ce..fc08839ee48 100644 --- a/src/rt/libuv/test/runner-win.c +++ b/src/rt/libuv/test/runner-win.c @@ -52,7 +52,7 @@ void platform_init(int argc, char **argv) { } -int process_start(char *name, process_info_t *p) { +int process_start(char *name, char *part, process_info_t *p) { HANDLE file = INVALID_HANDLE_VALUE; HANDLE nul = INVALID_HANDLE_VALUE; WCHAR path[MAX_PATH], filename[MAX_PATH]; @@ -97,12 +97,24 @@ int process_start(char *name, process_info_t *p) { if (result == 0 || result == sizeof(image)) goto error; - if (_snwprintf((wchar_t*)&args, - sizeof(args) / sizeof(wchar_t), - L"\"%s\" %S", - image, - name) < 0) - goto error; + if (part) { + if (_snwprintf((wchar_t*)args, + sizeof(args) / sizeof(wchar_t), + L"\"%s\" %S %S", + image, + name, + part) < 0) { + goto error; + } + } else { + if (_snwprintf((wchar_t*)args, + sizeof(args) / sizeof(wchar_t), + L"\"%s\" %S", + image, + name) < 0) { + goto error; + } + } memset((void*)&si, 0, sizeof(si)); si.cb = sizeof(si); diff --git a/src/rt/libuv/test/runner.c b/src/rt/libuv/test/runner.c index 7b6590f6792..73a3864ee41 100644 --- a/src/rt/libuv/test/runner.c +++ b/src/rt/libuv/test/runner.c @@ -26,102 +26,173 @@ char executable_path[PATHMAX] = { '\0' }; -/* Start a specific process declared by TEST_ENTRY or TEST_HELPER. */ -/* Returns the exit code of the specific process. */ -int run_process(char* name) { - task_entry_t *test; - for (test = (task_entry_t*)&TASKS; test->main; test++) { - if (strcmp(name, test->process_name) == 0) { - return test->main(); - } - } - - LOGF("Test process %s not found!\n", name); - return 255; +static void log_progress(int total, int passed, int failed, const char* name) { + LOGF("[%% %3d|+ %3d|- %3d]: %s", (passed + failed) / total * 100, + passed, failed, name); } -/* - * Runs all processes associated with a particular test or benchmark. - * It returns 1 if the test succeeded, 0 if it failed. - * If the test fails it prints diagnostic information. - * If benchmark_output is nonzero, the output from the main process is - * always shown. - */ -int run_task(task_entry_t *test, int timeout, int benchmark_output) { - int i, result, success; - char errmsg[256]; - task_entry_t *helper; - int process_count; - process_info_t processes[MAX_PROCESSES]; - process_info_t *main_process; +int run_tests(int timeout, int benchmark_output) { + int total, passed, failed; + task_entry_t* task; - success = 0; - - process_count = 0; - - /* Start all helpers for this test first. */ - for (helper = (task_entry_t*)&TASKS; helper->main; helper++) { - if (helper->is_helper && - strcmp(test->task_name, helper->task_name) == 0) { - if (process_start(helper->process_name, &processes[process_count]) == -1) { - snprintf((char*)&errmsg, - sizeof(errmsg), - "process `%s` failed to start.", - helper->process_name); - goto finalize; - } - process_count++; + /* Count the number of tests. */ + total = 0; + for (task = TASKS; task->main; task++) { + if (!task->is_helper) { + total++; } } - /* Wait a little bit to allow servers to start. Racy. */ - uv_sleep(100); + /* Run all tests. */ + passed = 0; + failed = 0; + for (task = TASKS; task->main; task++) { + if (task->is_helper) { + continue; + } - /* Start the main test process. */ - if (process_start(test->process_name, &processes[process_count]) == -1) { - snprintf((char*)&errmsg, sizeof(errmsg), "process `%s` failed to start.", - test->process_name); - goto finalize; + rewind_cursor(); + if (!benchmark_output) { + log_progress(total, passed, failed, task->task_name); + } + + if (run_test(task->task_name, timeout, benchmark_output) == 0) { + passed++; + } else { + failed++; + } } - main_process = &processes[process_count]; - process_count++; - /* Wait for the main process to terminate. */ - result = process_wait(main_process, 1, timeout); + rewind_cursor(); + + if (!benchmark_output) { + log_progress(total, passed, failed, "Done.\n"); + } + + return 0; +} + + +int run_test(const char* test, int timeout, int benchmark_output) { + char errmsg[1024] = "no error"; + process_info_t processes[1024]; + process_info_t *main_proc; + task_entry_t* task; + int process_count; + int result; + int status; + int i; + + status = 255; + process_count = 0; + + /* If it's a helper the user asks for, start it directly. */ + for (task = TASKS; task->main; task++) { + if (task->is_helper && strcmp(test, task->process_name) == 0) { + return task->main(); + } + } + + /* Start the helpers first. */ + for (task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) != 0) { + continue; + } + + /* Skip the test itself. */ + if (!task->is_helper) { + continue; + } + + if (process_start(task->task_name, + task->process_name, + &processes[process_count]) == -1) { + snprintf(errmsg, + sizeof errmsg, + "Process `%s` failed to start.", + task->process_name); + goto out; + } + + process_count++; + } + + /* Give the helpers time to settle. Race-y, fix this. */ + uv_sleep(250); + + /* Now start the test itself. */ + for (main_proc = NULL, task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) != 0) { + continue; + } + + if (task->is_helper) { + continue; + } + + if (process_start(task->task_name, + task->process_name, + &processes[process_count]) == -1) { + snprintf(errmsg, + sizeof errmsg, + "Process `%s` failed to start.", + task->process_name); + goto out; + } + + main_proc = &processes[process_count]; + process_count++; + break; + } + + if (main_proc == NULL) { + snprintf(errmsg, + sizeof errmsg, + "No test with that name: %s", + test); + goto out; + } + + result = process_wait(main_proc, 1, timeout); if (result == -1) { FATAL("process_wait failed"); } else if (result == -2) { - snprintf((char*)&errmsg, sizeof(errmsg), "timeout."); - goto finalize; + /* Don't have to clean up the process, process_wait() has killed it. */ + snprintf(errmsg, + sizeof errmsg, + "timeout"); + goto out; } - /* Reap the main process. */ - result = process_reap(main_process); - if (result != 0) { - snprintf((char*)&errmsg, sizeof(errmsg), "exit code %d.", result); - goto finalize; + status = process_reap(main_proc); + if (status != 0) { + snprintf(errmsg, + sizeof errmsg, + "exit code %d", + status); + goto out; } - /* Yes! did it. */ - success = 1; + if (benchmark_output) { + /* Give the helpers time to clean up their act. */ + uv_sleep(1000); + } -finalize: - /* Kill all (helper) processes that are still running. */ - for (i = 0; i < process_count; i++) { - /* If terminate fails the process is probably already closed. */ +out: + /* Reap running processes except the main process, it's already dead. */ + for (i = 0; i < process_count - 1; i++) { process_terminate(&processes[i]); } - /* Wait until all processes have really terminated. */ - if (process_wait((process_info_t*)&processes, process_count, -1) < 0) { + if (process_wait(processes, process_count - 1, -1) < 0) { FATAL("process_wait failed"); } /* Show error and output from processes if the test failed. */ - if (!success) { - LOGF("\n`%s` failed: %s\n", test->task_name, errmsg); + if (status != 0) { + LOGF("\n`%s` failed: %s\n", test, errmsg); for (i = 0; i < process_count; i++) { switch (process_output_size(&processes[i])) { @@ -145,13 +216,13 @@ finalize: /* In benchmark mode show concise output from the main process. */ } else if (benchmark_output) { - switch (process_output_size(main_process)) { + switch (process_output_size(main_proc)) { case -1: - LOGF("%s: (unavailabe)\n", test->task_name); + LOGF("%s: (unavailabe)\n", test); break; case 0: - LOGF("%s: (no output)\n", test->task_name); + LOGF("%s: (no output)\n", test); break; default: @@ -167,5 +238,74 @@ finalize: process_cleanup(&processes[i]); } - return success; + return status; +} + + +/* Returns the status code of the task part + * or 255 if no matching task was not found. + */ +int run_test_part(const char* test, const char* part) { + task_entry_t* task; + + for (task = TASKS; task->main; task++) { + if (strcmp(test, task->task_name) == 0 + && strcmp(part, task->process_name) == 0) { + return task->main(); + } + } + + LOGF("No test part with that name: %s:%s\n", test, part); + return 255; +} + + +static int compare_task(const void* va, const void* vb) { + const task_entry_t* a = va; + const task_entry_t* b = vb; + return strcmp(a->task_name, b->task_name); +} + + +static int find_helpers(const task_entry_t* task, const task_entry_t** helpers) { + const task_entry_t* helper; + int n_helpers; + + for (n_helpers = 0, helper = TASKS; helper->main; helper++) { + if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) { + *helpers++ = helper; + n_helpers++; + } + } + + return n_helpers; +} + + +void print_tests(FILE* stream) { + const task_entry_t* helpers[1024]; + const task_entry_t* task; + int n_helpers; + int n_tasks; + int i; + + for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++); + qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task); + + for (task = TASKS; task->main; task++) { + if (task->is_helper) { + continue; + } + + n_helpers = find_helpers(task, helpers); + if (n_helpers) { + printf("%-25s (helpers:", task->task_name); + for (i = 0; i < n_helpers; i++) { + printf(" %s", helpers[i]->process_name); + } + printf(")\n"); + } else { + printf("%s\n", task->task_name); + } + } } diff --git a/src/rt/libuv/test/runner.h b/src/rt/libuv/test/runner.h index 11d498020a1..3b93ffe991b 100644 --- a/src/rt/libuv/test/runner.h +++ b/src/rt/libuv/test/runner.h @@ -22,6 +22,8 @@ #ifndef RUNNER_H_ #define RUNNER_H_ +#include /* FILE */ + /* * The maximum number of processes (main + helpers) that a test / benchmark @@ -41,13 +43,6 @@ typedef struct { } task_entry_t, bench_entry_t; -/* Runs an individual task; returns 1 if the test succeeded, 0 if it failed. */ -/* If the test fails it prints diagnostic information. */ -/* If benchmark_output is nonzero, the output from the main process is -/* always shown. */ -int run_task(task_entry_t *test, int timeout, int benchmark_output); - - /* * Macros used by test-list.h and benchmark-list.h. */ @@ -95,13 +90,26 @@ extern char executable_path[PATHMAX]; /* The array that is filled by test-list.h or benchmark-list.h */ extern task_entry_t TASKS[]; -/* Start a specific process declared by TEST_ENTRY or TEST_HELPER. */ -/* Returns the exit code of the specific process. */ -int run_task(task_entry_t *test, int timeout, int benchmark_output); +/* + * Run all tests. + */ +int run_tests(int timeout, int benchmark_output); -/* Start a specific process declared by TEST_ENTRY or TEST_HELPER. */ -/* Returns the exit code of the specific process. */ -int run_process(char* name); +/* + * Run a single test. Starts up any helpers. + */ +int run_test(const char* test, int timeout, int benchmark_output); + +/* + * Run a test part, i.e. the test or one of its helpers. + */ +int run_test_part(const char* test, const char* part); + + +/* + * Print tests in sorted order to `stream`. Used by `./run-tests --list`. + */ +void print_tests(FILE* stream); /* @@ -113,9 +121,9 @@ int run_process(char* name); /* Do platform-specific initialization. */ void platform_init(); -/* Invoke "arv[0] test-name". Store process info in *p. */ +/* 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(char *name, process_info_t *p); +int process_start(char *name, char* part, process_info_t *p); /* Wait for all `n` processes in `vec` to terminate. */ /* Time out after `timeout` msec, or never if timeout == -1 */ diff --git a/src/rt/libuv/test/task.h b/src/rt/libuv/test/task.h index 8d9a1e8e0df..76c6903311e 100644 --- a/src/rt/libuv/test/task.h +++ b/src/rt/libuv/test/task.h @@ -30,10 +30,31 @@ #define TEST_PORT 9123 #define TEST_PORT_2 9124 +#ifdef _WIN32 +# define TEST_PIPENAME "\\\\.\\pipe\\uv-test" +# define TEST_PIPENAME_2 "\\\\.\\pipe\\uv-test2" +#else +# define TEST_PIPENAME "/tmp/uv-test-sock" +# define TEST_PIPENAME_2 "/tmp/uv-test-sock2" +#endif + +typedef enum { + TCP = 0, + PIPE +} stream_type; /* Log to stderr. */ -#define LOG(...) fprintf(stderr, "%s", __VA_ARGS__) -#define LOGF(...) fprintf(stderr, __VA_ARGS__) +#define LOG(...) \ + do { \ + fprintf(stderr, "%s", __VA_ARGS__); \ + fflush(stderr); \ + } while (0) + +#define LOGF(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } while (0) /* Die with fatal error. */ #define FATAL(msg) \ @@ -43,6 +64,7 @@ __FILE__, \ __LINE__, \ msg); \ + fflush(stderr); \ abort(); \ } while (0) diff --git a/src/rt/libuv/test/test-async.c b/src/rt/libuv/test/test-async.c index a9933fdcb14..3d8415183d1 100644 --- a/src/rt/libuv/test/test-async.c +++ b/src/rt/libuv/test/test-async.c @@ -47,8 +47,6 @@ static uintptr_t thread3_id = 0; /* Thread 1 makes sure that async1_cb_called reaches 3 before exiting. */ void thread1_entry(void *arg) { - int state = 0; - uv_sleep(50); while (1) { @@ -148,8 +146,6 @@ static void async2_cb(uv_handle_t* handle, int status) { static void prepare_cb(uv_prepare_t* handle, int status) { - int r; - ASSERT(handle == &prepare_handle); ASSERT(status == 0); @@ -172,8 +168,7 @@ static void prepare_cb(uv_prepare_t* handle, int status) { #endif case 1: - r = uv_close((uv_handle_t*)handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)handle, close_cb); break; default: @@ -187,14 +182,12 @@ static void prepare_cb(uv_prepare_t* handle, int status) { TEST_IMPL(async) { int r; - uv_init(); - - r = uv_prepare_init(&prepare_handle); + r = uv_prepare_init(uv_default_loop(), &prepare_handle); ASSERT(r == 0); r = uv_prepare_start(&prepare_handle, prepare_cb); ASSERT(r == 0); - r = uv_async_init(&async1_handle, async1_cb); + r = uv_async_init(uv_default_loop(), &async1_handle, async1_cb); ASSERT(r == 0); #if 0 @@ -202,7 +195,7 @@ TEST_IMPL(async) { ASSERT(r == 0); #endif - r = uv_run(); + r = uv_run(uv_default_loop()); ASSERT(r == 0); r = uv_wait_thread(thread1_id); diff --git a/src/rt/libuv/test/test-callback-stack.c b/src/rt/libuv/test/test-callback-stack.c index 5b12c8b9b42..4af63648861 100644 --- a/src/rt/libuv/test/test-callback-stack.c +++ b/src/rt/libuv/test/test-callback-stack.c @@ -32,7 +32,9 @@ static const char MESSAGE[] = "Failure is for the weak. Everyone dies alone."; static uv_tcp_t client; static uv_timer_t timer; -static uv_req_t connect_req, write_req, shutdown_req; +static uv_connect_t connect_req; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; static int nested = 0; static int close_cb_called = 0; @@ -43,7 +45,7 @@ static int bytes_received = 0; static int shutdown_cb_called = 0; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.len = size; buf.base = (char*) malloc(size); @@ -59,7 +61,7 @@ static void close_cb(uv_handle_t* handle) { } -static void shutdown_cb(uv_req_t* req, int status) { +static void shutdown_cb(uv_shutdown_t* req, int status) { ASSERT(status == 0); ASSERT(nested == 0 && "shutdown_cb must be called from a fresh stack"); @@ -74,16 +76,14 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { free(buf.base); if (nread == 0) { - ASSERT(uv_last_error().code == UV_EAGAIN); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EAGAIN); return; } else if (nread == -1) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); nested++; - if (uv_close((uv_handle_t*)tcp, close_cb)) { - FATAL("uv_close failed"); - } + uv_close((uv_handle_t*)tcp, close_cb); nested--; return; @@ -97,11 +97,10 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { /* from a fresh stack. */ if (bytes_received == sizeof MESSAGE) { nested++; - uv_req_init(&shutdown_req, (uv_handle_t*)tcp, (void *(*)(void *))shutdown_cb); puts("Shutdown"); - if (uv_shutdown(&shutdown_req)) { + if (uv_shutdown(&shutdown_req, (uv_stream_t*)tcp, shutdown_cb)) { FATAL("uv_shutdown failed"); } nested--; @@ -110,8 +109,6 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { static void timer_cb(uv_timer_t* handle, int status) { - int r; - ASSERT(handle == &timer); ASSERT(status == 0); ASSERT(nested == 0 && "timer_cb must be called from a fresh stack"); @@ -126,12 +123,11 @@ static void timer_cb(uv_timer_t* handle, int status) { timer_cb_called++; - r = uv_close((uv_handle_t*)handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)handle, close_cb); } -static void write_cb(uv_req_t* req, int status) { +static void write_cb(uv_write_t* req, int status) { int r; ASSERT(status == 0); @@ -144,7 +140,7 @@ static void write_cb(uv_req_t* req, int status) { /* back to our receive buffer when we start reading. This maximizes the */ /* tempation for the backend to use dirty stack for calling read_cb. */ nested++; - r = uv_timer_init(&timer); + r = uv_timer_init(uv_default_loop(), &timer); ASSERT(r == 0); r = uv_timer_start(&timer, timer_cb, 500, 0); ASSERT(r == 0); @@ -154,7 +150,7 @@ static void write_cb(uv_req_t* req, int status) { } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { uv_buf_t buf; puts("Connected. Write some data to echo server..."); @@ -167,9 +163,7 @@ static void connect_cb(uv_req_t* req, int status) { buf.base = (char*) &MESSAGE; buf.len = sizeof MESSAGE; - uv_req_init(&write_req, req->handle, (void *(*)(void *))write_cb); - - if (uv_write(&write_req, &buf, 1)) { + if (uv_write(&write_req, (uv_stream_t*)req->handle, &buf, 1, write_cb)) { FATAL("uv_write failed"); } @@ -182,24 +176,20 @@ static void connect_cb(uv_req_t* req, int status) { TEST_IMPL(callback_stack) { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); - uv_init(); - - if (uv_tcp_init(&client)) { + if (uv_tcp_init(uv_default_loop(), &client)) { FATAL("uv_tcp_init failed"); } puts("Connecting..."); nested++; - uv_req_init(&connect_req, (uv_handle_t*)&client, - (void *(*)(void *))connect_cb); - if (uv_tcp_connect(&connect_req, addr)) { + if (uv_tcp_connect(&connect_req, &client, addr, connect_cb)) { FATAL("uv_tcp_connect failed"); } nested--; - uv_run(); + uv_run(uv_default_loop()); ASSERT(nested == 0); ASSERT(connect_cb_called == 1 && "connect_cb must be called exactly once"); diff --git a/src/rt/libuv/test/test-connection-fail.c b/src/rt/libuv/test/test-connection-fail.c index 9fc3f0ba6eb..2762aa285a9 100644 --- a/src/rt/libuv/test/test-connection-fail.c +++ b/src/rt/libuv/test/test-connection-fail.c @@ -27,7 +27,7 @@ static uv_tcp_t tcp; -static uv_req_t req; +static uv_connect_t req; static int connect_cb_calls; static int close_cb_calls; @@ -66,20 +66,20 @@ static void timer_cb(uv_timer_t* handle, int status) { } -static void on_connect_with_close(uv_req_t *req, int status) { - ASSERT(&tcp == (uv_tcp_t*) req->handle); +static void on_connect_with_close(uv_connect_t *req, int status) { + ASSERT((uv_stream_t*) &tcp == req->handle); ASSERT(status == -1); - ASSERT(uv_last_error().code == UV_ECONNREFUSED); + ASSERT(uv_last_error(uv_default_loop()).code == UV_ECONNREFUSED); connect_cb_calls++; ASSERT(close_cb_calls == 0); - uv_close(req->handle, on_close); + uv_close((uv_handle_t*)req->handle, on_close); } -static void on_connect_without_close(uv_req_t *req, int status) { +static void on_connect_without_close(uv_connect_t *req, int status) { ASSERT(status == -1); - ASSERT(uv_last_error().code == UV_ECONNREFUSED); + ASSERT(uv_last_error(uv_default_loop()).code == UV_ECONNREFUSED); connect_cb_calls++; uv_timer_start(&timer, timer_cb, 100, 0); @@ -98,18 +98,16 @@ void connection_fail(uv_connect_cb connect_cb) { server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&tcp); + r = uv_tcp_init(uv_default_loop(), &tcp); ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ - uv_req_init(&req, (uv_handle_t*)&tcp, (void *(*)(void *))connect_cb); - uv_tcp_bind(&tcp, client_addr); - r = uv_tcp_connect(&req, server_addr); + r = uv_tcp_connect(&req, &tcp, server_addr, connect_cb); ASSERT(!r); - uv_run(); + uv_run(uv_default_loop()); ASSERT(connect_cb_calls == 1); ASSERT(close_cb_calls == 1); @@ -121,8 +119,6 @@ void connection_fail(uv_connect_cb connect_cb) { * expect an error. */ TEST_IMPL(connection_fail) { - uv_init(); - connection_fail(on_connect_with_close); ASSERT(timer_close_cb_calls == 0); @@ -138,9 +134,10 @@ TEST_IMPL(connection_fail) { * attempt. */ TEST_IMPL(connection_fail_doesnt_auto_close) { - uv_init(); + int r; - uv_timer_init(&timer); + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); connection_fail(on_connect_without_close); diff --git a/src/rt/libuv/test/test-delayed-accept.c b/src/rt/libuv/test/test-delayed-accept.c index eabf4818959..78531f68067 100644 --- a/src/rt/libuv/test/test-delayed-accept.c +++ b/src/rt/libuv/test/test-delayed-accept.c @@ -24,15 +24,13 @@ #include #include -static char BUFFER[1024]; - static int connection_cb_called = 0; static int do_accept_called = 0; static int close_cb_called = 0; static int connect_cb_called = 0; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -52,43 +50,41 @@ static void close_cb(uv_handle_t* handle) { static void do_accept(uv_timer_t* timer_handle, int status) { uv_tcp_t* server; uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle); + uint64_t tcpcnt; int r; - int tcpcnt; ASSERT(timer_handle != NULL); ASSERT(status == 0); ASSERT(accepted_handle != NULL); - uv_tcp_init(accepted_handle); - - /* Test to that uv_counters()->tcp_init does not increase across the uv_accept. */ - tcpcnt = uv_counters()->tcp_init; - - server = (uv_tcp_t*)timer_handle->data; - r = uv_accept((uv_handle_t*)server, (uv_stream_t*)accepted_handle); + r = uv_tcp_init(uv_default_loop(), accepted_handle); ASSERT(r == 0); - ASSERT(uv_counters()->tcp_init == tcpcnt); + /* Test to that uv_default_loop()->counters.tcp_init does not increase across the uv_accept. */ + tcpcnt = uv_default_loop()->counters.tcp_init; + + server = (uv_tcp_t*)timer_handle->data; + r = uv_accept((uv_stream_t*)server, (uv_stream_t*)accepted_handle); + ASSERT(r == 0); + + ASSERT(uv_default_loop()->counters.tcp_init == tcpcnt); do_accept_called++; /* Immediately close the accepted handle. */ - r = uv_close((uv_handle_t*)accepted_handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)accepted_handle, close_cb); /* After accepting the two clients close the server handle */ if (do_accept_called == 2) { - r = uv_close((uv_handle_t*)server, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)server, close_cb); } /* Dispose the timer. */ - r = uv_close((uv_handle_t*)timer_handle, close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)timer_handle, close_cb); } -static void connection_cb(uv_handle_t* tcp, int status) { +static void connection_cb(uv_stream_t* tcp, int status) { int r; uv_timer_t* timer_handle; @@ -98,7 +94,7 @@ static void connection_cb(uv_handle_t* tcp, int status) { ASSERT(timer_handle != NULL); /* Accept the client after 1 second */ - r = uv_timer_init(timer_handle); + r = uv_timer_init(uv_default_loop(), timer_handle); ASSERT(r == 0); timer_handle->data = tcp; @@ -117,15 +113,15 @@ static void start_server() { ASSERT(server != NULL); - r = uv_tcp_init(server); + r = uv_tcp_init(uv_default_loop(), server); ASSERT(r == 0); - ASSERT(uv_counters()->tcp_init == 1); - ASSERT(uv_counters()->handle_init == 1); + ASSERT(uv_default_loop()->counters.tcp_init == 1); + ASSERT(uv_default_loop()->counters.handle_init == 1); r = uv_tcp_bind(server, addr); ASSERT(r == 0); - r = uv_tcp_listen(server, 128, connection_cb); + r = uv_listen((uv_stream_t*)server, 128, connection_cb); ASSERT(r == 0); } @@ -139,17 +135,17 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { if (nread != -1) { ASSERT(nread == 0); - ASSERT(uv_last_error().code == UV_EAGAIN); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EAGAIN); } else { ASSERT(tcp != NULL); ASSERT(nread == -1); - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); uv_close((uv_handle_t*)tcp, close_cb); } } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { int r; ASSERT(req != NULL); @@ -169,31 +165,28 @@ static void connect_cb(uv_req_t* req, int status) { static void client_connect() { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof *client); - uv_req_t* connect_req = (uv_req_t*)malloc(sizeof *connect_req); + uv_connect_t* connect_req = malloc(sizeof *connect_req); int r; ASSERT(client != NULL); ASSERT(connect_req != NULL); - r = uv_tcp_init(client); + r = uv_tcp_init(uv_default_loop(), client); ASSERT(r == 0); - uv_req_init(connect_req, (uv_handle_t*)client, (void *(*)(void *))connect_cb); - r = uv_tcp_connect(connect_req, addr); + r = uv_tcp_connect(connect_req, client, addr, connect_cb); ASSERT(r == 0); } TEST_IMPL(delayed_accept) { - uv_init(); - start_server(); client_connect(); client_connect(); - uv_run(); + uv_run(uv_default_loop()); ASSERT(connection_cb_called == 2); ASSERT(do_accept_called == 2); diff --git a/src/rt/libuv/test/test-fs-event.c b/src/rt/libuv/test/test-fs-event.c new file mode 100644 index 00000000000..d249ec6692c --- /dev/null +++ b/src/rt/libuv/test/test-fs-event.c @@ -0,0 +1,217 @@ +/* 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 "task.h" + +#include +#include + +uv_fs_event_t fs_event; +uv_timer_t timer; +int timer_cb_called; +int close_cb_called; +int fs_event_cb_called; + +static void create_dir(uv_loop_t* loop, const char* name) { + int r; + uv_fs_t req; + r = uv_fs_mkdir(loop, &req, name, 0755, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void create_file(uv_loop_t* loop, const char* name) { + int r; + uv_file file; + uv_fs_t req; + + r = uv_fs_open(loop, &req, name, O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + file = r; + uv_fs_req_cleanup(&req); + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&req); +} + +static void touch_file(uv_loop_t* loop, const char* name) { + int r; + uv_file file; + uv_fs_t req; + + r = uv_fs_open(loop, &req, name, O_RDWR, 0, NULL); + ASSERT(r != -1); + file = r; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, "foo", 4, -1, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&req); + + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&req); +} + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle != NULL); + close_cb_called++; +} + +static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_RENAME); + ASSERT(strcmp(filename, "file1") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, + int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + ASSERT(strcmp(filename, "file2") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, + const char* filename, int events, int status) { + ++fs_event_cb_called; + ASSERT(handle == &fs_event); + ASSERT(status == 0); + ASSERT(events == UV_CHANGE); + ASSERT(strcmp(filename, "watch_file") == 0); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void timer_cb_dir(uv_timer_t* handle, int status) { + ++timer_cb_called; + create_file(handle->loop, "watch_dir/file1"); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void timer_cb_file(uv_timer_t* handle, int status) { + ++timer_cb_called; + + if (timer_cb_called == 1) { + touch_file(handle->loop, "watch_dir/file1"); + } else { + touch_file(handle->loop, "watch_dir/file2"); + uv_close((uv_handle_t*)handle, close_cb); + } +} + +TEST_IMPL(fs_event_watch_dir) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + create_dir(loop, "watch_dir"); + + r = uv_fs_event_init(loop, &fs_event, "watch_dir", fs_event_cb_dir); + ASSERT(r != -1); + r = uv_timer_init(loop, &timer); + ASSERT(r != -1); + r = uv_timer_start(&timer, timer_cb_dir, 100, 0); + ASSERT(r != -1); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + r = uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + + return 0; +} + +TEST_IMPL(fs_event_watch_file) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + create_dir(loop, "watch_dir"); + create_file(loop, "watch_dir/file1"); + create_file(loop, "watch_dir/file2"); + + r = uv_fs_event_init(loop, &fs_event, "watch_dir/file2", fs_event_cb_file); + ASSERT(r != -1); + r = uv_timer_init(loop, &timer); + ASSERT(r != -1); + r = uv_timer_start(&timer, timer_cb_file, 100, 100); + ASSERT(r != -1); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(timer_cb_called == 2); + ASSERT(close_cb_called == 2); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file1", NULL); + r = uv_fs_unlink(loop, &fs_req, "watch_dir/file2", NULL); + r = uv_fs_rmdir(loop, &fs_req, "watch_dir", NULL); + + return 0; +} + +TEST_IMPL(fs_event_watch_file_current_dir) { + uv_fs_t fs_req; + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + uv_fs_unlink(loop, &fs_req, "watch_file", NULL); + create_file(loop, "watch_file"); + + r = uv_fs_event_init(loop, &fs_event, "watch_file", + fs_event_cb_file_current_dir); + ASSERT(r != -1); + + touch_file(loop, "watch_file"); + + uv_run(loop); + + ASSERT(fs_event_cb_called == 1); + ASSERT(close_cb_called == 1); + + /* Cleanup */ + r = uv_fs_unlink(loop, &fs_req, "watch_file", NULL); + return 0; +} diff --git a/src/rt/libuv/test/test-fs.c b/src/rt/libuv/test/test-fs.c new file mode 100644 index 00000000000..fbe0396bf84 --- /dev/null +++ b/src/rt/libuv/test/test-fs.c @@ -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. + */ + +/* FIXME we shouldnt need to branch in this file */ +#define UNIX (defined(__unix__) || defined(__POSIX__) || defined(__APPLE__)) + +#include "uv.h" +#include "task.h" + +#include +#include /* memset */ +#include +#include + + +#if UNIX +#include /* unlink, rmdir, etc. */ +#else +# include +# include +# define unlink _unlink +# define rmdir _rmdir +# define stat _stati64 +# define open _open +# define write _write +# define lseek _lseek +# define close _close +#endif + + +typedef struct { + const char* path; + double atime; + double mtime; +} utime_check_t; + + +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 rmdir_cb_count; +static int readdir_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 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 rmdir_req; +static uv_fs_t readdir_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 test_buf[] = "test-buffer\n"; + + +void check_permission(const char* filename, int mode) { + int r; + uv_fs_t req; + struct stat* s; + + r = uv_fs_stat(uv_default_loop(), &req, filename, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + + s = req.ptr; +#ifdef _WIN32 + /* + * On Windows, chmod can only modify S_IWUSR (_S_IWRITE) bit, + * so only testing for the specified flags. + */ + ASSERT((s->st_mode & 0777) & mode); +#else + ASSERT((s->st_mode & 0777) == mode); +#endif + + uv_fs_req_cleanup(&req); +} + + +static void link_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_LINK); + ASSERT(req->result == 0); + link_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void symlink_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_SYMLINK); + ASSERT(req->result == 0); + symlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void readlink_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_READLINK); + ASSERT(req->result == 0); + ASSERT(strcmp(req->ptr, "test_file_symlink2") == 0); + readlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void fchmod_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_FCHMOD); + ASSERT(req->result == 0); + fchmod_cb_count++; + uv_fs_req_cleanup(req); + check_permission("test_file", (int)req->data); +} + + +static void chmod_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_CHMOD); + ASSERT(req->result == 0); + chmod_cb_count++; + uv_fs_req_cleanup(req); + check_permission("test_file", (int)req->data); +} + + +static void fchown_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_FCHOWN); + ASSERT(req->result == 0); + fchown_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void chown_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_CHOWN); + ASSERT(req->result == 0); + chown_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void unlink_cb(uv_fs_t* req) { + ASSERT(req == &unlink_req); + ASSERT(req->fs_type == UV_FS_UNLINK); + ASSERT(req->result != -1); + unlink_cb_count++; + uv_fs_req_cleanup(req); +} + +static void fstat_cb(uv_fs_t* req) { + struct stat* s = req->ptr; + ASSERT(req->fs_type == UV_FS_FSTAT); + ASSERT(req->result == 0); + 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; + ASSERT(req == &close_req); + ASSERT(req->fs_type == UV_FS_CLOSE); + ASSERT(req->result != -1); + close_cb_count++; + uv_fs_req_cleanup(req); + if (close_cb_count == 3) { + r = uv_fs_unlink(loop, &unlink_req, "test_file2", unlink_cb); + } +} + + +static void ftruncate_cb(uv_fs_t* req) { + int r; + ASSERT(req == &ftruncate_req); + ASSERT(req->fs_type == UV_FS_FTRUNCATE); + ASSERT(req->result != -1); + ftruncate_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); +} + + +static void read_cb(uv_fs_t* req) { + int r; + ASSERT(req == &read_req); + ASSERT(req->fs_type == UV_FS_READ); + ASSERT(req->result != -1); + read_cb_count++; + uv_fs_req_cleanup(req); + if (read_cb_count == 1) { + ASSERT(strcmp(buf, test_buf) == 0); + r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, + ftruncate_cb); + } else { + ASSERT(strcmp(buf, "test-bu") == 0); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); + } +} + + +static void open_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + if (req->result < 0) { + /* TODO get error with uv_last_error() */ + fprintf(stderr, "async open error: %d\n", req->errorno); + ASSERT(0); + } + open_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_file2\0", 11) == 0); + uv_fs_req_cleanup(req); + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + read_cb); +} + + +static void fsync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fsync_req); + ASSERT(req->fs_type == UV_FS_FSYNC); + ASSERT(req->result != -1); + fsync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); +} + + +static void fdatasync_cb(uv_fs_t* req) { + int r; + ASSERT(req == &fdatasync_req); + ASSERT(req->fs_type == UV_FS_FDATASYNC); + ASSERT(req->result != -1); + fdatasync_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fsync(loop, &fsync_req, open_req1.result, fsync_cb); +} + + +static void write_cb(uv_fs_t* req) { + int r; + ASSERT(req == &write_req); + ASSERT(req->fs_type == UV_FS_WRITE); + ASSERT(req->result != -1); + write_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb); +} + + +static void create_cb(uv_fs_t* req) { + int r; + ASSERT(req == &open_req1); + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->result != -1); + create_cb_count++; + uv_fs_req_cleanup(req); + r = uv_fs_write(loop, &write_req, req->result, test_buf, sizeof(test_buf), + -1, write_cb); +} + + +static void rename_cb(uv_fs_t* req) { + ASSERT(req == &rename_req); + ASSERT(req->fs_type == UV_FS_RENAME); + ASSERT(req->result != -1); + rename_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void mkdir_cb(uv_fs_t* req) { + ASSERT(req == &mkdir_req); + ASSERT(req->fs_type == UV_FS_MKDIR); + ASSERT(req->result != -1); + mkdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); +} + + +static void rmdir_cb(uv_fs_t* req) { + ASSERT(req == &rmdir_req); + ASSERT(req->fs_type == UV_FS_RMDIR); + ASSERT(req->result != -1); + rmdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); +} + + +static void readdir_cb(uv_fs_t* req) { + ASSERT(req == &readdir_req); + ASSERT(req->fs_type == UV_FS_READDIR); + ASSERT(req->result == 2); + ASSERT(req->ptr); + ASSERT(memcmp(req->ptr, "file1\0file2\0", 12) == 0 + || memcmp(req->ptr, "file2\0file1\0", 12) == 0); + readdir_cb_count++; + ASSERT(req->path); + ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void stat_cb(uv_fs_t* req) { + ASSERT(req == &stat_req); + ASSERT(req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT); + ASSERT(req->result != -1); + ASSERT(req->ptr); + stat_cb_count++; + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + + +static void sendfile_cb(uv_fs_t* req) { + ASSERT(req == &sendfile_req); + ASSERT(req->fs_type == UV_FS_SENDFILE); + ASSERT(req->result == 65546); + sendfile_cb_count++; + uv_fs_req_cleanup(req); +} + + +static void open_noent_cb(uv_fs_t* req) { + ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT(req->errorno == UV_ENOENT); + ASSERT(req->result == -1); + open_cb_count++; + uv_fs_req_cleanup(req); +} + + +TEST_IMPL(fs_file_noent) { + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "does_not_exist", O_RDONLY, 0, NULL); + ASSERT(r == -1); + ASSERT(req.result == -1); + ASSERT(uv_last_error(loop).code == UV_ENOENT); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "does_not_exist", O_RDONLY, 0, open_noent_cb); + ASSERT(r == 0); + + ASSERT(open_cb_count == 0); + uv_run(loop); + ASSERT(open_cb_count == 1); + + /* TODO add EACCES test */ + + return 0; +} + + +static void check_utime(const char* path, double atime, double mtime) { + struct stat* s; + uv_fs_t req; + int r; + + r = uv_fs_stat(loop, &req, path, NULL); + ASSERT(r == 0); + + ASSERT(req.result == 0); + s = req.ptr; + +#if _WIN32 + ASSERT(s->st_atime == atime); + ASSERT(s->st_mtime == mtime); +#elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) + ASSERT(s->st_atimespec.tv_sec == atime); + ASSERT(s->st_mtimespec.tv_sec == mtime); +#else + ASSERT(s->st_atim.tv_sec == atime); + ASSERT(s->st_mtim.tv_sec == mtime); +#endif + + uv_fs_req_cleanup(&req); +} + + +static void utime_cb(uv_fs_t* req) { + utime_check_t* c; + + ASSERT(req == &utime_req); + ASSERT(req->result == 0); + ASSERT(req->fs_type == UV_FS_UTIME); + + c = 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; + + ASSERT(req == &futime_req); + ASSERT(req->result == 0); + ASSERT(req->fs_type == UV_FS_FUTIME); + + c = req->data; + check_utime(c->path, c->atime, c->mtime); + + uv_fs_req_cleanup(req); + futime_cb_count++; +} + + +TEST_IMPL(fs_file_async) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IREAD | S_IWRITE, create_cb); + ASSERT(r == 0); + uv_run(loop); + + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(fsync_cb_count == 1); + ASSERT(fdatasync_cb_count == 1); + ASSERT(close_cb_count == 1); + + r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", rename_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(close_cb_count == 1); + ASSERT(rename_cb_count == 1); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDWR, 0, open_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(open_cb_count == 1); + ASSERT(read_cb_count == 1); + ASSERT(close_cb_count == 2); + ASSERT(rename_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, open_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(open_cb_count == 2); + ASSERT(read_cb_count == 2); + ASSERT(close_cb_count == 3); + ASSERT(rename_cb_count == 1); + ASSERT(unlink_cb_count == 1); + ASSERT(create_cb_count == 1); + ASSERT(write_cb_count == 1); + ASSERT(ftruncate_cb_count == 1); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_file_sync) { + int r; + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_write(loop, &write_req, open_req1.result, test_buf, + sizeof(test_buf), -1, NULL); + ASSERT(r != -1); + ASSERT(write_req.result != -1); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(loop, &open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + NULL); + ASSERT(r != -1); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, NULL); + ASSERT(r != -1); + ASSERT(ftruncate_req.result != -1); + uv_fs_req_cleanup(&ftruncate_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", NULL); + ASSERT(r != -1); + ASSERT(rename_req.result != -1); + uv_fs_req_cleanup(&rename_req); + + r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &read_req, open_req1.result, buf, sizeof(buf), -1, + NULL); + ASSERT(r != -1); + ASSERT(read_req.result != -1); + ASSERT(strcmp(buf, "test-bu") == 0); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r != -1); + ASSERT(close_req.result != -1); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_unlink(loop, &unlink_req, "test_file2", NULL); + ASSERT(r != -1); + ASSERT(unlink_req.result != -1); + uv_fs_req_cleanup(&unlink_req); + + /* Cleanup */ + unlink("test_file"); + unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_async_dir) { + int r; + + /* Setup */ + 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); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(mkdir_cb_count == 1); + + /* Create 2 files synchronously. */ + r = uv_fs_open(loop, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_open(loop, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + r = uv_fs_readdir(loop, &readdir_req, "test_dir", 0, readdir_cb); + ASSERT(r == 0); + + uv_run(loop); + ASSERT(readdir_cb_count == 1); + + /* sync uv_fs_readdir */ + r = uv_fs_readdir(loop, &readdir_req, "test_dir", 0, NULL); + ASSERT(r == 2); + ASSERT(readdir_req.result == 2); + ASSERT(readdir_req.ptr); + ASSERT(memcmp(readdir_req.ptr, "file1\0file2\0", 12) == 0 + || memcmp(readdir_req.ptr, "file2\0file1\0", 12) == 0); + uv_fs_req_cleanup(&readdir_req); + ASSERT(!readdir_req.ptr); + + r = uv_fs_stat(loop, &stat_req, "test_dir", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + r = uv_fs_stat(loop, &stat_req, "test_dir\\", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + r = uv_fs_lstat(loop, &stat_req, "test_dir", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + r = uv_fs_lstat(loop, &stat_req, "test_dir\\", stat_cb); + ASSERT(r == 0); + uv_run(loop); + + ASSERT(stat_cb_count == 4); + + r = uv_fs_unlink(loop, &unlink_req, "test_dir/file1", unlink_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(unlink_cb_count == 1); + + r = uv_fs_unlink(loop, &unlink_req, "test_dir/file2", unlink_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(unlink_cb_count == 2); + + r = uv_fs_rmdir(loop, &rmdir_req, "test_dir", rmdir_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(rmdir_cb_count == 1); + + /* Cleanup */ + unlink("test_dir/file1"); + unlink("test_dir/file2"); + rmdir("test_dir"); + + return 0; +} + + +TEST_IMPL(fs_async_sendfile) { + int f, r; + struct stat s1, s2; + + loop = uv_default_loop(); + + /* Setup. */ + unlink("test_file"); + unlink("test_file2"); + + f = open("test_file", O_WRONLY | O_CREAT, S_IWRITE | S_IREAD); + ASSERT(f != -1); + + r = write(f, "begin\n", 6); + ASSERT(r == 6); + + r = lseek(f, 65536, SEEK_CUR); + ASSERT(r == 65542); + + r = write(f, "end\n", 4); + ASSERT(r != -1); + + r = close(f); + ASSERT(r == 0); + + /* Test starts here. */ + r = uv_fs_open(loop, &open_req1, "test_file", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(open_req1.result != -1); + uv_fs_req_cleanup(&open_req1); + + r = uv_fs_open(loop, &open_req2, "test_file2", O_WRONLY | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(open_req2.result != -1); + uv_fs_req_cleanup(&open_req2); + + r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, + 0, 131072, sendfile_cb); + ASSERT(r == 0); + uv_run(loop); + + ASSERT(sendfile_cb_count == 1); + + r = uv_fs_close(loop, &close_req, open_req1.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + r = uv_fs_close(loop, &close_req, open_req2.result, NULL); + ASSERT(r == 0); + uv_fs_req_cleanup(&close_req); + + stat("test_file", &s1); + stat("test_file2", &s2); + ASSERT(65546 == s2.st_size && s1.st_size == s2.st_size); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file2"); + + return 0; +} + + +TEST_IMPL(fs_fstat) { + int r; + uv_fs_t req; + uv_file file; + struct stat* s; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + r = uv_fs_fstat(loop, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + s = req.ptr; + ASSERT(s->st_size == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + /* Now do the uv_fs_fstat call asynchronously */ + r = uv_fs_fstat(loop, &req, file, fstat_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(fstat_cb_count == 1); + + + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r == 0); + 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); + + /* Cleanup. */ + unlink("test_file"); + + return 0; +} + + +TEST_IMPL(fs_chmod) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + +#ifndef _WIN32 + /* Make the file write-only */ + r = uv_fs_chmod(loop, &req, "test_file", 0200, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0200); +#endif + + /* Make the file read-only */ + r = uv_fs_chmod(loop, &req, "test_file", 0400, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0400); + + /* Make the file read+write with sync uv_fs_fchmod */ + r = uv_fs_fchmod(loop, &req, file, 0600, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + check_permission("test_file", 0600); + +#ifndef _WIN32 + /* async chmod */ + req.data = (void*)0200; + r = uv_fs_chmod(loop, &req, "test_file", 0200, chmod_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(chmod_cb_count == 1); + chmod_cb_count = 0; /* reset for the next test */ +#endif + + /* async chmod */ + req.data = (void*)0400; + r = uv_fs_chmod(loop, &req, "test_file", 0400, chmod_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(chmod_cb_count == 1); + + /* async fchmod */ + req.data = (void*)0600; + r = uv_fs_fchmod(loop, &req, file, 0600, fchmod_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(fchmod_cb_count == 1); + + close(file); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + + return 0; +} + + +TEST_IMPL(fs_chown) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + /* sync chown */ + r = uv_fs_chown(loop, &req, "test_file", -1, -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* sync fchown */ + r = uv_fs_fchown(loop, &req, file, -1, -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* async chown */ + r = uv_fs_chown(loop, &req, "test_file", -1, -1, chown_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(chown_cb_count == 1); + + /* async fchown */ + r = uv_fs_fchown(loop, &req, file, -1, -1, fchown_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(fchown_cb_count == 1); + + close(file); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + + return 0; +} + + +TEST_IMPL(fs_link) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync link */ + r = uv_fs_link(loop, &req, "test_file", "test_file_link", NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "test_file_link", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* async link */ + r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(link_cb_count == 1); + + r = uv_fs_open(loop, &req, "test_file_link2", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_link"); + unlink("test_file_link2"); + + return 0; +} + + +TEST_IMPL(fs_symlink) { + int r; + uv_fs_t req; + uv_file file; + uv_file link; + + /* Setup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink2"); + unlink("test_file_symlink_symlink"); + unlink("test_file_symlink2_symlink"); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, + S_IWRITE | S_IREAD, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == sizeof(test_buf)); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + close(file); + + /* sync symlink */ + r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink", 0, NULL); +#ifdef _WIN32 + if (r == -1) { + if (req.errorno == ENOSYS) { + /* + * Windows doesn't support symlinks on older versions. + * We just pass the test and bail out early if we get ENOTSUP. + */ + return 0; + } else if (uv_last_error(loop).sys_errno_ == ERROR_PRIVILEGE_NOT_HELD) { + /* + * Creating a symlink is only allowed when running elevated. + * We pass the test and bail out early if we get ERROR_PRIVILEGE_NOT_HELD. + */ + return 0; + } + } +#endif + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_open(loop, &req, "test_file_symlink", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + r = uv_fs_symlink(loop, &req, "test_file_symlink", "test_file_symlink_symlink", 0, NULL); + ASSERT(r != -1); + r = uv_fs_readlink(loop, &req, "test_file_symlink_symlink", NULL); + ASSERT(r != -1); + ASSERT(strcmp(req.ptr, "test_file_symlink") == 0); + uv_fs_req_cleanup(&req); + + /* async link */ + r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink2", 0, symlink_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(symlink_cb_count == 1); + + r = uv_fs_open(loop, &req, "test_file_symlink2", O_RDWR, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + link = req.result; + uv_fs_req_cleanup(&req); + + memset(buf, 0, sizeof(buf)); + r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + ASSERT(strcmp(buf, test_buf) == 0); + + close(link); + + r = uv_fs_symlink(loop, &req, "test_file_symlink2", "test_file_symlink2_symlink", 0, NULL); + ASSERT(r != -1); + r = uv_fs_readlink(loop, &req, "test_file_symlink2_symlink", readlink_cb); + ASSERT(r != -1); + uv_run(loop); + ASSERT(readlink_cb_count == 1); + + /* + * Run the loop just to check we don't have make any extraneous uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + unlink("test_file_symlink"); + unlink("test_file_symlink_symlink"); + unlink("test_file_symlink2"); + unlink("test_file_symlink2_symlink"); + + return 0; +} + + +TEST_IMPL(fs_utime) { + utime_check_t checkme; + const char* path = "."; + double atime; + double mtime; + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ + + r = uv_fs_utime(loop, &req, path, atime, mtime, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_stat(loop, &req, path, NULL); + ASSERT(r == 0); + 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); + ASSERT(r == 0); + uv_run(loop); + ASSERT(utime_cb_count == 1); + + return 0; +} + + +TEST_IMPL(fs_futime) { + utime_check_t checkme; + const char* path = "."; + double atime; + double mtime; + uv_file file; + uv_fs_t req; + int r; + + loop = uv_default_loop(); + + atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ + + r = uv_fs_open(loop, &req, path, O_RDONLY, 0, NULL); + ASSERT(r != -1); + ASSERT(req.result != -1); + 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); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + r = uv_fs_stat(loop, &req, path, NULL); + ASSERT(r == 0); + 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); + ASSERT(r == 0); + uv_run(loop); + ASSERT(futime_cb_count == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-get-currentexe.c b/src/rt/libuv/test/test-get-currentexe.c index 1dc85565590..9dd267691c0 100644 --- a/src/rt/libuv/test/test-get-currentexe.c +++ b/src/rt/libuv/test/test-get-currentexe.c @@ -30,15 +30,25 @@ TEST_IMPL(get_currentexe) { char buffer[PATHMAX]; size_t size; char* match; + char* path; int r; size = sizeof(buffer) / sizeof(buffer[0]); r = uv_exepath(buffer, &size); ASSERT(!r); - match = strstr(buffer, executable_path); + /* uv_exepath can return an absolute path on darwin, so if the test runner + * was run with a relative prefix of "./", we need to strip that prefix off + * executable_path or we'll fail. */ + if (executable_path[0] == '.' && executable_path[1] == '/') { + path = executable_path + 2; + } else { + path = executable_path; + } + + match = strstr(buffer, path); /* Verify that the path returned from uv_exepath is a subdirectory of executable_path */ - ASSERT(match && !strcmp(match, executable_path)); + ASSERT(match && !strcmp(match, path)); ASSERT(size == strlen(buffer)); /* Negative tests */ diff --git a/src/rt/libuv/test/test-getaddrinfo.c b/src/rt/libuv/test/test-getaddrinfo.c index a33e3d075cc..2a8c94e7552 100644 --- a/src/rt/libuv/test/test-getaddrinfo.c +++ b/src/rt/libuv/test/test-getaddrinfo.c @@ -31,10 +31,10 @@ static const char* name = "localhost"; -static uv_getaddrinfo_t getaddrinfo_handle; static int getaddrinfo_cbs = 0; /* data used for running multiple calls concurrently */ +static uv_getaddrinfo_t* getaddrinfo_handle; static uv_getaddrinfo_t getaddrinfo_handles[CONCURRENT_COUNT]; static int callback_counts[CONCURRENT_COUNT]; @@ -42,8 +42,10 @@ static int callback_counts[CONCURRENT_COUNT]; static void getaddrinfo_basic_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { - ASSERT(handle == &getaddrinfo_handle); + ASSERT(handle == getaddrinfo_handle); getaddrinfo_cbs++; + free(handle); + uv_freeaddrinfo(res); } @@ -51,32 +53,38 @@ 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) { + ASSERT(i == *data); + callback_counts[i]++; break; } } ASSERT (i < CONCURRENT_COUNT); + free(data); + uv_freeaddrinfo(res); + getaddrinfo_cbs++; } TEST_IMPL(getaddrinfo_basic) { int r; + getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t)); - uv_init(); - - r = uv_getaddrinfo(&getaddrinfo_handle, + r = uv_getaddrinfo(uv_default_loop(), + getaddrinfo_handle, &getaddrinfo_basic_cb, name, NULL, NULL); ASSERT(r == 0); - uv_run(); + uv_run(uv_default_loop()); ASSERT(getaddrinfo_cbs == 1); @@ -86,21 +94,25 @@ TEST_IMPL(getaddrinfo_basic) { TEST_IMPL(getaddrinfo_concurrent) { int i, r; - - uv_init(); + int* data; for (i = 0; i < CONCURRENT_COUNT; i++) { callback_counts[i] = 0; - r = uv_getaddrinfo(&getaddrinfo_handles[i], - &getaddrinfo_cuncurrent_cb, - name, - NULL, - NULL); + data = (int*)malloc(sizeof(int)); + *data = i; + getaddrinfo_handles[i].data = data; + + r = uv_getaddrinfo(uv_default_loop(), + &getaddrinfo_handles[i], + &getaddrinfo_cuncurrent_cb, + name, + NULL, + NULL); ASSERT(r == 0); } - uv_run(); + uv_run(uv_default_loop()); for (i = 0; i < CONCURRENT_COUNT; i++) { ASSERT(callback_counts[i] == 1); diff --git a/src/rt/libuv/test/test-gethostbyname.c b/src/rt/libuv/test/test-gethostbyname.c index cbd25343bbb..583622e7245 100644 --- a/src/rt/libuv/test/test-gethostbyname.c +++ b/src/rt/libuv/test/test-gethostbyname.c @@ -74,8 +74,7 @@ static void prep_tcploopback() { options.tcp_port = htons(TEST_PORT); options.flags = ARES_FLAG_USEVC; - rc = uv_ares_init_options(&channel, &options, optmask); - + rc = uv_ares_init_options(uv_default_loop(), &channel, &options, optmask); ASSERT(rc == ARES_SUCCESS); } @@ -91,8 +90,6 @@ TEST_IMPL(gethostbyname) { return 1; } - uv_init(); - printf("Start basic gethostbyname test\n"); prep_tcploopback(); @@ -104,11 +101,11 @@ TEST_IMPL(gethostbyname) { AF_INET, &aresbynamecallback, &bynamecallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_bynamecallbacks == 1); - uv_ares_destroy(channel); + uv_ares_destroy(uv_default_loop(), channel); printf("Done basic gethostbyname test\n"); @@ -125,7 +122,7 @@ TEST_IMPL(gethostbyname) { AF_INET, &aresbynamecallback, &bynamecallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_bynamecallbacks == 1); @@ -143,11 +140,11 @@ TEST_IMPL(gethostbyname) { &aresbyaddrcallback, &byaddrcallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_byaddrcallbacks == 1); - uv_ares_destroy(channel); + uv_ares_destroy(uv_default_loop(), channel); printf("Done gethostbyname and gethostbyaddr sequential test\n"); @@ -179,13 +176,13 @@ TEST_IMPL(gethostbyname) { &aresbyaddrcallback, &byaddrcallbacksig); - uv_run(); + uv_run(uv_default_loop()); ASSERT(ares_bynamecallbacks == 1); ASSERT(ares_byaddrcallbacks == 1); - uv_ares_destroy(channel); + uv_ares_destroy(uv_default_loop(), channel); printf("Done gethostbyname and gethostbyaddr concurrent test\n"); return 0; diff --git a/src/rt/libuv/test/test-getsockname.c b/src/rt/libuv/test/test-getsockname.c new file mode 100644 index 00000000000..5dac88b73e4 --- /dev/null +++ b/src/rt/libuv/test/test-getsockname.c @@ -0,0 +1,342 @@ +/* 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 "task.h" + +#include +#include +#include + +static const int server_port = TEST_PORT; +/* Will be updated right after making the uv_connect_call */ +static int connect_port = -1; + +static int getsocknamecount = 0; +static int getpeernamecount = 0; + +static uv_loop_t* loop; +static uv_tcp_t tcp; +static uv_udp_t udp; +static uv_connect_t connect_req; +static uv_tcp_t tcpServer; +static uv_udp_t udpServer; +static uv_udp_send_t send_req; + + +static uv_buf_t alloc(uv_handle_t* handle, size_t suggested_size) { + uv_buf_t buf; + buf.base = (char*) malloc(suggested_size); + buf.len = suggested_size; + return buf; +} + + +static void on_close(uv_handle_t* peer) { + free(peer); + uv_close((uv_handle_t*)&tcpServer, NULL); +} + + +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, uv_buf_t buf) { + uv_shutdown_t* req; + int r; + + if (buf.base) { + free(buf.base); + } + + req = (uv_shutdown_t*) malloc(sizeof *req); + r = uv_shutdown(req, handle, after_shutdown); + ASSERT(r == 0); +} + + +static void check_sockname(struct sockaddr* addr, const char* compare_ip, + int compare_port, const char* context) { + struct sockaddr_in check_addr = *(struct sockaddr_in*) addr; + struct sockaddr_in compare_addr = uv_ip4_addr(compare_ip, compare_port); + char check_ip[17]; + int r; + + /* Both adresses should be ipv4 */ + ASSERT(check_addr.sin_family == AF_INET); + ASSERT(compare_addr.sin_family == AF_INET); + + /* Check if the ip matches */ + ASSERT(memcmp(&check_addr.sin_addr, + &compare_addr.sin_addr, + sizeof compare_addr.sin_addr) == 0); + + /* Check if the port matches. If port == 0 anything goes. */ + ASSERT(compare_port == 0 || check_addr.sin_port == compare_addr.sin_port); + + r = uv_ip4_name(&check_addr, (char*) check_ip, sizeof check_ip); + ASSERT(r == 0); + + printf("%s: %s:%d\n", context, check_ip, ntohs(check_addr.sin_port)); +} + + +static void on_connection(uv_stream_t* server, int status) { + struct sockaddr sockname, peername; + int namelen; + uv_handle_t* handle; + int r; + + if (status != 0) { + fprintf(stderr, "Connect error %d\n", + uv_last_error(loop).code); + } + ASSERT(status == 0); + + handle = (uv_handle_t*) malloc(sizeof(uv_tcp_t)); + ASSERT(handle != NULL); + + r = uv_tcp_init(loop, (uv_tcp_t*)handle); + ASSERT(r == 0); + + /* associate server with stream */ + handle->data = server; + + r = uv_accept(server, (uv_stream_t*)handle); + ASSERT(r == 0); + + namelen = sizeof sockname; + r = uv_tcp_getsockname((uv_tcp_t*) handle, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "127.0.0.1", server_port, "accepted socket"); + getsocknamecount++; + + namelen = sizeof peername; + r = uv_tcp_getpeername((uv_tcp_t*) handle, &peername, &namelen); + ASSERT(r == 0); + check_sockname(&peername, "127.0.0.1", connect_port, "accepted socket peer"); + getpeernamecount++; + + r = uv_read_start((uv_stream_t*)handle, alloc, after_read); + ASSERT(r == 0); +} + + +static void on_connect(uv_connect_t* req, int status) { + struct sockaddr sockname, peername; + int r, namelen; + + ASSERT(status == 0); + + namelen = sizeof sockname; + r = uv_tcp_getsockname((uv_tcp_t*) req->handle, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "127.0.0.1", 0, "connected socket"); + getsocknamecount++; + + namelen = sizeof peername; + r = uv_tcp_getpeername((uv_tcp_t*) req->handle, &peername, &namelen); + ASSERT(r == 0); + check_sockname(&peername, "127.0.0.1", server_port, "connected socket peer"); + getpeernamecount++; + + uv_close((uv_handle_t*)&tcp, NULL); +} + + +static int tcp_listener() { + struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", server_port); + struct sockaddr sockname, peername; + int namelen; + int r; + + r = uv_tcp_init(loop, &tcpServer); + if (r) { + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_tcp_bind(&tcpServer, addr); + if (r) { + fprintf(stderr, "Bind error\n"); + return 1; + } + + r = uv_listen((uv_stream_t*)&tcpServer, 128, on_connection); + if (r) { + fprintf(stderr, "Listen error\n"); + return 1; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof sockname; + r = uv_tcp_getsockname(&tcpServer, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", server_port, "server socket"); + getsocknamecount++; + + namelen = sizeof sockname; + r = uv_tcp_getpeername(&tcpServer, &peername, &namelen); + ASSERT(r == -1); + ASSERT(uv_last_error(loop).code == UV_ENOTCONN); + getpeernamecount++; + + return 0; +} + + +static void tcp_connector() { + struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", server_port); + struct sockaddr sockname; + int r, namelen; + + r = uv_tcp_init(loop, &tcp); + tcp.data = &connect_req; + ASSERT(!r); + + r = uv_tcp_connect(&connect_req, &tcp, server_addr, on_connect); + ASSERT(!r); + + /* Fetch the actual port used by the connecting socket. */ + namelen = sizeof sockname; + r = uv_tcp_getsockname(&tcp, &sockname, &namelen); + ASSERT(!r); + ASSERT(sockname.sa_family == AF_INET); + connect_port = ntohs(((struct sockaddr_in*) &sockname)->sin_port); + ASSERT(connect_port > 0); +} + + +static void udp_recv(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + struct sockaddr sockname; + int namelen; + int r; + + ASSERT(nread >= 0); + + if (nread == 0) { + free(buf.base); + return; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof(sockname); + r = uv_udp_getsockname(&udp, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", 0, "udp receiving socket"); + getsocknamecount++; + + uv_close((uv_handle_t*) &udp, NULL); + uv_close((uv_handle_t*) handle, NULL); +} + + +static void udp_send(uv_udp_send_t* req, int status) { + +} + + +static int udp_listener() { + struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", server_port); + struct sockaddr sockname; + int namelen; + int r; + + r = uv_udp_init(loop, &udpServer); + if (r) { + fprintf(stderr, "Socket creation error\n"); + return 1; + } + + r = uv_udp_bind(&udpServer, addr, 0); + if (r) { + fprintf(stderr, "Bind error\n"); + return 1; + } + + memset(&sockname, -1, sizeof sockname); + namelen = sizeof sockname; + r = uv_udp_getsockname(&udpServer, &sockname, &namelen); + ASSERT(r == 0); + check_sockname(&sockname, "0.0.0.0", server_port, "udp listener socket"); + getsocknamecount++; + + r = uv_udp_recv_start(&udpServer, alloc, udp_recv); + ASSERT(r == 0); + + return 0; +} + + +static void udp_sender(void) { + struct sockaddr_in server_addr; + uv_buf_t buf; + int r; + + r = uv_udp_init(loop, &udp); + ASSERT(!r); + + buf = uv_buf_init("PING", 4); + server_addr = uv_ip4_addr("127.0.0.1", server_port); + + r = uv_udp_send(&send_req, &udp, &buf, 1, server_addr, udp_send); + ASSERT(!r); +} + + +TEST_IMPL(getsockname_tcp) { + loop = uv_default_loop(); + + if (tcp_listener()) + return 1; + + tcp_connector(); + + uv_run(loop); + + ASSERT(getsocknamecount == 3); + ASSERT(getpeernamecount == 3); + + return 0; +} + + +TEST_IMPL(getsockname_udp) { + loop = uv_default_loop(); + + if (udp_listener()) + return 1; + + udp_sender(); + + uv_run(loop); + + ASSERT(getsocknamecount == 2); + + return 0; +} diff --git a/src/rt/libuv/test/test-hrtime.c b/src/rt/libuv/test/test-hrtime.c index 4d96e3331ec..566e0d2ee02 100644 --- a/src/rt/libuv/test/test-hrtime.c +++ b/src/rt/libuv/test/test-hrtime.c @@ -22,9 +22,8 @@ #include "uv.h" #include "task.h" - -#ifndef MICROSEC -# define MICROSEC 1000000 +#ifndef MILLISEC +# define MILLISEC 1000 #endif #ifndef NANOSEC @@ -32,11 +31,6 @@ #endif - -/* - * We expect the amount of time passed to be at least one us plus two system - * calls. Therefore checking that at least a microsecond has elapsed is safe. - */ TEST_IMPL(hrtime) { uint64_t a, b, diff; @@ -46,9 +40,12 @@ TEST_IMPL(hrtime) { diff = b - a; - printf("diff = %llu\n", diff); + printf("diff = %llu\n", (unsigned long long int) diff); - ASSERT(diff >= NANOSEC / MICROSEC); - ASSERT(diff > MICROSEC); + /* The windows Sleep() function has only a resolution of 10-20 ms. */ + /* Check that the difference between the two hrtime values is somewhat in */ + /* the range we expect it to be. */ + ASSERT(diff > (uint64_t) 80 * NANOSEC / MILLISEC); + ASSERT(diff < (uint64_t) 120 * NANOSEC / MILLISEC); return 0; } diff --git a/src/rt/libuv/test/test-idle.c b/src/rt/libuv/test/test-idle.c new file mode 100644 index 00000000000..95ef3a94959 --- /dev/null +++ b/src/rt/libuv/test/test-idle.c @@ -0,0 +1,81 @@ +/* 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 "task.h" + + +static uv_timer_t timer_handle; +static uv_idle_t idle_handle; + +static int idle_cb_called = 0; +static int timer_cb_called = 0; +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + + +static void timer_cb(uv_timer_t* handle, int status) { + ASSERT(handle == &timer_handle); + ASSERT(status == 0); + + uv_close((uv_handle_t*) &idle_handle, close_cb); + uv_close((uv_handle_t*) &timer_handle, close_cb); + + timer_cb_called++; + LOGF("timer_cb %d\n", timer_cb_called); +} + + +static void idle_cb(uv_idle_t* handle, int status) { + ASSERT(handle == &idle_handle); + ASSERT(status == 0); + + idle_cb_called++; + LOGF("idle_cb %d\n", idle_cb_called); +} + + +TEST_IMPL(idle_starvation) { + int r; + + r = uv_idle_init(uv_default_loop(), &idle_handle); + ASSERT(r == 0); + r = uv_idle_start(&idle_handle, idle_cb); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer_handle); + ASSERT(r == 0); + r = uv_timer_start(&timer_handle, timer_cb, 50, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(idle_cb_called > 0); + ASSERT(timer_cb_called == 1); + ASSERT(close_cb_called == 2); + + return 0; +} diff --git a/src/rt/libuv/test/test-list.h b/src/rt/libuv/test/test-list.h index 190574acaa8..bf376e39141 100644 --- a/src/rt/libuv/test/test-list.h +++ b/src/rt/libuv/test/test-list.h @@ -19,87 +19,152 @@ * IN THE SOFTWARE. */ -TEST_DECLARE (ping_pong) -TEST_DECLARE (ping_pong_v6) +TEST_DECLARE (tty) +TEST_DECLARE (tcp_ping_pong) +TEST_DECLARE (tcp_ping_pong_v6) +TEST_DECLARE (pipe_ping_pong) TEST_DECLARE (delayed_accept) TEST_DECLARE (tcp_writealot) -TEST_DECLARE (bind_error_addrinuse) -TEST_DECLARE (bind_error_addrnotavail_1) -TEST_DECLARE (bind_error_addrnotavail_2) -TEST_DECLARE (bind_error_fault) -TEST_DECLARE (bind_error_inval) -TEST_DECLARE (bind_localhost_ok) -TEST_DECLARE (bind6_error_addrinuse) -TEST_DECLARE (bind6_error_addrnotavail) -TEST_DECLARE (bind6_error_fault) -TEST_DECLARE (bind6_error_inval) -TEST_DECLARE (bind6_localhost_ok) +TEST_DECLARE (tcp_bind_error_addrinuse) +TEST_DECLARE (tcp_bind_error_addrnotavail_1) +TEST_DECLARE (tcp_bind_error_addrnotavail_2) +TEST_DECLARE (tcp_bind_error_fault) +TEST_DECLARE (tcp_bind_error_inval) +TEST_DECLARE (tcp_bind_localhost_ok) +TEST_DECLARE (tcp_listen_without_bind) +TEST_DECLARE (tcp_close) +TEST_DECLARE (tcp_write_error) +TEST_DECLARE (tcp_bind6_error_addrinuse) +TEST_DECLARE (tcp_bind6_error_addrnotavail) +TEST_DECLARE (tcp_bind6_error_fault) +TEST_DECLARE (tcp_bind6_error_inval) +TEST_DECLARE (tcp_bind6_localhost_ok) +TEST_DECLARE (udp_send_and_recv) +TEST_DECLARE (udp_dgram_too_big) +TEST_DECLARE (udp_dual_stack) +TEST_DECLARE (udp_ipv6_only) +TEST_DECLARE (pipe_bind_error_addrinuse) +TEST_DECLARE (pipe_bind_error_addrnotavail) +TEST_DECLARE (pipe_bind_error_inval) +TEST_DECLARE (pipe_listen_without_bind) TEST_DECLARE (connection_fail) TEST_DECLARE (connection_fail_doesnt_auto_close) TEST_DECLARE (shutdown_eof) TEST_DECLARE (callback_stack) TEST_DECLARE (timer) TEST_DECLARE (timer_again) +TEST_DECLARE (idle_starvation) TEST_DECLARE (loop_handles) TEST_DECLARE (ref) TEST_DECLARE (idle_ref) TEST_DECLARE (async_ref) TEST_DECLARE (prepare_ref) TEST_DECLARE (check_ref) +TEST_DECLARE (unref_in_prepare_cb) TEST_DECLARE (async) TEST_DECLARE (get_currentexe) TEST_DECLARE (hrtime) TEST_DECLARE (getaddrinfo_basic) TEST_DECLARE (getaddrinfo_concurrent) TEST_DECLARE (gethostbyname) +TEST_DECLARE (getsockname_tcp) +TEST_DECLARE (getsockname_udp) TEST_DECLARE (fail_always) TEST_DECLARE (pass_always) -HELPER_DECLARE (echo_server) +TEST_DECLARE (spawn_exit_code) +TEST_DECLARE (spawn_stdout) +TEST_DECLARE (spawn_stdin) +TEST_DECLARE (spawn_and_kill) +TEST_DECLARE (fs_file_noent) +TEST_DECLARE (fs_file_async) +TEST_DECLARE (fs_file_sync) +TEST_DECLARE (fs_async_dir) +TEST_DECLARE (fs_async_sendfile) +TEST_DECLARE (fs_fstat) +TEST_DECLARE (fs_chmod) +TEST_DECLARE (fs_chown) +TEST_DECLARE (fs_link) +TEST_DECLARE (fs_symlink) +TEST_DECLARE (fs_utime) +TEST_DECLARE (fs_futime) +TEST_DECLARE (fs_event_watch_dir) +TEST_DECLARE (fs_event_watch_file) +TEST_DECLARE (fs_event_watch_file_current_dir) +TEST_DECLARE (threadpool_queue_work_simple) +#ifdef _WIN32 +TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) +TEST_DECLARE (argument_escaping) +TEST_DECLARE (environment_creation) +#endif +HELPER_DECLARE (tcp4_echo_server) +HELPER_DECLARE (tcp6_echo_server) +HELPER_DECLARE (pipe_echo_server) TASK_LIST_START - TEST_ENTRY (ping_pong) - TEST_HELPER (ping_pong, echo_server) + TEST_ENTRY (tty) - TEST_ENTRY (ping_pong_v6) - TEST_HELPER (ping_pong_v6, echo_server) + TEST_ENTRY (tcp_ping_pong) + TEST_HELPER (tcp_ping_pong, tcp4_echo_server) + + TEST_ENTRY (tcp_ping_pong_v6) + TEST_HELPER (tcp_ping_pong_v6, tcp6_echo_server) + + TEST_ENTRY (pipe_ping_pong) + TEST_HELPER (pipe_ping_pong, pipe_echo_server) TEST_ENTRY (delayed_accept) TEST_ENTRY (tcp_writealot) - TEST_HELPER (tcp_writealot, echo_server) + TEST_HELPER (tcp_writealot, tcp4_echo_server) - TEST_ENTRY (bind_error_addrinuse) - TEST_ENTRY (bind_error_addrnotavail_1) - TEST_ENTRY (bind_error_addrnotavail_2) - TEST_ENTRY (bind_error_fault) - TEST_ENTRY (bind_error_inval) - TEST_ENTRY (bind_localhost_ok) + TEST_ENTRY (tcp_bind_error_addrinuse) + TEST_ENTRY (tcp_bind_error_addrnotavail_1) + TEST_ENTRY (tcp_bind_error_addrnotavail_2) + TEST_ENTRY (tcp_bind_error_fault) + TEST_ENTRY (tcp_bind_error_inval) + TEST_ENTRY (tcp_bind_localhost_ok) + TEST_ENTRY (tcp_listen_without_bind) + TEST_ENTRY (tcp_close) + TEST_ENTRY (tcp_write_error) - TEST_ENTRY (bind6_error_addrinuse) - TEST_ENTRY (bind6_error_addrnotavail) - TEST_ENTRY (bind6_error_fault) - TEST_ENTRY (bind6_error_inval) - TEST_ENTRY (bind6_localhost_ok) + TEST_ENTRY (tcp_bind6_error_addrinuse) + TEST_ENTRY (tcp_bind6_error_addrnotavail) + TEST_ENTRY (tcp_bind6_error_fault) + TEST_ENTRY (tcp_bind6_error_inval) + TEST_ENTRY (tcp_bind6_localhost_ok) + + TEST_ENTRY (udp_send_and_recv) + TEST_ENTRY (udp_dgram_too_big) + TEST_ENTRY (udp_dual_stack) + TEST_ENTRY (udp_ipv6_only) + + TEST_ENTRY (pipe_bind_error_addrinuse) + TEST_ENTRY (pipe_bind_error_addrnotavail) + TEST_ENTRY (pipe_bind_error_inval) + TEST_ENTRY (pipe_listen_without_bind) TEST_ENTRY (connection_fail) TEST_ENTRY (connection_fail_doesnt_auto_close) TEST_ENTRY (shutdown_eof) - TEST_HELPER (shutdown_eof, echo_server) + TEST_HELPER (shutdown_eof, tcp4_echo_server) TEST_ENTRY (callback_stack) - TEST_HELPER (callback_stack, echo_server) + TEST_HELPER (callback_stack, tcp4_echo_server) TEST_ENTRY (timer) TEST_ENTRY (timer_again) + TEST_ENTRY (idle_starvation) + TEST_ENTRY (ref) TEST_ENTRY (idle_ref) TEST_ENTRY (async_ref) TEST_ENTRY (prepare_ref) TEST_ENTRY (check_ref) + TEST_ENTRY (unref_in_prepare_cb) TEST_ENTRY (loop_handles) @@ -113,7 +178,37 @@ TASK_LIST_START TEST_ENTRY (getaddrinfo_concurrent) TEST_ENTRY (gethostbyname) - TEST_HELPER (gethostbyname, echo_server) + TEST_HELPER (gethostbyname, tcp4_echo_server) + + TEST_ENTRY (getsockname_tcp) + TEST_ENTRY (getsockname_udp) + + TEST_ENTRY (spawn_exit_code) + TEST_ENTRY (spawn_stdout) + TEST_ENTRY (spawn_stdin) + TEST_ENTRY (spawn_and_kill) +#ifdef _WIN32 + TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) + TEST_ENTRY (argument_escaping) + TEST_ENTRY (environment_creation) +#endif + + TEST_ENTRY (fs_file_noent) + TEST_ENTRY (fs_file_async) + TEST_ENTRY (fs_file_sync) + TEST_ENTRY (fs_async_dir) + TEST_ENTRY (fs_async_sendfile) + TEST_ENTRY (fs_fstat) + TEST_ENTRY (fs_chmod) + TEST_ENTRY (fs_chown) + TEST_ENTRY (fs_utime) + TEST_ENTRY (fs_futime) + TEST_ENTRY (fs_symlink) + TEST_ENTRY (fs_event_watch_dir) + TEST_ENTRY (fs_event_watch_file) + TEST_ENTRY (fs_event_watch_file_current_dir) + + TEST_ENTRY (threadpool_queue_work_simple) #if 0 /* These are for testing the test runner. */ @@ -121,4 +216,3 @@ TASK_LIST_START TEST_ENTRY (pass_always) #endif TASK_LIST_END - diff --git a/src/rt/libuv/test/test-loop-handles.c b/src/rt/libuv/test/test-loop-handles.c index 7979a74b4ae..9a76cc53fa6 100644 --- a/src/rt/libuv/test/test-loop-handles.c +++ b/src/rt/libuv/test/test-loop-handles.c @@ -130,8 +130,6 @@ static void idle_2_close_cb(uv_handle_t* handle) { static void idle_2_cb(uv_idle_t* handle, int status) { - int r; - LOG("IDLE_2_CB\n"); ASSERT(handle == &idle_2_handle); @@ -139,8 +137,7 @@ static void idle_2_cb(uv_idle_t* handle, int status) { idle_2_cb_called++; - r = uv_close((uv_handle_t*)handle, idle_2_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)handle, idle_2_close_cb); } @@ -156,7 +153,7 @@ static void idle_1_cb(uv_idle_t* handle, int status) { /* Init idle_2 and make it active */ if (!idle_2_is_active) { - r = uv_idle_init(&idle_2_handle); + r = uv_idle_init(uv_default_loop(), &idle_2_handle); ASSERT(r == 0); r = uv_idle_start(&idle_2_handle, idle_2_cb); ASSERT(r == 0); @@ -230,23 +227,18 @@ static void check_cb(uv_check_t* handle, int status) { } else { /* End of the test - close all handles */ - r = uv_close((uv_handle_t*)&prepare_1_handle, prepare_1_close_cb); - ASSERT(r == 0); - r = uv_close((uv_handle_t*)&check_handle, check_close_cb); - ASSERT(r == 0); - r = uv_close((uv_handle_t*)&prepare_2_handle, prepare_2_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)&prepare_1_handle, prepare_1_close_cb); + uv_close((uv_handle_t*)&check_handle, check_close_cb); + uv_close((uv_handle_t*)&prepare_2_handle, prepare_2_close_cb); for (i = 0; i < IDLE_COUNT; i++) { - r = uv_close((uv_handle_t*)&idle_1_handles[i], idle_1_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)&idle_1_handles[i], idle_1_close_cb); } /* This handle is closed/recreated every time, close it only if it is */ /* active.*/ if (idle_2_is_active) { - r = uv_close((uv_handle_t*)&idle_2_handle, idle_2_close_cb); - ASSERT(r == 0); + uv_close((uv_handle_t*)&idle_2_handle, idle_2_close_cb); } } @@ -307,25 +299,23 @@ TEST_IMPL(loop_handles) { int i; int r; - uv_init(); - - r = uv_prepare_init(&prepare_1_handle); + r = uv_prepare_init(uv_default_loop(), &prepare_1_handle); ASSERT(r == 0); r = uv_prepare_start(&prepare_1_handle, prepare_1_cb); ASSERT(r == 0); - r = uv_check_init(&check_handle); + r = uv_check_init(uv_default_loop(), &check_handle); ASSERT(r == 0); r = uv_check_start(&check_handle, check_cb); ASSERT(r == 0); /* initialize only, prepare_2 is started by prepare_1_cb */ - r = uv_prepare_init(&prepare_2_handle); + r = uv_prepare_init(uv_default_loop(), &prepare_2_handle); ASSERT(r == 0); for (i = 0; i < IDLE_COUNT; i++) { /* initialize only, idle_1 handles are started by check_cb */ - r = uv_idle_init(&idle_1_handles[i]); + r = uv_idle_init(uv_default_loop(), &idle_1_handles[i]); ASSERT(r == 0); } @@ -333,13 +323,13 @@ TEST_IMPL(loop_handles) { /* the timer callback is there to keep the event loop polling */ /* unref it as it is not supposed to keep the loop alive */ - r = uv_timer_init(&timer_handle); + r = uv_timer_init(uv_default_loop(), &timer_handle); ASSERT(r == 0); r = uv_timer_start(&timer_handle, timer_cb, TIMEOUT, TIMEOUT); ASSERT(r == 0); - uv_unref(); + uv_unref(uv_default_loop()); - r = uv_run(); + r = uv_run(uv_default_loop()); ASSERT(r == 0); ASSERT(loop_iteration == ITERATIONS); @@ -367,53 +357,3 @@ TEST_IMPL(loop_handles) { return 0; } - - -TEST_IMPL(ref) { - uv_init(); - uv_run(); - return 0; -} - - -TEST_IMPL(idle_ref) { - uv_idle_t h; - uv_init(); - uv_idle_init(&h); - uv_idle_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(async_ref) { - uv_async_t h; - uv_init(); - uv_async_init(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(prepare_ref) { - uv_prepare_t h; - uv_init(); - uv_prepare_init(&h); - uv_prepare_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} - - -TEST_IMPL(check_ref) { - uv_check_t h; - uv_init(); - uv_check_init(&h); - uv_check_start(&h, NULL); - uv_unref(); - uv_run(); - return 0; -} diff --git a/src/rt/libuv/test/test-ping-pong.c b/src/rt/libuv/test/test-ping-pong.c index 34de78b5b6c..f452fce50b5 100644 --- a/src/rt/libuv/test/test-ping-pong.c +++ b/src/rt/libuv/test/test-ping-pong.c @@ -34,21 +34,24 @@ static int completed_pingers = 0; #define BUFSIZE 10240 static char PING[] = "PING\n"; +static int pinger_on_connect_count; typedef struct { int pongs; int state; - uv_tcp_t tcp; - uv_req_t connect_req; - uv_req_t read_req; + union { + uv_tcp_t tcp; + uv_pipe_t pipe; + } stream; + uv_connect_t connect_req; char read_buffer[BUFSIZE]; } pinger_t; void pinger_try_read(pinger_t* pinger); -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -67,25 +70,22 @@ static void pinger_on_close(uv_handle_t* handle) { } -static void pinger_after_write(uv_req_t *req, int status) { +static void pinger_after_write(uv_write_t *req, int status) { ASSERT(status == 0); - free(req); } static void pinger_write_ping(pinger_t* pinger) { - uv_req_t *req; + uv_write_t *req; uv_buf_t buf; buf.base = (char*)&PING; buf.len = strlen(PING); - req = (uv_req_t*)malloc(sizeof(*req)); - uv_req_init(req, (uv_handle_t*)(&pinger->tcp), - (void *(*)(void *))pinger_after_write); + req = malloc(sizeof(uv_write_t)); - if (uv_write(req, &buf, 1)) { + if (uv_write(req, (uv_stream_t*)&pinger->stream.tcp, &buf, 1, pinger_after_write)) { FATAL("uv_write failed"); } @@ -93,14 +93,14 @@ static void pinger_write_ping(pinger_t* pinger) { } -static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { - unsigned int i; +static void pinger_read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { + ssize_t i; pinger_t* pinger; - pinger = (pinger_t*)tcp->data; + pinger = (pinger_t*)stream->data; if (nread < 0) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); puts("got EOF"); @@ -108,7 +108,7 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { free(buf.base); } - uv_close((uv_handle_t*)(&pinger->tcp), pinger_on_close); + uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); return; } @@ -123,7 +123,7 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { if (pinger->pongs < NUM_PINGS) { pinger_write_ping(pinger); } else { - uv_close((uv_handle_t*)(&pinger->tcp), pinger_on_close); + uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); return; } } @@ -131,9 +131,11 @@ static void pinger_read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { } -static void pinger_on_connect(uv_req_t *req, int status) { +static void pinger_on_connect(uv_connect_t *req, int status) { pinger_t *pinger = (pinger_t*)req->handle->data; + pinger_on_connect_count++; + ASSERT(status == 0); pinger_write_ping(pinger); @@ -142,44 +144,8 @@ static void pinger_on_connect(uv_req_t *req, int status) { } -static void pinger_new() { - int r; - struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); - pinger_t *pinger; - - pinger = (pinger_t*)malloc(sizeof(*pinger)); - pinger->state = 0; - pinger->pongs = 0; - - /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&pinger->tcp); - pinger->tcp.data = pinger; - ASSERT(!r); - - /* We are never doing multiple reads/connects at a time anyway. */ - /* so these handles can be pre-initialized. */ - uv_req_init(&pinger->connect_req, (uv_handle_t*)(&pinger->tcp), - (void *(*)(void *))pinger_on_connect); - - r = uv_tcp_connect(&pinger->connect_req, server_addr); - ASSERT(!r); -} - - -TEST_IMPL(ping_pong) { - uv_init(); - - pinger_new(); - uv_run(); - - ASSERT(completed_pingers == 1); - - return 0; -} - - /* same ping-pong test, but using IPv6 connection */ -static void pinger_v6_new() { +static void tcp_pinger_v6_new() { int r; struct sockaddr_in6 server_addr = uv_ip6_addr("::1", TEST_PORT); pinger_t *pinger; @@ -189,25 +155,94 @@ static void pinger_v6_new() { pinger->pongs = 0; /* Try to connec to the server and do NUM_PINGS ping-pongs. */ - r = uv_tcp_init(&pinger->tcp); - pinger->tcp.data = pinger; + r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); + pinger->stream.tcp.data = pinger; ASSERT(!r); /* We are never doing multiple reads/connects at a time anyway. */ /* so these handles can be pre-initialized. */ - uv_req_init(&pinger->connect_req, (uv_handle_t*)(&pinger->tcp), - (void *(*)(void *))pinger_on_connect); - - r = uv_tcp_connect6(&pinger->connect_req, server_addr); + r = uv_tcp_connect6(&pinger->connect_req, &pinger->stream.tcp, server_addr, + pinger_on_connect); ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); } -TEST_IMPL(ping_pong_v6) { - uv_init(); +static void tcp_pinger_new() { + int r; + struct sockaddr_in server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + pinger_t *pinger; - pinger_v6_new(); - uv_run(); + pinger = (pinger_t*)malloc(sizeof(*pinger)); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connec to the server and do NUM_PINGS ping-pongs. */ + r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); + pinger->stream.tcp.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + r = uv_tcp_connect(&pinger->connect_req, &pinger->stream.tcp, server_addr, + pinger_on_connect); + ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); +} + + +static void pipe_pinger_new() { + int r; + pinger_t *pinger; + + pinger = (pinger_t*)malloc(sizeof(*pinger)); + pinger->state = 0; + pinger->pongs = 0; + + /* Try to connec to the server and do NUM_PINGS ping-pongs. */ + r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe); + pinger->stream.pipe.data = pinger; + ASSERT(!r); + + /* We are never doing multiple reads/connects at a time anyway. */ + /* so these handles can be pre-initialized. */ + + r = uv_pipe_connect(&pinger->connect_req, &pinger->stream.pipe, TEST_PIPENAME, + pinger_on_connect); + ASSERT(!r); + + /* Synchronous connect callbacks are not allowed. */ + ASSERT(pinger_on_connect_count == 0); +} + + +TEST_IMPL(tcp_ping_pong) { + tcp_pinger_new(); + uv_run(uv_default_loop()); + + ASSERT(completed_pingers == 1); + + return 0; +} + + +TEST_IMPL(tcp_ping_pong_v6) { + tcp_pinger_v6_new(); + uv_run(uv_default_loop()); + + ASSERT(completed_pingers == 1); + + return 0; +} + + +TEST_IMPL(pipe_ping_pong) { + pipe_pinger_new(); + uv_run(uv_default_loop()); ASSERT(completed_pingers == 1); diff --git a/src/rt/libuv/test/test-pipe-bind-error.c b/src/rt/libuv/test/test-pipe-bind-error.c new file mode 100644 index 00000000000..832ce023153 --- /dev/null +++ b/src/rt/libuv/test/test-pipe-bind-error.c @@ -0,0 +1,140 @@ +/* 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 "task.h" +#include +#include + + +#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) { + ASSERT(handle != NULL); + close_cb_called++; +} + + +TEST_IMPL(pipe_bind_error_addrinuse) { + uv_pipe_t server1, server2; + int r; + + r = uv_pipe_init(uv_default_loop(), &server1); + ASSERT(r == 0); + r = uv_pipe_bind(&server1, TEST_PIPENAME); + ASSERT(r == 0); + + r = uv_pipe_init(uv_default_loop(), &server2); + ASSERT(r == 0); + r = uv_pipe_bind(&server2, TEST_PIPENAME); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRINUSE); + + r = uv_listen((uv_stream_t*)&server1, SOMAXCONN, NULL); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + + uv_close((uv_handle_t*)&server1, close_cb); + uv_close((uv_handle_t*)&server2, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 2); + + return 0; +} + + +TEST_IMPL(pipe_bind_error_addrnotavail) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_pipe_bind(&server, BAD_PIPENAME); + + ASSERT(r == -1); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EACCESS); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(pipe_bind_error_inval) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_pipe_bind(&server, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_pipe_bind(&server, TEST_PIPENAME_2); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(pipe_listen_without_bind) { + uv_pipe_t server; + int r; + + r = uv_pipe_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); + ASSERT(r == -1); + + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + + uv_close((uv_handle_t*)&server, close_cb); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-ref.c b/src/rt/libuv/test/test-ref.c new file mode 100644 index 00000000000..0083335fd12 --- /dev/null +++ b/src/rt/libuv/test/test-ref.c @@ -0,0 +1,85 @@ +/* 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 "task.h" + + +TEST_IMPL(ref) { + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(idle_ref) { + uv_idle_t h; + uv_idle_init(uv_default_loop(), &h); + uv_idle_start(&h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(async_ref) { + uv_async_t h; + uv_async_init(uv_default_loop(), &h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(prepare_ref) { + uv_prepare_t h; + uv_prepare_init(uv_default_loop(), &h); + uv_prepare_start(&h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +TEST_IMPL(check_ref) { + uv_check_t h; + uv_check_init(uv_default_loop(), &h); + uv_check_start(&h, NULL); + uv_unref(uv_default_loop()); + uv_run(uv_default_loop()); + return 0; +} + + +static void prepare_cb(uv_prepare_t* handle, int status) { + ASSERT(handle != NULL); + ASSERT(status == 0); + + uv_unref(uv_default_loop()); +} + + +TEST_IMPL(unref_in_prepare_cb) { + uv_prepare_t h; + uv_prepare_init(uv_default_loop(), &h); + uv_prepare_start(&h, prepare_cb); + uv_run(uv_default_loop()); + return 0; +} diff --git a/src/rt/libuv/test/test-shutdown-eof.c b/src/rt/libuv/test/test-shutdown-eof.c index 8a960c9ea6a..9d4f2cce74d 100644 --- a/src/rt/libuv/test/test-shutdown-eof.c +++ b/src/rt/libuv/test/test-shutdown-eof.c @@ -26,7 +26,9 @@ static uv_timer_t timer; static uv_tcp_t tcp; -static uv_req_t connect_req, write_req, shutdown_req; +static uv_connect_t connect_req; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; static uv_buf_t qbuf; static int got_q; static int got_eof; @@ -37,7 +39,7 @@ static int called_timer_close_cb; static int called_timer_cb; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -46,7 +48,7 @@ static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { static void read_cb(uv_stream_t* t, ssize_t nread, uv_buf_t buf) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(uv_default_loop()); ASSERT((uv_tcp_t*)t == &tcp); @@ -74,7 +76,7 @@ static void read_cb(uv_stream_t* t, ssize_t nread, uv_buf_t buf) { } -static void shutdown_cb(uv_req_t *req, int status) { +static void shutdown_cb(uv_shutdown_t *req, int status) { ASSERT(req == &shutdown_req); ASSERT(called_connect_cb == 1); @@ -87,7 +89,7 @@ static void shutdown_cb(uv_req_t *req, int status) { } -static void connect_cb(uv_req_t *req, int status) { +static void connect_cb(uv_connect_t *req, int status) { ASSERT(status == 0); ASSERT(req == &connect_req); @@ -98,12 +100,10 @@ static void connect_cb(uv_req_t *req, int status) { * Write the letter 'Q' to gracefully kill the echo-server. This will not * effect our connection. */ - uv_req_init(&write_req, (uv_handle_t*)&tcp, NULL); - uv_write(&write_req, &qbuf, 1); + uv_write(&write_req, (uv_stream_t*) &tcp, &qbuf, 1, NULL); /* Shutdown our end of the connection. */ - uv_req_init(&shutdown_req, (uv_handle_t*)&tcp, (void *(*)(void *))shutdown_cb); - uv_shutdown(&shutdown_req); + uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb); called_connect_cb++; ASSERT(called_shutdown_cb == 0); @@ -153,23 +153,22 @@ TEST_IMPL(shutdown_eof) { struct sockaddr_in server_addr; int r; - uv_init(); - qbuf.base = "Q"; qbuf.len = 1; - uv_timer_init(&timer); + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + uv_timer_start(&timer, timer_cb, 100, 0); server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); - r = uv_tcp_init(&tcp); + r = uv_tcp_init(uv_default_loop(), &tcp); ASSERT(!r); - uv_req_init(&connect_req, (uv_handle_t*) &tcp, (void *(*)(void *))connect_cb); - r = uv_tcp_connect(&connect_req, server_addr); + r = uv_tcp_connect(&connect_req, &tcp, server_addr, connect_cb); ASSERT(!r); - uv_run(); + uv_run(uv_default_loop()); ASSERT(called_connect_cb == 1); ASSERT(called_shutdown_cb == 1); diff --git a/src/rt/libuv/test/test-spawn.c b/src/rt/libuv/test/test-spawn.c new file mode 100644 index 00000000000..653f9ac95d3 --- /dev/null +++ b/src/rt/libuv/test/test-spawn.c @@ -0,0 +1,383 @@ +/* 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 "task.h" +#include +#include +#include + +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[3]; + +#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, int exit_status, int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; + ASSERT(exit_status == 1); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, close_cb); +} + + +static void kill_cb(uv_process_t* process, int exit_status, int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; +#ifdef _WIN32 + ASSERT(exit_status == 1); +#else + ASSERT(exit_status == 0); +#endif + ASSERT(term_signal == 15); + uv_close((uv_handle_t*)process, close_cb); +} + + +uv_buf_t 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; + return buf; +} + + +void on_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { + uv_err_t err = uv_last_error(uv_default_loop()); + + if (nread > 0) { + output_used += nread; + } else if (nread < 0) { + if (err.code == UV_EOF) { + uv_close((uv_handle_t*)tcp, close_cb); + } + } +} + + +void write_cb(uv_write_t* req, int status) { + 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); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + args[0] = exepath; + args[1] = test; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; +} + + +static void timer_cb(uv_timer_t* handle, int status) { + uv_process_kill(&process, /* SIGTERM */ 15); + uv_close((uv_handle_t*)handle, close_cb); +} + + +TEST_IMPL(spawn_exit_code) { + int r; + + init_process_options("spawn_helper1", exit_cb); + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(spawn_stdout) { + int r; + uv_pipe_t out; + + init_process_options("spawn_helper2", exit_cb); + + uv_pipe_init(uv_default_loop(), &out); + options.stdout_stream = &out; + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0 || strcmp("hello world\r\n", output) == 0); + + return 0; +} + + +TEST_IMPL(spawn_stdin) { +int r; + uv_pipe_t out; + uv_pipe_t in; + uv_write_t write_req; + uv_buf_t buf; + char buffer[] = "hello-from-spawn_stdin"; + + init_process_options("spawn_helper3", exit_cb); + + uv_pipe_init(uv_default_loop(), &out); + uv_pipe_init(uv_default_loop(), &in); + options.stdout_stream = &out; + options.stdin_stream = ∈ + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + buf.base = buffer; + buf.len = sizeof(buffer); + r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 3); /* Once for process twice for the pipe. */ + ASSERT(strcmp(buffer, output) == 0); + + return 0; +} + + +TEST_IMPL(spawn_and_kill) { + int r; + + init_process_options("spawn_helper4", kill_cb); + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT(r == 0); + + r = uv_timer_start(&timer, timer_cb, 500, 0); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process and once for timer. */ + + 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; + + init_process_options("spawn_helper2", exit_cb); + + uv_pipe_init(uv_default_loop(), &out); + options.stdout_stream = &out; + + /* 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); + ASSERT(pipe_handle != INVALID_HANDLE_VALUE); + + r = uv_spawn(uv_default_loop(), &process, options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(uv_default_loop()); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + printf("output is: %s", output); + ASSERT(strcmp("hello world\n", output) == 0 || strcmp("hello world\r\n", output) == 0); + + return 0; +} + + +wchar_t* make_program_args(char** args, int verbatim_arguments); +wchar_t* quote_cmd_arg(const wchar_t *source, wchar_t *target); + +TEST_IMPL(argument_escaping) { + const wchar_t* test_str[] = { + 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_t** test_output; + wchar_t* command_line; + wchar_t** cracked; + size_t total_size = 0; + int i; + int num_args; + + char* verbatim[] = { + "cmd.exe", + "/c", + "c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"", + NULL + }; + wchar_t* verbatim_output; + wchar_t* non_verbatim_output; + + test_output = calloc(count, sizeof(wchar_t*)); + for (i = 0; i < count; ++i) { + test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(wchar_t)); + 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_t)); + 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]); + ASSERT(wcscmp(test_str[i], cracked[i]) == 0); + } + + LocalFree(cracked); + for (i = 0; i < count; ++i) { + free(test_output[i]); + } + + verbatim_output = make_program_args(verbatim, 1); + non_verbatim_output = make_program_args(verbatim, 0); + + wprintf(L" verbatim_output: %s\n", verbatim_output); + wprintf(L"non_verbatim_output: %s\n", non_verbatim_output); + + ASSERT(wcscmp(verbatim_output, L"cmd.exe /c c:\\path\\to\\node.exe --eval \"require('c:\\\\path\\\\to\\\\test.js')\"") == 0); + ASSERT(wcscmp(non_verbatim_output, L"cmd.exe /c \"c:\\path\\to\\node.exe --eval \\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0); + + free(verbatim_output); + free(non_verbatim_output); + + return 0; +} + +wchar_t* make_program_env(char** env_block); + +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", + "BAZ=QUX", + NULL + }; + + wchar_t expected[512]; + wchar_t* ptr = expected; + wchar_t* result; + wchar_t* str; + + for (i = 0; i < sizeof(environment) / sizeof(environment[0]) - 1; i++) { + ptr += uv_utf8_to_utf16(environment[i], ptr, expected + sizeof(expected) - ptr); + } + + memcpy(ptr, L"SYSTEMROOT=", sizeof(L"SYSTEMROOT=")); + ptr += sizeof(L"SYSTEMROOT=")/sizeof(wchar_t) - 1; + ptr += GetEnvironmentVariableW(L"SYSTEMROOT", ptr, expected + sizeof(expected) - ptr); + ++ptr; + + memcpy(ptr, L"SYSTEMDRIVE=", sizeof(L"SYSTEMDRIVE=")); + ptr += sizeof(L"SYSTEMDRIVE=")/sizeof(wchar_t) - 1; + ptr += GetEnvironmentVariableW(L"SYSTEMDRIVE", ptr, expected + sizeof(expected) - ptr); + ++ptr; + *ptr = '\0'; + + result = make_program_env(environment); + + for (str = result; *str; str += wcslen(str) + 1) { + wprintf(L"%s\n", str); + } + + ASSERT(wcscmp(expected, result) == 0); + + return 0; +} +#endif diff --git a/src/rt/libuv/test/test-bind-error.c b/src/rt/libuv/test/test-tcp-bind-error.c similarity index 71% rename from src/rt/libuv/test/test-bind-error.c rename to src/rt/libuv/test/test-tcp-bind-error.c index 4ac60654f6d..9512519ac0c 100644 --- a/src/rt/libuv/test/test-bind-error.c +++ b/src/rt/libuv/test/test-tcp-bind-error.c @@ -34,34 +34,32 @@ static void close_cb(uv_handle_t* handle) { } -TEST_IMPL(bind_error_addrinuse) { +TEST_IMPL(tcp_bind_error_addrinuse) { struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", TEST_PORT); uv_tcp_t server1, server2; int r; - uv_init(); - - r = uv_tcp_init(&server1); + r = uv_tcp_init(uv_default_loop(), &server1); ASSERT(r == 0); r = uv_tcp_bind(&server1, addr); ASSERT(r == 0); - r = uv_tcp_init(&server2); + r = uv_tcp_init(uv_default_loop(), &server2); ASSERT(r == 0); r = uv_tcp_bind(&server2, addr); ASSERT(r == 0); - r = uv_tcp_listen(&server1, 128, NULL); + r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); - r = uv_tcp_listen(&server2, 128, NULL); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRINUSE); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 2); @@ -69,25 +67,23 @@ TEST_IMPL(bind_error_addrinuse) { } -TEST_IMPL(bind_error_addrnotavail_1) { +TEST_IMPL(tcp_bind_error_addrnotavail_1) { struct sockaddr_in addr = uv_ip4_addr("127.255.255.255", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr); /* It seems that Linux is broken here - bind succeeds. */ if (r == -1) { - ASSERT(uv_last_error().code == UV_EADDRNOTAVAIL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRNOTAVAIL); } uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -95,22 +91,20 @@ TEST_IMPL(bind_error_addrnotavail_1) { } -TEST_IMPL(bind_error_addrnotavail_2) { +TEST_IMPL(tcp_bind_error_addrnotavail_2) { struct sockaddr_in addr = uv_ip4_addr("4.4.4.4", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRNOTAVAIL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -118,7 +112,7 @@ TEST_IMPL(bind_error_addrnotavail_2) { } -TEST_IMPL(bind_error_fault) { +TEST_IMPL(tcp_bind_error_fault) { char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; struct sockaddr_in* garbage_addr; uv_tcp_t server; @@ -126,18 +120,16 @@ TEST_IMPL(bind_error_fault) { garbage_addr = (struct sockaddr_in*) &garbage; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, *garbage_addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EFAULT); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EFAULT); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -146,26 +138,24 @@ TEST_IMPL(bind_error_fault) { /* Notes: On Linux uv_bind(server, NULL) will segfault the program. */ -TEST_IMPL(bind_error_inval) { +TEST_IMPL(tcp_bind_error_inval) { struct sockaddr_in addr1 = uv_ip4_addr("0.0.0.0", TEST_PORT); struct sockaddr_in addr2 = uv_ip4_addr("0.0.0.0", TEST_PORT_2); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr1); ASSERT(r == 0); r = uv_tcp_bind(&server, addr2); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EINVAL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -173,18 +163,29 @@ TEST_IMPL(bind_error_inval) { } -TEST_IMPL(bind_localhost_ok) { +TEST_IMPL(tcp_bind_localhost_ok) { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind(&server, addr); ASSERT(r == 0); return 0; } + + +TEST_IMPL(tcp_listen_without_bind) { + int r; + uv_tcp_t server; + + r = uv_tcp_init(uv_default_loop(), &server); + ASSERT(r == 0); + r = uv_listen((uv_stream_t*)&server, 128, NULL); + ASSERT(r == 0); + + return 0; +} diff --git a/src/rt/libuv/test/test-bind6-error.c b/src/rt/libuv/test/test-tcp-bind6-error.c similarity index 76% rename from src/rt/libuv/test/test-bind6-error.c rename to src/rt/libuv/test/test-tcp-bind6-error.c index 6072dcdc090..5a8b76363a6 100644 --- a/src/rt/libuv/test/test-bind6-error.c +++ b/src/rt/libuv/test/test-tcp-bind6-error.c @@ -34,34 +34,32 @@ static void close_cb(uv_handle_t* handle) { } -TEST_IMPL(bind6_error_addrinuse) { +TEST_IMPL(tcp_bind6_error_addrinuse) { struct sockaddr_in6 addr = uv_ip6_addr("::", TEST_PORT); uv_tcp_t server1, server2; int r; - uv_init(); - - r = uv_tcp_init(&server1); + r = uv_tcp_init(uv_default_loop(), &server1); ASSERT(r == 0); r = uv_tcp_bind6(&server1, addr); ASSERT(r == 0); - r = uv_tcp_init(&server2); + r = uv_tcp_init(uv_default_loop(), &server2); ASSERT(r == 0); r = uv_tcp_bind6(&server2, addr); ASSERT(r == 0); - r = uv_tcp_listen(&server1, 128, NULL); + r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); - r = uv_tcp_listen(&server2, 128, NULL); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRINUSE); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 2); @@ -69,22 +67,20 @@ TEST_IMPL(bind6_error_addrinuse) { } -TEST_IMPL(bind6_error_addrnotavail) { +TEST_IMPL(tcp_bind6_error_addrnotavail) { struct sockaddr_in6 addr = uv_ip6_addr("4:4:4:4:4:4:4:4", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRNOTAVAIL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -92,7 +88,7 @@ TEST_IMPL(bind6_error_addrnotavail) { } -TEST_IMPL(bind6_error_fault) { +TEST_IMPL(tcp_bind6_error_fault) { char garbage[] = "blah blah blah blah blah blah blah blah blah blah blah blah"; struct sockaddr_in6* garbage_addr; uv_tcp_t server; @@ -100,18 +96,16 @@ TEST_IMPL(bind6_error_fault) { garbage_addr = (struct sockaddr_in6*) &garbage; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, *garbage_addr); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EFAULT); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EFAULT); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -120,26 +114,24 @@ TEST_IMPL(bind6_error_fault) { /* Notes: On Linux uv_bind6(server, NULL) will segfault the program. */ -TEST_IMPL(bind6_error_inval) { +TEST_IMPL(tcp_bind6_error_inval) { struct sockaddr_in6 addr1 = uv_ip6_addr("::", TEST_PORT); struct sockaddr_in6 addr2 = uv_ip6_addr("::", TEST_PORT_2); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr1); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr2); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EINVAL); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); - uv_run(); + uv_run(uv_default_loop()); ASSERT(close_cb_called == 1); @@ -147,15 +139,13 @@ TEST_IMPL(bind6_error_inval) { } -TEST_IMPL(bind6_localhost_ok) { +TEST_IMPL(tcp_bind6_localhost_ok) { struct sockaddr_in6 addr = uv_ip6_addr("::1", TEST_PORT); uv_tcp_t server; int r; - uv_init(); - - r = uv_tcp_init(&server); + r = uv_tcp_init(uv_default_loop(), &server); ASSERT(r == 0); r = uv_tcp_bind6(&server, addr); ASSERT(r == 0); diff --git a/src/rt/libuv/test/test-tcp-close.c b/src/rt/libuv/test/test-tcp-close.c new file mode 100644 index 00000000000..5da8a84f8a2 --- /dev/null +++ b/src/rt/libuv/test/test-tcp-close.c @@ -0,0 +1,129 @@ +/* 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 "task.h" + +#include +#include /* memset */ + +#define NUM_WRITE_REQS 32 + +static uv_tcp_t tcp_handle; +static uv_connect_t connect_req; + +static int write_cb_called; +static int close_cb_called; + +static void connect_cb(uv_connect_t* req, int status); +static void write_cb(uv_write_t* req, int status); +static void close_cb(uv_handle_t* handle); + + +static void connect_cb(uv_connect_t* conn_req, int status) { + uv_write_t* req; + uv_buf_t buf; + int i, r; + + buf = uv_buf_init("PING", 4); + for (i = 0; i < NUM_WRITE_REQS; i++) { + req = malloc(sizeof *req); + ASSERT(req != NULL); + + r = uv_write(req, (uv_stream_t*)&tcp_handle, &buf, 1, write_cb); + ASSERT(r == 0); + } + + uv_close((uv_handle_t*)&tcp_handle, close_cb); +} + + +static void write_cb(uv_write_t* req, int status) { + /* write callbacks should run before the close callback */ + ASSERT(close_cb_called == 0); + ASSERT(req->handle == (uv_stream_t*)&tcp_handle); + write_cb_called++; + free(req); +} + + +static void close_cb(uv_handle_t* handle) { + ASSERT(handle == (uv_handle_t*)&tcp_handle); + close_cb_called++; +} + + +static void connection_cb(uv_stream_t* server, int status) { + ASSERT(status == 0); +} + + +static void start_server(uv_loop_t* loop, uv_tcp_t* handle) { + int r; + + r = uv_tcp_init(loop, handle); + ASSERT(r == 0); + + r = uv_tcp_bind(handle, uv_ip4_addr("127.0.0.1", TEST_PORT)); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)handle, 128, connection_cb); + ASSERT(r == 0); + + uv_unref(loop); +} + + +/* Check that pending write requests have their callbacks + * invoked when the handle is closed. + */ +TEST_IMPL(tcp_close) { + uv_loop_t* loop; + uv_tcp_t tcp_server; + int r; + + loop = uv_default_loop(); + + /* We can't use the echo server, it doesn't handle ECONNRESET. */ + start_server(loop, &tcp_server); + + r = uv_tcp_init(loop, &tcp_handle); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &tcp_handle, + uv_ip4_addr("127.0.0.1", TEST_PORT), + connect_cb); + ASSERT(r == 0); + + ASSERT(write_cb_called == 0); + ASSERT(close_cb_called == 0); + + r = uv_run(loop); + ASSERT(r == 0); + + printf("%d of %d write reqs seen\n", write_cb_called, NUM_WRITE_REQS); + + ASSERT(write_cb_called == NUM_WRITE_REQS); + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-tcp-write-error.c b/src/rt/libuv/test/test-tcp-write-error.c new file mode 100644 index 00000000000..f3d12b8d6bf --- /dev/null +++ b/src/rt/libuv/test/test-tcp-write-error.c @@ -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 "uv.h" +#include "task.h" + +#include +#include +#include + +static void connection_cb(uv_stream_t* server, int status); +static void connect_cb(uv_connect_t* req, int status); +static void write_cb(uv_write_t* req, int status); +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf); +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size); + +static uv_tcp_t tcp_server; +static uv_tcp_t tcp_client; +static uv_tcp_t tcp_peer; /* client socket as accept()-ed by server */ +static uv_write_t write_req; +static uv_connect_t connect_req; + +static int write_cb_called; + + +static void connection_cb(uv_stream_t* server, int status) { + int r; + + ASSERT(server == (uv_stream_t*)&tcp_server); + ASSERT(status == 0); + + r = uv_tcp_init(server->loop, &tcp_peer); + ASSERT(r == 0); + + r = uv_accept(server, (uv_stream_t*)&tcp_peer); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*)&tcp_peer, alloc_cb, read_cb); + ASSERT(r == 0); +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[1024]; + return uv_buf_init(slab, sizeof slab); +} + + +static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) { + uv_close((uv_handle_t*)&tcp_server, NULL); + uv_close((uv_handle_t*)&tcp_peer, NULL); +} + + +static void connect_cb(uv_connect_t* req, int status) { + uv_buf_t buf; + size_t size; + char* data; + int r; + + ASSERT(req == &connect_req); + ASSERT(status == 0); + + size = 10*1024*1024; + data = malloc(size); + ASSERT(data != NULL); + + memset(data, '$', size); + buf = uv_buf_init(data, size); + + write_req.data = data; + + r = uv_write(&write_req, req->handle, &buf, 1, write_cb); + ASSERT(r == 0); + + /* Write queue should have been updated. */ + ASSERT(req->handle->write_queue_size > 0); + + /* write_queue_size <= size, part may have already been written. */ + ASSERT(req->handle->write_queue_size <= size); +} + + +static void write_cb(uv_write_t* req, int status) { + ASSERT(req == &write_req); + ASSERT(status == -1); + + /* This is what this test is all about. */ + ASSERT(tcp_client.write_queue_size == 0); + + free(write_req.data); + + uv_close((uv_handle_t*)&tcp_client, NULL); + + write_cb_called++; +} + + +/* + * Assert that a failing write does not leave + * the stream's write_queue_size in an inconsistent state. + */ +TEST_IMPL(tcp_write_error) { + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + ASSERT(loop != NULL); + + r = uv_tcp_init(loop, &tcp_server); + ASSERT(r == 0); + + r = uv_tcp_bind(&tcp_server, uv_ip4_addr("127.0.0.1", TEST_PORT)); + ASSERT(r == 0); + + r = uv_listen((uv_stream_t*)&tcp_server, 1, connection_cb); + ASSERT(r == 0); + + r = uv_tcp_init(loop, &tcp_client); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &tcp_client, + uv_ip4_addr("127.0.0.1", TEST_PORT), + connect_cb); + ASSERT(r == 0); + + ASSERT(write_cb_called == 0); + + r = uv_run(loop); + ASSERT(r == 0); + + ASSERT(write_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-tcp-writealot.c b/src/rt/libuv/test/test-tcp-writealot.c index 4e305a9f33d..f6b1dc6efa1 100644 --- a/src/rt/libuv/test/test-tcp-writealot.c +++ b/src/rt/libuv/test/test-tcp-writealot.c @@ -45,7 +45,7 @@ static int bytes_received = 0; static int bytes_received_done = 0; -static uv_buf_t alloc_cb(uv_stream_t* tcp, size_t size) { +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { uv_buf_t buf; buf.base = (char*)malloc(size); buf.len = size; @@ -62,7 +62,7 @@ static void close_cb(uv_handle_t* handle) { } -static void shutdown_cb(uv_req_t* req, int status) { +static void shutdown_cb(uv_shutdown_t* req, int status) { uv_tcp_t* tcp; ASSERT(req); @@ -87,7 +87,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { ASSERT(tcp != NULL); if (nread < 0) { - ASSERT(uv_last_error().code == UV_EOF); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF); printf("GOT EOF\n"); if (buf.base) { @@ -104,11 +104,11 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { } -static void write_cb(uv_req_t* req, int status) { +static void write_cb(uv_write_t* req, int status) { ASSERT(req != NULL); if (status) { - uv_err_t err = uv_last_error(); + uv_err_t err = uv_last_error(uv_default_loop()); fprintf(stderr, "uv_write error: %s\n", uv_strerror(err)); ASSERT(0); } @@ -120,9 +120,11 @@ static void write_cb(uv_req_t* req, int status) { } -static void connect_cb(uv_req_t* req, int status) { +static void connect_cb(uv_connect_t* req, int status) { uv_buf_t send_bufs[CHUNKS_PER_WRITE]; uv_tcp_t* tcp; + uv_write_t* write_req; + uv_shutdown_t* shutdown_req; int i, j, r; ASSERT(req != NULL); @@ -141,26 +143,21 @@ static void connect_cb(uv_req_t* req, int status) { bytes_sent += CHUNK_SIZE; } - req = (uv_req_t*)malloc(sizeof *req); - ASSERT(req != NULL); + write_req = malloc(sizeof(uv_write_t)); + ASSERT(write_req != NULL); - uv_req_init(req, (uv_handle_t*)tcp, (void *(*)(void *))write_cb); - r = uv_write(req, (uv_buf_t*)&send_bufs, CHUNKS_PER_WRITE); + r = uv_write(write_req, (uv_stream_t*) tcp, (uv_buf_t*)&send_bufs, + CHUNKS_PER_WRITE, write_cb); ASSERT(r == 0); } /* Shutdown on drain. FIXME: dealloc req? */ - req = (uv_req_t*) malloc(sizeof(uv_req_t)); - ASSERT(req != NULL); - uv_req_init(req, (uv_handle_t*)tcp, (void *(*)(void *))shutdown_cb); - r = uv_shutdown(req); + shutdown_req = malloc(sizeof(uv_shutdown_t)); + ASSERT(shutdown_req != NULL); + r = uv_shutdown(shutdown_req, (uv_stream_t*)tcp, shutdown_cb); ASSERT(r == 0); /* Start reading */ - req = (uv_req_t*)malloc(sizeof *req); - ASSERT(req != NULL); - - uv_req_init(req, (uv_handle_t*)tcp, (void *(*)(void *))read_cb); r = uv_read_start((uv_stream_t*)tcp, alloc_cb, read_cb); ASSERT(r == 0); } @@ -169,7 +166,7 @@ static void connect_cb(uv_req_t* req, int status) { TEST_IMPL(tcp_writealot) { struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT); uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof *client); - uv_req_t* connect_req = (uv_req_t*)malloc(sizeof *connect_req); + uv_connect_t* connect_req = malloc(sizeof(uv_connect_t)); int r; ASSERT(client != NULL); @@ -179,16 +176,13 @@ TEST_IMPL(tcp_writealot) { ASSERT(send_buffer != NULL); - uv_init(); - - r = uv_tcp_init(client); + r = uv_tcp_init(uv_default_loop(), client); ASSERT(r == 0); - uv_req_init(connect_req, (uv_handle_t*)client, (void *(*)(void *))connect_cb); - r = uv_tcp_connect(connect_req, addr); + r = uv_tcp_connect(connect_req, client, addr, connect_cb); ASSERT(r == 0); - uv_run(); + uv_run(uv_default_loop()); ASSERT(shutdown_cb_called == 1); ASSERT(connect_cb_called == 1); diff --git a/src/rt/libuv/test/test-threadpool.c b/src/rt/libuv/test/test-threadpool.c new file mode 100644 index 00000000000..92130b506c5 --- /dev/null +++ b/src/rt/libuv/test/test-threadpool.c @@ -0,0 +1,57 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.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) { + ASSERT(req == &work_req); + ASSERT(req->data == &data); + work_cb_count++; +} + + +static void after_work_cb(uv_work_t* req) { + ASSERT(req == &work_req); + ASSERT(req->data == &data); + after_work_cb_count++; +} + + +TEST_IMPL(threadpool_queue_work_simple) { + int r; + + work_req.data = &data; + r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); + ASSERT(r == 0); + uv_run(uv_default_loop()); + + ASSERT(work_cb_count == 1); + ASSERT(after_work_cb_count == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-timer-again.c b/src/rt/libuv/test/test-timer-again.c index e083b01ced1..9eeee1e34dc 100644 --- a/src/rt/libuv/test/test-timer-again.c +++ b/src/rt/libuv/test/test-timer-again.c @@ -49,14 +49,15 @@ static void repeat_1_cb(uv_timer_t* handle, int status) { ASSERT(uv_timer_get_repeat((uv_timer_t*)handle) == 50); - LOGF("repeat_1_cb called after %ld ms\n", (long int)(uv_now() - start_time)); + LOGF("repeat_1_cb called after %ld ms\n", + (long int)(uv_now(uv_default_loop()) - start_time)); repeat_1_cb_called++; r = uv_timer_again(&repeat_2); ASSERT(r == 0); - if (uv_now() >= start_time + 500) { + if (uv_now(uv_default_loop()) >= start_time + 500) { 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. */ @@ -71,7 +72,8 @@ static void repeat_2_cb(uv_timer_t* handle, int status) { ASSERT(status == 0); ASSERT(repeat_2_cb_allowed); - LOGF("repeat_2_cb called after %ld ms\n", (long int)(uv_now() - start_time)); + LOGF("repeat_2_cb called after %ld ms\n", + (long int)(uv_now(uv_default_loop()) - start_time)); repeat_2_cb_called++; @@ -93,21 +95,19 @@ static void repeat_2_cb(uv_timer_t* handle, int status) { TEST_IMPL(timer_again) { int r; - uv_init(); - - start_time = uv_now(); + start_time = uv_now(uv_default_loop()); ASSERT(0 < start_time); /* Verify that it is not possible to uv_timer_again a never-started timer. */ - r = uv_timer_init(&dummy); + r = uv_timer_init(uv_default_loop(), &dummy); ASSERT(r == 0); r = uv_timer_again(&dummy); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EINVAL); - uv_unref(); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EINVAL); + uv_unref(uv_default_loop()); /* Start timer repeat_1. */ - r = uv_timer_init(&repeat_1); + r = uv_timer_init(uv_default_loop(), &repeat_1); ASSERT(r == 0); r = uv_timer_start(&repeat_1, repeat_1_cb, 50, 0); ASSERT(r == 0); @@ -121,21 +121,21 @@ TEST_IMPL(timer_again) { * 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(&repeat_2); + r = uv_timer_init(uv_default_loop(), &repeat_2); ASSERT(r == 0); r = uv_timer_start(&repeat_2, repeat_2_cb, 100, 100); ASSERT(r == 0); ASSERT(uv_timer_get_repeat(&repeat_2) == 100); - uv_run(); + uv_run(uv_default_loop()); ASSERT(repeat_1_cb_called == 10); ASSERT(repeat_2_cb_called == 2); ASSERT(close_cb_called == 2); LOGF("Test took %ld ms (expected ~700 ms)\n", - (long int)(uv_now() - start_time)); - ASSERT(700 <= uv_now() - start_time); + (long int)(uv_now(uv_default_loop()) - start_time)); + ASSERT(700 <= uv_now(uv_default_loop()) - start_time); return 0; } diff --git a/src/rt/libuv/test/test-timer.c b/src/rt/libuv/test/test-timer.c index c62a8c68dbc..17bcb84b770 100644 --- a/src/rt/libuv/test/test-timer.c +++ b/src/rt/libuv/test/test-timer.c @@ -53,7 +53,7 @@ static void once_cb(uv_timer_t* handle, int status) { uv_close((uv_handle_t*)handle, once_close_cb); /* Just call this randomly for the code coverage. */ - uv_update_time(); + uv_update_time(uv_default_loop()); } @@ -90,37 +90,35 @@ TEST_IMPL(timer) { uv_timer_t repeat, never; int i, r; - uv_init(); - - start_time = uv_now(); + start_time = uv_now(uv_default_loop()); ASSERT(0 < start_time); /* Let 10 timers time out in 500 ms total. */ for (i = 0; i < 10; i++) { once = (uv_timer_t*)malloc(sizeof(*once)); ASSERT(once != NULL); - r = uv_timer_init(once); + r = uv_timer_init(uv_default_loop(), once); ASSERT(r == 0); r = uv_timer_start(once, once_cb, i * 50, 0); ASSERT(r == 0); } /* The 11th timer is a repeating timer that runs 4 times */ - r = uv_timer_init(&repeat); + r = uv_timer_init(uv_default_loop(), &repeat); ASSERT(r == 0); r = uv_timer_start(&repeat, repeat_cb, 100, 100); ASSERT(r == 0); /* The 12th timer should not do anything. */ - r = uv_timer_init(&never); + r = uv_timer_init(uv_default_loop(), &never); ASSERT(r == 0); r = uv_timer_start(&never, never_cb, 100, 100); ASSERT(r == 0); r = uv_timer_stop(&never); ASSERT(r == 0); - uv_unref(); + uv_unref(uv_default_loop()); - uv_run(); + uv_run(uv_default_loop()); ASSERT(once_cb_called == 10); ASSERT(once_close_cb_called == 10); @@ -128,7 +126,7 @@ TEST_IMPL(timer) { ASSERT(repeat_cb_called == 5); ASSERT(repeat_close_cb_called == 1); - ASSERT(500 <= uv_now() - start_time); + ASSERT(500 <= uv_now(uv_default_loop()) - start_time); return 0; } diff --git a/src/rt/libuv/test/test-tty.c b/src/rt/libuv/test/test-tty.c new file mode 100644 index 00000000000..11816156fff --- /dev/null +++ b/src/rt/libuv/test/test-tty.c @@ -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. + */ + +#include "uv.h" +#include "task.h" + +TEST_IMPL(tty) { + int r, width, height; + uv_tty_t tty; + uv_loop_t* loop = uv_default_loop(); + + /* + * Not necessarally a problem if this assert goes off. E.G you are piping + * this test to a file. 0 == stdin. + */ + ASSERT(UV_TTY == uv_guess_handle(0)); + + r = uv_tty_init(uv_default_loop(), &tty, 0); + ASSERT(r == 0); + + r = uv_tty_get_winsize(&tty, &width, &height); + ASSERT(r == 0); + + printf("width=%d height=%d\n", width, height); + + /* + * Is it a safe assumption that most people have terminals larger than + * 10x10? + */ + ASSERT(width > 10); + ASSERT(height > 10); + + uv_close((uv_handle_t*)&tty, NULL); + + uv_run(loop); + + return 0; +} diff --git a/src/rt/libuv/test/test-udp-dgram-too-big.c b/src/rt/libuv/test/test-udp-dgram-too-big.c new file mode 100644 index 00000000000..2d172c0640c --- /dev/null +++ b/src/rt/libuv/test/test-udp-dgram-too-big.c @@ -0,0 +1,86 @@ +/* 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 "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &handle_) + +#define CHECK_REQ(req) \ + ASSERT((req) == &req_); + +static uv_udp_t handle_; +static uv_udp_send_t req_; + +static int send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + CHECK_REQ(req); + CHECK_HANDLE(req->handle); + + ASSERT(status == -1); + ASSERT(uv_last_error(uv_default_loop()).code == UV_EMSGSIZE); + + uv_close((uv_handle_t*)req->handle, close_cb); + send_cb_called++; +} + + +TEST_IMPL(udp_dgram_too_big) { + char dgram[65536]; /* 64K MTU is unlikely, even on localhost */ + struct sockaddr_in addr; + uv_buf_t buf; + int r; + + memset(dgram, 42, sizeof dgram); /* silence valgrind */ + + r = uv_udp_init(uv_default_loop(), &handle_); + ASSERT(r == 0); + + buf = uv_buf_init(dgram, sizeof dgram); + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_udp_send(&req_, &handle_, &buf, 1, addr, send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(send_cb_called == 0); + + uv_run(uv_default_loop()); + + ASSERT(send_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-udp-ipv6.c b/src/rt/libuv/test/test-udp-ipv6.c new file mode 100644 index 00000000000..6ff36b32b01 --- /dev/null +++ b/src/rt/libuv/test/test-udp-ipv6.c @@ -0,0 +1,156 @@ +/* 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 "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server \ + || (uv_udp_t*)(handle) == &client \ + || (uv_timer_t*)(handle) == &timeout) + +#define CHECK_REQ(req) \ + ASSERT((req) == &req_); + +static uv_udp_t client; +static uv_udp_t server; +static uv_udp_send_t req_; +static uv_timer_t timeout; + +static int send_cb_called; +static int recv_cb_called; +static int close_cb_called; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + CHECK_HANDLE(handle); + return uv_buf_init(slab, sizeof slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + CHECK_REQ(req); + CHECK_HANDLE(req->handle); + ASSERT(status == 0); + send_cb_called++; +} + + +static void ipv6_recv_fail(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + ASSERT(0 && "this function should not have been called"); +} + + +static void ipv6_recv_ok(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(nread >= 0); + + if (nread) + recv_cb_called++; +} + + +static void timeout_cb(uv_timer_t* timer, int status) { + uv_close((uv_handle_t*)&server, close_cb); + uv_close((uv_handle_t*)&client, close_cb); + uv_close((uv_handle_t*)&timeout, close_cb); +} + + +static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { + struct sockaddr_in6 addr6; + struct sockaddr_in addr; + uv_buf_t buf; + int r; + + addr6 = uv_ip6_addr("::0", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind6(&server, addr6, bind_flags); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, recv_cb); + ASSERT(r == 0); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + buf = uv_buf_init("PING", 4); + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_udp_send(&req_, &client, &buf, 1, addr, send_cb); + ASSERT(r == 0); + + r = uv_timer_init(uv_default_loop(), &timeout); + ASSERT(r == 0); + + r = uv_timer_start(&timeout, timeout_cb, 500, 0); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(send_cb_called == 0); + ASSERT(recv_cb_called == 0); + + uv_run(uv_default_loop()); + + ASSERT(close_cb_called == 3); +} + + +TEST_IMPL(udp_dual_stack) { + do_test(ipv6_recv_ok, 0); + + ASSERT(recv_cb_called == 1); + ASSERT(send_cb_called == 1); + + return 0; +} + + +TEST_IMPL(udp_ipv6_only) { + do_test(ipv6_recv_fail, UV_UDP_IPV6ONLY); + + ASSERT(recv_cb_called == 0); + ASSERT(send_cb_called == 1); + + return 0; +} diff --git a/src/rt/libuv/test/test-udp-send-and-recv.c b/src/rt/libuv/test/test-udp-send-and-recv.c new file mode 100644 index 00000000000..ab47e91c21c --- /dev/null +++ b/src/rt/libuv/test/test-udp-send-and-recv.c @@ -0,0 +1,208 @@ +/* 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 "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; + +static int cl_send_cb_called; +static int cl_recv_cb_called; + +static int sv_send_cb_called; +static int sv_recv_cb_called; + +static int close_cb_called; + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { + static char slab[65536]; + + CHECK_HANDLE(handle); + ASSERT(suggested_size <= sizeof slab); + + return uv_buf_init(slab, sizeof slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + close_cb_called++; +} + + +static void cl_recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards cl_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PONG", buf.base, nread)); + + cl_recv_cb_called++; + + uv_close((uv_handle_t*) handle, close_cb); +} + + +static void cl_send_cb(uv_udp_send_t* req, int status) { + int r; + + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + r = uv_udp_recv_start(req->handle, alloc_cb, cl_recv_cb); + ASSERT(r == 0); + + cl_send_cb_called++; +} + + +static void sv_send_cb(uv_udp_send_t* req, int status) { + ASSERT(req != NULL); + ASSERT(status == 0); + CHECK_HANDLE(req->handle); + + uv_close((uv_handle_t*) req->handle, close_cb); + free(req); + + sv_send_cb_called++; +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + uv_udp_send_t* req; + int r; + + if (nread < 0) { + ASSERT(0 && "unexpected error"); + } + + if (nread == 0) { + /* Returning unused buffer */ + /* Don't count towards sv_recv_cb_called */ + ASSERT(addr == NULL); + return; + } + + CHECK_HANDLE(handle); + ASSERT(flags == 0); + + ASSERT(addr != NULL); + ASSERT(nread == 4); + ASSERT(!memcmp("PING", buf.base, nread)); + + /* FIXME? `uv_udp_recv_stop` does what it says: recv_cb is not called + * anymore. That's problematic because the read buffer won't be returned + * either... Not sure I like that but it's consistent with `uv_read_stop`. + */ + r = uv_udp_recv_stop(handle); + ASSERT(r == 0); + + req = malloc(sizeof *req); + ASSERT(req != NULL); + + buf = uv_buf_init("PONG", 4); + + r = uv_udp_send(req, + handle, + &buf, + 1, + *(struct sockaddr_in*)addr, + sv_send_cb); + ASSERT(r == 0); + + sv_recv_cb_called++; +} + + +TEST_IMPL(udp_send_and_recv) { + struct sockaddr_in addr; + uv_udp_send_t req; + uv_buf_t buf; + int r; + + addr = uv_ip4_addr("0.0.0.0", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT(r == 0); + + r = uv_udp_bind(&server, addr, 0); + ASSERT(r == 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT(r == 0); + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT(r == 0); + + /* client sends "PING", expects "PONG" */ + buf = uv_buf_init("PING", 4); + + r = uv_udp_send(&req, &client, &buf, 1, addr, cl_send_cb); + ASSERT(r == 0); + + ASSERT(close_cb_called == 0); + ASSERT(cl_send_cb_called == 0); + ASSERT(cl_recv_cb_called == 0); + ASSERT(sv_send_cb_called == 0); + ASSERT(sv_recv_cb_called == 0); + + uv_run(uv_default_loop()); + + ASSERT(cl_send_cb_called == 1); + ASSERT(cl_recv_cb_called == 1); + ASSERT(sv_send_cb_called == 1); + ASSERT(sv_recv_cb_called == 1); + ASSERT(close_cb_called == 2); + + return 0; +} diff --git a/src/rt/libuv/uv.gyp b/src/rt/libuv/uv.gyp new file mode 100644 index 00000000000..e32dddd3b5d --- /dev/null +++ b/src/rt/libuv/uv.gyp @@ -0,0 +1,339 @@ +{ + 'targets': [ + { + 'target_name': 'uv', + 'type': '<(library)', + 'include_dirs': [ + 'include', + 'include/uv-private', + 'src/', + ], + 'direct_dependent_settings': { + 'include_dirs': [ 'include' ], + }, + + 'defines': [ + 'HAVE_CONFIG_H' + ], + 'sources': [ + 'include/ares.h', + 'include/ares_version.h', + 'include/uv.h', + 'src/uv-common.c', + 'src/uv-common.h', + 'src/ares/ares__close_sockets.c', + 'src/ares/ares__get_hostent.c', + 'src/ares/ares__read_line.c', + 'src/ares/ares__timeval.c', + 'src/ares/ares_cancel.c', + 'src/ares/ares_data.c', + 'src/ares/ares_data.h', + 'src/ares/ares_destroy.c', + 'src/ares/ares_dns.h', + 'src/ares/ares_expand_name.c', + 'src/ares/ares_expand_string.c', + 'src/ares/ares_fds.c', + 'src/ares/ares_free_hostent.c', + 'src/ares/ares_free_string.c', + 'src/ares/ares_gethostbyaddr.c', + 'src/ares/ares_gethostbyname.c', + 'src/ares/ares_getnameinfo.c', + 'src/ares/ares_getopt.c', + 'src/ares/ares_getopt.h', + 'src/ares/ares_getsock.c', + 'src/ares/ares_init.c', + 'src/ares/ares_ipv6.h', + 'src/ares/ares_library_init.c', + 'src/ares/ares_library_init.h', + 'src/ares/ares_llist.c', + 'src/ares/ares_llist.h', + 'src/ares/ares_mkquery.c', + 'src/ares/ares_nowarn.c', + 'src/ares/ares_nowarn.h', + 'src/ares/ares_options.c', + 'src/ares/ares_parse_a_reply.c', + 'src/ares/ares_parse_aaaa_reply.c', + 'src/ares/ares_parse_mx_reply.c', + 'src/ares/ares_parse_ns_reply.c', + 'src/ares/ares_parse_ptr_reply.c', + 'src/ares/ares_parse_srv_reply.c', + 'src/ares/ares_parse_txt_reply.c', + 'src/ares/ares_private.h', + 'src/ares/ares_process.c', + 'src/ares/ares_query.c', + 'src/ares/ares_rules.h', + 'src/ares/ares_search.c', + 'src/ares/ares_send.c', + 'src/ares/ares_setup.h', + 'src/ares/ares_strcasecmp.c', + 'src/ares/ares_strcasecmp.h', + 'src/ares/ares_strdup.c', + 'src/ares/ares_strdup.h', + 'src/ares/ares_strerror.c', + 'src/ares/ares_timeout.c', + 'src/ares/ares_version.c', + 'src/ares/ares_writev.c', + 'src/ares/ares_writev.h', + 'src/ares/bitncmp.c', + 'src/ares/bitncmp.h', + 'src/ares/inet_net_pton.c', + 'src/ares/inet_net_pton.h', + 'src/ares/inet_ntop.c', + 'src/ares/inet_ntop.h', + 'src/ares/nameser.h', + 'src/ares/setup_once.h', + ], + 'conditions': [ + [ 'OS=="win"', { + 'include_dirs': [ + 'src/ares/config_win32' + ], + 'defines': [ + '_WIN32_WINNT=0x0502', + 'EIO_STACKSIZE=262144', + '_GNU_SOURCE', + ], + 'sources': [ + 'include/uv-private/tree.h', + 'include/uv-private/uv-win.h', + 'src/ares/config_win32/ares_config.h', + 'src/ares/windows_port.c', + 'src/win/async.c', + 'src/win/cares.c', + 'src/win/core.c', + 'src/win/error.c', + 'src/win/fs.c', + 'src/win/fs-event.c', + 'src/win/getaddrinfo.c', + 'src/win/handle.c', + 'src/win/internal.h', + 'src/win/loop-watcher.c', + 'src/win/pipe.c', + 'src/win/process.c', + 'src/win/req.c', + 'src/win/stdio.c', + 'src/win/stream.c', + 'src/win/tcp.c', + 'src/win/tty.c', + 'src/win/threadpool.c', + 'src/win/threads.c', + 'src/win/timer.c', + 'src/win/udp.c', + 'src/win/util.c', + 'src/win/winapi.c', + 'src/win/winapi.h', + 'src/win/winsock.c', + 'src/win/winsock.h', + ], + 'link_settings': { + 'libraries': [ + '-lws2_32.lib', + ], + }, + }, { # Not Windows i.e. POSIX + 'cflags': [ + '-g', + '--std=gnu89', + '-pedantic', + '-Wall', + '-Wextra', + '-Wno-unused-parameter' + ], + 'sources': [ + 'include/uv-private/eio.h', + 'include/uv-private/ev.h', + 'include/uv-private/ngx-queue.h', + 'include/uv-private/uv-unix.h', + 'src/unix/core.c', + 'src/unix/uv-eio.c', + 'src/unix/uv-eio.h', + 'src/unix/fs.c', + 'src/unix/udp.c', + 'src/unix/tcp.c', + 'src/unix/pipe.c', + 'src/unix/tty.c', + 'src/unix/stream.c', + 'src/unix/cares.c', + 'src/unix/error.c', + 'src/unix/process.c', + 'src/unix/internal.h', + 'src/unix/eio/ecb.h', + 'src/unix/eio/eio.c', + 'src/unix/eio/xthread.h', + 'src/unix/ev/ev.c', + 'src/unix/ev/ev_vars.h', + 'src/unix/ev/ev_wrap.h', + 'src/unix/ev/event.h', + ], + 'include_dirs': [ 'src/unix/ev', ], + 'defines': [ + '_LARGEFILE_SOURCE', + '_FILE_OFFSET_BITS=64', + '_GNU_SOURCE', + 'EIO_STACKSIZE=262144' + ], + 'libraries': [ '-lm' ] + }], + [ 'OS=="mac"', { + 'include_dirs': [ 'src/ares/config_darwin' ], + 'sources': [ 'src/unix/darwin.c' ], + 'direct_dependent_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Carbon.framework', + '$(SDKROOT)/System/Library/Frameworks/CoreServices.framework', + ], + }, + 'defines': [ + 'EV_CONFIG_H="config_darwin.h"', + 'EIO_CONFIG_H="config_darwin.h"', + ] + }], + [ 'OS=="linux"', { + 'include_dirs': [ 'src/ares/config_linux' ], + 'sources': [ 'src/unix/linux.c' ], + 'defines': [ + 'EV_CONFIG_H="config_linux.h"', + 'EIO_CONFIG_H="config_linux.h"', + ], + 'direct_dependent_settings': { + 'libraries': [ '-lrt' ], + }, + }], + [ 'OS=="solaris"', { + 'include_dirs': [ 'src/ares/config_sunos' ], + 'sources': [ 'src/unix/sunos.c' ], + 'defines': [ + '__EXTENSIONS__', + '_XOPEN_SOURCE=500', + 'EV_CONFIG_H="config_sunos.h"', + 'EIO_CONFIG_H="config_sunos.h"', + ], + 'direct_dependent_settings': { + 'libraries': [ '-lrt' ], + }, + }], + [ 'OS=="freebsd"', { + 'include_dirs': [ 'src/ares/config_freebsd' ], + 'sources': [ 'src/unix/freebsd.c' ], + 'defines': [ + 'EV_CONFIG_H="config_freebsd.h"', + 'EIO_CONFIG_H="config_freebsd.h"', + ], + }], + ] + }, + + { + 'target_name': 'run-tests', + 'type': 'executable', + 'dependencies': [ 'uv' ], + 'sources': [ + 'test/echo-server.c', + 'test/run-tests.c', + 'test/runner.c', + 'test/runner.h', + 'test/task.h', + 'test/test-async.c', + 'test/test-callback-stack.c', + 'test/test-connection-fail.c', + 'test/test-delayed-accept.c', + 'test/test-fail-always.c', + 'test/test-fs.c', + 'test/test-fs-event.c', + 'test/test-get-currentexe.c', + 'test/test-getaddrinfo.c', + 'test/test-gethostbyname.c', + 'test/test-getsockname.c', + 'test/test-hrtime.c', + 'test/test-idle.c', + 'test/test-list.h', + 'test/test-loop-handles.c', + 'test/test-pass-always.c', + 'test/test-ping-pong.c', + 'test/test-pipe-bind-error.c', + 'test/test-ref.c', + 'test/test-shutdown-eof.c', + 'test/test-spawn.c', + 'test/test-tcp-bind-error.c', + 'test/test-tcp-bind6-error.c', + 'test/test-tcp-close.c', + 'test/test-tcp-write-error.c', + 'test/test-tcp-writealot.c', + 'test/test-threadpool.c', + 'test/test-timer-again.c', + 'test/test-timer.c', + 'test/test-tty.c', + 'test/test-udp-dgram-too-big.c', + 'test/test-udp-ipv6.c', + 'test/test-udp-send-and-recv.c', + ], + 'conditions': [ + [ 'OS=="win"', { + 'sources': [ + 'test/runner-win.c', + 'test/runner-win.h' + ], + 'libraries': [ 'ws2_32.lib' ] + }, { # POSIX + 'defines': [ '_GNU_SOURCE' ], + 'ldflags': [ '-pthread' ], + 'sources': [ + 'test/runner-unix.c', + 'test/runner-unix.h', + ] + }] + ], + 'msvs-settings': { + 'VCLinkerTool': { + 'SubSystem': 1, # /subsystem:console + }, + }, + }, + + { + 'target_name': 'run-benchmarks', + 'type': 'executable', + 'dependencies': [ 'uv' ], + 'sources': [ + 'test/benchmark-ares.c', + 'test/benchmark-getaddrinfo.c', + 'test/benchmark-list.h', + 'test/benchmark-ping-pongs.c', + 'test/benchmark-pound.c', + 'test/benchmark-pump.c', + 'test/benchmark-sizes.c', + 'test/benchmark-spawn.c', + 'test/benchmark-udp-packet-storm.c', + 'test/dns-server.c', + 'test/echo-server.c', + 'test/run-benchmarks.c', + 'test/runner.c', + 'test/runner.h', + 'test/task.h', + ], + 'conditions': [ + [ 'OS=="win"', { + 'sources': [ + 'test/runner-win.c', + 'test/runner-win.h', + ], + 'libraries': [ 'ws2_32.lib' ] + }, { # POSIX + 'defines': [ '_GNU_SOURCE' ], + 'ldflags': [ '-pthread' ], + 'sources': [ + 'test/runner-unix.c', + 'test/runner-unix.h', + ] + }] + ], + 'msvs-settings': { + 'VCLinkerTool': { + 'SubSystem': 1, # /subsystem:console + }, + }, + } + ] +} + + diff --git a/src/rt/libuv/vcbuild.bat b/src/rt/libuv/vcbuild.bat new file mode 100644 index 00000000000..bccefff9dcd --- /dev/null +++ b/src/rt/libuv/vcbuild.bat @@ -0,0 +1,93 @@ +@echo off + +cd %~dp0 + +if /i "%1"=="help" goto help +if /i "%1"=="--help" goto help +if /i "%1"=="-help" goto help +if /i "%1"=="/help" goto help +if /i "%1"=="?" goto help +if /i "%1"=="-?" goto help +if /i "%1"=="--?" goto help +if /i "%1"=="/?" goto help + +@rem Process arguments. +set config= +set target=Build +set noprojgen= +set nobuild= +set run= + +:next-arg +if "%1"=="" goto args-done +if /i "%1"=="debug" set config=Debug&goto arg-ok +if /i "%1"=="release" set config=Release&goto arg-ok +if /i "%1"=="test" set run=run-tests.exe&goto arg-ok +if /i "%1"=="bench" set run=run-benchmarks.exe&goto arg-ok +if /i "%1"=="clean" set target=Clean&goto arg-ok +if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok +if /i "%1"=="nobuild" set nobuild=1&goto arg-ok +:arg-ok +shift +goto next-arg +:args-done + +if not "%config%"=="" goto project-gen +if "%run%"=="run-tests.exe" set config=Debug& goto project-gen +if "%run%"=="run-benchmarks.exe" set config=Release& goto project-gen +set config=Debug + +:project-gen +@rem Skip project generation if requested. +if defined noprojgen goto msbuild + +@rem Generate the VS project. + +if exist build\gyp goto have_gyp +echo svn co http://gyp.googlecode.com/svn/trunk@983 build/gyp +svn co http://gyp.googlecode.com/svn/trunk@983 build/gyp +if errorlevel 1 goto gyp_install_failed +goto have_gyp + +:gyp_install_failed +echo Failed to download gyp. Make sure you have subversion installed, or +echo manually install gyp into %~dp0build\gyp. +goto exit + +:have_gyp +python gyp_uv +if errorlevel 1 goto create-msvs-files-failed +if not exist uv.sln goto create-msvs-files-failed +echo Project files generated. + +:msbuild +@rem Skip project generation if requested. +if defined nobuild goto run + +if not defined VCINSTALLDIR echo Build skipped. To build, this file needs to run from VS cmd prompt.& goto run + +@rem Build the sln with msbuild. +msbuild uv.sln /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +if errorlevel 1 goto exit + +:run +@rem Run tests if requested. +if "%run%"=="" goto exit +if not exist %config%\%run% goto exit +echo running '%config%\%run%' +%config%\%run% +goto exit + +:create-msvs-files-failed +echo Failed to create vc project files. +goto exit + +:help +echo vcbuild.bat [debug/release] [test/bench] [clean] [noprojgen] [nobuild] +echo Examples: +echo vcbuild.bat : builds debug build +echo vcbuild.bat test : builds debug build and runs tests +echo vcbuild.bat release bench: builds release build and runs benchmarks +goto exit + +:exit diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index add0e8da455..39f99b9b190 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -1,10 +1,11 @@ -#include "rust_internal.h" -#include "rust_upcall.h" // Disable libev prototypes - they will make inline compatability functions // which are unused and so trigger a warning in gcc since -Wall is on. #define EV_PROTOTYPES 0 #include "uv.h" +#include "rust_internal.h" +#include "rust_upcall.h" + #ifdef __GNUC__ #define LOG_CALLBACK_ENTRY(p) \ LOG(iotask, callback, "> IO CALLBACK %s %p", __FUNCTION__, p) @@ -37,22 +38,7 @@ struct socket_data : public task_owned { if (reader) reader->deref(); } -}; -struct request : public uv_req_t, public task_owned { - rust_task *task; - // Used for notifying about completion of connections, writes - rust_chan *chan; - request(socket_data *data, rust_chan *chan, - void (*cb)(request *req, int status)) { - uv_req_init(this, (uv_handle_t*)&data->socket, (void*(*)(void*))cb); - this->data = data; - this->task = data->task; - this->chan = chan->clone(iotask); - } - socket_data *socket() { - return (socket_data*)data; - } void send_result(void *data) { chan->send(&data); chan->deref(); @@ -60,6 +46,9 @@ struct request : public uv_req_t, public task_owned { } }; +struct req_connect : public uv_connect_t, public task_owned {}; +struct req_write : public uv_write_t, public task_owned {}; + extern "C" CDECL void aio_close_socket(rust_task *task, socket_data *); static uv_idle_s idle_handler; @@ -72,15 +61,14 @@ static void idle_callback(uv_idle_t* handle, int status) { extern "C" CDECL void aio_init(rust_task *task) { LOG_UPCALL_ENTRY(task); iotask = task; - uv_init(); - uv_idle_init(&idle_handler); + uv_idle_init(uv_default_loop(), &idle_handler); uv_idle_start(&idle_handler, idle_callback); } extern "C" CDECL void aio_run(rust_task *task) { LOG_UPCALL_ENTRY(task); idle_handler.data = task; - uv_run(); + uv_run(uv_default_loop()); } void nop_close(uv_handle_t* handle) {} @@ -93,9 +81,10 @@ extern "C" CDECL void aio_stop(rust_task *task) { static socket_data *make_socket(rust_task *task, rust_chan *chan) { socket_data *data = new (task, "make_socket") socket_data; if (!data || - uv_tcp_init(&data->socket)) { + uv_tcp_init(uv_default_loop(), &data->socket)) { return NULL; } + data->socket.data = data; data->task = task; // Connections from servers don't have a channel if (chan) { @@ -110,7 +99,7 @@ static socket_data *make_socket(rust_task *task, rust_chan *chan) { // We allocate the requested space + rust_vec but return a pointer at a // +rust_vec offset so that it writes the bytes to the correct location. -static uv_buf_t alloc_buffer(uv_stream_t *socket, size_t suggested_size) { +static uv_buf_t alloc_buffer(uv_handle_t *socket, size_t suggested_size) { LOG_CALLBACK_ENTRY(socket); uv_buf_t buf; size_t actual_size = suggested_size + sizeof (rust_vec); @@ -148,7 +137,7 @@ static void read_progress(uv_stream_t *socket, ssize_t nread, uv_buf_t buf) { data->reader->send(v); } -static void new_connection(uv_handle_t *socket, int status) { +static void new_connection(uv_stream_t *socket, int status) { LOG_CALLBACK_ENTRY(socket); socket_data *server = (socket_data*)socket->data; I(server->task->sched, (uv_tcp_t*)socket == &server->socket); @@ -176,7 +165,7 @@ extern "C" CDECL socket_data *aio_serve(rust_task *task, const char *ip, if (!server) goto oom; if (uv_tcp_bind(&server->socket, addr) || - uv_tcp_listen(&server->socket, 128, new_connection)) { + uv_listen((uv_stream_t*)&server->socket, 128, new_connection)) { aio_close_socket(task, server); chan->deref(); return NULL; @@ -198,7 +187,7 @@ static void free_socket(uv_handle_t *handle) { // reading and should send the close notification. if (data->reader) { if (data->reader->is_associated()) { - uv_buf_t buf = alloc_buffer((uv_stream_t*)socket, 0); + uv_buf_t buf = alloc_buffer((uv_handle_t*)socket, 0); read_progress((uv_stream_t*)socket, -1, buf); uv_read_stop((uv_stream_t*)socket); } @@ -214,9 +203,7 @@ static void free_socket(uv_handle_t *handle) { extern "C" CDECL void aio_close_socket(rust_task *task, socket_data *client) { LOG_UPCALL_ENTRY(task); - if (uv_close((uv_handle_t*)&client->socket, free_socket)) { - task->fail(); - } + uv_close((uv_handle_t*)&client->socket, free_socket); } extern "C" CDECL void aio_close_server(rust_task *task, socket_data *server, @@ -240,33 +227,35 @@ extern "C" CDECL bool aio_is_null_client(rust_task *task, return server == NULL; } -static void connection_complete(request *req, int status) { +static void connection_complete(uv_connect_t *req, int status) { LOG_CALLBACK_ENTRY(socket); - socket_data *client = req->socket(); - req->send_result(client); - delete req; + socket_data *client = (socket_data*)req->data; + client->send_result(client); + free(req); } extern "C" CDECL void aio_connect(rust_task *task, const char *host, int port, chan_handle *_chan) { LOG_UPCALL_ENTRY(task); rust_chan *chan = task->get_chan_by_handle(_chan); + uv_connect_t *req = NULL; if(!chan) return; struct sockaddr_in addr = uv_ip4_addr(const_cast(host), port); - request *req; socket_data *client = make_socket(iotask, NULL); if (!client) { goto oom_client; } - req = new (client->task, "connection request") - request(client, chan, connection_complete); + req = (uv_connect_t*)client->task->malloc( + sizeof(uv_connect_t), "connection request"); if (!req) { goto oom_req; } - if (0 == uv_tcp_connect(req, addr)) { + req->data = client; + if (0 == uv_tcp_connect(req, &client->socket, addr, connection_complete)) { chan->deref(); return; } + free(req); oom_req: aio_close_socket(task, client); oom_client: @@ -275,11 +264,12 @@ oom_client: return; } -static void write_complete(request *req, int status) { +static void write_complete(uv_write_t *req, int status) { LOG_CALLBACK_ENTRY(socket); bool success = status == 0; - req->send_result(&success); - delete req; + socket_data *client = (socket_data*)req->data; + client->send_result(&success); + free(req); } extern "C" CDECL void aio_writedata(rust_task *task, socket_data *data, @@ -287,6 +277,7 @@ extern "C" CDECL void aio_writedata(rust_task *task, socket_data *data, chan_handle *_chan) { LOG_UPCALL_ENTRY(task); rust_chan *chan = task->get_chan_by_handle(_chan); + uv_write_t *req; if(!chan) return; // uv_buf_t is defined backwards on win32... @@ -297,13 +288,14 @@ extern "C" CDECL void aio_writedata(rust_task *task, socket_data *data, uv_buf_t buffer = { buf, size }; #endif - request *req = new (data->task, "write request") - request(data, chan, write_complete); + req = (uv_write_t*)data->task->malloc(sizeof(uv_write_t), "write request"); if (!req) { goto fail; } - if (uv_write(req, &buffer, 1)) { - delete req; + req->data = data; + if (uv_write(req, (uv_stream_t*)&data->socket, &buffer, 1, + write_complete)) { + free(req); goto fail; } chan->deref();